Передача объектов разных типов с одинаковым интерфейсом - PullRequest
2 голосов
/ 19 июля 2010

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

Таким образом, интерфейс выглядит примерно так

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 ::классы функций, которые, как я думал, могли бы предложить какое-то решение, но они, похоже, не дают мне правильного ответа.

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

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

Ответы [ 2 ]

2 голосов
/ 19 июля 2010

Что мне не совсем понятно, так это то, как вы преобразовываете параметр T в 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 голос
/ 19 июля 2010

Если вы используете шаблоны, вам нужно знать В КОМПЛЕЙНОЕ ВРЕМЯ, какой тип (типы) вы используете.Это просто природа шаблонов (шаблоны выглядят как динамический код во время выполнения, но на самом деле это просто сокращение, которое сообщает компилятору, какие версии функции нужно скомпилировать и включить в объектный код).В лучшем случае 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;
}

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...