Add Own ROOT Classes

I am trying to add my own ROOT classes.
Here is a simple script I am trying to make work. The file is called test.cpp:


#include "TFile.h"
#include "TTree.h"

#include <iostream>

class A : public TObject {
  //class A {

public:

  A() {;}
  A(int input) : val(input) {;}
  int get_val() const { return val; }
  ~A() {;}

public:

  ClassDef(A,1);

private:

  int val;

};

class B : public TObject {
  //class B {

public:

  B() {;}
  B(A input_A, int input) : A_in_B(input_A), val(input) {;}
  ~B() {;}

  int get_val() const { return val; }

  A A_in_B;

public:

  ClassDef(B,1);

private:

  int val;

};

int main(){
  //void test(){

  TTree* t = new TTree("t","description");

  A A_object;
  B B_object;

  TBranch* branch_A = t->Branch("A", &A_object);
  TBranch* branch_B = t->Branch("B", &B_object);

  for (int i=0; i<10; i++){

    A_object = A(i);
    B_object = B(A_object, 2*i);

    t->Fill();

  }

  TFile* fOut = new TFile("output.root","RECREATE");
  t->Write();
  fOut->Close();

  return 0;

}

The code runs fine and produces the intended result when compiling with ACLiC.
I tried to compile this with g++ using the my LinkDef.h:


#ifdef __CINT__

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

#pragma link C++ class A+;
#pragma link C++ class B+;

#endif

and the following commands at the command line:

rootcint test_dictionary.cxx -c test.cpp LinkDef.h
g++ test.cpp $(root-config --libs --cflags) -o test

and get the following errors:

/tmp/ccr2DNfY.o: In function `A::A()':
test.cpp:(.text._ZN1AC2Ev[_ZN1AC5Ev]+0x1f): undefined reference to `vtable for A'
/tmp/ccr2DNfY.o: In function `A::A(int)':
test.cpp:(.text._ZN1AC2Ei[_ZN1AC5Ei]+0x22): undefined reference to `vtable for A'
/tmp/ccr2DNfY.o: In function `A::~A()':
test.cpp:(.text._ZN1AD2Ev[_ZN1AD5Ev]+0x13): undefined reference to `vtable for A'
/tmp/ccr2DNfY.o: In function `B::B()':
test.cpp:(.text._ZN1BC2Ev[_ZN1BC5Ev]+0x20): undefined reference to `vtable for B'
/tmp/ccr2DNfY.o: In function `A::A(A const&)':
test.cpp:(.text._ZN1AC2ERKS_[_ZN1AC5ERKS_]+0x2a): undefined reference to `vtable for A'
/tmp/ccr2DNfY.o: In function `B::B(A, int)':
test.cpp:(.text._ZN1BC2E1Ai[_ZN1BC5E1Ai]+0x27): undefined reference to `vtable for B'
/tmp/ccr2DNfY.o: In function `B::~B()':
test.cpp:(.text._ZN1BD2Ev[_ZN1BD5Ev]+0x14): undefined reference to `vtable for B'
/tmp/ccr2DNfY.o: In function `TBranch* TTree::Branch<A>(char const*, A*, int, int)':
test.cpp:(.text._ZN5TTree6BranchI1AEEP7TBranchPKcPT_ii[_ZN5TTree6BranchI1AEEP7TBranchPKcPT_ii]+0x2f): undefined reference to `typeinfo for A'
test.cpp:(.text._ZN5TTree6BranchI1AEEP7TBranchPKcPT_ii[_ZN5TTree6BranchI1AEEP7TBranchPKcPT_ii]+0x3c): undefined reference to `typeinfo for A'
/tmp/ccr2DNfY.o: In function `TBranch* TTree::Branch<B>(char const*, B*, int, int)':
test.cpp:(.text._ZN5TTree6BranchI1BEEP7TBranchPKcPT_ii[_ZN5TTree6BranchI1BEEP7TBranchPKcPT_ii]+0x2f): undefined reference to `typeinfo for B'
test.cpp:(.text._ZN5TTree6BranchI1BEEP7TBranchPKcPT_ii[_ZN5TTree6BranchI1BEEP7TBranchPKcPT_ii]+0x3c): undefined reference to `typeinfo for B'
collect2: error: ld returned 1 exit status

In general, when adding ROOT classes, must one have separate header and implementation files for each class? If not, what is the problem with my above code?

If one must have separate header and implementation files for each class, I also tried that and received errors. Below is A.hh


#ifndef A_HH
#define A_HH

#include "TObject.h"

class A : public TObject {

public:

  A();
  A(int input);
  ~A();

  int get_val() const;

public:

