从抽象基础到其他抽象类,继承c&#39和tor,以及具体化

时间:2015-01-26 10:17:50

标签: c++ inheritance c++11 clang abstract-class

主要问题

我正在尝试按照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

2 个答案:

答案 0 :(得分: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)

答案 1 :(得分:1)

  

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

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

  

可能导致这些链接器错误的原因是什么?

您声明析构函数,但不要定义它。如果它不需要做任何事情(通常是抽象类中的情况),你可以用空体来定义它

virtual ~FrontendAction() {}

或默认

virtual ~FrontendAction() = default;

在类定义中。