https://github.com/visualgmq/mirrow
A TMP utility framework in C++17. Contain dynamic&static reflection and serialization
https://github.com/visualgmq/mirrow
Last synced: 11 months ago
JSON representation
A TMP utility framework in C++17. Contain dynamic&static reflection and serialization
- Host: GitHub
- URL: https://github.com/visualgmq/mirrow
- Owner: VisualGMQ
- Created: 2023-08-31T01:12:30.000Z (almost 3 years ago)
- Default Branch: main
- Last Pushed: 2025-05-12T06:25:03.000Z (about 1 year ago)
- Last Synced: 2025-06-10T17:57:39.355Z (about 1 year ago)
- Language: C++
- Homepage:
- Size: 342 KB
- Stars: 69
- Watchers: 1
- Forks: 8
- Open Issues: 1
-
Metadata Files:
- Readme: ReadMe.md
Awesome Lists containing this project
README
# Mirrow - A Template Meta Programming utility framework
`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](https://github.com/skypjack/meta) and [ponder](https://github.com/billyquith/ponder).
Nowadays, `mirrow` has these parts:
* `util`: some common utilities
* `srefl`: static reflection framework
* `drefl`: dynamic reflection framework
* `serd`: a serialize framework based on reflection(serial with `drefl`&`srefl`) with TOML
## :book: docs
### util
`util`(utility) has some convenient utility to do TMP:
* `type_list`: compile-time type list. [:microscope: unittest](./test/utility/type_list.cpp)
* `function_traits`: compile-time function info trait. [:microscope: unittest](./test/utility/function_traits.cpp)
* `variable_traits`: compile-time variable info trait. [:microscope: unittest](./test/utility/variable_traits.cpp)
* `const_str`: compile-time string. [:microscope: unittest](./test/utility/const_str.cpp)
### srefl
static reflection framework. :microscope: [do reflect unittest](./test/srefl/srefl.cpp), [get reflected info unittest](./test/srefl/reflect.cpp)
To reflect your class, you must do like this:
```cpp
// 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(&Foo::foo)),
field(static_cast(&Foo::foo)),
field(static_cast(&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](./include/mirrow/srefl/srefl_begin.hpp) provide a bunch of macros to help you regist your class. And [srefl_end.hpp](./include/mirrow/srefl/srefl_end.hpp) will `#undef` these macros to avoid pollute your codes.
Then, use `srefl_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();` to get reflected informations. And visit member variables:
```cpp
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*
### drefl
dynamic reflection framework.
#### any
`any` is similar to `std::any`, but support ownership :microscope:[unittest](./test/drefl/any.cpp)
`any` has 3 ownership(defined in `any::access_type`):
* `Null`: don't contain data
* `ConstRef`: const reference to a value
* `Ref`: mutable reference to a value
* `Copy`: the data's ownership is any itself, when any destruct, data will destruct together
use `any_make_xxx` to create an any from ownership:
```cpp
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 data
```
and 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
`factory` is where you reflect your type:microscope:[unittest](./test/drefl/factory.cpp)
register your class by:
```cpp
struct Person {
std::string name;
float height;
const bool hasChild;
const Person* couple;
};
// in main():
mirrow::drefl::factory::instance()
.regist("Person")
.property("name", &Person::name)
.property("height", &Person::height)
.property("hasChild", &Person::hasChild)
.property("couple", &Person::couple);
```
or register your enum by:
```cpp
enum class MyEnum {
Value1 = 1,
Value2 = 2,
Value3 = 3,
};
// in main():
auto& inst = mirrow::drefl::factory::instance()
.regist("MyEnum")
.add("Value1", MyEnum::Value1)
.add("Value2", MyEnum::Value2)
.add("Value3", MyEnum::Value3);
```
then use
```cpp
auto info = mirrow::drefl::typeinfo();
```
to get registered type information;
**NOTE: currently we don't support register member function.**
there are may type information you can access(by member function`as_xxx()`):
* `enum_info`: enumerates
* `numeric`: numerics(`int`,`float`,`char`...)
* `boolean`: boolean
* `string`:`std::string` or `std::string_view` (may add `const char*` support later)
* `pointer`: pointers like `T*`, `T* const`, `T**`...
* `array`: `std::vector`, `std::array`, `std::list`, `T[N]`
* `class`: other classes
*future support:*
* `map`: `std::unordered_map`, `std::map`
* `set`: `std::unordered_set`, `std::set`
* `optional`: `std::optional`
* `smart points`: `std::unique_ptr`. `std::shared_ptr`
* `pair`: `std::pair`
### serd
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 [:microscope: unittest](./test/serd/drefl_serd.cpp)
static reflection based serialize [:microscope: unittest](./test/serd/srefl_serd.cpp)
After reflected type, you can do serialize like:
```cpp
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` to get the type.
#### custom serialize function
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`))
* `bool`
* `std::vector`
* `std::array`
* `std::optional`
* `std::unordered_map`
* `std::unordered_set`
If you want do specific \[de\]serialize method on your own type, here:
for static \[de\]serialize, need two step:
```cpp
namespace mirrow::serd::srefl {
// 1. tell serd which toml node you want to serialize to
// use SFINEA
namespace impl {
template <>
struct serialize_destination_type {
// we want [de]serialize to/from toml::value
using type = toml::value;
};
}
// 2. provide your [de]serialize method
// also use SFINEA, serialize function
template
std::enable_if_t>
serialize(const T& value, serialize_destination_type_t& node) {
// try put value into node
...
}
// also use SFINEA, deserialize function
template
std::enable_if_t>
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:
```cpp
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:
```cpp
// serialize
void serialize(toml::node&, const any&)>;
// deserialize
void deserialize(const toml::node&, any&)>;
```
after these, you can use serialize/deserialize:
```cpp
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);
```