查找C ++模板标识符列表的结尾

时间:2013-11-10 15:26:46

标签: c++ parsing templates

假设有一个,为了解析的目的,想要跳过模板标识符列表的内容:

template<(valid code)>
        ^            ^
        | from       | to

首先想到的是盲目地找到第一个&gt;,但这并不总是有效:

template<(valid code),template<(valid code)> >
        ^                                  ^
        | from                             | to, oops

更好的方法是跳过成对的&lt;和&gt;递归:

template<(valid code),template<(valid code)> >
        ^                                    ^
        | from                               | to, better

然而,即使这种方法对于像这样的奥术但有效的杰作也是失败的(来自bits \ random.h,第69行,GCC 4.7.x):

template<(...),bool = __w < static_cast<size_t>(...)>
        ^                 ^            ^      ^     ^     ^
        | 1               | 2          | 3    | 2   | 1   | where did 0 go?

我的问题是,找到任何有效模板标识符列表结尾的正确方法是什么?

2 个答案:

答案 0 :(得分:2)

找到任何有效的模板标识符列表的结尾真的没有简单的方法,因为除了bits/random.h的示例之外,还有更多的病态可能性。 (在这个答案的最后有一个病态案例:Is C++ context-free or context-sensitive?,其中标识符是否是模板,取决于较大的整数是否为素数。)

该算法易于说明(在C ++ 11标准的第14.2节第3段[temp.names]中)。基本上,<是模板括号,当且仅当它跟随模板标识符,单词template或其中一个cast运算符时。一旦打开<,它就会与第一个>匹配,而第一个>不会嵌套在某些括号语法(包括大括号和括号)中,并且与某些嵌套模板括号不匹配。在C ++ 11中,这包括>>,它是random.h令牌的一部分。

例如,如果bool = __w > ...示例说>,则<将被视为结束模板括号。但由于它是__w<不是模板标识符,因此将其视为小于运算符。恕我直言的好风格总是用括号中的比较和移位运算符包装,如果它们在模板括号内,但那只是我。

标准的准确措辞:

  

名称查找后发现名称是模板名称 operator-function-id 文字operator-id 是指一组重载函数,如果后跟<,则其中任何成员都是函数模板,>始终被视为 template-argument-list <的分隔符/ em>并且从不作为小于运营商。解析 template-argument-list 时,第一个非嵌套{{1}}被视为结束分隔符而不是大于运算符。

该算法难以实现,因为没有简单的方法可以知道哪些标识符是模板标识符,因为您需要解析整个程序(至少在使用点之前)以了解每个标识符是什么类型的。这包括查找和解析包含的文件,以及预处理源代码。

如果你真的希望它是准确的,你需要使用C ++解析器并注意它可能是一个非常缓慢的过程,因为它可以包括模板实例化(与引用的prime-check示例一样)。如果你只是尝试做类似语法着色的事情,你可能会得到近似值。

答案 1 :(得分:1)

您需要跳过()括号内的任何内容 您需要在<之前查看标识符,并知道它是否是类型名称。

第二个是有问题的,您需要扫描所有源代码并识别class/struct/uniontypedef的所有名称,以便在到达表单{ {1}}(此示例已简化)您知道__w < __a是否为某个类型命名 这有问题的原因是如果您遇到任何预处理器元编程(如Boost的主题库),您基本上不会编写一个程序,可以对这些程序进行评估以查看创建的类型名称。
此外,请考虑这段代码(稍微复杂一点,以显示它有多难):

__w

这种复杂性是 template <template <class> class T> struct Base { template <class X> using type = T<X>; }; template <> struct Base<std::numeric_limits>//Let's assume numeric_limits only has one template argument for this example { static const int type = 0; }; template <class T, Base<T>::type < 0> struct OtherClass{}; 关键字的用途,因为typename是一个依赖范围,您无法立即判断Base<T>是否为类型或成员变量命名。通过这种方式,语法要求Base<T>::type是一个成员变量而Base<T>::type是一个类型(我们不知道哪种类型,但是没关系,我们只是试图找到结束模板标识符列表) 除上述示例之外,您还需要处理typename Base<T>::type关键字的鲜为人知的使用。由于template是一个依赖范围,您如何解析Base<T>?这取决于Base<T>::type<0>是什么。在这种情况下,语法要求type是成员变量并被解析为Base<T>::type,而(Base<T>::type < 0) >是模板表达式。

关键字Base<T>::template type<0>typename虽然在了解相关范围之前难以理解,但最终会让您的工作更轻松。否则,如果没有编写完整的编译器,模板标识符列表的结尾几乎无法回答的简单问题(即使编译器编写起来也更难)。

除了依赖范围之外,您仍然需要能够处理非依赖范围。这意味着您需要知道template是否是类型,这意味着扫描每个Base<numeric_limits>::type struct/class/union并解析基类typedef和私有继承的公共继承+ typedef陈述。

如果你限制自己编写的代码,你的工作仍然易于处理,否则你需要做更多的工作。

我不保证这就是一切,只是这很可能会让你忙碌一段时间。