Универсальные обратные вызовы

Расширяется

Похожие страницы

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

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

// First function to call
int add( int x, int y ) ;

// Second function to call
double square( double x ) ;

// Third func to call
void go() ;

Код создания обратного вызова должен быть похожим:

// Write a callback object that
// will be executed after 42ms for "add"
Callback c1 ;
c1.func = add ;
c1.args.push_back( 2 );  // these are the 2 args
c1.args.push_back( 5 );  // to pass to the "add" function
                         // when it is called

Callback c2 ;
c2.func = square ;
c2.args.push_back( 52.2 ) ;

То, что я думаю, с помощью шаблонного метапрограммирования, как которое я хочу смочь объявить обратные вызовы, записать структуру как это (имейте в виду, что это - ОЧЕНЬ ПСЕВДОКОД),

> >
struct Callback
{
    double execTime ; // when to execute
    TYPE1 (*func)( TYPE2 a, TYPE3 b ) ;

    void* argList ;   // a stored list of arguments
                      // to plug in when it is time to call __func__
} ;

Таким образом для при вызове

Callback c1 ;

Вы автоматически создать для Вас <ХАРДКОРНОЕ ДЕЙСТВИЕ ШАБЛОННОЙ ОБРАБОТКИ> структура как

struct Callback
{
    double execTime ; // when to execute
    int (*func)( int a, int b ) ;

    void* argList ;   // this would still be void*,
                      // but I somehow need to remember
                      // the types of the args..
} ;

Какие-либо указатели в правильном направлении для начала работы при записи этого?

5
задан Community 23 May 2017 в 12:03
поделиться

4 ответа

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

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

template <typename ... Args>
class Variadic {
public:
   operator()(Args&& ... args);
};

В выражении вызова функции многоточие распаковывает свой левый аргумент.

Variadic<Args>::operator(Args&& ... args) {
    func(args...);
}

Для пересылки может потребоваться использовать std :: forward ; это одна из областей, в которой мои знания становятся нечеткими. Сложите это вместе, и мы получим:

template <typename ReturnValue, typename ... Args>
class Callback {
    typedef ReturnValue (*Func)(Args ... args);

    double execTime;
    Func func;
    Args... args;

public:
    Callback(double et, Func f) : execTime(et), func(f) {}
    ReturnValue operator()(Args&& ... a);
    ReturnValue operator()();
};

template <typename ReturnValue, typename ... Args>
ReturnValue Callback<ReturnValue, Args>::operator()(Args&& ... a) {
    return (*func)(std::forward(a)...);
}
template <typename ReturnValue, typename ... Args>
ReturnValue Callback<ReturnValue, Args>::operator()() {
    return operator(*func)(args...);
}
2
ответ дан 15 December 2019 в 06:22
поделиться

В C ++ 0x добавлены вариативные шаблоны, которые напрямую поддерживают шаблон, который принимает произвольное количество параметров. Без этого вы можете использовать частичную специализацию для ее моделирования, хотя она требует отдельной специализации для каждого количества параметров. Например, вы можете поддерживать от 1 до 3 таких параметров:

class null_class {};

template <class func, class arg1, class arg2, class arg3>
class callback_t { 
    func f;
    arg1 a;
    arg2 b;
    arg3 c;
public:
    callback_t(func f, arg1 a, arg2 b, arg3 c) : f(f), a(a), b(b), c(c) {}
    double operator()() const { return f(a, b, c); }
};

template <class func, class arg1, class arg2> 
class callback_t<func, arg1, arg2, null_class> { 
    func f;
    arg1 a;
    arg2 b;
public:
    callback_t(func f, arg1 a, arg2 b) : f(f), a(a), b(b) {}
    double operator()() const { return f(a, b); }
};

template <class func, class arg1> 
class callback_t<func, arg1, null_class, null_class> {
    func f;
    arg1 a;
public:
    callback_t(func f, arg1 a) : f(f), a(a) {}
    double operator()() const { return f(a); }
};

template <class func, class arg1, class arg2, class arg3>
callback_t<func, arg1, arg2, arg3> 
callback(func f, arg1 a, arg2 b, arg3 c) { 
    return callback_t<func, arg1, arg2, arg3>(f, a, b, c);
}

template <class func, class arg1, class arg2>
callback_t<func, arg1, arg2, null_class> 
callback(func f, arg1 a, arg2 b) {
    return callback_t<func, arg1, arg2, null_class>(f, a, b);
}