  ClassDef(A,1);

private:

  int val;

};

#endif

Below is A.cc


#include "A.hh"

A::A() {;}
A::A(int input) : val(input) {;}
A::~A() {;}

int A::get_val() const { return val; }

ClassImp(A);

Below is B.hh


#ifndef B_HH
#define B_HH

#include "A.hh"

#include "TObject.h"

class B : public TObject {

public:

  B();
  B(A input_A, int input);
  ~B();

  int get_val() const;

  A A_in_B;

public:

  ClassDef(B,1);

private:

  int val;

};

#endif

Below is B.cc


#include "A.hh"
#include "B.hh"

B::B() {;}
B::B(A input_A, int input) : A_in_B(input_A), val(input) {;}
B::~B() {;}

int B::get_val() const { return val; }

ClassImp(B);

The LinkDef.h and commands at the command line are the same as before. The error is


/tmp/cchkhdsw.o: In function `__static_initialization_and_destruction_0(int, int)':
A.cc:(.text+0xff): undefined reference to `ROOT::GenerateInitInstance(A const*)'
/tmp/cchkhdsw.o: In function `A::IsA() const':
A.cc:(.text._ZNK1A3IsAEv[_ZNK1A3IsAEv]+0xd): undefined reference to `A::Class()'
/tmp/cchkhdsw.o:(.rodata._ZTV1A[_ZTV1A]+0x1d0): undefined reference to `A::ShowMembers(TMemberInspector&)'
/tmp/cchkhdsw.o:(.rodata._ZTV1A[_ZTV1A]+0x1d8): undefined reference to `A::Streamer(TBuffer&)'
/tmp/cc5KAyy7.o: In function `__static_initialization_and_destruction_0(int, int)':
B.cc:(.text+0x1a1): undefined reference to `ROOT::GenerateInitInstance(B const*)'
/tmp/cc5KAyy7.o: In function `B::IsA() const':
B.cc:(.text._ZNK1B3IsAEv[_ZNK1B3IsAEv]+0xd): undefined reference to `B::Class()'
/tmp/cc5KAyy7.o:(.rodata._ZTV1B[_ZTV1B]+0x1d0): undefined reference to `B::ShowMembers(TMemberInspector&)'
/tmp/cc5KAyy7.o:(.rodata._ZTV1B[_ZTV1B]+0x1d8): undefined reference to `B::Streamer(TBuffer&)'
collect2: error: ld returned 1 exit status

Any help would be appreciated. Thanks.

Hi,

A preliminary remark: if you want to interactively use your classes in ROOT, via the command line C++ interpreter, in macros or in python programs, just include the necessary headers. Dictionaries are necessary only for performing IO operations.

Said that, I reorganised your example.
Let’s start with test.h, it is a header since you define the interface of your class. Note that it is not necessary to inherit from TObject for interactivity with ROOT nor for I/O even if this can be beneficial as many methods will be available, e.g. setters and getters for object names and so on.

#include "TFile.h"
#include "TTree.h"

#include <iostream>

class A : public TObject {
  //class A {

public:

  A() {;}
  A(int input) : val(input) {;}
  int get_val() const { return val; }
  ~A() {;}

public:

  ClassDef(A,1);

private:

  int val;

};

class B : public TObject {
  //class B {

public:

  B() {;}
  B(A input_A, int input) : A_in_B(input_A), val(input) {;}
  ~B() {;}

  int get_val() const { return val; }

  A A_in_B;

public:

  ClassDef(B,1);

private:

  int val;

};

int main(){
  //void test(){

  TTree* t = new TTree("t","description");

  A A_object;
  B B_object;

  TBranch* branch_A = t->Branch("A", &A_object);
  TBranch* branch_B = t->Branch("B", &B_object);

  for (int i=0; i<10; i++){

    A_object = A(i);
    B_object = B(A_object, 2*i);

    t->Fill();

  }

  TFile* fOut = new TFile("output.root","RECREATE");
  t->Write();
  fOut->Close();

  return 0;

}

the linkdef:

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

#pragma link C++ class A+;
#pragma link C++ class B+;

And the commands:

rootcling test_dictionary.cxx  -rml libtest.so -rmf libtest.rootmap test.h LinkDef.h
g++ test_dictionary.cxx `root-config --libs --cflags` -o libtest.so -fPIC -shared 

Two remarks:

  1. We need a shared library.
  2. I added the commands necessary to generate a rootmap, a file ROOT reads at startup in order to be able to automatically load the necessary libraries w/o the need of linking them at compile time or loading them manually.

Cheers,
Danilo

Thanks!

Sure. Let us know if you encounter problems.

Cheers,
D