clang:FunctionDecl::isOutOfLine()和FunctionDecl::isInlined()能同时返回true吗?

Clang源码解析:isOutOfLine与isInlined
本文深入解析Clang源码中FunctionDecl::isOutOfLine()和FunctionDecl::isInlined()两个方法的具体实现与含义,揭示它们在函数声明与定义上下文中的作用,以及如何判断函数是否为内联或外部定义。

最近读clang源码时发现这么一段代码:

FunctionDecl *FD = ......
......
if (auto *MD = dyn_cast<CXXMethodDecl>(FD)) {
      ......
      if (MD->isOutOfLine() && ......) {
        ......
        if (FD->isInlined() && ......) {
          ......
        }
        ......
      }
    }

虽说CXXMethodDeclFunctionDecl是继承关系,isOutOfLine()也是虚函数,但是CXXMethodDecl并没有对其进行override,所以MD->isOutOfLine()实际上调用的还是FunctionDecl::isOutOfLine()。那么问题来了:在代码片段中,第一个if语句调用了FunctionDecl::isOutOfLine(),嵌套在第一个if中的第二个if调用了FunctionDecl::isInlined(),从方法名上看,这两个方法的返回值不该是互逆的吗?第一个返回true则第二个返回false,第一个返回false则第二个返回true,这样的话第二个if语句岂不是一个死分支了吗?

问题出在哪里呢?问题在于FunctionDecl::isOutOfLine()FunctionDecl::isInlined()的返回值是没有任何关联的,它们并不成互逆的关系,这两个方法的名字很容易让人望文生义。我们来看一下这两个方法的返回值究竟是什么含义。

FunctionDecl::isOutOfLine()

方法定义如下:

bool FunctionDecl::isOutOfLine() const {
  if (Decl::isOutOfLine())
    return true;

  // If this function was instantiated from a member function of a
  // class template, check whether that member function was defined out-of-line.
  if (FunctionDecl *FD = getInstantiatedFromMemberFunction()) {
    const FunctionDecl *Definition;
    if (FD->hasBody(Definition))
      return Definition->isOutOfLine();
  }

  // If this function was instantiated from a function template,
  // check whether that function template was defined out-of-line.
  if (FunctionTemplateDecl *FunTmpl = getPrimaryTemplate()) {
    const FunctionDecl *Definition;
    if (FunTmpl->getTemplatedDecl()->hasBody(Definition))
      return Definition->isOutOfLine();
  }

  return false;
}

总共有4个return,中间两个return是对FunctionDecl::isOutOfLine()的递归调用(虽说不是同一个对象),递归终止时的出口要么是第一个return true,要么是最后的return false。我们来看一下Decl::isOutOfLine()的定义:

bool Decl::isOutOfLine() const {
  return !getLexicalDeclContext()->Equals(getDeclContext());
}

getLexicalDeclContext()getDeclContext()返回值相等时返回false,否则返回true。那么getLexicalDeclContext()getDeclContext()又是什么呢:

  /// getLexicalDeclContext - The declaration context where this Decl was
  /// lexically declared (LexicalDC). May be different from
  /// getDeclContext() (SemanticDC).
  /// e.g.:
  ///
  ///   namespace A {
  ///      void f(); // SemanticDC == LexicalDC == 'namespace A'
  ///   }
  ///   void A::f(); // SemanticDC == namespace 'A'
  ///                // LexicalDC == global namespace
  DeclContext *getLexicalDeclContext() {
    if (isInSemaDC())
      return getSemanticDC();
    return getMultipleDC()->LexicalDC;
  }

getLexicalDeclContext()的注释写的很清楚,每个声明都有两个context:一个是LexicalDCgetLexicalDeclContext()的返回值),一个是SemanticDCgetDeclContext()的返回值),这两个context可能相同,也可能不同:

// 1
namespace A {
   void f(); // SemanticDC == LexicalDC == 'namespace A'
}

// 2
void A::f(); // SemanticDC == namespace 'A'
             // LexicalDC == global namespace

对于情况1,LexicalDCSemanticDC相同,均为A命名空间;对于情况2,LexicalDC为全局命名空间,SemanticDC为A命名空间。

那这样的话FunctionDecl::isOutOfLine()含义就清楚了:当一个函数声明的LexicalDCSemanticDC不同时,该函数返回trueLexicalDCSemanticDC相同时,该函数返回false。

