【问题标题】:Variadic Template Dispatcher可变参数模板调度程序
【发布时间】:2014-11-04 13:31:00
【问题描述】:

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

我已经使用经过验证的真实(但不是类型安全)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;
}

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

【问题讨论】:

  • 为什么不使用普通的旧函数重载?定义一个函数DispatchCommand,它接受4个参数,重载一个接受5个参数的函数,以此类推。
  • 在编译时是否知道COMMANDS cmd
  • 也许这会有所帮助:stackoverflow.com/a/25264850
  • @Jarod42:必须提供正确的参数类型。因此,这个问题毫无意义。他试图“忘记”他将要使用哪个函数,然后再次推演,却无法正确推演。解决方案是首先不要忘记签名。
  • @Jarod40 - 是的,COMMANDS 枚举在编译时是已知的

标签: c++ templates variadic


【解决方案1】:

编写一些代码,给定一个函数指针和一组参数,使用这些参数中最长的前缀调用它。

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 被覆盖,则必须使用重载集技术。

【讨论】:

    【解决方案2】:

    您可以使用以下内容:

    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);
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-10-01
      • 2016-12-01
      • 1970-01-01
      • 1970-01-01
      • 2022-01-15
      • 2013-09-14
      • 1970-01-01
      • 2012-03-28
      相关资源
      最近更新 更多