编译几个源文件(主文件和头文件)并在ROOT CINT中链接它们



让我首先设置上下文,它是CERN的ROOTCINT以及ACLiC等。

假设我有一个名为macro.cpp的主macro和两个标题h1.cpp(包含函数的定义)和h1.h(包含h1.cpp中定义的函数的声明),类似地,我有h2.cpph2.h。主程序CCD_ 8调用CCD_ 9和CCD_。我成功地编译了源文件使用:

root [0] .L h1.cpp+
root [1] .L h2.cpp+
root [2] .L macro.cpp+

生成了三个.so文件macro_cpp.soh1_cpp.soh2_cpp.so。我想知道该怎么处理他们?我如何link它们,以便我有一个类似"macro.out"或类似的东西(某种类型的单个executable文件),我可以执行(尽管我不知道如何!)并用宏实现我想要实现的任何目标。

注意:如果我只是使用.L file_name.cpp等加载所有文件,并使用.x macro.cpp执行主宏,那么一切都很好,我有结果,但这不是我想要的!我想像往常一样编译g++,顺便说一句,在每个论坛上,每个人都建议使用.L file_name.cpp+++进行编译。。我真的很想知道整个故事。因为除了.L file_name.cpp+之外似乎没有人能解释。。下一步怎么办?如何处理.so

我是一个初学者,我会非常感激一个简单而循序渐进的回答和解释。

谢谢。

第1版:我正在使用:

g++(Ubuntu 5.4.0-6ubuntu1~16.04.4)5.4.0 20160609

第2版:ROOT相关信息:根部5.34/36(v5-34-36@v5-34-36,dic 07 2016,23:31:51在linuxx8664gcc上)CINT/ROOT C/C++解释器版本5.18.00,2010年7月2日

如果要编译和链接,可以使用标准编译器而不是Cint/Aclic。例如,假设您在*nix平台上工作,您可以使用以下示例文件:

h1.h

int add_one(int a);

h1.cpp

#include "h1.h"
int add_one(int a)
{
return a+1;
}

h2.h

#include <TLorentzVector.h>
TLorentzVector multiply_by_two(const TLorentzVector v);

h2.cpp

#include "h2.h"
TLorentzVector multiply_by_two(const TLorentzVector v)
{
return 2.0*v;
}

宏.cpp

#include "h1.h"
#include "h2.h"
#include <TLorentzVector.h>
#include <iostream>
using std::cout;
using std::endl;
int main()
{
int a = 0;
TLorentzVector v;
v.SetPtEtaPhiM(1.0, 0.0, 0.0, 0.0);
cout<<"calling add_one on "<<a<<": "<<add_one(a)<<endl;
cout<<"calling multiply_by_two on "<<v.Pt()<<": "<<multiply_by_two(v).Pt()<<endl;
return 0;
}

然后你可以用进行编译

g++ -c -g -Wall `root-config --cflags` h1.cpp
g++ -c -g -Wall `root-config --cflags` h2.cpp
g++ -c -g -Wall `root-config --cflags` macro.cpp

并与链接

g++ `root-config --glibs` h1.o h2.o macro.o

可执行文件将是a.out:

$ ./a.out 
calling add_one on 0: 1
calling multiply_by_two on 1: 2

您可以将这些g++命令放在脚本中,或者,当您开始拥有多个文件和目录时,您可以编写make文件(或cmake)。关于最后一步,请参阅这里的教程

http://www-pnp.physics.ox.ac.uk/~brisbane/教学/Makefiles/Tutorial_1_Makefiles_and_ROOT.pdf

注意1:使用g++的一个优点是,当某些内容未编译时,您将获得清晰的错误消息。来自Cint的错误消息可以很难理解——尽管这在使用Cling的根6中得到了很大的改善。

注意2:使用标准编译器的另一个优点是,您可以轻松地将主可执行文件链接到根以外的库。

此答案主要基于用户2148414的答案,但如果按照答案进行操作,则会注意到链接源(*.cpp)文件的方法存在一些问题。我的回答还涉及另一个重要对象,称为TApplication,它将在涉及根库的应用程序中发挥关键作用。以下链接步骤:

g++ `root-config --glibs` h1.o h2.o macro.o

可能会显示很多错误,抱怨像TWhatever这样的根对象(在用户2148414的回答中,TLorentzVector将显示问题)。在对该答案的评论中,可以找到关于包括各种可以解决问题的物理库的讨论,但不讨论(我也不舒服:)让我写下解决所有问题的命令。

这个过程是一个单行程序,不需要编译单个文件,创建*.cpp文件和*.h文件,如答案中所述,然后使用编译和链接并创建一个名为"someExecutable"的可执行文件

g++ macro.cpp h1.cpp h2.cpp `root-config --libs --cflags` -o someExecutable

或者更好(应该这样做)

g++ -Wall -Wextra -Werror -pedantic -std=c++14 macro.cpp h1.cpp h2.cpp `root-config --libs --cflags` -o someExecutable

这将解决我最初的答案,但为了完整性,我想再添加一些内容。

TA应用程序

我最初的动机是创建一个与"ROOT"对话的应用程序,但我不想使用ROOTshell、CINT、ACLiC等,而是想完全使用g++。user2148414和我的答案将解决创建应用程序的部分,但该应用程序不会有任何用途,它会运行、创建直方图、绘制直方图并完成所有工作,但当代码达到"return 0;"时,所有画布最终都会关闭。为了保持画布的开放,我们需要"TApplication"。因此,考虑用户2148414的答案的main,我将只再包含两行,并包含main:的两个参数

宏.cpp

#include "h1.h"
#include "h2.h"
#include <TLorentzVector.h>
#include <iostream>
using std::cout;
using std::endl;
int main(int argc, char* argv[])  //introduced arguments to main
{
// here I introduce TApplication
TApplication* SomeApp = new TApplication("SomeApp",&argc, argv); 
int a = 0;
TLorentzVector v;
v.SetPtEtaPhiM(1.0, 0.0, 0.0, 0.0);
cout<<"calling add_one on "<<a<<": "<<add_one(a)<<endl;
cout<<"calling multiply_by_two on "<<v.Pt()<<": "<<multiply_by_two(v).Pt()<<endl;
//and just before returning 0
SomeApp->Run();
return 0;
}

最新更新