Compile using ACLiC Across Sibling Directories

I have a project tree structure that looks like:

Mu_Analysis/
—>Template_Selector/
------->Template_Selector.C
------->Template_Selector.h
—>Compute_L1XE30Efficiency/
------->test_l1xe30_efficiency.C
------->ComputeL1XE30toZBEfficiency.C
------->ComputeL1XE30toZBEfficiency.h
—>Compute_L1XE50_L1XE30_Efficiency/
------->test_l1xe50_efficiency.C
------->ComputeL1XE50toL1XE30Efficiency.C
------->ComputeL1XE50toL1XE30Efficiency.h
—>mu_analysis.root
—>totalCorrection.cpp

Inside Template_Selector and Compute_L1XE30Efficiency, I have generated two sets of TSelectors using TTree::MakeSelector from the same ROOT file. Inside Template_Selector contains the skeleton, default, TSelector generated for the ROOT file. Inside each of the latter sibling directories, I perform one piece of a larger analysis, that reads from/writes to mu_analysis.root . Each TSelector in the sibling directories are run from totalCorrection.cpp using a code block that looks like:

CorrectL1XE30toZB* correctToZB = (CorrectL1XE30toZB*) TSelector::GetSelector("CorrectL1XE30toZB/CorrectL1XE30toZB.C+");

jetm10_chain->Process(correctToZB);

I want for each TSelector class in the sibling directories to derive from the skeleton one in Template_Selector. This is so that I can create member functions in the base class and use them in the derived classes without having to define the same member functions in each TSelector class afterwords (which is currently how I’ve been doing it).

The way I tried to implement this was to add an include line inside each of the header files for the derived classes:

#include "../Template_Selector/Template_Selector.h"

And I switched each derived class definition in the header files to:

class ComputeL1XE30toZBEfficiency : public Template_Selector{...};

Instead of running from totalCorrection.cpp each time, I wrote a macro inside each of the sibling directories to test each TSelector separately. Each one of these smaller macros contain a code block like the one I listed above, but for just the TSelector in the same directory.

When I run the unit test for the ComputeL1XE30toZBEfficiency TSelector, I get a crash. Although I admittedly can’t interpret a lot of it, I received the familiar:

undefined symbol: _ZTI23Jburr_Template_Selector ;

symbol ‘_ZTV27ComputeL1XE30toZBEfficiency’ unresolved while linking symbol ; and

‘You are probably missing the definition of vtable for ComputeL1XE30toZBEfficiency
Maybe you need to load the corresponding shared library?
IncrementalExecutor::executeFunction: symbol ‘_ZTV23Jburr_Template_Selector’ unresolved while linking symbol ‘__ctor_2’!
You are probably missing the definition of vtable for Jburr_Template_Selector
Maybe you need to load the corresponding shared library?’

These error messages tell me that I didn’t specify some includes or library paths to ACLiC properly.
I am not sure if the solution would be to write a LinkDef.h file and then specify the correct path to the Template_Selector inside when listing my #pragmas. Or can I specify the pragmas with the correct path inside the header file to the derived classes as:

#ifdef __MAKECINT__ #pragma link C++ class ../Template_Selector/Template_Selector+; #endif

I’ve attached the full error output, as well as the header files, macros and source code mentioned in the question.

The output of root --version is:

ROOT Version: 6.17/01
Built for linuxx8664gcc on Dec 27 2018, 22:29:43
From heads/master@v6-13-04-2658-g795dc102cd

Thanks,
Joseph

root_question.zip (11.7 KB)

Hi Joseph,

did you try to

.L totalCorrection.cpp+

or the corresponding tests in the directories, for that matter, in ROOT? If I’m not mistaken, it should take care of the rest. Otherwise, we will hand it over to the dictionary experts.

Hi,

So I tried to run

.L totalCorrection.cpp+ 

as well as the corresponding statement for the unit tests inside a ROOT interpreter that was launched in the same directory. I still get the same error: ‘undefined symbol… unresolved while linking’ etc.

I am a little bit confused as to how ACLiC should even know of the location of the implementation of the template selector code. In normal C++, I would explicitly pass the paths to g++ and a command line arg for an include path, but in ROOT it’s a little more ambiguous as to what project structure works best given the way ACLiC is called.

Anyways, I’ve attached the error log I get when I try compiling the unit test using ACLiC. I’ll keep trying things to figure out how to make the code from my base class available to my derived classes during linking.

output.txt (9.0 KB)

Thanks,
Joseph

Hi Joseph,

it looks like you need to go through the process of generating a dictionary for all the classes you have defined. The HowTo is here:
https://root.cern.ch/interacting-shared-libraries-rootcling

How you write the LinkDef.h is explained on the next page (at the bottom of the page that I linked above).

When you have the library, load it from the interpreter or inside the test macros to have all the class information available:
gSystem->Load("<path>")
In that case, I suppose, you also don’t need to compile the TSelectors in the main macro using the GetSelector() calls.

Concerning the paths:

In the LinkDef, just mention the class names. The -I when you invoke rootcling will take care of the paths.

Hi Stephan,

So I’ve made an attempt that I hope is closer to a correct solution. I’ve moved the code for the template selector (BurrSelector.C) and the derived selectors (Compute*.C etc.) to a folder called src, and all of the corresponding header files to a folder named include. And I have also created a lib folder containing the linkdef.h, makefile, MyDict.cxx and the shared library that is created. I used the code in the link you provided as a template for my makefile. Doing this, I am able to successfully generate the shared library. However, when I go into the ROOT interpreter and try:

gSystem->Load("mylib.so")

From within the lib folder, I get a DynamicLibraryManager error for the undefined symbol: ComputeL1XE50toL1XE30Efficiency8StreamerBuffer.
This is a little confusing to me, as I first compile the header files, including ComputeL1XE50toL1XE30Efficiency.h, into the dictionary MyDict.cxx . And then I compile and link MyDict.cxx and all of the corresponding .C files into the shared library myLib.so .
So I know that the file for the undefined symbol is being compiled and linked into the shared library. I think I might have messed up a path somewhere inside the makefile as I attempt to weave different files from different directories together. I’ve attached the entire directory as is so you can see the structure and my makefile.

Thanks,
Joseph

MyACLiCQuestion.zip (192 Bytes)

Hi Joseph,

If something with the streamer is missing, my bet is on the ClassDef/ClassImp macros.

  • Did you include a ClassDef in the class definition and a ClassImp in the implementation?
  • Is the class listed as #pragma link ... <ClassName>+?

Hi Stephan,

Thanks for your reply. I wound up achieving the result I wanted by instead putting all of the classes in the same src folder, and then compiling and linking everything in that source folder into a shared library, which I now load upon launch of the root interpreter by loading it in rootlogon.C. Now I can always start my environment in the root directory of my project, and run any plotting or analysis macros I want with the compiled and linked classes available, which was my desired affect.

I think a large contributing factor to my asking this question was a fundamental lack of knowledge of how source files and a Linkdef.h relate to the creation of a dictionary and a shared library, which led me to asking the wrong question. There is a lot of documentation on this process, but for beginners its sometimes difficult to put all of the pieces together from the users guide and the linkdef guides. Once I got a minimal example working, it became clear how to proceed and then how to use compiled libraries relative to interpreted macros.

Thanks for all your help,
Joseph