2014-09-10 55 views
2

我想使用可变参数模板来帮助使用va-args解决问题。基本上,我想调用一个单一的函数,将一个“命令”和一个变量列表一起传递给函数,然后将这些参数派发到另一个函数中。Variadic Template Dispatcher

我已经实现了使用尝试和真实(但不是类型安全)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; 
} 

有没有人对我怎么能修改此功能,以正确地使用可变参数模板的想法?

+0

为什么不使用普通的旧函数重载?定义一个函数'DispatchCommand',它需要4个参数,用一个参数重载它,等等。 – dyp 2014-09-10 19:13:57

+2

编译时是否已知'COMMANDS cmd'? – Jarod42 2014-09-10 19:15:31

+0

也许这将有助于:https://stackoverflow.com/a/25264850 – Deduplicator 2014-09-10 19:19:34

回答

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

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