2017-10-18 162 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

相关问题