Geant4 / Root classes

Hi there,

Just for information (and to end the post root.cern.ch/phpBB2/viewtopic.php?t=3995),
here’s a way to couple Geant4 with a regular root class (containing TString or not) such as:

#ifndef TMyRootEvent_hpp
#define TMyRootEvent_hpp

#include "TObject.h"
#include "TString.h"


class TMyRootEvent : public TObject
{		

	protected :

		Int_t fNCells ; 

		TString   fParticleName ; 
		Double_t  fEkin_0       ;  // initial kinetic energy [MeV]

		TString  *fSensitiveCellName ;  //[fNCells] sensitive cell name
		Double_t *fEkin              ;  //[fNCells] kinetic energy at the entrance of each sensitive cell [MeV]


	public :
			
		TMyRootEvent ()
		{	
			fNCells = -1 ;
			fSensitiveCellName = NULL ; 
			fEkin              = NULL ; 
		}
			
		~TMyRootEvent ()
		{
			delete fSensitiveCellName ; fSensitiveCellName = NULL ; 
			delete fEkin              ; fEkin              = NULL ; 
		}
					
		void InitEvent ( Int_t aNCells )
		{
			fNCells            = aNCells ;
			fSensitiveCellName = new TString  [fNCells] ; 
			fEkin              = new Double_t [fNCells] ; 
			return ; 
		}

		// Getters...
		// Setters...

  ClassDef (TMyRootEvent,1) ;  
			
} ; 


#endif

The implementation has to be inside the class ! (before the end of the class “};”)
(Otherwise some “Multiple Definition” error messages appear…)

The GNUmakefile in that case looks like this:

name := exampleN01
G4TARGET := $(name)
G4EXLIB := true

.PHONY: all
all: dict.cxx lib bin

dict.cxx :
   rootcint -f dict.cxx -c include/TMyRootEvent.hpp include/linkdef.h

myclean :
   rm -rf dict.* $(G4TMP)/$(G4SYSTEM)/exampleN01

include $(G4INSTALL)/config/architecture.gmk

G4NOHIST := true
CPPFLAGS += $(shell root-config --cflags)
# Here comes dict.cxx
LDFLAGS  += $(shell root-config --libs) dict.cxx

include $(G4INSTALL)/config/binmake.gmk 

The include/linkdef.h file just contains:

#ifdef __CINT__
#pragma link C++ class TMyRootEvent ;
#endif

The root file & the tree are created in RunAction:

RunAction()

	fTMyRootEvent = new TMyRootEvent () ; 
	fTMyRootEvent->InitEvent(fNCells) ; 

BeginOfRunAction()

	fRootFout = new TFile ("test.root","RECREATE","My GEANT4 simulation") ;
	fRootTree = new TTree ("tree","My GEANT4 simulation") ; 
	fRootTree->Branch("evt","TMyRootEvent",&fTMyRootEvent) ; 

EndOfRunAction()

	fRootTree->Write() ;  
	fRootFout->Close() ;  

The tree can be filled (fRootTree->Fill()) wherever (EndOfEventAction, SteppingAction, …).

To read the root file generated, one can use:

{
	gROOT->ProcessLine(".L TMyRootEvent.hpp+") ; 
	TFile *fin = new TFile ("test.root") ; 
	TTree *tree = (TTree*)fin->Get("tree") ;
	TMyRootEvent* evt = new TMyRootEvent() ;
	tree->SetBranchAddress("evt",&evt)  ; 
	tree->Show(0) ;
	tree->Draw("evt->GetEkin_0()") ;
	tree->Draw("evt->GetEkin(1)","evt->GetParticleName()==\"alpha\"") ;
}

Thanks to Philippe Canal for his precious help.
Z

[quote]The implementation has to be inside the class ! (before the end of the class “};”) Otherwise some “Multiple Definition” error messages appear…

[/quote]

This is because of the One Definition Rule (ODR) of C++: you are not allowed multiple definitions (as opposed to multiple declarations, which are allowed) of any “C++ thingy” whose address can be taken with unary operator&, within a single “translation unit”. A TU is basically a source file after all the preprocessor magic has been completed (#include, #define). You also can’t have the same object or function defined in multiple object files when you reach the linker. The one exception to this is that you can define an inline function more than once in a program (but still never more than once in a TU!) since they don’t need an address, so you sometimes can put those in header files.

This basically means you really shouldn’t put definitions of any non-inline function in header files ever, and you should protect all your header files with include guards (the #ifndef/#define#endif stuff at the top and bottom of your header) to prevent them from being processed multiple times in the same TU.

Thus, you typically have to define member functions in one of two ways:
[ul]
[li] in a a guard protected header file, within the body of the class definition (where they are implicitly inline qualified!), or[/li]
[li] in a separately compiled source file that you then link in to your executable.[/li][/ul]
If you do the latter, but don’t link the resulting object file, you will get a whole raft of “undefined reference” errors from the linker.

Don’t define member functions out-of-class within a header file, because then they aren’t implicitly inline anymore, and the linker will complain about not being able to resolve references when link time comes.[/list]

OK, thanks for this clear explanation :wink:
Below’s Philippe’s comment (end of post root.cern.ch/phpBB2/viewtopic.php?t=3995)

[quote=“pcanal”][quote]The implementation has to be inside the class ! (before the end of the class “};”)
(Otherwise some “Multiple Definition” error messages appear…) [/quote]A priori this is not required. You could move the implementation to a source file or if you move them outside the class declaration, do not forget to pre-pend the implementation with the keyword inline

[quote]The include/linkdef.h file just contains:…[/quote]Actually you may want to request the ‘new’ I/O by appending a + sign:#ifdef __CINT__ #pragma link C++ class TMyRootEvent+; #endif
[/quote]
Thanks for your help,
Z

Hi there,

FYI, with CMake, the makefile above becomes
hypernews.slac.stanford.edu/Hype … 0/3/1.html

Cheers,
M