C ++中的“穷人的反思”(AKA自下而上的反思)

时间:2014-01-11 02:44:49

标签: c++ design-patterns plugins reflection types

我正在C ++中实现一些基于超模块化体系结构的基本反射,其中几乎所有功能都作为插件加载并在运行时动态解释。由于系统中存在结构自组织的独特元素,因此组件需要一些相互检查的方法(对于需要这种反射的情况的示例,请参阅此问题:“Best fit” dynamic type matching for plugins in C++)。

到目前为止,该体系结构是用C#开发的,但我现在正在研究如何用C ++实现它。在这一点上,我基于以下模型为“穷人的反思”创建了骨架:

用于保存相关课程信息的Type课程:

namespace Reflection {

    class Type {
    public:
        Type(Object &, string, bool(*)(Type *));
        ~Type();
        Object & RefObj();
        string Name();
        bool IsAssignableFrom(Type *);
    private:
        Object & _refObj;
        string _name;
        bool(*_isAssignableFrom_Handler)(Type *);
    };

}

Object类,反射模型中的所有参与者都将从该类下降:

class Object {
public:
    Object();
    virtual ~Object();
    virtual string ToString();
    virtual Reflection::Type * GetType();
    static Reflection::Type * Type();
    static bool IsAssignableFrom(Reflection::Type *);
private:
    static Object _refObj;
    static Reflection::Type _type;
};

..定义如下:

string Object::ToString() { return GetType()->Name(); }

// all derived classes must implement the equivalent of this:

Reflection::Type * Object::GetType() { return &_type; }

Object Object::_refObj;

Reflection::Type Object::_type(_refObj, "Object", Object::IsAssignableFrom);

Reflection::Type * Object::Type() { return &_type; }

bool Object::IsAssignableFrom(Reflection::Type * type) {
    return dynamic_cast<Object*>(&type->RefObj()) != nullptr;
}

请注意,我只需要我的反射率在我自己的类层次结构中运行(所有这些都继承自Object)。因此,上面的代码使我能够:

  • 获取任何实例的类型:instance.GetType()

  • 获取任何课程的类型:class::Type()

  • 比较类型:例如(instance.GetType() == class::Type())(instanceA.GetType() == instanceB.GetType())

  • 执行运行时检查以查看是否可以为一个Type实例分配另一个(即动态,有两个“未知数”.C ++中的所有内置选项似乎都需要在编译时知道至少一个类型) ,基本上相当于is和推断继承关系的关键:(instanceA.GetType()->IsAssignableFrom(instanceB.GetType()))

  • 通过类型

  • 动态引用抽象类型
  • 获取类型的一致友好名称(即类)

这足以满足我的直接需求,并且可以通过向Type类添加功能来扩展功能范围(接下来是能够在给定Type实例的情况下实例化类 - 类似于.Net的Activator.CreateInstance )。但与“正确”反射不同,后者本质上是一种自上而下的方法,其中关于类的[meta]信息是在编译器级别/集中管理的,这是通过手动和自下而上的方式完成的,将知识分配到对象本身并给出它们是在运行时在彼此之间进行通信的一种方式。因此,为了实现这一目标,本系统中包含的每个类都需要实现与Object类相同的成员和功能,以封装自身的相关方面以进行“导出”。例如,Plugin类看起来像这样(定义):

Reflection::Type * Plugin::GetType() { return &_type; }

Plugin Plugin::_refObj;

Reflection::Type Plugin::_type(_refObj, "Plugin", Plugin::IsAssignableFrom);

Reflection::Type * Plugin::Type() { return &_type; }

bool Plugin::IsAssignableFrom(Reflection::Type * type) {
    return dynamic_cast<Plugin*>(&type->RefObj()) != nullptr;
}

如您所见,它与其父类Object几乎完全相同。几乎所有这些函数都只根据类类型(和名称)而有所不同。

所以我有几个问题。

  • 第一个是是否有任何方法可以通过编译器宏或聪明的继承/模板等来简化这一点,因为存在/将会有如此多的重复。它让我觉得可以自动化吗?就像提取类的名称(可能包括名称空间)并从中生成代码一样?或者基于一个或两个变量的一些源代码片段模板(想到的类名)。

  • 第二个更通用(以及我包含所有这些代码的原因)。我只是在很短的时间内使用C ++,所以我觉得相当笨拙,并且认为我的方法和实现细节可能非常幼稚。如果有其他人在类似的架构上工作/有类似的需求,也许他们可以分享他们的经验(甚至只是指出我的模型/代码中的缺陷)。

