/pse_lib

Parse is a helpful open-source library for C++. Consisting of many features that are part of the C++ Standard Library. (!¬)

Primary LanguageC++MIT LicenseMIT

Parse

!¬)

Total Number Of Lines: 3100 Last Checked: 12/11/2022 15:42 PM London.

Parse is a helpful open-source library for C++. Consisting of many features that are part of the C++ Standard Library Here are some examples:

A pse::vector is just like a std::vector, consisting of all of the same functionality.

template<typename T>
struct Vector{};

Tuple is again just like the std::tuple with all of the functionality.

template<typename T, typename... Ts>
struct Tuple{};

But this one is currently in progress. I am currently now making one that doesn't increase in size but decreases in size only, so it has part of the functionality of a vector but not the push_back functionality part. It will be called DecreasingTuple

template<typename T, typename... Ts>
struct TupleLike
{
  template<typename T>
  auto push_back(B b);
  
  auto operator[](int i);
}; // Just Tuple combined with a vector

Notice the operator[], it is novel, but bear in mind it only works for tuple of the same types. This is because in order to have varying return types it has to be done at comile time, and passing an arguement to a function like this: foo(int x) is not done at compile time, therefore it will always return the type of the first type in the parameter pack!

swap is also just like std::swap. But a bit more basic.

template<typename T>
auto swap(T& a, T& b)
{
  // Implementation
}

There is also swap for pse::Iterator using pse::Itertor.get_ref and swapping like that. so if you had: pse::Iterator<int> it1{int42pointer}; and pse::Iterator<T> it2{int24pointer}; and then we called pse::swap that this time is implemented in the Algo.cpp header file, and did std::cout << it1.get_val(); and std::cout << it2.get_val(); the output would be 2442. Notice this is because we didn't output \n. It looks the same as pse::swap but the generic version.

// Algo.cpp
template<typename T>
auto swap(Iterator<T>& it)
{
  // swaps the two values
}

Others also include:

pse::Range:

This is just a structure like a Initializer_List in that it stores data as a list with member functions. But it can also hold different types of types like int and float but it is not used for this purpose it is used for being like an Initialiser_List. This Range structure also has member functions, more are still to be implemented but the major one for now is pse::Range::Take(int a, int b) it takes two integers and returns the bit of the array in between those two indexes. There is not any error checking yet but will be soon.

template<typename T, typename... Ts>
struct Range
{
  // Implemenetation.
};

There is also pse::Iterator which looks like the following:

template<typename T>
struct Iterator
{
  auto operator++();
  auto operator--();
  auto operator+=();
  auto operator-=();
  
  auto get();
  auto get_val();
  decltype(auto) get_ref();
  
  auto get_Increment();
  auto get_Dectrement();
  
  auto get_PlusEquals();
  auto get_MinusEquals();
  
  auto begin();
  auto end();
  
  int size;
};

It is just a safer way to use T* in that you can increment: ++, decrement: -- but it is just better, there are set member functions get and get_val and not accidently using a pointer rather than the value conatained beneath the pointer. It also has the size member variable. pse::Iterator.begin() return the value datamember because that is the start. Also pse::Iterator.end() returns a default initialized pse::Iterator<T> like this return pse::Iterator<T>{};. We do this because we are using a higher level iterator and pse::Iterator<T>{} is a good sentinel value.

Also in the Algo.cpp header file, there are many algorithms:

template<typename Callable>
pse::Invoke(Callable& c)
{
  return c();
}

It is just like std::invoke but oviously in the pse library. It just returns the invoked callable passed in. It can be a lambda expression: [](){} a struct that has the operator() like this struct s{auto operator();} and that would work. It is a very simple function.

There is also pse::OnAll which takes an Iterator and a lamba and calls that lambda on all of the elemts:

template<typename T, typename Callable>
auto OnAll(Iterator<T>& it, Callable& c)
{
  // calls the lamdba on all of the it elements in the Iterator.
  // Only works if the constructor was passed a size.
}

pse::OnAll acts just like pse::for_each. There is no error handling for this though so if you pass in a lambda that takes an pse::StringList and you pass an Iterator<T> of type int you will just get an error. So use the power to pass in what you want wisely!

There is also pse::Sort it sorts all the elements in a pse::Iterator<T> by using the pse::swap overload for pse::Iterator. Its implementation looks like this:

template<typename T>
auto Sort(Iterator<T>& it)
{
  // sorts by increasing value.
}

But there is also an overload that takes a lambda/callable to use on pse::Sort. It looks like this:

template<typename T, typename Callable>
auto Sort(Iterator<T>& it)
{
  // sort based on what value the callable/lambda returns.
  // when using this function the lambda/callable has to return a bool value.
}

pse::pack_size is a way to find the size of a parameter pack at compile time. It works a bit like a pse::Tuple in that it stores it's elements like pse::Tuple<Ts...> other; but it is for finding the size of a parameter pack instead. Say you have a parameter pack of int, double, bool, char and we didn't know the size of this parameter pack, we could use pse::pack_size by doing pse::pack_size<int, double, bool, char>::value and that will give us the value of 4. An example implementation looks like this:

template<typename T, typename... Ts>
struct pack_size
{
};

template<typename T>
struct pack_size<T>
{
};

