2010-07-19 2 views
2

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

Так выглядит интерфейс что-то вроде этого

class MyClass 
{ 
public: 
    void Func1(); 

    template <typename T> 
    void Func2(T param); 
}; 

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

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

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

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

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

Что-то вдоль линий

class MyInterface 
{ 
public: 
virtual void Func1() = 0; 
}; 

template <typename T> 
class MyImplementation 
{ 
public: 
    virtual void Func1() 
    { 
    m_impl->Func1(); 
    } 

private: 
    T* m_impl; 
}; 

Но опять же шаблонная функция-членов, кажется, чтобы блокировать этот подход.

Я посмотрел на классы boost :: any и boost ::, которые, как я думал, могут предложить какое-то решение, но они, похоже, не дают мне правильного ответа.

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

Заранее спасибо

+0

Какую проблему вы действительно пытаетесь решить? –

+0

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

+0

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

ответ

2

Что не совсем ясно для меня, так это то, как вы решаете параметр T to Func2, вам нужна какая-то динамическая отправка, или это известно во время компиляции на сайте вызова?

В первом случае это звучит как мультиметоды. В последнем, как об этом изменении на вашем интерфейсе идеи:

#include <iostream> 

template<class T> struct generic_delegate 
{ 
    virtual void call(T param) = 0; 
}; 

template<class U, class T> class fn_delegate : public generic_delegate<T> 
{ 
    U* obj; 
    void (U::*fn)(T); 

public: 
    fn_delegate(U* o, void (U::*f)(T)) : 
    obj(o), fn(f) 
    {} 

    virtual void call(T param) 
    { 
    (obj->*fn)(param); 
    } 
}; 


class A 
{ 
public: 
    template<class T> void fn(T param) 
    { 
    std::cout << "A: " << param << std::endl; 
    } 
}; 

class B 
{ 
public: 
    template<class T> void fn(T param) 
    { 
    std::cout << "B: " << param << std::endl; 
    } 
}; 


template<class T, class U> generic_delegate<T>* fn_deleg(U* o) 
{ 
    return new fn_delegate<U, T>(o, &U::template fn<T>); 
} 


int main() 
{ 
    A a; 
    B b; 

    generic_delegate<int>* i = fn_deleg<int>(&a); 
    generic_delegate<int>* j = fn_deleg<int>(&b); 

    i->call(4); 
    j->call(5); 
} 

Очевидно, что вы бы огибают являются общие указатели делегата.

1

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

template <class T> 
void DoSomethingWithMyInterface(MyInterface<T> X) 
{ 
    //do something 
} 

... 

switch (MyObject.GetTypeCode()) 
{ 
case TYPE1: DoSomethingWithMyInterface<type1>(MyObject); break; 
case TYPE2: DoSomethingWithMyInterface<type2>(MyObject); break; 
case TYPE3: DoSomethingWithMyInterface<type3>(MyObject); break; 
case TYPE4: DoSomethingWithMyInterface<type4>(MyObject); break; 
} 

На самом деле я использую эту ситуацию. Я пишу templated код C++, который обрабатывает динамически типизированный язык. Это означает, что язык верхнего уровня не знает типы данных до времени выполнения, но мне нужно знать их во время компиляции. Поэтому я создаю этот «TypeSwitch» (у меня действительно есть многообещающий многоразовый). Это смотрит на типы данных во время выполнения, а затем вычисляет, какая из уже скомпилированных функций шаблона запускается.

Примечание: для этого требуется, чтобы я знал все типы, которые я буду поддерживать до того, как это будет сделано (и я это сделаю), и оператор switch фактически заставит компилятор сгенерировать весь код, который может быть выполнен. Затем во время выполнения выбирается правильный.

+0

Это интересное решение, и я думаю, может решить проблему в этом случае. Клиент работает с небольшим подмножеством различных объектов, для которого не требуется массовый оператор switch. –