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:
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);