C ++ приведение базового указателя к указателю на интерфейс - PullRequest
3 голосов
/ 04 августа 2011

Вот некоторый псевдокод моей установки:

class IMyClass { ... }; // pure virtual class
class CMyBaseClass { .... };
class CMyClass : public CMyBaseClass, public IMyClass { ... }

Тогда у меня есть коллекция CMyBaseClass *.У меня есть пользовательский RTTI, который позволяет мне узнать, реализует ли класс данный интерфейс.Таким образом, я могу найти, какой из объектов имеет реализацию IMyClass.Моя проблема в том, что я не могу привести его к этому интерфейсу.Я не хочу использовать стандартный RTTI и динамическое приведение.

Я думаю, что в моем собственном RTTI для хранения различий указателей для приведения между парами классов, но я не понимаю, реализация, которая заставляет менясчастлив.

Есть ли другие решения?

Ответы [ 2 ]

5 голосов
/ 04 августа 2011

Ну, если вы настаиваете на том, чтобы не использовать язык RTTI, вы можете использовать так же, как старый COM: сделать все ваши классы или интерфейсы производными от следующего интерфейса:

class IMyCast  // similar to IUnknown
{
public:
    virtual void *CastTo(interfaceId_t id) = 0; //Similar to IUnknown::QueryInterface
};

Теперь в вашем CMyClass:

class CMyClass : public CMyBaseClass, public IMyClass
{
    //...
    void *CastTo(interfaceId_t id)
    {
        switch (id)
        {
            case IMyClass_id: //or whatever
                return static_cast<IMyClass*>(this);
            //...other cases
            default:
                throw std::bad_cast(); //or return NULL
        }
    }
};

Тогда в коде пользователя:

CMyBaseClass *obj;
IMyClass *my = static_cast<IMyClass*>(obj->CastTo(IMyClass_id));
1 голос
/ 04 августа 2011

Вероятно, вам нужно увеличить свой RTTI;по крайней мере, это то, что я сделал в той же ситуации.Вместо использования указателей diff, мое решение создает шаблон функции «заклинателя» для необходимых пар (Class, Interface).Это примерно так:

  • Все интерфейсы имеют уникальный идентификатор int.Для этого MyInterface должен быть получен из InterfaceBase.Идентификатор присваивается автоматически при первом вызове MyInterface :: GetId ().
  • Разработчик MyClass (который реализует MyInterface) должен добавить макрос IMPLEMENTS (MyClass, MyInterface) в файл .cpp.
  • Макрос создает экземпляр функции void* GetInterface<C, I>(void*), а затем регистрирует указатель на эту функцию в карте interfaceId -> GetInterface-function (которая принадлежит классу C).Эта функция переводит свой аргумент в C *, затем C * в I * и, наконец, обратно в void *.(Взлом с использованием void * необходим, чтобы все эти функции имели одинаковую подпись, поэтому они могут быть сохранены на карте.)
  • Чтобы получить интерфейс, пользователь должен вызвать myObject->Функция GetInterface (), реализованная в CMyBaseClass.Он находит карту, принадлежащую динамическому классу объекта this, ищет соответствующую функцию заклинателя на основе I :: GetId () и вызывает ее, передавая this.
...