template <class func, class arg>
callback_t<func, arg, null_class, null_class>
callback(func f, arg a) {
    return callback_t<func, arg, null_class, null_class>(f, a);
}

#ifdef TEST
#include <iostream>

double square(double d) { 
    return d * d;
}

double add(double a, double b) { 
    return a + b;
}

double sum(double a, double b, double c) { 
    return a + b + c;
}

int main() {
    double a = 2.0, b = 3.0, c=4.0;

    double d = callback(square, a)();
    double e = callback(add, b, c)();
    double f = callback(sum, a, b, c)();

    std::cout << "2.0 squared = " << d << "\n";
    std::cout << "3.0 + 4.0 = " << e << "\n";
    std::cout << "Sum = " << f << "\n";
    return 0;
}

#endif

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

0
ответ дан 15 December 2019 в 06:22
поделиться

Посмотрите на boost::bind. Мне больше нечего сказать... вероятно, лучше потратить время на изучение их исходников и попытку их переделать, если вы действительно хотите понять внутреннюю суть. Но учитывая, как хорошо они его отполировали, переделка - это только академическое занятие.

1
ответ дан 15 December 2019 в 06:22
поделиться

Для начала ознакомьтесь с Boost.Function , поскольку речь идет об автоматической упаковке функций, это даст вам идеи. think;)

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

Callback< int(int,int) > callback;

Это означает, что ваш обратный вызов будет принимать указатель на функцию с подписью, аналогичной вашей add : int add (int, int) . На самом деле я предпочитаю этот синтаксис, поскольку он проясняет то, что мы передаем.

Прежде чем мы начнем, у меня есть вопрос: что вы хотите сделать с возвращаемым типом?

1. Ссылки

Также есть такие вещи, как библиотека Boost.Fusion , которая может вам очень помочь (в основном, кортежи).

Также ознакомьтесь с Boost.FunctionTypes , который предлагает средства для анализа сигнатуры функции.

2. Снова в пути

// It is nice to have a base class
// Generally callbacks do not return anything though...
struct CallbackBase
{ 
  virtual ~CallbackBase();
  virtual void execute() const = 0;
};

namespace func_ = boost::function_types;

template <
  class Parameters,
  class N = typename mpl_::minus<
    typename mpl_::size< Parameters >::type,
    mpl_::size_t<1>
  >::type
>
class Streamer
{
public:
  typedef Streamer< Parameters, typename mpl_::minus<N,1>::type > next_type;
  typedef typename mpl_::size< Parameters >::type size_type;
  typedef typename mpl_::minus< size_type, N >::type index_type;
  typedef typename mpl_::at<Parameters, index_type>::type arg_type;

  Streamer(Parameters& p): mParameters(p) {}

  next_type operator<<(arg_type arg)
  {
    boost::fusion::at_c<index_type>(mParameters) = arg;
    return next_type(mParameters);
  }

private:
  Parameters& mParameters;
};

template <class Parameters>
struct Streamer<Paramaters,0>
{
  Streamer(Parameters&) {}
};


template <class Function>
class Callback: public CallbackBase
{
public:
  typedef typename func_::result_type<Function>::type result_type;
  typedef typename func_::parameters_type<Function>::type parameters_type;
  typedef typename func_::function_pointer<
    typename func_::components<Function>::type
  >::type function_pointer;

  Callback(function_pointer f): mFunction(f) {}

  virtual void execute() const
  {
    mReturn = Invoke<function_pointer>::Do(f,mParameters);
  }

  Streamer<parameters_type> operator<<(typename mpl_::at<parameters_type, 0>::type arg)
  {
    boost::fusion::at_c<0>(mParameters) = arg;
    return Streamer<parameters_type>(mParameters);
  }

private:
  function_pointer f;
  result_type mResult;
  parameters_type mParameters;
};

Ну вот как далеко я зашел. Я не занимался фактическим вызовом, который требует распаковки кортежа для передачи аргументов функции.

На данный момент использование будет следующим:

int add(int,int);

void pass(const CallbackBase& b);

int main(int argc, char* argv[])
{
  Callback<int(int,int)> c(&add);
  c << 2 << 4;
  pass(c);
}

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

0
ответ дан 15 December 2019 в 06:22
поделиться
Другие вопросы по тегам:

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