It is very simple, and all you have to do to access to size is to get the static datamember: value. There is also a container called pse::Bitmap, it consists of a collection of datamembers that are aranged like this:

	[0, 0], [1, 0], [2, 0], [3, 0], [4, 0], [5, 0]
	
	[0, 1], [1, 1], [2, 1], [3, 1], [4, 1], [5, 1]
	
	[0, 2], [1, 2], [2, 2], [3, 2], [4, 2], [5, 2]
	
	[0, 3], [1, 3], [2, 3], [3, 3], [4, 3], [5, 3]
	
	[0, 4], [1, 4], [2, 4], [3, 4], [4, 4], [5, 4]
	
	[0, 5], [1, 5], [2, 5], [3, 5], [4, 5], [5, 5]

pse::Bitmap's implementation looks a bit like this:

struct Bitmap
{
  // all of the usual constructors +
  template<typename T, typename... Ts>
  Bitmap(T t, Ts... ts);
  
  int m_bitmap[1][1];
};

It's constructor is very simple to use, you can simply pass in pse::Down{3}, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14 and the pse::Bitmap will end up looking like this:

                 1, 6, 11
		 2, 7, 12
		 3, 8, 13
		 4, 9, 14
		 5, 10

We pass in pse::Down{} this tells us how many numbers we want across, in this case we passed in three, so it has three numbers down for example: 1, 2, 3, 4, 5 and the rest depends on how many numbers you passed in and the value of the value data member of pse::Down. The pse::Down shows us that we want pse::Down::value going down and the rest depends on what parameters we have passed in.

In the Traits.cpp header file, there are many useful classes/structs that are all just instances of meta-programming. For example pse::Is_Type, it's implementation looks like this:

template<typename T>
struct Is_Type
{
};

pse::Is_Same compares two types to see if they are the same using partial template specialization. An example implementation looks like this:

template<typename A,  typename B>
struct Is_Same
{
};

There is also pse::Add_Const which just adds const to a type passed in, an example implementation looks like this:

template<typename T>
struct Add_Const
{
};

There is also a class/struct called pse::Remove_Const which just removes the const from a value (only if the type passed in is const). An example implementation looks like this:

template<typename T>
struct Remove_Const
{
};

There is also the same as for pse::Add_Const and pse::Remove_Const, there is pse::Add_Ref and pse::Remove_Ref. An example implementation of pse::Add_Ref looks like this:

template<typename T>
struct Add_Ref
{
};

And an example implemenatation of pse::Remove_Ref looks like this:

template<typename T>
struct Remove_Ref
{
};

There is also pse::Add_Pointer which adds a pointer to a given type. An example implementation of pse::Add_Pointer looks like this:

template<typename T>
struct Add_Pointer
{
};

Also you can check if a type is a pointer by doing this:

auto x = pse::Is_Same<pse::Add_Pointer<T>::value, T>::value;

To check wheather adding a pointer to an already pointer is the same type. Or you can just do:

pse::Is_Pointer<T>::value;

An example implementation of pse::Is_Pointer is:

template<typename T>
struct Is_Pointer
{
};

Also there is pse::GeneratorSame which is just a specialised pse::Generator for a gererator of the same type. It still has the same interface as pse::Generator but just small changes in the internal mechanisms. An example implementation looks like this:

template<auto T, auto... Ts>
struct GeneratorSame
{
  TupleCTAD<T, Ts...> types{};
  auto operator()();
};

So, as stated before the interface is just like the one seen in pse::Generator, so for more information look at the details of the pse::Generator implemenatation. The only difference between the two is that pse::GeneratorSame has a different internal mechanism of operator()().

There are also lots of other algorithms in the Algo.cpp header file. One example is pse::find it just takes a data structure that has the operator[] and takes something to find. If it finds that thing in the data structure it returns the index that it was found at, if nothing was found then 0 will be retuned. An example implementation of pse::find looks like this:

auto find(auto& a, auto& ToFind)
{
 // Implemenatation.
}

There is also pse::find_if which finds an element in a data structure if it meets the requirement of the callable passed in. An example implementation looks like this:

auto find_if(auto& a, auto& Callable)
{
  // Implemenatation.
}

If the callable returns true with a given index of the data structure, that index is returned otherwise 0 is returned, to symbol that no value that met the condition of the Callable(something with operator()) evaluated to true in the data structure.

There is also pse::reverse which takes a given container a and reverses the elements in that array. The implementation looks a bit like this:

auto reverse(auto& a)
{
  // reverse the elements in the array.
}

So having a given input: 1, 2, 3, 4, when passed to the reverse function, pse::reverse it will now look like: 4, 3, 2, 1. Which is succesfully reversed! But the container that stores the elements it has to have operator[] and the types stored have to support operator= and those are mostly all of the constraints!

pse::CodeMeaning contained in the CodeMeaning.cpp header file has lots of different if's and else if's in order to find the write error code, here is a collection of the error codes and their values.

026: Out Of Bounds Access.
054: Unvalid Type

The pse Library blog! https://rshepherdcpp.wixsite.com/none/post/the-pse-library-new-idiom

SonarLink: https://sonarcloud.io/project/configuration?id=robertshepherdcpp_pse&analysisMode=GitHubActions