Как передать указатель на метод в качестве параметра шаблона - PullRequest
3 голосов
/ 09 июля 2010

Я пытаюсь написать код, который вызывает метод класса, заданный в качестве параметра шаблона. Для упрощения можно предположить, что метод имеет единственный параметр (произвольного типа) и возвращает void. Цель состоит в том, чтобы избежать шаблонов в вызывающем сайте, не вводя тип параметра. Вот пример кода:

template <class Method> class WrapMethod {
  public:
   template <class Object>
   Param* getParam() { return &param_; }
   Run(Object* obj) { (object->*method_)(param_); }
  private:
    typedef typename boost::mpl::at_c<boost::function_types::parameter_types<Method>, 1>::type Param;
    Method method_;
    Param param_
};

Теперь на вызывающем сайте я могу использовать метод, даже не записывая тип параметра.

Foo foo;
WrapMethod<BOOST_TYPEOF(&Foo::Bar)> foo_bar;
foo_bar.GetParam()->FillWithSomething();
foo_bar.Run(foo);

Итак, этот код работает, и это почти то, что я хочу. Единственная проблема заключается в том, что я хочу избавиться от вызова макроса BOOST_TYPEOF на вызывающем сайте. Я хотел бы иметь возможность написать что-то вроде WrapMethod<Foo::Bar> foo_bar вместо WrapMethod<BOOST_TYPEOF(&Foo::Bar)> foo_bar.

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

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

Просто чтобы уточнить мои потребности: в решении не должно быть типизированного имени Param на вызывающем сайте. Кроме того, он не может вызвать FillWithSomething изнутри WrapMethod (или аналогичный). Поскольку имя этого метода может измениться с типа Param на тип Param, оно должно находиться на вызывающем сайте. Решение, которое я дал, удовлетворяет обоим этим ограничениям, но нуждается в уродливом BOOST_TYPEOF на вызывающем сайте (его можно использовать внутри WrapMethod или другого косвенного обращения, так как это код, который мои пользователи API не увидят, пока он будет правильным). *

Ответ:

Насколько я могу сказать, нет никакого возможного решения. Это сводится к тому, что невозможно написать что-то вроде WrapMethod<&Foo::Bar>, если подпись Бар не известна заранее, хотя необходима только мощность. В целом, вы не можете иметь параметры шаблона, которые принимают значения (не типы), если тип не является фиксированным. Например, невозможно написать что-то вроде typeof_literal<0>::type, которое будет равно int и typeof_literal<&Foo::Bar>::type, что в моем примере будет равно void (Foo*::)(Param). Обратите внимание, что ни BOOST_TYPEOF, ни decltype не помогут, потому что они должны жить в месте вызова и не могут быть углублены в код. Допустимый, но неверный синтаксис, приведенный ниже, решит проблему:

template <template<class T> T value> struct typeof_literal {
  typedef decltype(T) type; 
};

В C ++ 0x, как указано в выбранном ответе (и в других, использующих BOOST_AUTO), можно использовать ключевое слово auto для достижения той же цели другим способом:

template <class T> WrapMethod<T> GetWrapMethod(T) { return WrapMethod<T>(); }
auto foo_bar = GetWrapMethod(&Foo::Bar);

Ответы [ 5 ]

3 голосов
/ 09 июля 2010

Запишите это как:

template <typename Object, typename Param, void (Object::*F)(Param)> 
class WrapMethod { 
public: 
   Param* getParam() { return &param_; } 
   void Run(Object* obj) { (obj->*F)(param_); } 

private: 
    Param param_;
}; 

и

Foo foo; 
WrapMethod<Foo, Param, &Foo::Bar> foo_bar;       
foo_bar.getParam()->FillWithSomething();
foo_bar.Run(foo);

РЕДАКТИРОВАТЬ: Отображение функции шаблона, позволяющей сделать то же самое без каких-либо специальных оболочек шаблона:

template <typename Foo, typename Param>
void call(Foo& obj, void (Foo::*f)(Param))
{
    Param param;
    param.FillWithSomthing();
    obj.*f(param);
}

и использовать его как:

Foo foo;
call(foo, &Foo::Bar);

2-е РЕДАКТИРОВАНИЕ: Изменение функции шаблона для получения функции инициализации в качестве параметра:

template <typename Foo, typename Param>
void call(Foo& obj, void (Foo::*f)(Param), void (Param::*init)())
{
    Param param;
    param.*init();
    obj.*f(param);
}

и используйте его как:

Foo foo;
call(foo, &Foo::Bar, &Param::FillWithSomething);
2 голосов
/ 09 июля 2010

Если ваш компилятор поддерживает decltype, используйте decltype:

WrapMethod<decltype(&Foo::Bar)> foo_bar;

EDIT: или, если вы действительно хотите сохранить типизацию и иметь компилятор, совместимый с C ++ 0x:

template <class T> WrapMethod<T> GetWrapMethod(T) { return WrapMethod<T>(); }
auto foo_bar= GetWrapMethod(&Foo::Bar);

EDIT2: Хотя на самом деле, если вы хотите, чтобы он выглядел красиво, вы должны либо познакомить пользователей со сложностями языка C ++, либо обернуть его самостоятельно в макрос препроцессора:

#define WrapMethodBlah(func) WrapMethod<decltype(func)>
0 голосов
/ 09 июля 2010

Хорошо, давайте посмотрим на это.

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

Итак, вотреализация (своего рода):

// WARNING: no virtual destructor, memory leaks, etc...

struct Foo
{
  void func(int e) { std::cout << e << std::endl; }
};

template <class Object>
struct Wrapper
{
  virtual void Run(Object& o) = 0;
};

template <class Object, class Param>
struct Wrap: Wrapper<Object>
{
  typedef void (Object::*member_function)(Param);

  Wrap(member_function func, Param param): mFunction(func), mParam(param) {}

  member_function mFunction;
  Param mParam;

  virtual void Run(Object& o) { (o.*mFunction)(mParam); }
};

template <class Object, class Param>
Wrap<Object,Param>* makeWrapper(void (Object::*func)(Param), Param p = Param())
{
  return new Wrap<Object,Param>(func, p);
}


int main(int argc, char* argv[])
{
  Foo foo;
  Wrap<Foo,int>* fooW = makeWrapper(&Foo::func);
  fooW->mParam = 1;
  fooW->Run(foo);

  Wrapper<Foo>* fooW2 = makeWrapper(&Foo::func, 1);
  fooW2->Run(foo);
  return 0;
}

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

0 голосов
/ 09 июля 2010

Если вы уже разрешаете BOOST_TYEPOF(), рассмотрите возможность использования BOOST_AUTO() с функцией генератора объектов, чтобы разрешить вывод типа:

template<class Method> WrapMethod<Method> makeWrapMethod(Method mfp) {
    return WrapMethod<Method>(mfp);
}

BOOST_AUTO(foo_bar, makeWrapMethod(&Foo::Bar));
0 голосов
/ 09 июля 2010

Рассматривали ли вы использование шаблонов методов?

 template <typename T> void method(T & param)
 {
   //body
 }

Теперь компилятор может неявно определять тип параметра

           int i;
           bool b;

           method(i);
           method(b);

Или вы можете явно указать тип

           method<int>(i);

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

           template <> void method<int>(int param)
           {
               //body
           }
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...