Реализация интерфейса времени компиляции регистрируется в C++

Я использую псевдоинтерфейсы в C++, то есть, чистых абстрактных классах. Предположим, что у меня есть три интерфейса, IFoo, IBar и IQuux. У меня также есть класс Fred, который реализует все три из них:

interface IFoo
{
    void foo (void);
}   

interface IBar
{
    void bar (void);
}

interface IQuux
{
    void quux (void);
}   

class Fred : implements IFoo, IBar, IQuux
{
}

Я хочу объявить метод, который принимает любой объект, который реализует IFoo и IBar - Fred работал бы, например. Единственное время компиляции способ сделать это, которое я могу вообразить, должно определить третий интерфейс IFooAndBar, который реализует обоих, и повторно объявите Fred:

interface IFooAndBar : extends IFoo, IBar
{   
}

class Fred : implements IFooAndBar, IQuux
{
}

Теперь я могу объявить свой метод как получение IFooAndBar*.Пока все хорошо.


Однако, что происходит, если я также хочу другой метод, который принимает IBar и IQuux? Я пытался объявить новый интерфейс IBarAndQuux и объявить Fred как наследовавший обоих:

class IFooAndBar : IFoo, IBar
{
};


class IBarAndQuux : IBar, IQuux
{
};


class Fred : IFooAndBar, IBarAndQuux
{
};

Это работает, когда я передаю Fred как IFooAndBar к методу; однако, когда я пытаюсь позвонить Fred:: панель () непосредственно, gcc жалуется:

error: request for member ‘bar’ is ambiguous
error: candidates are: void IBar::bar()
error:                 void IBar::bar()

который делает это решение более или менее бесполезным.


Моя следующая попытка состояла в том, чтобы объявить Fred как наследовавшийся трем отдельным интерфейсам и заставляющий метод принять один из гибридных интерфейсов в качестве параметра:

class Fred : public IFoo, public IBar, public IBaz
{

};

void doTest (IBarAndBaz* pObj)
{
    pObj->bar();
    pObj->baz();
}

Когда я пытаюсь передать Fred как IBarAndBaz* параметр, я получаю ошибку, как ожидалось:

error: cannot convert ‘Fred*’ to ‘IBarAndBaz*’ for argument ‘1’ to ‘void doTest(IBarAndBaz*)’

dynamic_cast <> также производит ошибку (который я не понимаю),

error: cannot dynamic_cast ‘pFred’ (of type ‘class Fred*’) to type ‘class IBarAndBaz*’ (source type is not polymorphic)

Принуждение броска действительно работает, однако:

doTest((IBarAndBaz*)pFred);

но интересно, насколько безопасный и портативный это (я разрабатываю для Linux, Mac и Windows), и работает ли он в реальной ситуации.


Наконец, я понимаю, что мой метод может принять, что указатель на один из интерфейсов и dynamic_cast к другому (s) осуществляет корректный тип параметра во времени выполнения, но я предпочитаю решение времени компиляции.

5
задан ggambett 18 January 2010 в 17:06
поделиться

4 ответа

Подумайте об использовании проверенных решений - Boost.typeepuit к спасению:

template<class T>
void takeFooAndBar(const T& t) {
    BOOST_STATIC_ASSERT(
           boost::is_base_of<IFoo, T>::value 
        && boost::is_base_of<IBar, T>::value);
    /* ... */
}
7
ответ дан 13 December 2019 в 05:35
поделиться

Вы можете добиться эффекта с использованием шаблона MetaProgramming:

tempate<class C>
void doTest( C* pObj )
{
  pObj->bar();
  pObj->baz();
}

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

1
ответ дан 13 December 2019 в 05:35
поделиться

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

struct IFoo 
{
    virtual void foo() = 0;
};

struct IBar 
{
    virtual void bar() = 0;
};

struct IFooAndBar : public IFoo, public IBar {};

class FooAndBarCompositor : public IFooAndBar
{
public:
    template <class T>
    FooAndBarCompositor(T* pImpl) : m_pFoo(pImpl), m_pBar(pImpl) {}

    void foo() {m_pFoo->foo();}
    void bar() {m_pBar->bar();}

private:
    IFoo* m_pFoo;
    IBar* m_pBar;
};

Затем вы пишете функцию, которая принимает IFOONANDBAR *, если требуются оба интерфейса, и вызывающий абонент может построить FOOANDBARCOMPOSITOR на стеке, который отправляет к объекту их выбора. Похоже:

void testFooAndBar(IFooAndBar* pI) {}

void baz(Fred* pFred)
{
    FooAndBarCompositor fb(pFred);
    testFooAndBar(&fb);
}

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

template <class IA, class IB>
class InterfaceCompositor
{
public:
    template <class T>
    InterfaceCompositor(T* pObj) : m_pIA(pObj), m_pIB(pObj) {}

    IA* AsA() const {return m_pIA;}
    operator IA* () const {return AsA();}
    IB* AsB() cosnt {return m_pIB;}
    operator IB* () const {return AsB();}

private:
    IA* m_pIA;
    IB* m_pIB;
};

, то функция выглядит как:

void testFooAndBar(InterfaceCompositor<IFoo, IBar> pI)
{
    IFoo* pFoo = pI; // Or pI.AsA();
    IBar* pBar = pI; // Of pI.AsB();
}

Это требует функции, которая хочет обеспечить установление нескольких интерфейсов для использования композитора, где ожидается A * или B * (например, Назначение или параметр функции) или явно вызовите соответствующий метод ASX (). В частности, интерфейс для использования не может быть выведен с использованием оператора «>», а оператор * не имеет смысла на композите.

Если вы переходите с универсальным кодом, вы можете использовать тот же шаблон для обеспечения применения того, что объект поддерживает как IBAR, так и IBAZ.

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

1
ответ дан 13 December 2019 в 05:35
поделиться

Для этого в стиле OO нужна виртуальная наследование, чтобы убедиться, что fred заканчивается только одной копией IBAR :

class IFooAndBar : public IFoo, public virtual IBar {};
class IBarAndQuux : public virtual IBar, public IQuux {};

class Fred : public IFooAndBar, public IBarAndQuux {};

Fred fred;
fred.bar(); // unambiguous due to virtual inheritence

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

Литые, которые вы пытались, не возможно, в качестве экземпляра Фред не является экземпляром IBARANDBAZ . Принудительные литые компилирования, потому что большинство принудительных отливок будут скомпилировать, независимо от того, безопасно ли преобразование, но в этом случае он даст неопределенное поведение.

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

void doTest(IBar *bar, IBaz *baz)
{
    bar->bar();
    baz->baz();
}

class Fred : public IBar, public IBaz {};

Fred fred;
doTest(&fred,&fred);
3
ответ дан 13 December 2019 в 05:35
поделиться
Другие вопросы по тегам:

Похожие вопросы: