Shared libraries - interpreter vs compiled macro

I have a minimalistic event class that I would eventually like to write to a TTree. I would like to compile the class into a shared library object (in reality I will have more classes in the shared library) and load the shared library in a compiled macro for usage.

Below are the source codes of five files: MyEvt.h, MyEvt.C, LinkDef.h, Makefile, app.C, also attached as a tar ball example.tar (10 KB)
. To re-produce the problem, put all of these file to a single directory and run make to compile the shared library libexample.so.

When doing

root
.L libexample.so
.x app.C+

Everything works nicely. However, when one does

root app.C++

there is a linker error. Hence, why is there different behavior, although I load the shared library inside the macro with ‘gSystem->Load(“libexample.so”)’? How do I need to change the Makefile, such that running with ‘root app.C++’ would work?

#ifndef MyEvt_h
#define MyEvt_h

#include "TObject.h"

class MyEvt : public TObject {

 public:
  MyEvt();
  ~MyEvt();

  // setters/getters
  void     SetVar(Double_t v) { fVar = v; }       
  Double_t GetVar()           { return fVar; }
 
 private:
  Double_t        fVar;

  ClassDef(MyEvt, 1)
};
#include "MyEvt.h"

MyEvt::MyEvt() {

  fVar = 0;

}

MyEvt::~MyEvt() {}

LinkDef.h def and Makefile:

#ifdef __CINT__

#pragma link off all globals;
#pragma link off all classes;
#pragma link off all functions;

#pragma link C++ class MyEvt+;

#endif
ROOTCFLAGS    = $(shell root-config --cflags)
ROOTLIBS      = $(shell root-config --libs)
CXX           = $(shell root-config --cxx)
CXXFLAGS      = -g -Wall -std=c++11 -fPIC

CXXFLAGS  += $(ROOTCFLAGS)
LDFLAGS   += $(ROOTLIBS)

PACKAGE = example
HEADERS = $(filter-out $(CURDIR)/LinkDef.h, $(wildcard $(CURDIR)/*.h))
SOURCES = $(filter-out $(CURDIR)/app.C, $(wildcard $(CURDIR)/*.C))
TARGET_LIB = $(CURDIR)/lib$(PACKAGE).so
DICTIONARY = $(CURDIR)/dic$(PACKAGE).cxx

all: $(TARGET_LIB)

$(TARGET_LIB): $(SOURCES) $(DICTIONARY)
        @echo "  Building $(PACKAGE)..."
        @$(CXX) $(CXXFLAGS) -shared -O3 -o $@ $^ $(LDFLAGS)

$(DICTIONARY): $(HEADERS) LinkDef.h
        @echo "  Making dictionary for $(PACKAGE)..."
        @rootcint -f $@ -c $^

clean:
        @echo "  Cleaning $(PACKAGE)..."
        @rm -f $(TARGET_LIB)
        @rm -f $(DICTIONARY)
        @rm -f $(DICTIONARY:%.cxx=%.h)
        @rm -f *rdict.pcm

.PHONY: clean all

And finally the macro where library is used:

#include "MyEvt.h"
#include "TSystem.h"
#include "TTree.h"
#include "TFile.h"

void app() {

  gSystem->Load("libexample.so");
  MyEvt *evt = new MyEvt();

  TFile fout("fout.root","RECREATE");
  TTree tout("t","t");
  tout.Branch("evt", &evt);

  for (Int_t i = 0; i < 10; i++) {
    evt->SetVar(i);
    tout.Fill();
  }

  fout.Close();

}

ROOT Version: 6.12.06, downloaded binary
Platform: Scientific linux 7
Compiler: gcc version 4.8.5


Linking happens prior to script execution. Therefore gSystem->LoadLibrary is too late.

You can tell ROOT to load your library automatically. To do that, you need to create a rootmap file:

rootcling -f dictexample.cxx -rml libexample.so -rmf libMyEvt.rootmap MyEvt.h LinkDef.h

Then you can also open your root file without loading libexample.so - it will then be loaded automatically.

Note that ROOT6 has the R__LOAD_LIBRARY macro to load libraries. However, that does not (yet) solve your problem. See also: https://sft.its.cern.ch/jira/browse/ROOT-8687

1 Like

Dear Wolf,

Thank you for your quick reply, this indeed fixes my problem.

From what I understand, with ROOT 6 we are using CLING instead CINT, hence when compiling custom libraries this should be taken into account. For this example, I made the following changes:

in LinkDef.h I replaced
#ifdef __CINT__
with
#ifdef __CLING__.

In Makefile I replaced

$(DICTIONARY): $(HEADERS) LinkDef.h
        @echo " Making dictionary for $(PACKAGE)…"
        @rootcint -f $@ -c $^

with

$(DICTIONARY): $(HEADERS) LinkDef.h                                                                                                                                                      
        @echo "  Making dictionary for $(PACKAGE)..."
        @rootcling -f $@ -rml $(TARGET_LIB) -rmf lib$(PACKAGE).rootmap $^

ROOT will automatically load the library if the shared library and the rootmap file are in the directory where ROOT prompt is opened. For this to work outside the shared library directory, the shared library directory needs to be in LD_LIBRARY_PATH.

Thank you again for the reply!

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.