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

2nbm6dog  于 12个月前  发布在  其他
关注(0)|答案(1)|浏览(137)

我有一个函数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中放置一些返回值,其中包含一个布尔值和一个错误消息字符串。
我正在为命令控制台写一个基于字符串的解释器。
这里给出了这个问题的完整演示:如何为基于文本的命令控制台存储参数化的强类型函数,特别是@jarod42的答案。我使用这个unpack函数而不是extract
关于如何扁平化std::tuplehere,有一个很好的解释,但我希望通过首先不创建嵌套元组来避免这种代码。
但我好像不知道正确的语法。

nbysray5

nbysray51#

我会先从alteralize开始:

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处消除unpack调用)的那些最终会导致元组的层数过多,unpack<Ts>...是一组元组,您可以将其 Package 在元组中。

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)...);


只需删除unpack s上tuple额外层; 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元素版本优雅。
最后,我们可以写一个tuple flatten函数,它接受任何一组参数,如果其中任何一个是元组,它就将它们替换,并返回融合列表。我个人认为这个函数很有吸引力,但这是一个坏主意,因为它立即“解决”了一个问题,并在以后导致棘手的问题。
这有点像写一个函数,它不转义文本,但不转义任何层,直到没有更多的转义序列。或者一个转义文本,但拒绝转义任何已经转义的东西。
你把它扔到一个业务逻辑链中,它“解决”了一个不均匀转义或未转义数据的问题......并以无法修复的方式破坏了事物。

相关问题