Variadic模板调度程序

时间:2014-09-10 19:09:46

标签: c++ templates variadic

我想使用可变参数模板来帮助解决使用va-args的问题。基本上,我想调用一个单数函数,传入函数a"命令"以及变量参数列表,然后将参数分派给另一个函数。

我已经使用久经考验的(但不是类型安全的)va_list实现了这一点。这是我尝试使用可变参数模板进行的尝试。这个例子不在下面编译,因为你会很快找到原因......

#include <iostream>

using namespace std;
typedef enum cmd_t
{
    CMD_ZERO,
    CMD_ONE,
    CMD_TWO,
} COMMANDS;


int cmd0(double a, double b, double c)
{
    cout << "cmd0  " << a << ", " << b << ", " << c << endl;
    return 0;
}

int cmd1(int a, int b, int c)
{
    cout << "cmd1  " << a << ", " << b << ", " << c << endl;
    return 1;
}

template<typename... Args>
int DispatchCommand(COMMANDS cmd, Args... args)
{
    int stat = 0;
    switch (cmd)
    {
    case CMD_ZERO:
        cmd0(args...);
        break;
    case CMD_ONE:
        cmd1(args...);
        break;
    default:
        stat = -1;
        break;
    }
    return stat;
}

int main()
{
    int stat;
    stat = DispatchCommand(CMD_ZERO, 1, 3.141, 4);
    stat = DispatchCommand(CMD_ONE, 5, 6, 7);
    stat = DispatchCommand(CMD_TWO, 5, 6, 7, 8, 9);

    system("pause");
    return 0;
}

有没有人知道如何修改此函数以正确使用可变参数模板?

2 个答案:

答案 0 :(得分:0)

编写一些代码,给定一个函数指针和一组参数,用那些有效的参数的最长前缀来调用它。

template<class...>struct types{using type=types;};
template<class types0, size_t N, class types1=types<>>
struct split;

template<class t00, class...t0s, size_t N, class...t1s>
struct split<types<t00,t0s...>,N,types<t1s...>>:
  split<types<t0s...>,N-1,types<t1s...,t00>>
{};
template<class...t0s, class...t1s>
struct split<types<t0s...>,0,types<t1s...>>
{
  using right=types<t0s...>;
  using left=types<t1s...>;
};
template<class>using void_t=void;
template<class Sig,class=void>
struct valid_call:std::false_type{};
template<class F, class...Args>
struct valid_call<F(Args...), void_t<
  decltype( std::declval<F>()(std::declval<Args>()...) )
>>:std::true_type {};

template<class R, class types>
struct prefix_call;

template<class R, class...Args>
struct prefix_call<R, types<Args...>> {
  template<class F, class... Extra>
  std::enable_if_t< valid_call<F(Args...)>::value, R >
  operator()(R default, F f, Args&&...args, Extra&&...) const
  {
    return std::forward<F>(f)(args...);
  }
  template<class F, class... Extra>
  std::enable_if_t< !valid_call<F(Args...)>::value, R >
  operator()(R default, F f, Args&&...args, Extra&&...) const
  {
    return prefix_call<R, typename split<types<Args...>, sizeof...(Args)-1>::left>{}(
      std::forward<R>(default), std::forward<F>(f), std::forward<Args>(args)...
    );
  }
};

template<class R>
struct prefix_call<R, types<>> {
  template<class F, class... Extra>
  std::enable_if_t< valid_call<F()>::value, R >
  operator()(R default, F f, Extra&&...) const
  {
    return std::forward<F>(f)();
  }
  template<class F, class... Extra>
  std::enable_if_t< !valid_call<F()>::value, R >
  operator()(R default, F f, Extra&&...) const
  {
    return std::forward<R>(default);
  }
};

上面的代码可能包含错别字。

template<typename... Args>
int DispatchCommand(COMMANDS cmd, Args... args)
{
  int stat = 0;
  switch (cmd) {
    case CMD_ZERO: {
      stat = prefix_call<int, Args...>{}(-1, cmd0, std::forward<Args>(args)...);
    } break;
    case CMD_ONE: {
      stat = prefix_call<int, Args...>{}(-1, cmd1, std::forward<Args>(args)...);
    } break;
    default: {
      stat = -1;
    } break;
  }
  return stat;
}

如果覆盖cmd0cmd1,则必须使用重载设置技术。

答案 1 :(得分:0)

您可以使用以下内容:

template <COMMANDS cmd> struct command
{
    template <typename ... Args>
    int operator() (Args&&...) const { return -1; }
};

template <> struct command<CMD_ZERO>
{
    int operator()(double a, double b, double c) const
    {
        std::cout << "cmd0  " << a << ", " << b << ", " << c << std::endl;
        return 0;
    }
};

template <> struct command<CMD_ONE>
{
    int operator()(int a, int b, int c) const
    {
        std::cout << "cmd1  " << a << ", " << b << ", " << c << std::endl;
        return 1;
    }
};

template <COMMANDS cmd, typename... Args> int DispatchCommand(Args&&... args)
{
    return command<cmd>()(std::forward<Args>(args)...);
}

然后使用它:

DispatchCommand<CMD_ZERO>(1., 3.141, 4.);
DispatchCommand<CMD_ONE>(5, 6, 7);
DispatchCommand<CMD_TWO>(5, 6, 7, 8, 9);

Live example

但直接使用不同的功能似乎更简单:

cmd0(1., 3.141, 4.);
cmd1(5, 6, 7);