2010-12-04 3 views
10

У меня есть иерархия классов, как этот:C++: эмуляция RTTI

class A  { }       // 
class AA : A { }       //   A 
class AAA : AA { }       //  / \ 
class AAB : AA { }       //  AA  AB 
class AB : A { }       // /\ /\ 
class ABA : AB { }       // AAA AAB ABA ABB 
class ABB : AB { }       // 

я хотел бы подражать RTTI (без его использования, конечно) для этого ierarchy, таким образом, что, учитывая указатель/ссылка на A, я могу узнать его фактический тип (аналогично тому, что делает typeid), как целое число, идентифицирующее класс.

Кроме того, я хотел бы, что множество целых чисел идентификации моих типов является непрерывным и от 0 до N-1 (от 0 до 6 в моем примере):

class A  { virtual int t(){return 0;} } // 
class AA : A { virtual int t(){return 1;} } //   A(0) 
class AAA : AA { virtual int t(){return 2;} } //  / \ 
class AAB : AA { virtual int t(){return 3;} } //  AA(1)  AB(4) 
class AB : A { virtual int t(){return 4;} } // / \  / \ 
class ABA : AB { virtual int t(){return 5;} } // AAA(2) AAB(3) ABA(5) ABB(6) 
class ABB : AB { virtual int t(){return 6;} } // 

