How can I add reflection to a C++ application?

前端 未结 28 1617
感动是毒
感动是毒 2020-11-21 11:25

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

28条回答
  •  攒了一身酷
    2020-11-21 11:36

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

提交回复
热议问题