有关需要这种反射风格的示例,请参阅此问题:“Best fit” dynamic type matching for plugins in C++

更新

就第一个问题而言,这就是我最终做的事情:

我制作了两个宏,一个用于.h文件,一个用于.cpp文件。现在,我需要做的是在类声明中添加REFLECTION_H(TYPE_NAME),在类定义中添加REFLECTION_CPP(TYPE_NAME),并自动包含所有反射样板。然后,我可以正常添加特定于类的成员,而不必考虑反射,知道所需的所有管道都已到位且一切正常。例如,对于当前的实现,新的Surface类现在看起来像这样:

Surface.h:

class Surface : public Plugin
{
    REFLECTION_H(Surface)
public:
    // ...class specific public member declarations...
private:
    // ...class specific private member declarations...
};

Surface.cpp:

REFLECTION_CPP(Surface);

Surface::~Surface() {}

// ...class specific member definitions...

宏的定义如下:

#define REFLECTION_H(TYPE_NAME) \
    public:\
    virtual ~TYPE_NAME();\
    static Reflection::Type& Type();\
    private:\
    virtual Reflection::Type& _getType();\
    static TYPE_NAME _refObj;\
    static Reflection::Type _type;\
    static bool IsAssignableFrom(Reflection::Type&);\
    static plugin_ptr CreateInstance();

#define REFLECTION_CPP(TYPE_NAME) \
    Reflection::Type& TYPE_NAME::Type() { return _type; }\
    Reflection::Type& TYPE_NAME::_getType() { return _type; }\
    TYPE_NAME TYPE_NAME::_refObj;\
    Reflection::Type TYPE_NAME::_type(_refObj, #TYPE_NAME, true, IsAssignableFrom, CreateInstance);\
    bool TYPE_NAME::IsAssignableFrom(Reflection::Type& type) { return dynamic_cast<TYPE_NAME*>(&type.RefObj()) != nullptr; }\
    plugin_ptr TYPE_NAME::CreateInstance() { return plugin_ptr(new TYPE_NAME); }

3 个答案:

答案 0 :(得分:2)

总结一下:

  1. 在C ++中实现可加载模块有很多现有的行业标准框架,具有不同级别的模块内省。仅举几例:MSWindows COM和变体,CORBA(各种实现),KDE KParts,Linux上的DBus启用服务以及其他类Unix操作系统等。一般情况下,我会选择现有的变体,具体取决于目标平台和其他考虑

  2. 如果您绝对需要构建自己的自行车^ W框架,我会将实现模块业务逻辑的类与样板分开。当然,这种方法引入了另一个间接层,这可能会导致一些性能问题。但如果做得好聪明,这个样板可能很薄,几乎是不可感知的。将BL从框架中分离出来将允许在将来完全改变马匹。要采用这种方法,我会选择代码操作工具,如GCC-XML或适当的CLang模块。

  3. 此外,还有许多现有的C ++库,从简单到复杂,构建自己的紧密组合框架。示例:ROOT ReflexBoost.Reflect

  4. 其余的由您自己选择。我知道Gnome项目的人不满足于C ++的缺陷和缺点,他们在普通C(GLib / GObject)上发明了他们自己的OOP框架,后来在此基础上开发了一种类似于C#的新功能语言(Vala) 。这完全取决于你在哪里阻止自己:)

答案 1 :(得分:1)

的Python

既然你提到你在VisualStudio中,我在下面写的宏可能不适合你(根据我的经验,宏可能是令人讨厌的跨平台)。所以,这是一个Python示例,您可以作为预构建脚本运行,根据文本文件中的名称生成* .cpp文件。

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

(文本文件)

Blossom
Bubbles
Buttercup

使用上面的模板创建Blossom.cpp,Bubbles.cpp和Buttercup.cpp。在“类”文本文件中获取正确的名称取决于您。 :)

我确信你可以调整它来分割* .hpp和* .cpp中的每个定义,让我知道这是否有用。

C ++宏

尽管我不喜欢宏(我仍然建议尽可能不使用它们!)这里有一些宏将生成你的代码。我没有彻底测试过,所以他们可以用脚射击你。它们的名字也很差(不清楚宏的名字是什么),但这就是这个想法。

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

如果运行g++ -E macros.cpp,则会获得预处理器输出。看看预处理器的想法:

$ 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; }

这是否符合您的要求?

答案 2 :(得分:0)

你可以试试这个..也看看: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;
}

打印:

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