I\'d like to be able to introspect a C++ class for its name, contents (i.e. members and their types) etc. I\'m talking native C++ here, not managed C++, which has reflection
Reflection in C++ is very useful, in cases there you need to run some method for each member(For example: serialization, hashing, compare). I came with generic solution, with very simple syntax:
struct S1
{
ENUMERATE_MEMBERS(str,i);
std::string str;
int i;
};
struct S2
{
ENUMERATE_MEMBERS(s1,i2);
S1 s1;
int i2;
};
Where ENUMERATE_MEMBERS is a macro, which is described later(UPDATE):
Assume we have defined serialization function for int and std::string like this:
void EnumerateWith(BinaryWriter & writer, int val)
{
//store integer
writer.WriteBuffer(&val, sizeof(int));
}
void EnumerateWith(BinaryWriter & writer, std::string val)
{
//store string
writer.WriteBuffer(val.c_str(), val.size());
}
And we have generic function near the "secret macro" ;)
template
auto EnumerateWith(TWriter && writer, T && val) -> is_enumerable_t
{
val.EnumerateWith(write); //method generated by ENUMERATE_MEMBERS macro
}
Now you can write
S1 s1;
S2 s2;
//....
BinaryWriter writer("serialized.bin");
EnumerateWith(writer, s1); //this will call EnumerateWith for all members of S1
EnumerateWith(writer, s2); //this will call EnumerateWith for all members of S2 and S2::s1 (recursively)
So having ENUMERATE_MEMBERS macro in struct definition, you can build serialization, compare, hashing, and other stuffs without touching original type, the only requirement is to implement "EnumerateWith" method for each type, which is not enumerable, per enumerator(like BinaryWriter). Usually you will have to implement 10-20 "simple" types to support any type in your project.
This macro should have zero-overhead to struct creation/destruction in run-time, and the code of T.EnumerateWith() should be generated on-demand, which can be achieved by making it template-inline function, so the only overhead in all the story is to add ENUMERATE_MEMBERS(m1,m2,m3...) to each struct, while implementing specific method per member type is a must in any solution, so I do not assume it as overhead.
UPDATE: There is very simple implementation of ENUMERATE_MEMBERS macro(however it could be a little be extended to support inheritance from enumerable struct)
#define ENUMERATE_MEMBERS(...) \
template inline void EnumerateWith(TEnumerator & enumerator) const { EnumerateWithHelper(enumerator, __VA_ARGS__ ); }\
template inline void EnumerateWith(TEnumerator & enumerator) { EnumerateWithHelper(enumerator, __VA_ARGS__); }
// EnumerateWithHelper
template inline void EnumerateWithHelper(TEnumerator & enumerator, T &...v)
{
int x[] = { (EnumerateWith(enumerator, v), 1)... };
}
// Generic EnumerateWith
template
auto EnumerateWith(TEnumerator & enumerator, T & val) -> std::void_t
{
val.EnumerateWith(enumerator);
}
And you do not need any 3rd party library for these 15 lines of code ;)