“Poor Man's Reflection” (AKA bottom-up reflection) in C++

后端 未结 3 1709
隐瞒了意图╮
隐瞒了意图╮ 2020-12-19 15:01

I am implementing some rudimentary reflection in C++ for an ultra modular architecture where virtually all features are loaded as plugins and interpreted dynamically at run-

相关标签:
3条回答
  • 2020-12-19 15:22

    Python

    Since you mentioned you're in VisualStudio, the macros I wrote below may not work for you (in my experience, macros can be obnoxious cross-platform). So, here's a Python sample that you can run as a pre-build script generate the *.cpp files based on the names in a text file.

    create_classes.py

    template = """Reflection::Type * {0}::GetType() {{
        return &_type;
    }}
    
    // static type info
    
    {0} {0}::_refObj;
    
    Reflection::Type {0}::_type(_refObj, "{0}", {0}::IsAssignableFrom);
    
    Reflection::Type * {0}::Type() {{
        return &_type;
    }}
    
    bool {0}::IsAssignableFrom(Reflection::Type * type) {{
        return dynamic_cast<{0}*>(&type->RefObj()) != nullptr;
    }}
    """
    
    if __name__ == '__main__':
        with open('classes', 'r') as classes:
            for class_name in classes:
                class_name = class_name.strip()
                with open('{0}.cpp'.format(class_name), 'w') as source:
                    source.write(template.format(class_name))
    

    classes (text file)

    Blossom
    Bubbles
    Buttercup
    

    Which will create Blossom.cpp, Bubbles.cpp, and Buttercup.cpp using the template above. Getting the correct names into the 'classes' text file is up to you. :)

    I'm sure you can adapt this to split each definition across *.hpp and *.cpp, let me know if this is helpful.

    C++ Macros

    As much as I dislike macros (and I still advise against using them when possible!) here are macros that will generate your code. I haven't tested them thoroughly, so they could shoot you in the foot. They're also named poorly (unclear what the macros do from their names), but this is the idea.

    macros.cpp

    #define GET_TYPE_METHOD(X) \
        Reflection::Type * X::GetType() { return &_type; }
    
    #define GET_REF_OBJ(X) X X::_refObj;
    
    #define GET_UNDERSCORE_TYPE(X) \
        Reflection::Type X::_type(_refObj, #X, X::IsAssignableFrom);
    
    #define GET_TYPE(X) \
        Reflection::Type * X::Type() { return &_type; }
    
    #define GET_IS_ASSIGNABLE_FROM(X) \
        bool X::IsAssignableFrom(Reflection::Type * type) { return dynamic_cast<X*>(&type->RefObj()) != nullptr; }
    
    GET_TYPE_METHOD(Keeler)
    GET_REF_OBJ(Keeler)
    GET_UNDERSCORE_TYPE(Keeler)
    GET_TYPE(Keeler)
    GET_IS_ASSIGNABLE_FROM(Keeler)
    

    If you run g++ -E macros.cpp, you get the preprocessor output. Check out what the preprocessor thinks:

    $ g++ -E macros.cpp
    # 1 "macros.cpp"
    # 1 "<command-line>"
    # 1 "/usr/include/stdc-predef.h" 1 3 4
    # 1 "<command-line>" 2
    # 1 "macros.cpp"
    # 16 "macros.cpp"
    Reflection::Type * Keeler::GetType() { return &_type; }
    Keeler Keeler::_refObj;
    Reflection::Type Keeler::_type(_refObj, "Keeler", Keeler::IsAssignableFrom);
    Reflection::Type * Keeler::Type() { return &_type; }
    bool Keeler::IsAssignableFrom(Reflection::Type * type) { return dynamic_cast<Keeler*>(&type->RefObj()) != nullptr; }
    

    Is this along the lines of what you were looking for?

    0 讨论(0)
  • 2020-12-19 15:25

    You can try this.. Also look through: http://en.cppreference.com/w/cpp/header/type_traits

    #include <iostream>
    #include <type_traits>
    
    #ifdef _MSC_VER
    #define __FUNC_NAME__ __FUNCTION__
    #else
    #define __FUNC_NAME__ __PRETTY_FUNCTION__
    #endif // _MS_VER
    
    #ifdef _MSC_VER
    #define RF_DETAIL  std::string GetDetail() {return __FUNCSIG__;}
    #else
    #define RF_DETAIL  std::string GetDetail() {return __FUNC_NAME__;}
    #endif
    
    
    #define RF_CLASS   std::string GetClass() {\
                             const std::string name = __FUNC_NAME__;\
                             std::string::size_type beg = name.find_first_of(" "), end = name.find_first_of("<");\
                             if (!end) end = name.find_first_of("::");\
                             return name.substr(beg + 1, end - beg - 1); }
    
    #define RF_TYPE    auto GetType() -> std::remove_reference<decltype(*this)>::type {return std::move(std::remove_reference<decltype(*this)>::type()); }
    
    #define RF_TYPE_PTR    auto GetTypePtr() -> decltype(this) {return this;}
    
    template<typename T>
    class Foo
    {
        public:
            RF_DETAIL;
            RF_CLASS;
            RF_TYPE;
            virtual ~Foo(){}
    };
    
    template<typename T>
    class Meh : public Foo<T>
    {
        public:
            RF_DETAIL;
            RF_CLASS;
            RF_TYPE;
            virtual ~Meh(){}
    };
    
    class Go
    {
        public:
            RF_DETAIL;
            RF_CLASS;
            RF_TYPE;
            RF_TYPE_PTR;
    };
    
    int main()
    {
        Foo<int> f;
        std::cout<<f.GetDetail()<<"\n\n";
        std::cout<<f.GetClass()<<"\n\n\n";
    
        Meh<int> g;
        std::cout<<g.GetDetail()<<"\n\n";
        std::cout<<g.GetClass()<<"\n";
    
    
        Goo h;
        decltype(h.GetType()) i;
        std::cout<<i.GetClass();
        return 0;
    }
    

    Prints:

    std::string Foo<T>::GetDetail() [with T = int; std::string = std::basic_string<c
    har>]
    
    Foo
    
    
    std::string Meh<T>::GetDetail() [with T = int; std::string = std::basic_string<c
    har>]
    
    Meh
    
    
    Foo::GetClass()
    
    0 讨论(0)
  • 2020-12-19 15:47

    Just to summarize:

    1. there's a lot of existing industry-standard frameworks to implement loadable modules in C++, with different levels of module introspection. Just to name a few: MSWindows COM and variants, CORBA (various implementations), KDE KParts, DBus-enabled services on Linux and other Unix-like OSes etc. In general I would go with one of existing variant depending on target platform and other considerations

    2. If you absolutely need to costruct your own bike^W framework, I would separate classes implementing module business-logic from the boilerplate. Certainly this approach introduces another level of indirection and this may cause some performance issues. But if done clever this boilerplate may be quite thin, almost unperceivable. Also separating BL from the framework would allow to completely change the horse w/o much efforts in future. To go with this approach I would choose code manipulating tools like GCC-XML or appropriate CLang modules.

    3. Also there're a number of existing C++ libraries from simple to complex to build your own tightly composed framework. Examples: ROOT Reflex, Boost.Reflect

    The rest is under your own choice. I know that people of Gnome project, unsatisfied with deficiencies and shortcomings of C++, invented their own OOP framework on plain C (GLib/GObject) and later developed on that basis a new fully functional language similar to C# (Vala). It's all up to you where to stop yourself :)

    0 讨论(0)
提交回复
热议问题