我正在尝试编写某种命令处理程序,它可以对istringstream进行标记,自动将标记转换为特定类型的变量,并使用转换后的变量作为参数调用回调函数。这是我的代码的简化版本:
void Callback(int x, char y, float z) {
// do whatever
// note: For simplicity, I use a callback with a fixed signature
// here. In my actual implementation, the callback can be
// called with any number and types of arguments - but that
// I have solved already.
}
template<typename T>
T GetNextArgument(std::istringstream& strm) {
// get one token from the input stream and convert it to the required type
T val;
strm >> val;
return val;
}
template<typename ...Args>
void ParseAndExecute(std::istringstream& input_stream) {
Callback(GetNextArgument<Args>(input_stream)...);
}
int main() {
std::istringstream strm("15 a 17.3");
ParseAndExecute(strm);
return 0;
}
我遇到的问题是参数包扩展后的ParseAndExecute()
函数如下所示:
void ParseAndExecute(std::istringstream& strm) {
Callback(GetNextArgument<int>(strm),
GetNextArgument<char>(strm),
GetNextArgument<float>(strm));
}
由于未定义参数的评估顺序,因此可能会以不正确的顺序从流中获取标记(在我的情况下,它们始终是)。相反,我需要扩展来给我更多这样的东西:
void ParseAndExecute(std::istringstream& strm) {
int a1 = GetNextArgument<int>(strm);
char a2 = GetNextArgument<char>(strm);
float a3 = GetNextArgument<float>(strm);
Callback(a1, a2, a3);
}
但我无法看到如何通过参数包扩展实现这一目标。也许用递归模板......?或者你有任何其他建议来实现类似的功能吗?
答案 0 :(得分:3)
您可以将中间std::tuple
与list initialization一起使用,因为在这种情况下,从左到右的顺序是强制性的:
std::tuple<Args...> tuple_args = {GetNextArgument<Args>(input_stream)... };
std::apply([](auto&&... args) {
Callback(std::forward<decltype(args)>(args)... );
}, std::move(tuple_args));
如果Callback
没有你提到的固定签名并且你想依赖扣除,你必须使用lambda。
答案 1 :(得分:3)
struct Caller {
template<class...Args>
Caller(Args&&... args) { Callback(std::forward<Args>(args)...); }
};
template<typename ...Args>
void ParseAndExecute(std::istringstream& input_stream) {
Caller{GetNextArgument<Args>(input_stream)...};
}