tor和tor从抽象基类通过其他抽象类继承到具体

Posted

技术标签:

【中文标题】tor和tor从抽象基类通过其他抽象类继承到具体【英文标题】:Inheritence of c'tor and d'tor from abstract base through other abstract classes into concrete 【发布时间】:2015-01-26 10:17:49 【问题描述】:

主要问题

我正在尝试按照说明 here 构建一个 clang 插件,但是我在尝试构建时遇到了链接器错误。

这些是错误:

/tmp/Test-1ea47e.o: In function `ASTFrontendAction':
/usr/lib/llvm-3.4/include/clang/Frontend/FrontendAction.h:216: undefined reference to `clang::FrontendAction::FrontendAction()'

/tmp/Test-1ea47e.o: In function `~TestPlugin':
/home/path/to/plugin/Test.cpp:12: undefined reference to `clang::FrontendAction::~FrontendAction()'

/tmp/Test-1ea47e.o:(.data.rel.ro+0x20): undefined reference to `clang::FrontendAction::~FrontendAction()'

/tmp/Test-1ea47e.o:(.data.rel.ro+0x50): undefined reference to `typeinfo for clang::PluginASTAction'

我的类是TestPlugin(代码如下),它从以下链中的三个抽象库类扩展而来:FrontendAction > ASTFrontendAction > PluginASTAction > TestPlugin。我本来期望由于库类是抽象的,因此永远不需要它们的构造函数和析构函数,但是我对 C++ 相当陌生,如果我错了,请纠正我。什么可能导致这些链接器错误?

补充信息

对于背景:我遵循的说明适用于 clang 3.7,但我使用的是 clang 3.4 的标准 Ubuntu 发行版,因此这可能是问题的一部分。未经修改的教程代码甚至无法编译,因此我必须进行一些更改(主要是删除)才能达到这一点,但在链接过程中我仍然遇到上述错误。

这是我的整个插件文件(为简洁起见,压缩了一些空格)

Test.cpp:

#include "clang/AST/AST.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/ASTConsumer.h"
#include "clang/Frontend/FrontendPluginRegistry.h"
#include "clang/Frontend/CompilerInstance.h"
#include "clang/Frontend/FrontendAction.h"
#include "clang/Tooling/Tooling.h"
#include "llvm/Support/raw_ostream.h"
using namespace clang;

namespace 
    class TestPlugin: public PluginASTAction 
        private:
            void anchor()

        protected:
            ASTConsumer* CreateASTConsumer(CompilerInstance &CI, llvm::StringRef) return NULL; 
            void ExecuteAction() return; 
            bool shouldEraseOutputFiles() return false; 

        public:
            bool ParseArgs(const CompilerInstance &CI, const std::vector<std::string>& args) return true; 
    ;


static FrontendPluginRegistry::Add<TestPlugin>
    X("test-stuff", "Does some test stuff");

int main() return 0; 

这是我继承的库代码的相关部分(我认为)

FrontendAction.h:

//////// snip ////////
namespace clang 

    class FrontendAction 
    //////// snip ////////
        public:
            FrontendAction();
            virtual ~FrontendAction();
            virtual bool usesPreprocessorOnly() const = 0;
            //////// snip ////////
    ; // class FrontendAction


    class ASTFrontendAction : public FrontendAction 
        protected:
            virtual void ExecuteAction();

        public:
            virtual bool usesPreprocessorOnly() const  return false; 
    ; // class ASTFrontendAction


    class PluginASTAction : public ASTFrontendAction 
            virtual void anchor();

        protected:
            virtual ASTConsumer *CreateASTConsumer(CompilerInstance &CI, StringRef InFile) = 0;

        public:
            virtual bool ParseArgs(const CompilerInstance &CI, const std::vector<std::string> &arg) = 0;
    ; // class PluginASTAction

    //////// snip ////////
 // namespace clang

我很高兴提供任何其他可能有用的信息。

更新

