Advanced C++ Stateful Template MetaProgramming Library
#include <cassert>
#include <iostream>
#include <smp.hpp>
struct W
{
int rank;
std::string key;
std::string val;
};
struct X
{
float f;
std::string s;
};
struct Y
{
int i;
double d;
char c;
X x;
};
int main(int argc, char* argv[])
{
// reflect part
X x { 15.32f, "template" };
Y y { 2048, 12.14, '+', { 27.85f, "smp" } };
x.*smp::get<0, X>() = 30.18f;
smp::get<1>(smp::get<X>(y)) = "Stateful";
assert(x.f == 30.18f);
assert(y.x.s == "Stateful");
assert(smp::index(&X::s) == 1);
assert(smp::index(&Y::x) == 3);
static_assert(smp::tuple_size_v<X> == 2);
static_assert(smp::tuple_size_v<Y> == 4);
static_assert(std::is_same_v<smp::tuple_element_t<0, X>, float>);
static_assert(std::is_same_v<smp::tuple_element_t<1, Y>, double>);
auto f = smp::tie_fuple(x);
auto t = smp::tie_tuple(y);
smp::get<std::string&>(f) = "Dark Army";
std::get<X&>(t) = { 72.68f, "Programs" };
assert(x.s == "Dark Army");
assert(y.x.f == 72.68f);
assert(y.x.s == "Programs");
x.*smp::get<0, X>() = 48.65f;
x.*smp::get<1, X>() = "transformer";
assert(smp::get<1>(f) == "transformer");
assert(smp::get<float>(f) == 48.65f);
y.*smp::get<3, Y>() = { 25.65f, "Template" };
assert(std::get<3>(t).f == 25.65f);
assert(std::get<X&>(t).s == "Template");
W w1;
W w2;
std::stringstream ss1;
std::stringstream ss2;
std::string str1 = "100, \"Modern C++ Template\", \"MetaProgramming Library\"";
std::string str2 = "101, \"Modern C++ Stateful\", \"MetaProgramming Framework\"";
ss1 << str1;
ss1 >> smp::io(w1);
ss2 << str2;
ss2 >> smp::io(w2);
std::cout << smp::io(w1) << std::endl;
std::cout << smp::io(w2) << std::endl;
/* outputs
100, "Modern C++ Template", "MetaProgramming Library"
101, "Modern C++ Stateful", "MetaProgramming Framework"
*/
assert(w1.rank == 100);
assert(w2.rank == 101);
assert(w1.key == "Modern C++ Template");
assert(w2.key == "Modern C++ Stateful");
assert(w1.val == "MetaProgramming Library");
assert(w2.val == "MetaProgramming Framework");
std::cout << "smp::lt " << smp::lt(w1, w2) << std::endl;
std::cout << "smp::le " << smp::le(w1, w2) << std::endl;
std::cout << "smp::ne " << smp::ne(w1, w2) << std::endl;
std::cout << "smp::eq " << smp::eq(w1, w2) << std::endl;
std::cout << "smp::ge " << smp::ge(w1, w2) << std::endl;
std::cout << "smp::gt " << smp::gt(w1, w2) << std::endl;
auto f1 = smp::tie_fuple(w1);
auto f2 = smp::tie_fuple(w2);
auto t1 = smp::tie_tuple(w1);
auto t2 = smp::tie_tuple(w2);
std::cout << "f1 < f2 " << (f1 < f2) << std::endl;
std::cout << "f1 <= f2 " << (f1 <= f2) << std::endl;
std::cout << "f1 != f2 " << (f1 != f2) << std::endl;
std::cout << "f1 == f2 " << (f1 == f2) << std::endl;
std::cout << "f1 >= f2 " << (f1 >= f2) << std::endl;
std::cout << "f1 > f2 " << (f1 > f2) << std::endl;
std::cout << "t1 < t2 " << (t1 < t2) << std::endl;
std::cout << "t1 <= t2 " << (t1 <= t2) << std::endl;
std::cout << "t1 != t2 " << (t1 != t2) << std::endl;
std::cout << "t1 == t2 " << (t1 == t2) << std::endl;
std::cout << "t1 >= t2 " << (t1 >= t2) << std::endl;
std::cout << "t1 > t2 " << (t1 > t2) << std::endl;
int fr = 0;
int tr = 0;
std::string fk;
std::string tk;
std::string fv;
std::string tv;
smp::ref_fuple(fr, fk, fv) = w1;
smp::ref_tuple(tr, tk, tv) = w2;
assert(fr == 100);
assert(tr == 101);
assert(fk == "Modern C++ Template");
assert(tk == "Modern C++ Stateful");
assert(fv == "MetaProgramming Library");
assert(tv == "MetaProgramming Framework");
auto print_backward = []<typename... Args>(Args&&... args)
{
const char* sep = " ";
((std::cout << args << sep, sep) = ... = " ");
std::cout << std::endl;
};
smp::for_each(print_backward, w1);
std::cout << std::endl;
smp::for_each(print_backward, w2);
std::cout << std::endl;
auto mptrs_backward = []<typename T>(T&& t)
{
return [&]<typename... Args>(Args&&... args)
{
const char* sep = " ";
((std::cout << t.*args << sep, sep) = ... = " ");
std::cout << std::endl;
};
};
smp::for_mptr(mptrs_backward(w1), w1);
std::cout << std::endl;
smp::for_mptr(mptrs_backward(w2), w2);
std::cout << std::endl;
smp::apply(print_backward, w1);
smp::apply(print_backward, w2);
std::cout << std::endl;
smp::zip(print_backward, w1, w2, x, y);
// marshal and unmarshal a structure
std::string xs = smp::marshal(x);
std::string ys = smp::marshal(y);
X x0 = smp::unmarshal<X>(xs);
Y y0 = smp::unmarshal<Y>(ys);
assert(smp::eq(x, x0));
assert(y.i == y0.i);
assert(y.d == y0.d);
assert(y.c == y0.c);
assert(smp::eq(y.x, y0.x));
// marshal and unmarshal with allocated structure
X x1;
std::string s0;
smp::marshal(s0, x);
smp::unmarshal(s0, x1);
assert(smp::eq(x1, x));
Y y1;
std::string s1 = "prefix";
size_t prelen = s1.length();
size_t length = s1.length();
smp::marshal(s1, y);
smp::unmarshal(length, s1, y1);
assert(y.i == y1.i);
assert(y.d == y1.d);
assert(y.c == y1.c);
assert(smp::eq(y.x, y1.x));
assert(length == s1.length());
assert(smp::marshal(y).length() == length - prelen);
auto y2 = smp::unmarshal<Y>(s1.substr(prelen));
assert(y.i == y2.i);
assert(y.d == y2.d);
assert(y.c == y2.c);
assert(smp::eq(y.x, y2.x));
// marshal and unmarshal a smp::fuple or std::tuple
std::string ws1 = smp::marshal(f1);
std::string ws2 = smp::marshal(t2);
auto w3 = smp::unmarshal<W>(ws1);
auto w4 = smp::unmarshal<W>(ws2);
assert(smp::eq(w1, w3));
assert(smp::eq(w2, w4));
auto f0 = smp::make_fuple(1, 2.0f, std::string("marshal"), 'X', std::make_tuple(100.3, std::string("Unmarshal")));
auto t0 = std::make_tuple(2, 3.0f, std::string("Marshal"), 'Y', smp::make_fuple(200.3, std::string("unmarshal")));
std::string fs0 = smp::marshal(f0);
std::string ts0 = smp::marshal(t0);
auto f3 = smp::unmarshal<decltype(f0)>(fs0);
auto t3 = smp::unmarshal<decltype(t0)>(ts0);
assert(f3 == f0);
assert(t3 == t0);
assert(std::get<std::string>(smp::get<4>(f3)) == std::get<1>(smp::get<4>(f0)));
assert(smp::get<1>(std::get<4>(t3)) == smp::get<std::string>(std::get<4>(t0)));
// indexer part
static_assert(smp::next<>() == 0);
static_assert(smp::next<>() == 1);
static_assert(smp::next<>() == 2);
static_assert(smp::next<>() == 3);
static_assert(std::is_same_v<smp::index_sequence_for<>, std::index_sequence<>>);
static_assert(std::is_same_v<smp::index_sequence_for<int, float, char, int>, std::index_sequence<0, 1, 2, 3>>);
static_assert(std::is_same_v<smp::take<>, smp::lists<void, void, void, void, int, float, char, int>>);
smp::clear<>();
smp::push_front<int>();
smp::push_back<char>();
static_assert(std::is_same_v<smp::take<>, smp::lists<int, char>>);
static_assert(smp::next<>() == 11);
static_assert(smp::next<>() == 12);
return 0;
}smp is a stateful metaprogramming library, which is header-only, extensible and modern C++ oriented.
It exhibits a form of stateful metaprogramming of compile time type list, index sequence generator and provides many powerful algorithms for manipulating structure elements, marshaling and unmarshaling a structure, smp::fuple or std::tuple.
smp is mainly consist of three parts:
- fuple A flat tuple implemented with multiple inheritance is a drop-in replacement for std::tuple
- indexer A compile time type list and index sequence generator with queryable type states embeded in it
- reflect A reflection, marshaling and unmarshaling library enable you to manipulate structure elements by index or type and provides many std::tuple like methods
The library relies on a C++20 compiler and standard library, but nothing else is required.
More specifically, smp requires a compiler/standard library supporting the following C++20 features (non-exhaustively):
- concepts
- lambda templates
- All the C++20 type traits from the <type_traits> header
smp is header-only. To use it just add the necessary #include line to your source files, like this:
#include <smp.hpp>To build the example with cmake, cd to the root of the project and setup the build directory:
mkdir build
cd build
cmake ..Make and install the executables:
make -j4
make install
The executables are now located at the bin directory of the root of the project.
The example can also be built with the script build.sh, just run it, the executables will be put at the /tmp directory.
Please see example.
smp is licensed as Boost Software License 1.0.