Функции как аргумент шаблона, плюс вариационный аргумент шаблона

Я пишу обобщенную оболочку функций, которая может обернуть любую функцию в вызов типа lua, который имеет форму

int lua_function (lua_State * L)

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

template <typename R, typename Arg1, typename Arg2, R F(Arg1, Args)>
struct wrapper

Тем не менее, я не знаю числа, поэтому я прошу аргумент varadic template для справки

// This won't work
template <typename R, typename... Args, R F(Args...)>
struct wrapper

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

template <typename R, typename... Args>
struct func_type<R(Args...)>
{
  // Inner function wrapper take the function pointer as a template argument
  template <R F(Args...)>
  struct func
  {
    static int call( lua_State *L )
    {
      // extract arguments from L
      F(/*arguments*/);
      return 1;
    }
  };
};

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

double sin(double d) {}

пользователь должен написать

func_type<decltype(sin)>::func<sin>::apply

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

Ответы

Ответ 1

Такие вещи, как std::function и std::result_of, используют следующий метод, чтобы делать то, что вы хотите относительно вариативных шаблонов:

template<typename Signature>
struct wrapper; // no base template

template<typename Ret, typename... Args>
struct wrapper<Ret(Args...)> {
    // instantiated for any function type
};

Вы можете развернуть приведенное выше, чтобы добавить параметр шаблона не-type Ret(&P)(Args...) (указатели на функцию работают так же хорошо), но вам все равно потребуется decltype на уровне пользователя, т.е. wrapper<decltype(sin), sin>::apply. Возможно, это будет законное использование препроцессора, если вы решите использовать макрос для удаления повторения.

template<typename Sig, Sig& S>
struct wrapper;

template<typename Ret, typename... Args, Ret(&P)(Args...)>
struct wrapper<Ret(Args...), P> {
    int
    static apply(lua_State*)
    {
        // pop arguments
        // Ret result = P(args...);
        // push result & return
        return 1;
    }
};

// &wrapper<decltype(sin), sin>::apply is your Lua-style wrapper function.

Выше компилируется с gcc-4.5 на ideone. Удачи вам в реализации приложения, которое (вариативно) выдает аргументы (оставьте мне комментарий, если вы откроете вопрос об этом). Рассматривали ли вы использование Luabind?

Ответ 2

Как отмечает @Juraj в своем комментарии, указатель функции может быть аргументом шаблона, см. следующий простой пример:

#include <iostream>
#include <boost/typeof/typeof.hpp>

void f(int b, double c, std::string const& g)
{
  std::cout << "f(): " << g << std::endl;
}

template <typename F, F* addr>
struct wrapper
{
  void operator()()
  {
    std::string bar("bar");
    (*addr)(1, 10., bar);
  }  
};

int main(void)
{
  wrapper<BOOST_TYPEOF(f), &f> w;
  w();
  return 0;
}

рабочая версия: http://www.ideone.com/LP0TO

Я использую BOOST_TYPEOF, как обычно, всегда предлагаю примеры в текущем стандарте, но он делает что-то похожее на decltype. Это то, что вы были после?