可以将类类型信息MACRO替换为模板吗?

时间:2018-12-07 12:20:58

标签: c++ templates refactoring c++17

我有一个传统的大型类继承树(域类),实现了它自己的类类型信息和注册表。完成的方式是每个类都“调用” 这样的宏

class MyClass : public MyParent {
  MY_DECLARE_DYNAMIC(MyClass)

  ...
};

//in .c file:
MY_IMPLEMENT_DYNAMIC(MyClass, MyParent)

基本上,这些宏插入了一个静态函数,该函数是迈耶的单例,返回指向MyClassInfo的指针,如下所示:

class MyClass : public MyParent {
    MyClassInfo* GetClassInfo_() {
      static MyClassInfo instance=...;
      return &instance;
    }

    virtual MyClassInfo* GetClassInfo() const;
};

该宏插入的另一件事是一个虚函数,用于检索类信息以供运行时使用(即从某个基本类型的指针)。

MyClassInfo*包含该类的字符串名称以及获取父类信息的某种方式(当前实现为此使用函数指针)。

现在可以通过直接编写以下信息来使用此信息:MyClass::GetClassInfo_()myClass.GetClassInfo()

在实际情况下,(您可能已经猜到了)更多的细节,但是我认为这足以说明该概念,只是说每个类可以具有更多的特征,例如数据(例如,枚举值)指示是否已弃用该特定类)

此模式在WxWidgets和MFC https://docs.microsoft.com/en-us/previous-versions/ywz9k63y(v=vs.140)中也是众所周知的。

有人能摆脱这些MACRO吗?除了手动编写所有内容(例如使用模板或虚拟继承等)之外,最好不要增加每个对象的大小(超过一个虚拟函数)。

3 个答案:

答案 0 :(得分:2)

使代码更好的一种合理,简单的方法就是将MyClassInfo的静态实例完全移出类,并使其成为模板函数:

这是一个完整的例子:

#include <type_traits>

class MyClassInfo {
public:
    virtual ~MyClassInfo() {}
    virtual MyClassInfo* get_parent() const = 0; 
};

template<typename T>
MyClassInfo* get_class_info();

template<typename T>
class MyClassInfo_impl : public MyClassInfo {
public:
    using parent_t = typename T::parent_t;
    MyClassInfo* get_parent() const override {
        if constexpr(std::is_same_v<parent_t, void>) {
            return nullptr;
        }
        else {
          return get_class_info<parent_t>();
        }
    }
};

template<typename T>
MyClassInfo* get_class_info() {
    static MyClassInfo_impl<T> impl;
    return &impl;
}

class Parent {
public:
    // Could probably be inferred through SFINAE instead.
    using parent_t = void;
};

class MyClass : public Parent {
public:
    using parent_t = Parent;
    virtual MyClassInfo* GetClassInfo() { 
      return get_class_info<MyClass>();
    } 
};

int main() {
    auto class_info = get_class_info<MyClass>();
}

这样,您只剩下虚拟函数,正如您在注释中提到的那样,处理任何基于继承的解决方案变得非常混乱。

答案 1 :(得分:1)

另一种实现,在模板包装器上具有层次结构。我正在使用RTTI获取类名,可以通过将名称传递给构造函数来避免这种情况。

#include <iostream>
#include <memory>
#include <type_traits>

struct class_info {
    const char* name;
    const class_info* parent;
};

template<class C>
constexpr const char* class_name() noexcept
{
    return typeid(C).name();
}

class object
{
protected:
  constexpr object() noexcept
  {}
public:
  virtual class_info* get_class_info() {
        static class_info _instance = {"object",nullptr};
        return &_instance;
  }
};

template<class Base,class Derived>
class reflective:public Base {
    reflective(const reflective&) = delete;
    reflective& operator=(const reflective&) = delete;
protected:
    constexpr reflective() noexcept:
        Base()
    {
        static_assert( std::is_base_of<object,Base>::value && std::is_base_of<Base,Derived>::value , "Base must inherit object, and Derived must inherit base" );
    }
public:
    virtual class_info* get_class_info() override {
        static class_info _instance = {::class_name<Derived>(), Base::get_class_info() };
        return &_instance;
    }
};

class offspring_0 :public reflective<object,offspring_0> {
public:
    constexpr offspring_0() noexcept:
        reflective<object,offspring_0>()
    {}
};

class offspring_1 : public reflective<offspring_0,offspring_1> {
public:
    constexpr offspring_1() noexcept:
        reflective<offspring_0,offspring_1>()
    {}
};

int main(int argc, const char** argv) {

    std::shared_ptr<object> offspring = std::make_shared<offspring_1>();
    const class_info *ci = offspring->get_class_info();
    std::cout << "Self name: " << ci->name << std::endl;
    unsigned i = 1;
    for(ci = ci->parent; ci != nullptr; ci = ci->parent) {
        for(unsigned j=0; j < i; j++)
            std::cout << '\t';
        std::cout<< "parent " << i << " name: " << ci->name << std::endl;
        ++i;
    }
    return 0;
}

输出:

Self name: 11offspring_1
        parent 1 name: 11offspring_0
                parent 2 name: object

Process returned 0 (0x0)   execution time : 0.084 s
Press any key to continue.

答案 2 :(得分:0)

template<typename base_class, type_name crtp_class>
class h_traits{
public:
    using this_type=crtp_class;
    using base_type=base_class;
    using traits_class=h_traits;
    static_assert(std::is_same_v<base_class, typename base_class::this_type>, "invalid hierarchy");
    //Rest of definitions follows
    //...
};

class object_base//grand common base
{
public:
    using this_type=object_base;
    //... 
};
//...
class my_new_class: 
   public h_traits<my_old_class, my_new_class>
{
    //...
};