Можно ли заменить информацию о типе класса MACRO на шаблоны? - PullRequest
0 голосов
/ 07 декабря 2018

У меня есть традиционное большое дерево наследования классов (доменные классы), которое реализует собственную информацию о типах классов и реестр.Это делается так, что каждый класс 'вызывает' макрос, подобный этому

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

Есть ли у кого-нибудь решение дляизбавиться от этих макросов?Помимо написания всего вручную - например, с использованием шаблонов или виртуального наследования и т. Д. Предпочтительно тот, который не увеличивает размер каждого объекта (больше, чем одна виртуальная функция).

Ответы [ 3 ]

0 голосов
/ 07 декабря 2018

Еще одна реализация с иерархией над оберткой шаблона.Я использую 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.
0 голосов
/ 07 декабря 2018

Разумный и простой способ сделать код намного приятнее - просто полностью удалить статический экземпляр 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>();
}

Таким образом, у вас остается только виртуальная функция, которая, как вы упоминали в комментариях, становится действительно грязной, чтобы иметь дело с любым решением на основе наследования.

0 голосов
/ 07 декабря 2018
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>
{
    //...
};
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...