User defined class

Dear experts,
I have written a class (AdditionalCuts.h) with some members and functions to take two strings (input filename and decay mode) to open the input file and read a tree and apply some cuts and save it in a tree in an other file. I have added necessary lines such as
#ifndef CINT
ClassImp(AdditionalCuts);
#endif
and ClassDef(AdditionalCuts,1);
and TObject.h header.

I also have a simple code “ApplyAdditionalCuts.C” for testing that only loops over the input tree.
When trying to rin it in Root, I receive the following error:

root [0] .L ApplyAdditionalCuts.C++
Info in TUnixSystem::ACLiC: creating shared library /root/Projects/ZAnalysis/MyDesktop/./ApplyAdditionalCuts_C.so
cling::DynamicLibraryManager::loadLibrary(): /root/Projects/ZAnalysis/MyDesktop/ApplyAdditionalCuts_C.so: undefined symbol: _ZTV14AdditionalCuts
/usr/lib/gcc/x86_64-redhat-linux/4.8.5/…/…/…/…/lib64/crt1.o: In function _start': (.text+0x20): undefined reference to main’
/root/Projects/ZAnalysis/MyDesktop/ApplyAdditionalCuts_C_ACLiC_dict.o: In function AdditionalCuts::AdditionalCuts(std::string, std::string)': ApplyAdditionalCuts_C_ACLiC_dict.cxx:(.text+0x130a): undefined reference to vtable for AdditionalCuts’
ApplyAdditionalCuts_C_ACLiC_dict.cxx:(.text+0x135a): undefined reference to TTree::Class()' ApplyAdditionalCuts_C_ACLiC_dict.cxx:(.text+0x412c): undefined reference to TTree::TTree(char const*, char const*, int, TDirectory*)’
/root/Projects/ZAnalysis/MyDesktop/ApplyAdditionalCuts_C_ACLiC_dict.o: In function _GLOBAL__sub_I__ZN14AdditionalCutsC2ESsSs': ApplyAdditionalCuts_C_ACLiC_dict.cxx:(.text.startup+0x34): undefined reference to ROOT::GenerateInitInstance(AdditionalCuts const*)’
collect2: error: ld returned 1 exit status

I would appreciate any help


Please read tips for efficient and successful posting and posting code

ROOT Version: Not Provided
Platform: Not Provided
Compiler: Not Provided


Hi,
First of all you don’t need the lines #ifndef CINT since if you are using ROOT version 6, CINT is not anymore part of ROOT.

If all the symbols of your class are defined and implemented in AdditionalCuts.h it is enough that you do before using your class in your macro (from the ROOT prompt).
.L AdditionalCuts.h

Lorenzo

Dear Moneta,
Thank you very much for your response.
Sorry I should have mentioned that I am using root 6.20/08.
Also I get the error when I do “.L ApplyAdditionalCuts.C++”. When I do “.L AdditionalCuts.h” I get no error messages.
If I change my header file and write it as a list of variables and functions (old fashioned C), my code works perfectly, but I would like to know why writing it as a C++ class causes the mentioned problem.
I would be happy to share both the .h file and the macro. The .h file is not very complicated but long due to large number of variables and corresponding long trees I use.
Please let me know if this is possible.
Thanks again,

P.S. In case it help, I am adding an outline of my files (AdditionalCuts.h, and ApplyAdditionalCuts.C)

#include “TROOT.h”
#include “TObject.h”
#include “TFile.h”
#include “TTree.h”
…more headers

class AdditionalCuts : public TObject {
public:

TFile* InputRootF;
TTree* InputTree;

std::vector<unsigned int> *In_EventNumber;
std::vector<unsigned int> *In_RunNumber;
std::vector<double>  *In_ZMass;
...more variables

TBranch        *In_b_EventNumber;
TBranch        *In_b_RunNumber;
TBranch        *In_b_ZMass;
...more branches

AdditionalCuts();
virtual ~AdditionalCuts();

ClassDef(AdditionalCuts,1);

};

AdditionalCuts::AdditionalCuts() {

string InputRootFile;
string DecayMode;

TFile* InputRootF = new TFile(InputRootFile.c_str(),"READ");
TDirectory* InputDir = (TDirectory*)InputRootF->Get("ZDecayAnalyzer");
InputDir->GetObject("OutputTree",InputTree);

In_EventNumber = 0;
In_RunNumber = 0;
In_ZMass = 0;
... more of this

InputTree->SetBranchAddress("EventNumber", &In_EventNumber, &In_b_EventNumber);
InputTree->SetBranchAddress("RunNumber", &In_RunNumber, &In_b_RunNumber);
InputTree->SetBranchAddress("ZMass", &In_ZMass, &In_b_ZMass);
...more branches

}