FunctionDecl::isInlined()

方法定义为:

  /// Determine whether this function should be inlined, because it is
  /// either marked "inline" or "constexpr" or is a member function of a class
  /// that was defined in the class body.
  bool isInlined() const { return FunctionDeclBits.IsInline; }

注释说的很清楚,该函数返回值用来标识3种情况:

  1. 函数声明被标记为inline
  2. 函数声明被标记为constexpr
  3. 成员函数体定义在class内部

总结

现在我们可以枚举出这两个函数返回值所有组合对应的代码:

  1. FunctionDecl::isOutOfLine() == false && FunctionDecl::isInlined() == false
  2. FunctionDecl::isOutOfLine() == false && FunctionDecl::isInlined() == true
  3. FunctionDecl::isOutOfLine() == true && FunctionDecl::isInlined() == false
  4. FunctionDecl::isOutOfLine() == true && FunctionDecl::isInlined() == true
// 1
namespace A
{
    void f() {}
}

// 2
namespace B
{
    inline void f() {}
}

// 3
namespace C
{
    void f();
}

void C::f() {}

// 4
namespace D
{
    inline void f();
}

void D::f() {}

但是如果FunctionDecl对象是一个成员函数的话,似乎会少一种情况,因为以下两种情况都对应FunctionDecl::isOutOfLine() == false && FunctionDecl::isInlined() == true

// 1
class A
{
    void f() {}
};

// 2
class B
{
    inline void f() {}
};
`virtual std::unique_ptr<clang::ASTConsumer> CreateASTConsumer` 通常是在使用 Clang 进行抽象语法树(AST)处理时会遇到的一个虚函数。下面从原理使用方法两方面进行介绍: ### 原理 - **虚函数机制**:`virtual` 关键字表明这是一个虚函数。在面向对象编程中,虚函数允许在派生类中重写基类的函数。这意味着在运行时,会根据对象的实际类型来决定调用哪个版本的函数,而不是在编译时就确定。 - **`std::unique_ptr`**:这是 C++ 标准库中的智能指针,用于管理动态分配的对象的生命周期。`std::unique_ptr` 保证在其生命周期结束时,自动释放所管理的对象,避免内存泄漏。 - **`clang::ASTConsumer`**:`clang::ASTConsumer` 是 Clang 库中的一个抽象基类,用于处理抽象语法树(AST)的遍历分析。当 Clang 解析源文件并构建 AST 后,会调用 `ASTConsumer` 的相应方法来处理 AST 节点。 ### 使用方法 以下是一个简单的示例,展示了如何使用 `virtual std::unique_ptr<clang::ASTConsumer> CreateASTConsumer`: ```cpp #include "clang/Frontend/CompilerInstance.h" #include "clang/Frontend/FrontendAction.h" #include "clang/AST/ASTConsumer.h" #include "clang/AST/RecursiveASTVisitor.h" #include "clang/Frontend/FrontendPluginRegistry.h" #include <memory> using namespace clang; // 自定义的 ASTConsumer class MyASTConsumer : public ASTConsumer { public: bool HandleTopLevelDecl(DeclGroupRef DG) override { // 处理顶级声明 for (DeclGroupRef::iterator I = DG.begin(), E = DG.end(); I != E; ++I) { const Decl *D = *I; // 可以在这里添加具体的处理逻辑 } return true; } }; // 自定义的 FrontendAction class MyFrontendAction : public ASTFrontendAction { public: std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &CI, StringRef file) override { return std::make_unique<MyASTConsumer>(); } }; // 注册插件 static FrontendPluginRegistry::Add<MyFrontendAction> X("my-plugin", "My custom plugin"); ``` 在上述代码中: 1. 定义了一个自定义的 `ASTConsumer` 类 `MyASTConsumer`,并重写了 `HandleTopLevelDecl` 方法来处理顶级声明。 2. 定义了一个自定义的 `FrontendAction` 类 `MyFrontendAction`,并重写了 `CreateASTConsumer` 方法,该方法返回一个 `std::unique_ptr<ASTConsumer>`,指向自定义的 `MyASTConsumer` 对象。 3. 使用 `FrontendPluginRegistry::Add` 注册了这个插件。 ### 相关问题
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值