2017-10-18 262 views
0

我正在設置一個控制檯命令,它接受可變數量的參數,每個參數都可以是基本類型(int,float,bool,string),然後將它們傳遞給一個具有8個重載以支持不同數字的函數不同類型的論點。我如何將命令行字符串解析爲基於類型的值,然後將它們傳遞給函數?如何將可變數量和類型的參數傳遞給模板函數?

我可以通過函數const char* GetArg(int index)檢索每個參數。將char*轉換爲正確的類型不是問題,所以不要擔心該部分。存儲這些值並以某種方式傳遞給模板函數是我堅持的部分。

例如,如果該命令是用下面的字符串執行的:「命令66真5‘的字符串值’0.56」

它隨後將被分成下面的指定參數和以某種方式存儲:

int arg1 = GetArg(1); // 66 
bool arg2 = GetArg(2); // true 
int arg3 = GetArg(3); // 5 
char* arg4 = GetArg(4); // "string value" 
float arg5 = GetArg(5); // 0.56 

,然後根據args來數,調用正確的模板函數:

// The function definition looks something like this: 
void SomeFunc(); 
template<typename T1> 
void SomeFunc(const T1& arg1); 
template<typename T1, typename T2> 
void SomeFunc(const T1& arg1, const T2& arg2); 
// etc... 

// And then somehow it would be called. This is just an example. I don't 
// know how to call it in a way that would work with variable number and 
// type of args. 
switch (argCount) 
{ 
case 0: 
    SomeFunc(); 
    break; 
case 1: 
    SomeFunc(arg1); 
    break; 
case 2: 
    SomeFunc(arg1, arg2); 
    break; 
case 3: 
    SomeFunc(arg1, arg2, arg3); 
    break; 
case 4: 
    SomeFunc(arg1, arg2, arg3, arg4); 
    break; 
case 5: 
    SomeFunc(arg1, arg2, arg3, arg4, arg5); 
    break; 
} 

你將如何實現這一目標?以某種可以傳遞給模板函數的方式存儲參數,以便它知道每個參數的類型似乎不可能,但我覺得我只是沒有想到什麼。

我也不能改變這個接口。這是我必須處理的第三方功能。所以無論它如何實施,最終都必須經過SomeFunc()

重要提示:我在Visual Studio 2012中這樣做,所以我對新的C++功能有限制。它可以做一點C++ 11,但就是這樣。試圖將項目升級到更新的版本,但現在這是我必須處理的。

+0

這聽起來像你正試圖混合編譯時和運行時邏輯。模板是編譯時,你需要在編譯時知道參數的數量和類型。這是否真的是你想要實現的其他東西? –

+0

那麼如果你可以構建一個元組,那麼你可以使用[this](https://stackoverflow.com/questions/7858817/unpacking-a-tuple-to-call-a-matching-function-pointer)來打開元組進入函數調用。 – NathanOliver

+0

@TommyAndersen聽起來很準確。我真的沒有辦法解決這個問題。我只是試圖製作一個輔助控制檯命令來輕鬆觸發事件,每個事件可以有不定數量的參數和類型。如果這種情況失控,我很可能只是想出這個想法,而這開始看起來可能是這樣。 – Shenjoku

回答

1
using basic_type = std::variant<int, float, bool, std::string>; 

using flat_arguments = std::vector<basic_type>; 

template<std::size_t...Ns> 
using packed_arguments = std::variant< std::array<basic_type, Ns>... >; 

template<class T, std::size_t...Ns> 
std::array<T, sizeof...(Ns)> pack_one(std::vector<T> n, std::index_sequence<Ns...>) { 
    return {{ std::move(n[Ns])... }}; 
} 

template<class T, std::size_t...Ns> 
std::optional<std::variant< std::array<T, Ns>... >> 
pack_all(std::vector<T> n, std::index_sequence<Ns...>) { 
    std::optional<std::variant< std::array<T, Ns>... >> retval; 
    if (n.size() >= sizeof...(Ns)) { return retval; } 
    (
    (
     (n.size()==Ns)? 
     void(retval.emplace(pack_one(std::move(n), std::make_index_sequence<Ns>{}): 
     void() 
    ),... 
); 
    return retval; 
} 

flat_arguments get_arguments(int argc, char const* const*argv); // write this 

auto invoke_somefunc = [](auto&&...args){ 
    return SomeFunc(decltype(args)(args)...); 
}; 

int main(int argc, char const*const* argv) { 
    auto args = get_arguments(argc, argv); 
    auto args_packed = pack_all(std::move(args), std::make_index_sequence<9>{}); 
    if (!args_packed) return -1; 
    std::visit([](auto&& args){ 
    std::apply([](auto&&...args){ 
     std::visit(invoke_somefunc, args...); 
    }, args); 
    }, args_packed); 
} 

應該這樣做。可能包含錯別字。

boost有相同的類型(variantoptional),可以取代上面的std使用,並做了一些調整。

摺疊包擴展可以用或更高版本中的展開式擴展替換。

+0

對不起,我忘了提及我對新語言功能的限制。我在VS2012中這樣做,所以我不認爲這是可用的。我用這些信息更新了原始帖子。 – Shenjoku

+0

@ Shenjoku哦,那麼不,你不能。我的意思是,有圖靈焦油坑,在那裏你可以做任何事情,但你基本上將上述工作手動擴展到一堆意大利麪代碼。使用VS 2012支持的C++子語言編寫C++編譯器要容易得多。也許如果你可以找到一個支持變體的古老版本的boost,並且可以在VS 2012中工作,並且手動編寫函數對象來替換lambda表達式和所有這些痛苦。 – Yakk

相關問題