这是nm 给出的关于 c'tor 和 d'tor 下落的输出:

/usr/lib/llvm-3.4/lib$ nm -AC *.a | grep 'FrontendAction::~\?FrontendAction'
...
libclangFrontend.a:FrontendAction.o:00000960 T clang::FrontendAction::FrontendAction()
libclangFrontend.a:FrontendAction.o:000004a0 T clang::FrontendAction::~FrontendAction()
...

它们出现在libclangFrontend.a 中,状态为“T”,我理解这意味着该方法的实现就在那里。它们还以状态“U”(未定义)出现在其他几个库中。 libclangFrontend.a 还声称持有失踪的typeinfo for clang::PluginASTAction

但是,我已经包含了该库,即使将其定位在其他 libclangXYZ 库之前,我仍然会遇到相同的错误。这是我当前的链接器调用(由make 生成,为便于阅读添加了换行符):

"/usr/bin/ld"
    -z relro --hash-style=gnu --build-id --eh-frame-hdr -m elf_i386
    -dynamic-linker /lib/ld-linux.so.2

    -o Test

    /usr/bin/../lib/gcc/i686-linux-gnu/4.8/../../../i386-linux-gnu/crt1.o
    /usr/bin/../lib/gcc/i686-linux-gnu/4.8/../../../i386-linux-gnu/crti.o
    /usr/bin/../lib/gcc/i686-linux-gnu/4.8/crtbegin.o

    -L/usr/lib/llvm-3.4/lib/
    -L/usr/bin/../lib/gcc/i686-linux-gnu/4.8
    -L/usr/bin/../lib/gcc/i686-linux-gnu/4.8/../../../i386-linux-gnu
    -L/lib/i386-linux-gnu
    -L/usr/lib/i386-linux-gnu
    -L/usr/bin/../lib/gcc/i686-linux-gnu/4.8/../../..
    -L/lib
    -L/usr/lib

    -lpthread -lffi -ltinfo -ldl -lm

    -lclangFrontend
    -lclang
    -lclangBasic
    -lclangAST
    -lclangFrontendTool
    -lclangRewriteFrontend
    -lclangStaticAnalyzerFrontend
    -lclangCodeGen
    -lclangTooling
    -lclangARCMigrate
    -lclangTidy

    /tmp/Test-fd7749.o

    -lc++ -lm -lgcc_s -lgcc -lc -lgcc_s -lgcc

    /usr/bin/../lib/gcc/i686-linux-gnu/4.8/crtend.o
    /usr/bin/../lib/gcc/i686-linux-gnu/4.8/../../../i386-linux-gnu/crtn.o

更新:已解决!

事实证明,我在链接器调用中遗​​漏了大量 LLVM 库(例如 libLLVMSupport.a)和一些 clang 库。我想我还需要明确添加libstdc++.so

这是对链接器的最终工作调用,由make 生成(注意:我不确定这是一个最小集合,但至少它链接...):

