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

Я пишу обобщенную оболочку функций, которая может обернуть любую функцию в вызов типа 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. Это то, что вы были после?