#include “AdditionalCuts.h”
void ApplyAdditionalCuts() {

AdditionalCuts* DC;

int AllEntries = (DC->InputTree)->GetEntries();
for(int i=0; i<AllEntries; i++) {
	if(i%100==0)
		cout << "\r" << (double)i/(double)AllEntries*100 << "\% Processed" << flush;
}

}

Hi @Natilus ,
sorry we missed this reply.

We cannot reproduce the problem with incomplete code, can you please share a self-contained, minimal reproducer?

Cheers,
Enrico

Hi @eguiraud
Thank you for your response. The following is the minimal version of my class and a simple code that only loops over the input tree.
When I run it, I simply do :

.L AdditionalCuts_min.h
.L ApplyAdditionalCuts_min.C++

and receive the following error message:

cling::DynamicLibraryManager::loadLibrary(): /root/Projects/ZAnalysis/Development/Class/ApplyAdditionalCuts_min_C.so: undefined symbol: _ZN14AdditionalCuts8StreamerER7TBuffer
/usr/lib/gcc/x86_64-redhat-linux/4.8.5/../../../../lib64/crt1.o: In function `_start':
(.text+0x20): undefined reference to `main'
/root/Projects/ZAnalysis/Development/Class/ApplyAdditionalCuts_min_C_ACLiC_dict.o: In function `AdditionalCuts::AdditionalCuts()':
ApplyAdditionalCuts_min_C_ACLiC_dict.cxx:(.text+0x550): undefined reference to `TTree::Class()'
/root/Projects/ZAnalysis/Development/Class/ApplyAdditionalCuts_min_C_ACLiC_dict.o: In function `AdditionalCuts::IsA() const':
ApplyAdditionalCuts_min_C_ACLiC_dict.cxx:(.text._ZNK14AdditionalCuts3IsAEv[_ZNK14AdditionalCuts3IsAEv]+0x1): undefined reference to `AdditionalCuts::Class()'
/root/Projects/ZAnalysis/Development/Class/ApplyAdditionalCuts_min_C_ACLiC_dict.o: In function `AdditionalCuts::ShowMembers(TMemberInspector&) const':
ApplyAdditionalCuts_min_C_ACLiC_dict.cxx:(.text._ZNK14AdditionalCuts11ShowMembersER16TMemberInspector[_ZNK14AdditionalCuts11ShowMembersER16TMemberInspector]+0xd): undefined reference to `AdditionalCuts::Class()'
/root/Projects/ZAnalysis/Development/Class/ApplyAdditionalCuts_min_C_ACLiC_dict.o:(.rodata._ZTV14AdditionalCuts[_ZTV14AdditionalCuts]+0x1e0): undefined reference to `AdditionalCuts::Streamer(TBuffer&)'
collect2: error: ld returned 1 exit status

---------------------------------------------------------Class---------------------------------------------------------

#ifndef ADDITIONALCUTS_H
#define ADDITIONALCUTS_H

#include "TROOT.h"
#include "TObject.h"
#include "TFile.h"
#include "TTree.h"
#include "TBranch.h"
#include "TChain.h"
#include "TH1.h"
#include "TH2.h"
#include "TStyle.h"
#include "TCanvas.h"
#include "TLegend.h"
#include "TAxis.h"
#include "TFitResult.h"
#include "TFitResultPtr.h"
#include "TGraphErrors.h"
#include "TMath.h"
#include "TF1.h"

#include "vector"
#include "fstream"
#include "iostream"
#include "string"
#include "map"
#include "math.h"

#include <sys/stat.h> 		// <---<< mkdir()
#include <sys/types.h> 		//

using namespace std;

class AdditionalCuts : public TObject {
public:

	TFile* InputRootF;
	//TFile* OutputRootF;
	TTree* InputTree;
	//TTree* OutputTree;

	/// Input Tree Leaves ///
	std::vector<string>  *In_TriggerList;

	std::vector<unsigned int> *In_EventNumber;
	std::vector<unsigned int> *In_RunNumber;

	std::vector<double>  *In_ZMass;
	std::vector<double>  *In_ZPt;
	std::vector<double>  *In_ZEta;

	std::vector<double>  *In_PsiMass;
	std::vector<double>  *In_PsiPt;
	std::vector<double>  *In_PsiEta;

	std::vector<double>  *In_JPsiMass;
	std::vector<double>  *In_JPsiPt;
	std::vector<double>  *In_JPsiEta;

	/// Input Tree Branches ///
	TBranch        *In_b_TriggerList;

	TBranch        *In_b_EventNumber;
	TBranch        *In_b_RunNumber;

	TBranch        *In_b_ZMass;
	TBranch        *In_b_ZPt;
	TBranch        *In_b_ZEta;

	TBranch        *In_b_PsiMass;
	TBranch        *In_b_PsiPt;
	TBranch        *In_b_PsiEta;

	TBranch        *In_b_JPsiMass;
	TBranch        *In_b_JPsiPt;
	TBranch        *In_b_JPsiEta;

	/// Output Tree Leaves ///
	std::vector<string>  *Out_TriggerList;

	std::vector<unsigned int> *Out_EventNumber;
	std::vector<unsigned int> *Out_RunNumber;

	std::vector<double>  *Out_ZMass;
	std::vector<double>  *Out_ZPt;
	std::vector<double>  *Out_ZEta;

	std::vector<double>  *Out_PsiMass;
	std::vector<double>  *Out_PsiPt;
	std::vector<double>  *Out_PsiEta;

	std::vector<double>  *Out_JPsiMass;
	std::vector<double>  *Out_JPsiPt;
	std::vector<double>  *Out_JPsiEta;

	AdditionalCuts();
	virtual ~AdditionalCuts();
	ClassDef(AdditionalCuts,1);
};

#endif

#ifdef AdditionalCuts_cxx
AdditionalCuts::AdditionalCuts() {

	TFile* InputRootF = new TFile("M-Zto4Muons.root","READ");
	TDirectory* InputDir = (TDirectory*)InputRootF->Get("ZDecayAnalyzer");
	InputDir->GetObject("OutputTree",InputTree);
	/// Set Object Pointer ///
	In_TriggerList = 0;

	In_EventNumber = 0;
	In_RunNumber = 0;

	In_ZMass = 0;
	In_ZPt = 0;
	In_ZEta = 0;

	In_PsiMass = 0;
	In_PsiPt = 0;
	In_PsiEta = 0;

	In_JPsiMass = 0;
	In_JPsiPt = 0;
	In_JPsiEta = 0;

	InputTree->SetBranchAddress("TriggerList", &In_TriggerList, &In_b_TriggerList);
	InputTree->SetBranchAddress("EventNumber", &In_EventNumber, &In_b_EventNumber);
	InputTree->SetBranchAddress("RunNumber", &In_RunNumber, &In_b_RunNumber);
	InputTree->SetBranchAddress("ZMass", &In_ZMass, &In_b_ZMass);
	InputTree->SetBranchAddress("ZPt", &In_ZPt, &In_b_ZPt);
	InputTree->SetBranchAddress("ZEta", &In_ZEta, &In_b_ZEta);
	InputTree->SetBranchAddress("PsiMass", &In_PsiMass, &In_b_PsiMass);
	InputTree->SetBranchAddress("PsiPt", &In_PsiPt, &In_b_PsiPt);
	InputTree->SetBranchAddress("PsiEta", &In_PsiEta, &In_b_PsiEta);
	InputTree->SetBranchAddress("JPsiMass", &In_JPsiMass, &In_b_JPsiMass);
	InputTree->SetBranchAddress("JPsiPt", &In_JPsiPt, &In_b_JPsiPt);
	InputTree->SetBranchAddress("JPsiEta", &In_JPsiEta, &In_b_JPsiEta);

}

AdditionalCuts::~AdditionalCuts()
{
   if (!InputTree) return;
}

#endif

--------------------------------------------code-----------------------------------------------

#define AdditionalCuts_cxx
#include "AdditionalCuts.h"

void ApplyAdditionalCuts(string InputRootFile, string DecayMode) {

	AdditionalCuts* DC;

//****************
// Additional Cuts
//****************

	int AllEntries = (DC->InputTree)->GetEntries();
	for(int i=0; i<AllEntries; i++) {
		if(i%100==0)
			cout << "\r" << (double)i/(double)AllEntries*100 << "\% Processed" << flush;
	}
}

Thanks for your help in advance

Hi,
thanks for the code, I’ll try to reproduce the issue as soon as possible (currently on holidays).

Cheers
Enrico

Hi,
there is something wrong with the generation of the dictionaries and streamers for the AdditionalCuts class, I am not quite sure what.

However, do you actually need to perform I/O of the AdditionalCuts class, i.e. do you need to save instances of that class to a ROOT file?

If not, you can remove the inheritance from TObject and remove the ClassDef statement and things should then work fine.

Cheers,
Enrico

Dear Enrico,
Thank you very much for your help.
I actually need to save them to a root file since they need to be processed by another code.
As I mentioned before, I can do everything by using old school C functions without using classes; however, it would be more elegant to use classes.
Thank you very much again,

If you really need to save AdditionalCuts objects to a ROOT file, then you need to generate dictionaries for it. See Generating dictionaries - ROOT and some examples at GitHub - eguiraud/root_dictionaries_tutorial: A tutorial on creating ROOT dictionaries to perform I/O of custom C++ classes .

Cheers,
Enrico

Hi Enrico,
Sorry for my delay. I am trying to read from the input file containing a Tree and after applying some cuts right the values to a new Tree in the output file. I tried adding a function member to the class that would write the values that are simple double or bool values. I don’t need to write the AdditionalCuts objects to a ROOT file. However, even then I get the mentioned error.

Great, then as I mention above you can remove the inheritance from TObject and the ClassDef statement from the AdditionalCuts class. If there is still something wrong then, please post the new version of the code so that I can reproduce/take a look.

Cheers,
Enrico

Dear Enrico,
Once again, thank you for your help. I managed to change some parts of the code and get it to work after, as you suggested, removing the inheritance from TObject and the ClassDef statemen. However, I still have a small problem. I encountered this before and by moving the OutputTree definition, I managed to solve it. But this time I don’t know how to solve it.
The error is this:

Error in TBranchElement::TBranch::WriteBasketImpl: basket’s WriteBuffer failed.

Error in TBranchElement::TBranch::Fill: Failed to write out basket.

Error in TBranchElement::Fill: Failed filling branch:TriggerList, nbytes=-1
Error in TTree::Fill: Failed filling branch:OutputTree.TriggerList, nbytes=-1, entry=3237
This error is symptomatic of a Tree created as a memory-resident Tree
Instead of doing:
TTree *T = new TTree(…)
TFile *f = new TFile(…)
you should do:
TFile *f = new TFile(…)
TTree *T = new TTree(…)

I’ll upload a minimal version of the class in my next reply so you can take a look.
Please note that the names are a little different but essentially the structure is the same.

#include "TROOT.h"
#include "TObject.h"
#include "TFile.h"
#include "TTree.h"
#include "TBranch.h"
#include "TChain.h"
#include "TH1.h"
#include "TH2.h"
#include "TStyle.h"
#include "TCanvas.h"
#include "TLegend.h"
#include "TAxis.h"
#include "TFitResult.h"
#include "TFitResultPtr.h"
#include "TGraphErrors.h"
#include "TMath.h"
#include "TF1.h"

#include "vector"
#include "fstream"
#include "iostream"
#include "string"
#include "map"
#include "math.h"

#include <sys/stat.h> 		// <---<< mkdir()
#include <sys/types.h> 		//

using namespace std;

class ResultAnalyzer {
public:

	TFile* InputRootF;
	TFile* OutputRootF;
	TTree* InputTree;
	TTree* OutputTree = new TTree("OutputTree", "OutputTree");

	std::string DecayMode;

	/// Input Tree Leaves ///
	std::vector<unsigned int> *In_EventNumber;
	std::vector<unsigned int> *In_RunNumber;

	std::vector<double>  *In_ZMass;
	std::vector<double>  *In_ZPt;
	std::vector<double>  *In_ZEta;

	std::vector<double>  *In_PsiMass;
	std::vector<double>  *In_PsiPt;
	std::vector<double>  *In_PsiEta;


	/// Input Tree Branches ///
	TBranch        *In_b_EventNumber;
	TBranch        *In_b_RunNumber;

	TBranch        *In_b_ZMass;
	TBranch        *In_b_ZPt;
	TBranch        *In_b_ZEta;

	TBranch        *In_b_PsiMass;
	TBranch        *In_b_PsiPt;
	TBranch        *In_b_PsiEta;


	/// Output Tree Leaves ///
	unsigned int Out_EventNumber;
	unsigned int Out_RunNumber;

	double  Out_ZMass;
	double  Out_ZPt;
	double  Out_ZEta;

	double  Out_PsiMass;
	double  Out_PsiPt;
	double  Out_PsiEta;

	ResultAnalyzer(string InputRootFile, string DecayMode);
	virtual ~ResultAnalyzer();
	virtual void AnalyzeResults();
	virtual void FillOutputTree(int TheIndex, string DecayMode);
	virtual void WritetoOutputFile();
};

ResultAnalyzer::ResultAnalyzer(string InputRootFile, string DecayMode) {

	TFile* InputRootF = new TFile(InputRootFile.c_str(),"READ");
	TDirectory* InputDir = (TDirectory*)InputRootF->Get("ZDecayAnalyzer");
	InputTree = new TTree("InputTree", "InputTree");
	InputDir->GetObject("OutputTree",InputTree);

		/// Input Tree ///
		InputTree->SetBranchAddress("EventNumber", &In_EventNumber, &In_b_EventNumber);
		InputTree->SetBranchAddress("RunNumber", &In_RunNumber, &In_b_RunNumber);

		InputTree->SetBranchAddress("ZMass", &In_ZMass, &In_b_ZMass);
		InputTree->SetBranchAddress("ZPt", &In_ZPt, &In_b_ZPt);
		InputTree->SetBranchAddress("ZEta", &In_ZEta, &In_b_ZEta);

		InputTree->SetBranchAddress("PsiMass", &In_PsiMass, &In_b_PsiMass);
		InputTree->SetBranchAddress("PsiPt", &In_PsiPt, &In_b_PsiPt);
		InputTree->SetBranchAddress("PsiEta", &In_PsiEta, &In_b_PsiEta);

		/// Output Tree ///
		OutputTree->Branch("EventNumber", &Out_EventNumber);
		OutputTree->Branch("RunNumber", &Out_RunNumber);

		OutputTree->Branch("ZMass", &Out_ZMass);
		OutputTree->Branch("ZPt", &Out_ZPt);
		OutputTree->Branch("ZEta", &Out_ZEta);

		OutputTree->Branch("PsiMass", &Out_PsiMass);
		OutputTree->Branch("PsiPt", &Out_PsiPt);
		OutputTree->Branch("PsiEta", &Out_PsiEta);

}

ResultAnalyzer::~ResultAnalyzer()
{
	if (!InputTree)
		return;
}

void ResultAnalyzer::FillOutputTree (int TheIndex, string DecayMode) {

	Out_EventNumber = In_EventNumber->at(TheIndex);
	Out_RunNumber = In_RunNumber->at(TheIndex);

	Out_ZMass = In_ZMass->at(TheIndex);
	Out_ZPt = In_ZPt->at(TheIndex);
	Out_ZEta = In_ZEta->at(TheIndex);

	Out_PsiMass = In_PsiMass->at(TheIndex);
	Out_PsiPt = In_PsiPt->at(TheIndex);
	Out_PsiEta = In_PsiEta->at(TheIndex);

	OutputTree->Fill();
}

void ResultAnalyzer::WritetoOutputFile() {
	OutputTree->Write();
	OutputRootF->Write();
	OutputRootF->Close();
	std::cout << endl;
}

and

#include "ResultAnalyzer.h"

void ResultAnalyzer::AnalyzeResults() {

	/// Additional Cuts ///
	int AllEntries = InputTree->GetEntries();
	for(int i=0; i<AllEntries; i++) {
		if(i%100==0)
			cout << "\r" << (double)i/(double)AllEntries*100 << "\% Processed" << flush;

		InputTree->GetEntry(i);
		int TheIndex = 0;

		FillOutputTree(TheIndex, DecayMode);
	}

	WritetoOutputFile();
}

To run it, I do the following:
.L AnalyzeResults_RootForum.C++
ResultAnalyzer myobj(“myfile.root”,“Zto4Muons”)
obj.AnalyzeResults()

Thanks again

Hi @Natilus ,
please check Posting code? Read this first! and edit your reply accordingly.

Cheers,
Enrico

The problem is likely just what the error message says: OutputTree is not linked to any file on disk, i.e. it is not associated to any TFile, so it doesn’t know where to write itself.

You have to either create OutputTree after OutputRootF or you have to associate the tree to the file with OutputTree->SetDirectory(OutputRootF).

Cheers,
Enrico

Hi Enrico,
Thank you very much. That solved my problem. I finally can use this class in root without a problem.

1 Like