"/usr/bin/ld"
    -z relro --hash-style=gnu --build-id --eh-frame-hdr -m elf_i386
    -dynamic-linker /lib/ld-linux.so.2

    -o Test

    /usr/bin/../lib/gcc/i686-linux-gnu/4.8/../../../i386-linux-gnu/crt1.o
    /usr/bin/../lib/gcc/i686-linux-gnu/4.8/../../../i386-linux-gnu/crti.o
    /usr/bin/../lib/gcc/i686-linux-gnu/4.8/crtbegin.o

    -L/usr/lib/llvm-3.4/lib
    -L/usr/bin/../lib/gcc/i686-linux-gnu/4.8
    -L/usr/bin/../lib/gcc/i686-linux-gnu/4.8/../../../i386-linux-gnu
    -L/lib/i386-linux-gnu
    -L/usr/lib/i386-linux-gnu
    -L/usr/bin/../lib/gcc/i686-linux-gnu/4.8/../../..
    -L/lib
    -L/usr/lib

    /tmp/Test-6c811b.o

    /usr/lib/gcc/i686-linux-gnu/4.8/libstdc++.so

    -lpthread -lffi -ltinfo -ldl -lm -lc++

    -lclangFrontend
    -lclangSerialization
    -lclangDriver
    -lclangTooling
    -lclangParse
    -lclangSema
    -lclangStaticAnalyzerFrontend
    -lclangStaticAnalyzerCheckers
    -lclangStaticAnalyzerCore
    -lclangAnalysis
    -lclangRewriteFrontend
    -lclangEdit
    -lclangAST
    -lclangLex
    -lclangBasic

    -lLLVMTransformUtils
    -lLLVMCore
    -lLLVMSupport
    -lLLVMOption
    -lLLVMMCParser
    -lLLVMMC
    -lLLVMBitReader

    -lc++ -lm -lgcc_s -lgcc -lc -lgcc_s -lgcc

    /usr/bin/../lib/gcc/i686-linux-gnu/4.8/crtend.o
    /usr/bin/../lib/gcc/i686-linux-gnu/4.8/../../../i386-linux-gnu/crtn.o

【问题讨论】:

【参考方案1】:

这看起来你没有链接到任何工具clang::FrontendAction::~FrontendAction()。试试看这个 Makefile:http://llvm.org/viewvc/llvm-project/cfe/trunk/tools/clang-check/Makefile?view=markup

在this article 中,将其放入您的cmake 文件中:

set(LLVM_USED_LIBS clangTooling clangBasic clangAST)

【讨论】:

我使用的是make,而不是cmake,正如该页面所暗示的那样。我会尝试切换到后者,看看是否有帮助。但是,我已经包含了您最后一行中提到的三个库。 作为make 人而不是cmake 人,我可能会去找出哪个库实际上 包含它(使用nm),并且我会检查我的链接行是否将库包含在合理的位置。 谢谢,我也是make 人 :-) nm 表明构造函数和析构函数都在libclangFrontend.a 中,但该库已经包含在我的ld 中调用。我已经更新了我的问题以包括nm 输出和ld 调用。感谢您迄今为止的帮助! 认为这只是一个链接排序问题。如果您将-lclang* 项放在/tmp/Test-fd7749.o 之后会怎样? 感谢您的帮助,@abligh!原来这是一个订购问题,但一旦解决,它也揭示了一些缺失的库。我已经用最终有效的调用更新了我的问题,但由于我只是添加了我能想到的尽可能多的东西,我确信这不是一个最小的集合。【参考方案2】:

我原以为库类是抽象的,所以永远不需要它们的构造函数和析构函数。

如果实例化,所有类都需要构造函数和析构函数。这包括抽象类——即使它们不能直接实例化,它们也可以作为具体派生类的一部分进行实例化,并且为此需要构造函数和析构函数。

什么可能导致这些链接器错误?

你声明了析构函数,但没有定义它。如果它不需要做任何事情(就像抽象类中通常的情况),你可以用一个空的主体来定义它

virtual ~FrontendAction() 

或默认

virtual ~FrontendAction() = default;

在类定义中。

【讨论】:

感谢关于构造函数的提示!我明白为什么现在有必要这样做了。不幸的是,我认为我不应该更改库文件中的任何内容,因为我希望我的插件与其他人的 clang 安装兼容。 @pieman72:假设析构函数是在库中定义的,因为它是声明它的库头。 @abligh:很好,我没有意识到这些来自图书馆。在这种情况下,问题可能是库没有被链接。 @MikeSeymour 这就是我在回答中的想法:-)

以上是关于tor和tor从抽象基类通过其他抽象类继承到具体的主要内容,如果未能解决你的问题,请参考以下文章

十接口(接口的概念,实现,继承,实现)抽象类与抽象方法(抽象类,抽象方法概念,使用)

从抽象基类构造函数创建继承类的实例

C++抽象类

如何从 C++ 基类继承抽象行为

何时在抽象类 cpp 中声明受保护字段

java抽象类