mirrow is a TMP(template meta programming) utility framework in C++17. It aimed to make some utility to help programmer do TMP easier. Referenced meta and ponder.
Nowadays, mirrow has these parts:
util: some common utilitiessrefl: static reflection frameworkdrefl: dynamic reflection frameworkserd: a serialize framework based on reflection(serial withdrefl&srefl) with TOML
util(utility) has some convenient utility to do TMP: mirrow/tree/main/
type_list: compile-time type list. 🔬 unittestfunction_traits: compile-time function info trait. 🔬 unittestvariable_traits: compile-time variable info trait. 🔬 unittestconst_str: compile-time string. 🔬 unittest
static reflection framework. 🔬 do reflect unittest, get reflected info unittest
To reflect your class, you must do like this:
// your class class Foo final { public: void foo() {} void foo(int) {} void foo(float) {} void another() {} int value_1 = 1; int value_2 = 2; }; // include srefl_begin.hpp #include "mirrow/srefl/srefl_begin.hpp" // do your reflection srefl_class(Foo, ctors() fields( field(static_cast<void(Foo::*)(void)>(&Foo::foo)), field(static_cast<void(Foo::*)(int)>(&Foo::foo)), field(static_cast<void(Foo::*)(float)>(&Foo::foo)), field(&Foo::another), field(&Foo::value_1), field(&Foo::value_2) ) ) // include srefl_end.hpp #include "mirrow/srefl/srefl_end.hpp"srefl_begin.hpp provide a bunch of macros to help you regist your class. And srefl_end.hpp will #undef these macros to avoid pollute your codes.
Then, use srefl_class(<your class>, ...) to start regist your class. use ctors() to regist constructors(optional), use fields(...) to start regist member/static variable/functions.
After reflect, you can use auto refl = reflect<Foo>(); to get reflected informations. And visit member variables:
refl.visit_member_variables([&vars](auto&& value) { vars.push_back(value.name()); });visit function/static fields is WIP, it is easy to implement but currently I don't need them
dynamic reflection framework.
any is similar to std::any, but support ownership 🔬unittest
any has 3 ownership(defined in any::access_type):
Null: don't contain dataConstRef: const reference to a valueRef: mutable reference to a valueCopy: the data's ownership is any itself, when any destruct, data will destruct together
use any_make_xxx to create an any from ownership:
int a = 123; auto cref = mirrow::drefl::any_make_constref(a);// make a const reference auto ref = mirrow::drefl::any_make_ref(a);// make a reference auto new_value = mirrow::drefl::any_make_copy(a); // copy a to any inner dataand use member function constref(), ref(), copy(), steal() to translate ownership.
use try_cast() & try_cast_const() to cast any to a determined type. use try_cast() on ConstRef any will throw a bad_any_access exception and return nullptr.
factory is where you reflect your type:microscope:unittest
register your class by:
struct Person { std::string name; float height; const bool hasChild; const Person* couple; }; // in main(): mirrow::drefl::factory<Person>::instance() .regist("Person") .property("name", &Person::name) .property("height", &Person::height) .property("hasChild", &Person::hasChild) .property("couple", &Person::couple);or register your enum by:
enum class MyEnum { Value1 = 1, Value2 = 2, Value3 = 3, }; // in main(): auto& inst = mirrow::drefl::factory<MyEnum>::instance() .regist("MyEnum") .add("Value1", MyEnum::Value1) .add("Value2", MyEnum::Value2) .add("Value3", MyEnum::Value3);then use
auto info = mirrow::drefl::typeinfo<Person>();to get registered type information;
NOTE: currently we don't support register member function.
there are may type information you can access(by member functionas_xxx()):
enum_info: enumeratesnumeric: numerics(int,float,char...)boolean: booleanstring:std::stringorstd::string_view(may addconst char*support later)pointer: pointers likeT*,T* const,T**...array:std::vector,std::array,std::list,T[N]class: other classes
future support:
map:std::unordered_map,std::mapset:std::unordered_set,std::setoptional:std::optionalsmart points:std::unique_ptr.std::shared_ptrpair:std::pair
A serialize/deserialize tools based on dynamic/static reflection.
serd provide two serialize/deserialize method: dynamic and static, which need you use dynamic/static reflection to provide type info first.
dynamic reflection based serialize 🔬 unittest static reflection based serialize 🔬 unittest
After reflected type, you can do serialize like:
type instance; // create an instance // use static reflection based serialize toml::table tbl; mirrow::serd::srefl::serialize(instance, tbl); // use static reflection based deserialize mirrow::serd::srefl::deserialize(tbl, instance); // convert instance to any to prepare serialize mirrow::drefl::reference_any data{instance}; // use dynamic reflection based serialize toml::table tbl = mirrow::serd::drefl::serialize(data); // use dynamic reflection based deserialize mirrow::serd::drefl::deserialize(tbl, data);If you don't know which toml node would be serialize/deserialize, you can use mirrow::serd::srefl::serialize_destination_type_t<your-type> to get the type.
all serialize and deserialize function will iterate all member fields in your type info and [de]serialize them. If field not exists when deserialize, it will log and ignore this field.
There are some inner-support type:
- numeric(integer like
int,char..., and floating point(float,double)) boolstd::vectorstd::arraystd::optionalstd::unordered_mapstd::unordered_set
If you want do specific [de]serialize method on your own type, here:
for static [de]serialize, need two step:
namespace mirrow::serd::srefl { // 1. tell serd which toml node you want to serialize to // use SFINEA namespace impl { template <> struct serialize_destination_type<your_own_type> { // we want [de]serialize to/from toml::value using type = toml::value<your_own_type>; }; } // 2. provide your [de]serialize method // also use SFINEA, serialize function template <typename T> std::enable_if_t<std::is_same_v<your_own_type, T>> serialize(const T& value, serialize_destination_type_t<T>& node) { // try put value into node ... } // also use SFINEA, deserialize function template <typename T> std::enable_if_t<std::is_same_v<T, your_own_type>> deserialize(const toml::node& node, T& elem) { // try parse elem from node ... } }for dynamic [de]serialize, you need regist your function into serialize_method_storage at runtime:
mirrow::serd::drefl::serialize_method_storage::instance().regist(type_info, serialize_fn, deserialize_fn);the second and thrid param is your serialize/deserialize function, must be:
// serialize void serialize(toml::node&, const any&)>; // deserialize void deserialize(const toml::node&, any&)>;after these, you can use serialize/deserialize:
Person p; mirrow::drefl::any any = mirrow::drefl::any_make_ref(p); // serialize to toml node auto tbl = ::mirrow::serd::drefl::serialize(any); // deserialize from toml node ::mirrow::serd::drefl::deserialize(p, tbl);