【问题标题】:Char sequence from string at compile time WITHOUT recursion编译时字符串中的字符序列没有递归
【发布时间】:2021-07-30 20:06:45
【问题描述】:

是的,这个话题可能看起来已经被问了一百次了,但我问的是非常不同的。

请不要误会我的意思:模板递归在 C++03 中使用时可能很棒,并且是某些习语的唯一方法,但在 MSVS 2017/2019 编译器中使用时可能会出现问题并变得可怕fatal error C1202: recursive type or function dependency context too complex.

我知道 gcc 和 clang 管理的递归比 MSVS 好得多,但这次编译器是合同主体,不能更改。

我开始使用boost::hana::string,但使用复杂的字符串(超过 2000 个字符)会出现错误。

我想知道是否有某种方法可以复制 BOOST_HANA_STRING 宏的行为(基本上它需要一个字符串并将其转换为 char 序列,或根据输入字符串转换为 wchar)但摆脱递归。

我猜要付出的代价是只使用 C++17,而是使用 fold,但对我来说这是负担得起的。

顺便说一下,请不要提供operator""_cs的解决方案,因为MSVS不兼容这样的gcc扩展。

我想要实现的伪代码:

STRING("Hello") => my_static_string<char, 'H', 'e', 'l', 'l', 'o'>

【问题讨论】:

  • "超过 2000 个字符" 大多数编译器是否允许在参数包中包含 2000 个元素?似乎问题不在于递归;就是编译器根本不支持那么大的包。
  • 测试参数包;如果这不是你的问题,那么日志深度递归是可能的(在伪代码中,split(n, seq):={split(n/2,seq).left+split(n-n/2, split(n/2,seq).right).left, split(n-n/2,split(n/2,seq).right).right) 带有子表达式保存是 n 中的日志 2 深度。这意味着你可以制作一棵二叉树并仅使用日志深度遍历它。但我我猜 hana 已经这样做了。
  • @Nicol Bolas:感谢您的评论,但我不同意您的说法,至少部分不同意。相同长度的相同字符串在 Clang 和 Gcc 中工作正常,但在 MSVS2017 中失败,所以问题主要出在 MS 编译器中,至少有大约 200 万个字符。顺便说一句,它并不像看起来那样,像 html 或 xaml 这样的标记语言可以很容易地达到这个长度。
  • @Yakk - Adam Nevraumont 感谢您的评论。 Hana 基于 N-1 递归模式和 std::integer_sequence 的初始使用。 Gcc 和 Clang 管理相同的字符串没有问题,所以我猜 MSVS2017 在递归(或参数包的元素数量)方面存在一些问题。但是我的问题仍然悬而未决,是否可以从字符串中获取字符序列而无需递归?
  • @Pablo:“所以问题主要出在 MS 编译器中,至少有大约 200 万个字符。”我的意思是限制是 不是 i>“递归”;限制是包中允许的可变参数模板参数的数量。同样重要的是,这个限制可能不会消失,所以如果你想在那个编译器上运行,你需要在编译时停止尝试处理 HTML 或 XAML 或任何东西。

标签: c++ templates c++17 compile-time


【解决方案1】:

我想知道是否有某种方法可以复制BOOST_HANA_STRING 宏的行为(基本上它需要一个字符串并将其转换为字符序列,或根据输入字符串转换为 wchar)但摆脱递归。

嗯,这会很困难,因为BOOST_HANA_STRING doesn't use recursion

    namespace string_detail {
        template <typename S, std::size_t ...N>
        constexpr string<S::get()[N]...>
        prepare_impl(S, std::index_sequence<N...>)
        { return {}; }

        template <typename S>
        constexpr decltype(auto) prepare(S s) {
            return prepare_impl(s,
                std::make_index_sequence<sizeof(S::get()) - 1>{});
        }
    }

#define BOOST_HANA_STRING(s)                                                \
    (::boost::hana::string_detail::prepare([]{                              \
        struct tmp {                                                        \
            static constexpr decltype(auto) get() { return s; }             \
        };                                                                  \
        return tmp{};                                                       \
    }()))                                                                   \

注意没有递归。它只是获取字符串的大小,使用它来构建索引序列,并使用该序列来构建字符串元素的包扩展。它总是恰好是 2 个函数调用深度;不多不少。

你的问题不是递归;您的问题是 Visual Studio 不支持“2000”元素参数包。 C++ 标准只推荐编译器支持 1024 个模板参数和Visual C++ support 2046。我不会指望这个数字在未来会变得更大,所以你需要找到替代方法来做你正在做的任何事情。

此外,C++20 允许您创建一个真正的编译时字符串类,该类存储一个实际字符的实际数组,您可以在正常的实际代码而不是元编程中实际使用这些字符。并且可以作为模板参数使用; 单个模板参数。并且 VC++ 已经对此提供了支持。

【讨论】:

  • 谢谢,现在我知道你从一开始就是对的。由于未验证的 MSVS 错误消息,我被蒙蔽和混淆了,这是一个递归问题。可悲的是,编译器对我来说是合同问题,所以我现在不能使用 C++20,所以我想我应该使用运行时字符串,即使它们可以在编译时连接......
  • 没有内在函数,std::make_index_sequence 是递归的。
  • @Jarod42 除了参数包的限制,这是一个好点
猜你喜欢
  • 2010-10-28
  • 2011-06-25
  • 1970-01-01
  • 1970-01-01
  • 2016-04-20
  • 1970-01-01
  • 2015-05-12
  • 1970-01-01
  • 2017-04-24
相关资源
最近更新 更多