Compiling several source (main and headers) files and linking them in ROOT CINT?

1.7k views Asked by At

Let me first set the context, it is CERN's ROOT and CINT and ACLiC etc.

Suppose I have a main macro named macro.cpp and two headers h1.cpp (contains the definition of a function) and h1.h containing the declaration of the function defined in h1.cpp similarly I have h2.cpp and h2.h. The main program macro.cpp calls those functions inside h1 and h2. I was successful compiling the source files using:

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

which generated three .so files macro_cpp.so, h1_cpp.so and h2_cpp.so. I want to know what to do with them ? How do I link them so that I have something like a "macro.out" or something like that (a single executable file of some kind) which I can execute (although I don't know how !) and achieve whatever I wished to achieve with the macro.

Note: If I just load all the files using .L file_name.cpp etc and just execute the main macro using .x macro.cpp then everything works fine and I have results, but this is not what I want ! I want to compile like we do in usual g++ and by the way in every forum everyone keeps advising on compiling using .L file_name.cpp+ or ++ .. I would really like to know the whole story. Because nobody seems to explain beyond .L file_name.cpp+ .. what next ? What to do with the .so etc.

I am a beginner, I will really appreciate a simple and step by step answer and explanation.

Thanks.

Edit-1: I am working with:

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

Edit-2: ROOT related information: ROOT 5.34/36 (v5-34-36@v5-34-36, dic 07 2016, 23:31:51 on linuxx8664gcc) CINT/ROOT C/C++ Interpreter version 5.18.00, July 2, 2010

2

There are 2 answers

21
user2148414 On

If you want to compile and link you can use a standard compiler instead of Cint/Aclic. For example, assuming you are working on a *nix platform, you can use the example files below:

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;
}

macro.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;
}

Then you can compile with

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

and link with

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

The executable will be a.out:

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

You can put these g++ commands in a script or, when you start having several files and directories, you can write your make file (or cmake). For this last step, see for example the tutorial here

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

Note 1: one advantage of using g++ is that you will get clear error messages when something doesn't compile. The error messages from Cint can be difficult to understand--although this is very much improved in root 6 with Cling.

Note 2: another advantage of using a standard compiler is that you will be able to easily link your main executable against libraries other than root.

0
quanta On

This answer is based mostly on the answer by user2148414, but if one follows the answer will notice that there were some issues with the method of linking the source (*.cpp) files. My answer also addresses another important object called a TApplication that will play a crucial role in such applications involving root libraries. The following linking step:

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

will likely show a lot of errors complaining about the root objects like TWhatever (in user2148414's answer TLorentzVector will show problems). In the comments to that answer one can find the discussion on including various physics libraries that can solve the problem but without discussing that (and I am not comfortable either :) ) let me write down the command that solves everthing.

This procedure is a one-liner, that is no need to compile individual files, create *.cpp files and *.h files as discussed in that answer then compile and link and create a single executable named "someExecutable" using:

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

or better (and one should do it)

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

This will solve my original answer but for completeness I would like to add a few more things.

TApplication

My original motivation was to create an application that talks to "ROOT" but I didn't want to work with the ROOT shell, CINT, ACLiC etc and wanted to work entirely with g++. user2148414's and my answer will solve the part of creating an application but the application will not serve any purpose, it will run, create histograms draw them and do all the stuff but all the canvases will close in the end when the code reaches "return 0;". To keep the canvases open we will need "TApplication". So the consider the main of user2148414's answer, I am going include just two more lines and include two arguments to the main:

macro.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;
    }