我有一个传统的大型类继承树(域类),实现了它自己的类类型信息和注册表。完成的方式是每个类都“调用” 这样的宏
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吗?除了手动编写所有内容(例如使用模板或虚拟继承等)之外,最好不要增加每个对象的大小(超过一个虚拟函数)。
答案 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>
{
//...
};