0

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

quanta
  • 215
  • 3
  • 14
  • .so are dynamic linked library like .dll on Windows. In short it is compiled code who can be use by several program. You can use the shell command : " ldd ./myprogram" to see all .so needed by your program. – baddger964 Jan 04 '17 at 12:37
  • since you ask "what next?": after `.L macro.cpp+` you can execute functions which are defined in that file, like `.L macro.cpp+; macro(3.1415);`. the saved `.so` will be read the next time you do `.L macro.cpp+` (no recompilation of macros which are compiled already). or you can just load previously compiled `.so` files with `gSystem->Load("macro_C.so");`. – pseyfert Jan 05 '17 at 08:18
  • @pseyfert that's wonderful. – quanta Jan 05 '17 at 16:59

2 Answers2

1

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.

user2148414
  • 312
  • 3
  • 14
  • I agree with using g++ rather than cint/aclic. In fact I know how to link usual C++ sources that doesn't involve root libraries, from your answer I now know how to link root libraries. I will try and will be back. The problem though, with the "`main`" is that I cannot feed a ROOT file name and other parameters during the execution itself as `main` doesn't take such arguments I guess. Suppose the `macro` is like `void macro(TString filename, Int_t nbin)` how does this change things ? – quanta Jan 05 '17 at 16:58
  • I can of course change the macro to `main` as you did and then `std::cin>>filename` etc. That will work too.. but asking otherwise just for the sake of knowledge. – quanta Jan 05 '17 at 17:02
  • the linking command `g++ `root-config --glibs` h1.o h2.o macro.o` produces a lot of error (something to do with TLorentzVector and root's include path maybe), which I cannot accommodate in a single comment so posting in several (following) comments: – quanta Jan 05 '17 at 18:13
  • `h2.o: In function `__static_initialization_and_destruction_0(int, int)': /home/me/root/include/TVersionCheck.h:34: undefined reference to `TVersionCheck::TVersionCheck(int)' h2.o: In function `operator*(double, TLorentzVector const&)': /home/me/root/include/TLorentzVector.h:596: undefined reference to `TLorentzVector::TLorentzVector(double, double, double, double)' macro.o: In function `main': /home/me/macro.cpp:13: undefined reference to `TLorentzVector::TLorentzVector()' /home/me/macro.cpp:16: undefined reference to `TLorentzVector::TLorentzVector(TLorentzVector const&)'` – quanta Jan 05 '17 at 18:15
  • `/home/me/macro.cpp:16: undefined reference to `TLorentzVector::~TLorentzVector()' /home/me/macro.cpp:16: undefined reference to `TLorentzVector::~TLorentzVector()' /home/me/macro.cpp:13: undefined reference to `TLorentzVector::~TLorentzVector()' /home/me/macro.cpp:16: undefined reference to `TLorentzVector::~TLorentzVector()' /home/me/macro.cpp:16: undefined reference to `TLorentzVector::~TLorentzVector()'` – quanta Jan 05 '17 at 18:15
  • `macro.o:/home/macro.cpp:13: more undefined references to `TLorentzVector::~TLorentzVector()' follow macro.o: In function `__static_initialization_and_destruction_0(int, int)': /home/me/root/include/TVersionCheck.h:34: undefined reference to `TVersionCheck::TVersionCheck(int)' macro.o: In function `TLorentzVector::Perp() const': /home/me/root/include/TLorentzVector.h:421: undefined reference to `TVector3::Perp() const' collect2: error: ld returned 1 exit status` – quanta Jan 05 '17 at 18:16
  • with `root-config --libs` you don't get *all* root libraries (or their link commands) but only the basic ones (those one always needs). (and similar for `--glibs`, look at `root-config --help` for the difference). – pseyfert Jan 05 '17 at 18:23
  • In your case you need to add `-lPhysics` after the root-config call: `$(root-config --libs) -lPhysics`. How I found out: `grep TLorentzVector $(root-config --libdir)/*.rootmap` (the *.rootmap files essentially list which class is in which standard root library. the libraries and the rootmap files are in the directory `root-config --libdir`. the rootmap files are plaintext. – pseyfert Jan 05 '17 at 18:25
  • @pseyfert g++ neither ``root-config --glibs` -lPhysics h1.o h2.o macro.o` nor ``root-config --glibs -lPhysics` h1.o h2.o macro.o` worked ! – quanta Jan 05 '17 at 19:43
  • with the same error (missing TLorentzVector things)? does `$(root-config --libdir)/libPhysics.so` exist? when doing `nm -C libPhysics.so` you should see what's inside that library. so you can verify if the TLorentzVector things you need are inside (especially the methods your linker complains about). – pseyfert Jan 05 '17 at 21:55
  • Yes the error is still the same and libPhysics.so library does exist. – quanta Jan 05 '17 at 23:54
  • I would suggest you use `argc` and `argv` to pass in your arguments and options (see for example this question on [stackoverflow/3024197](http://stackoverflow.com/questions/3024197/what-does-int-argc-char-argv-mean). – user2148414 Jan 06 '17 at 08:28
  • Regarding the errors you see when linking: @pseyfert is right, depending on the headers you include, you might need to add some libraries by hand. What version of root are you using? For example, with root `6.04/06` I have ``` $ root-config --glibs -L/usr/lib/root -lGui -lCore -lRIO -lNet -lHist -lGraf -lGraf3d -lGpad -lTree -lRint -lPostscript -lMatrix -lPhysics -lMathCore -lThread -pthread -lm -ldl -rdynamic ``` The library is indicated in the top right corner of the doc web page – user2148414 Jan 06 '17 at 08:31
  • edit: "top left corner of the doc". For example, for `TLorentzVector` see [here](https://root.cern.ch/doc/master/classTLorentzVector.html) – user2148414 Jan 06 '17 at 08:41
  • I tried this: ```g++ main.cpp h1.cpp h2.cpp `root-config --libs --cflags` -o main.o``` and I think it worked and produced `main.o` as executable, `./main.o` produces `calling add_one on 0: 1 calling multiply_by_two on 1: 2` – quanta Jan 06 '17 at 20:48
  • The single-step `g++ main.cpp ... -o main.o` is also a valid solution. I suggested two separate compilation+linking steps because that seemed to be the request in the initial post. Unfortunately I cannot reproduce the error you observe, not even when using a root version close to the one you indicate (root `5.34/25`, g++ `4.8.1`). For reference, with this version the libraries indicated in `libPhysics.rootmap` are `-lMatrix -lPhysics -lMathCore`, which do appear in the list produced by `root-config --glibs`. – user2148414 Jan 07 '17 at 09:16
  • Can you please update the exact command that you are using to link the .o files ? Does just ```g++ `root-config --glibs` h1.o h2.o macro.o``` work without any errors for you ? I am wondering why that single command ```g++ main.cpp h1.cpp h2.cpp `root-config --libs --cflags` -o main.o``` compile without errors while the other one needs library specifications. I am a beginner, it will be great if you share your full linking command for this exact example and please mention if you include more root headers. Thank you. – quanta Jan 07 '17 at 10:13
  • These are the exact commands I used: `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` or alternatively `g++ macro.cpp h1.cpp h2.cpp \`root-config --libs --cflags\` -o main.o` – user2148414 Jan 09 '17 at 08:11
  • The `root-config` commands expand to `--cflags` : `-pthread -m64 -I/usr/include/root` `--glibs` : `-L/usr/lib64/root -lGui -lCore -lCint -lRIO -lNet -lHist -lGraf -lGraf3d -lGpad -lTree -lRint -lPostscript -lMatrix -lPhysics -lMathCore -lThread -pthread -lm -ldl -rdynamic` Using the same version `5.34/36`. Did you install root from source, or installed it as a ubuntu package? `root-config --features` can tell the what features were enabled when root was built. – user2148414 Jan 09 '17 at 08:17
  • @user2148414 sorry for not coming back sooner. I installed root from the source. I think my installation is complete and no library problems, I am saying this because "g++ macro.cpp h1.cpp h2.cpp `root-config --libs --cflags` -o main.o" this method has solved my problem and since then I have created way more complicated applications involving so many root objects and the compiler didn't complain at all. Your answer and the comments gave me the direction towards this single liner solution and I would like to write an answer aiming to a future newbie, hope nobody minds. Thanks a lot really. – quanta Jan 10 '17 at 23:10
  • @user2148414 I would request you to read my update (http://stackoverflow.com/a/41580377/4962474) and comment, if you feel like it, of course. Thanks a lot. – quanta Jan 10 '17 at 23:45
0

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;
    }
Community
  • 1
  • 1
quanta
  • 215
  • 3
  • 14