(порядок не действительно имеет значение: A::t может вернуться 3 и AAB::t 0, например


можно ли позволить компилятору присвоить индексы для моих классов

я Тхи.? nk, что может помочь мне CRTP; что-то вроде:

class X : A, AssignFirstAvailableIndex<X> { } 

, но я недостаточно хорош с шаблонами. Как я мог реализовать этот класс шаблонов AssignFirstAvailableIndex?

(конечно компилятор может увидеть все классы во время компиляции)

+0

для чего вы хотите этого? – 2010-12-04 12:02:52

+2

Некоторые компиляторы предоставляют макросы препроцессора, которые расширяются до нового целого числа каждый раз, когда они используются в одном и том же ТУ. Кроме этого, я думаю, вам не повезло с этим. Тем не менее, он просто задает вопрос: «Если вы хотите RTTI, то почему бы вам не использовать RTTI? _ – sbi 2010-12-04 12:08:05

+0

Это в основном улучшить мои навыки с помощью шаблонов и понять новые способы их использования. Кроме того, это упростит мой код: на данный момент у меня есть те функции A :: t`, которые жестко закодированы вручную. Мне нужны эти интегральные значения, идентифицирующие типы для создания контейнера, который связывает тип с указателем функции; если эти целые числа непрерывны, я могу использовать массив, иначе вам понадобится таблица карт/хэшей, и я бы предпочел избежать. – peoro 2010-12-04 12:12:36

ответ

3

Существует стандартный метод реализации того, что вам нужно. Стандартные языковые грани используют его для идентификации себя. Рассмотрим рассмотрение стандартного заголовка «locale».

class Base { 
    public: 
    // Being constructed contains a new unique identifier 
    class Id { 
    // An id factory returns a sequence of nonnegative ints 
    static int allocate() { 
     static int total = 0; 
     return total++; 
    } 
    int _local; 
    public: 
    Id(): _local(allocate()) {} 
    int get() const {return _local;} 
    }; 
    //Child classes should make this function return an id generated by Base::Id constructed as static member. 
    virtual int id() const = 0; 
}; 

class Child1{ 
    public: 
    static const Base::Id _id; 
    virtual int id() { return _id.get(); } 
}; 

class Child2 { 
    public: 
    static const Base::Id _id; 
    virtual int id() { return _id.get(); } 
}; 

class Child3 { 
    public: 
    static const Base::Id _id; 
    virtual int id() { return _id.get(); } 
}; 

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

0

Может быть что-то вроде этого? (Синтаксис, вероятно, неправильно повсюду, но вы получите идею)

class A { 
    virtual int t() { return 0; } 
} 

template <bool AB> 
class AX<AB>: A { 
    virtual int t() { return AB ? 1 : 2; } 
}; 

template <bool AB2> 
template <bool AB> 
class AXX<AB2>: AX<AB> { 
    virtual int t() { return AX<AB>::t() * 2 + (AB2 ? 1 : 2); } 
} 

... А если серьезно, пожалуйста просто использовать RTTI.

0

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

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

Во-первых, имейте объект, представляющий тип, и используйте типичный подход, сделанный вручную: RTTI: у каждого класса есть статический экземпляр этого объекта и его виртуальная функция get-type-info возвращает указатель на этот объект. Таким образом, каждый класс будет иметь немного коды в нем, как это:

static TypeInfo ms_type_info; 
virtual const TypeInfo *GetTypeInfo() const { 
    return &ms_type_info; 
} 

И вы бы определить данные типа, ввод в секцию любой информации <<whatever you info you want>> в TypeInfo магазины прочь, чтобы сделать его лучше, чем компилятор RTTI;

TypeInfo WhateverClass::ms_type_info(<<whatever info you want>>); 

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

TypeInfo сама структура будет выглядеть немного так:

struct TypeInfo { 
    int type_index; 
    TypeInfo *next; 
    TypeInfo(<<whatever>>) {<<see below>>} 
}; 

Если читатель предпочел бы получить и установить функции, он может иметь те.

Объект TypeInfo должен быть статичным для класса, а не функцией, поскольку целью является создание списка всех TypeInfos. У вас есть два варианта. Автоматический должен иметь каждый TypeInfo, в конструкторе, который был оставлен пустым выше, добавить себя в какой-то глобальный связанный список; другой должен иметь большую функцию, которая добавляет те, которые она хочет к глобальному списку вручную.

Затем, при запуске, пройдите через объекты TypeInfo и назначьте каждый индекс. Что-то, как это будет делать, если предположить, что есть TypeInfo *g_first_type_info, что указывает на первую информацию типа в списке:

int next_type_index=0; 
for(TypeInfo *ti=g_first_type_info;ti;ti=ti->next) 
    ti->type_index=next_type_index++; 

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

object->GetTypeInfo()->type_index; 

Вы могли бы реализовать t достаточно легко в настоящее время:

virtual int t() const { 
    return ms_type_info.type_index; 
} 

Полное раскрытие проблем, я могу думать:

  • Индексы типов не установлены во время компиляции, поэтому, если вы создаете массивы, вам нужно будет их создать во время выполнения. Это может быть пустой тратой кода, если ваши массивы в противном случае были бы компиляцией констант времени, однако для компиляторов, которые не поддерживают синтаксис инициализации массива в стиле C99, это может иногда сделать код более читаемым.

  • Если вы хотите сделать все, что в глобальных конструкторов, прежде чем main начинается, вы можете отклеилась, как TypeInfo объекты являются обычными глобальными объектами и (в этой ситуации так или иначе) не должным образом не готовы к использованию в любом случае до тех пор, типа индексы были назначены.

  • Линкеры имеют тенденцию выделять глобальные объекты, которые, как представляется, не используются, поэтому объекты типа info в статических библиотеках никогда не могут добавить себя в список. Поэтому вы должны иметь в виду, что, хотя подход автоматической регистрации работает хорошо, когда он не работает, он этого не делает, поэтому в любом случае вы можете вручную регистрировать вещи вручную.

0

Я успешно использовал метод, описанный здесь: http://www.flipcode.com/archives/Run_Time_Type_Information.shtml. В основном каждый класс, который требует RTTI, имеет статическую функцию, которая возвращает свой тип rtti. преимущество использования структуры для типа заключается в том, что вы можете добавлять функции в свою структуру rtti.

Я изменил этот подход, чтобы разрешить такие вещи, как component->IsOfType(CollisionComponent::GetClass()).

Я также расширил структуру RTTI, чтобы предоставить имя класса, и теперь я могу позвонить component->IsOfType("CollisionComponent") Без включения CollisionComponent.h.

Этот подход очень удобен в сочетании с созданием динамического класса this. Я могу создавать и идентифицировать классы C++ в сценариях и создавать экземпляры большого количества различных компонентов, которые загружаются только тогда, когда OS/hardware поддерживает необходимые типы. Я также могу перенастроить типы загружаемых компонентов в зависимости от версии сервера. Например, сервер может разрешить использование "CollisionComponent_V2_0", а другой будет принудительно использовать "CollisionComponent_Proxy_V2"

Смежные вопросы