C++17 嵌套 std::tuple 的递归展平

问题描述 投票:0回答:1

我有一个函数

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
这里有一个很好的解释,但我希望通过首先不创建嵌套元组可以避免这种代码。

但我似乎无法弄清楚正确的语法。

c++ c++17 variadic-templates stdtuple
1个回答
0
投票

我将从反序列化开始:

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 元素版本优雅。

最后,我们可以编写一个元组展平函数,它接受任何一组参数,如果其中任何一个是元组,它就会展平它们,并返回融合列表。我个人认为这个函数很诱人,但不是一个好主意,因为它会立即“解决”问题,但随后会导致棘手的问题。

这有点像编写一个对文本进行取消转义的函数,但对任意数量的层进行取消转义,直到不再有转义序列为止。或者转义文本,但拒绝转义任何已经转义的内容。

您将其放入业务逻辑链中,它“解决”了不均匀转义或未转义数据的问题......并以无法修复的方式破坏事物。

© www.soinside.com 2019 - 2024. All rights reserved.