2014-09-10 56 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);