我有一个函数
unpack
,它应该从可变参数模板表达式构造一个 std::tuple
。该函数如下所示(包括注释掉的失败尝试):
template<typename T, typename... Ts>
std::tuple<T,Ts...> unpack(std::stringstream& ss)
{
T t{};
WriteTypeStruct ret{};
if (writeType(ret, ss, t); (!ss || ret.err)) {
throw std::runtime_error(ret.msg);
}
//std::tuple args = {extract<std::decay_t<Ts>>(ss)...};
//std::tuple args = unpack<typename std::decay_t<Ts>...>(ss);
//return {t, unpack<Ts>(ss)...};
//return std::make_tuple(t, unpack<Ts>(ss)...);
//std::tuple<Ts...> unpacked = {unpack<Ts>(ss)...};
//return std::make_tuple(t, unpacked);
//return std::tuple_cat(std::tuple<T>(t), std::make_from_tuple(unpack<Ts>(ss)...));
return std::make_tuple(t, std::make_from_tuple<Ts...>(unpack<Ts>(ss)...)); // <-- cannot compile!
}
函数
writeType
是一系列模板化重载函数,我用它来检查是否可以从 std::stringstream
中提取预期类型。该函数在 WriteTypeStruct
中放置一些返回值,其中包含一个布尔值和一个用于错误消息的字符串。
TLDR;我正在为命令控制台编写一个基于字符串的解释器。
问题的完整介绍如下:如何为基于文本的命令控制台存储参数化的强类型函数,特别是@jarod42的答案。我正在使用这个
unpack
函数而不是 extract
。
关于如何展平
std::tuple
这里有一个很好的解释,但我希望通过首先不创建嵌套元组可以避免这种代码。
但我似乎无法弄清楚正确的语法。
我将从反序列化开始:
template<class T>
T deserialize(std::stringstream& ss) {
T t{};
WriteTypeStruct ret{};
if (writeType(ret, ss, t); (!ss || ret.err)) {
throw std::runtime_error(ret.msg);
}
return t;
}
其语义非常简单。
template<class...Ts>
std::tuple<Ts...> unpack(std::stringstream& ss) {
return std::tuple<Ts...>{ unpack<Ts>(ss)... };
}
保证这些
{}
内的评估顺序。 (注意:这是因为我使用了 {}
ctor,而不是函数调用。程序员可以轻松地对上面的内容进行更改,“不执行任何操作”,并以某种方式严重破坏它,具体取决于您使用的编译器。)
大多数
unpack
尝试遇到的问题是它们不能很好地处理 unpack<>
(终止情况)。
那些调用
unpack<Ts>...
而不是 unpack<Ts...>
的(即,保证通过 1,并且解包调用在 0 处被消除)最终会导致 1 层过多的元组,unpack<Ts>...
是一组元组,您可以包裹在元组中。
return std::tuple_cat(std::tuple<T>(t), std::make_from_tuple(unpack<Ts>(ss)...));
这已经很接近了。要修复它:
return std::tuple_cat(std::tuple<T>(t), unpack<Ts>(ss)...);
只需去除
tuple
上多余的 unpack
层即可; unpack
已经返回一个元组。然后我们tuple_cat
,它支持任意数量的元组。
我们可以尝试优化这个:
return std::tuple_cat(std::tuple<T>(t), unpack<Ts...>(ss));
但这会遇到空的
unpack<>
问题。
我遇到的一个问题是,您将所有内容放入一个元组中,只是为了再次将其取出。
空的
unpack<>
也可以处理。在 unpack
之前添加:
template<bool empty_pack=true>
std::tuple<> unpack(std::stringstream& ss) { return {}; }
那么
unpack<>
应该称之为重载。
但是,我发现所有这些都不如从元组版本调用的 1 元素版本优雅。
最后,我们可以编写一个元组展平函数,它接受任何一组参数,如果其中任何一个是元组,它就会展平它们,并返回融合列表。我个人认为这个函数很诱人,但不是一个好主意,因为它会立即“解决”问题,但随后会导致棘手的问题。
这有点像编写一个对文本进行取消转义的函数,但对任意数量的层进行取消转义,直到不再有转义序列为止。或者转义文本,但拒绝转义任何已经转义的内容。
您将其放入业务逻辑链中,它“解决”了不均匀转义或未转义数据的问题......并以无法修复的方式破坏事物。