Автоматизировать код RTTI в стиле LLVM

Для конкретной иерархии классов мне нужно знать, является ли ссылка на базовый класс экземпляром определенного производного класса.
По разным причинам я не могу использовать стандартный RTTI C ++ здесь, и мне нужно реализовать пользовательский instanceof механизм.

LLVM-stle RTTI будет соответствовать моим потребностям, но мне было интересно, если бы существовал способ (каким-то образом с помощью шаблонов) для автоматизации реализации classof метод?

Существуют ли другие / более простые реализации такого механизма, которые позволили бы узнать, является ли базовый класс экземпляром производного класса?

Мои ограничения:

  • У меня нет множественного наследования, но у меня есть несколько уровней наследования.
  • Влияние на объем памяти должно быть минимально возможным, а динамическое распределение невозможно.

4

Решение

Мне было интересно, если бы существовал способ (каким-то образом с помощью шаблонов) для автоматизации реализации метода classof?

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

Вот очень простое решение:

class TypedObject {
public:
virtual ~TypedObject() { };

virtual int getClassId() const { return 0; };
static int getStaticClassId() { return 0; };

virtual bool isOfType(int aID) const { return (aID == 0); };

template <typename T>
bool isOfClass() const { return isOfType( T::getStaticClassId() ); };

};

Приведение во время выполнения (т.е. dynamic_cast) функции будут выглядеть так:

template <typename T>
T* runtime_ptr_cast(TypedObject* p) {
if( (p) && (p->isOfClass<T>()) )
return static_cast<T*>( p );
return NULL;
};

template <typename T>
typename std::enable_if<
std::is_const< T >::value,
T* >::type runtime_ptr_cast(const TypedObject* p) {
if( (p) && (p->isOfClass<T>()) )
return static_cast<T*>( p );
return NULL;
};

тогда все, что вам нужно, это MACRO для автоматизации создания виртуальных и статических функций:

#define MY_RTTI_SYSTEM_CREATE_TYPE_1_BASE( NEWCLASSID, BASECLASSNAME ) \
public: \
virtual int getClassId() const { return NEWCLASSID; }; \
static int getStaticClassId() { return NEWCLASSID; }; \
\
virtual bool isOfType(int aID) const { \
return ((aID == NEWCLASSID) || BASECLASSNAME::isOfType(aID)); \
};

Затем вы можете создать новый класс, например:

class Foo : public TypedObject {

// ... some code, as usual ...

// call the macro with a given ID number and the name of the base-class:
MY_RTTI_SYSTEM_CREATE_TYPE_1_BASE(1, TypedObject)
};

Что приводит к:

int main() {
Foo f;
TypedObject* b = &f;

// check the type:
if( b->isOfClass<Foo>() )
std::cout << "b is indeed for class Foo!" << std::endl;

// make a dynamic cast:
Foo* pf = runtime_ptr_cast<Foo>( b );
if( pf )
std::cout << "cast to 'Foo*' was successful!" << std::endl;

const TypedObject* cb = b;
const Foo* cpf = runtime_ptr_cast<const Foo>( cb );
if( cpf )
std::cout << "cast to 'const Foo*' was successful!" << std::endl;

Foo* pf2 = runtime_ptr_cast<Foo>( cb ); // ERROR: no such function (invalid cast).
};

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

Я не думаю, что есть какой-либо практический способ избежать необходимости использовать MACRO-вызов в каждом создаваемом вами классе. Я думал об этом некоторое время (некоторое время назад, когда я делал свой собственный), и я пришел к выводу, что самым простым и чистым решением было сделать MACRO-вызов в классах (даже если я испытываю большое презрение к MACRO в генеральный). Но я не знаю, возможно, у других есть лучшее (основанное на шаблонах) решение, которое не вызывает слишком много беспорядка или не слишком навязчиво. Я использую эту схему в течение многих лет, и она очень хорошая и чистая.

У меня нет множественного наследования, но у меня есть несколько уровней наследования.

Приведенная выше схема работает для любого уровня наследования (т.е. это масштабируемое решение). Он также может быть легко адаптирован к множественному наследованию, если однажды вы захотите это сделать.

Влияние на объем памяти должно быть минимально возможным

Я знаю, что LLVM предпочитает решение без каких-либо виртуальных функций и использует вместо этого элемент данных с интегральным идентификатором в базовых классах. С такой схемой становится немного сложнее достичь той же функциональности, что и выше (но возможно). Это намного проще с виртуальными функциями, которые занимают только пространство одного указателя (указатель vtable), который часто не намного больше, чем член данных с интегральным идентификатором. И если классы уже полиморфны, стоимость вообще ничего не значит. И, конечно же, вышесказанное намного легче, чем встроенный C ++ RTTI. Поэтому, если вы действительно не хотите сжать те несколько байтов, которые вы можете сэкономить, с помощью решения с интегральным идентификатором (или enum), я бы порекомендовал вам использовать решение, основанное на виртуальных функциях, как я показал выше.

невозможно выполнить динамическое распределение.

Динамическое распределение вообще не требуется. Только более сложные (и многофункциональные) реализации RTTI потребуют некоторого динамического распределения. Если все, что вам нужно, это уметь выполнять «classof ()» (и, следовательно, динамическое приведение), то динамическое выделение памяти не требуется, наверняка.

4

Другие решения

Вы хотите, чтобы какая-то древовидная структура данных была глобальной переменной для хранения иерархии классов

class Foo : public Foo_Parent {
IS_PART_OF_HIERARCHY

public:
Foo();
...
}

#define IS_PART_OF_HIERARCHY
private:
static Hierarchy<string> *node;
public:
bool isChildOf( string parent ) const;
bool isParentOf( string child ) const;

В .cpp файле

INSERT_INTO_HIERARCHY( Foo, Foo_Parent )

Foo::Foo() {}
....

#define INSERT_INTO_HIERARCHY( class_name, parent_class_name )
Hierarchy<string> class_name::node = classes_hierarchy.insertAfter( #parent_class_name );
bool class_name::isChildOf const( string ) {
auto *node = class_name::node;
// traverse the parents of node
}
bool class_name::isParentOf const( string ) {
auto *node = class_name::node;
// traverse the children of node
}

Я не могу найти иерархический класс в STL, его сложно реализовать, я не знаю, стоит ли это усилий.

0

По вопросам рекламы [email protected]