Problem linking with my own class-based TTree definitions

Hello there.

I am using ROOT to create output files from a GEANT4 simulation, and I am having trouble linking classes that are used to define TTrees. I have two TTrees (GEFInput and GEFOutput), both of which inherit from TObject and have the requisite ClassDef and ClassImp calls (yes, without semicolons). During the linking stage I get the messages:

Linking GEF …
/usr/bin/ld: Undefined symbols:
ROOT::GenerateInitInstance(GEFInput const*)
GEFInput::ShowMembers(TMemberInspector&, char*)
GEFInput::Class()
GEFInput::Streamer(TBuffer&)
ROOT::GenerateInitInstance(GEFOutput const*)
GEFOutput::ShowMembers(TMemberInspector&, char*)
GEFOutput::Class()
GEFOutput::Streamer(TBuffer&)
collect2: ld returned 1 exit status

If I run nm on, for instance, GEFInput.o, I get (among many lines), the following:


U __ZN4ROOT20GenerateInitInstanceEPK8GEFInput

U __ZN8GEFInput11ShowMembersER16TMemberInspectorPc
U __ZN8GEFInput5ClassEv
U __ZN8GEFInput8StreamerER7TBuffer

with similar results for GEFOutput.o.

I followed “Example 4: A Tree with an Event Class” in the latest User’s Guide to create these classes, but I must be missing something. For clarification, I am not trying to create an interactive session, but simply write out TTrees to a ROOT file from within the GEANT4 simulation.

Any assistance is greatly appreciated.

I’m now trying to define the dictionaries to see if that fixes the problem. Looking over the info available in Root Talk as well as Google, I try the following command:

 rootcint -f GEFInputDict.cc -c GEFInput.hh

Unfortunately for me, the GEFInput class also has GEANT4 classes in its methods (although not its member variables). As a consequence, I get these error messages

 Error: cannot open file "globals.hh"  GEFInput.hh:6:
 Error: cannot open file "G4ThreeVector.hh"  GEFInput.hh:7:
 Syntax error GEFInput.hh:57:
 Syntax error GEFInput.hh:58:
 Syntax error GEFInput.hh:59:
 Syntax error GEFInput.hh:61:
 Syntax error GEFInput.hh:62:
 Syntax error GEFInput.hh:64:
 Syntax error GEFInput.hh:65:
 Syntax error GEFInput.hh:67:
 Syntax error GEFInput.hh:68:
 Syntax error GEFInput.hh:72:
 Syntax error GEFInput.hh:74:
 Syntax error GEFInput.hh:76:
 Syntax error GEFInput.hh:80:
 Syntax error GEFInput.hh:85:
 Syntax error GEFInput.hh:87:
 Syntax error GEFInput.hh:88:
 Syntax error GEFInput.hh:92:
 Syntax error GEFInput.hh:94:
 Syntax error GEFInput.hh:96:
 Syntax error GEFInput.hh:100:
 Warning: Error occurred during reading source files
 Warning: Error occurred during dictionary source generation
 !!!Removing GEFInputDict.cc GEFInputDict.h !!!
 Error: rootcint: error loading headers...

I try adding the directory with the GEANT4 header files:

 rootcint -f GEFInputDict.cc -c -I/usr/local/geant4.9.0/source/global/management/include GEFInput.hh

And get different error messages:

 Error: Symbol ostreamG4cout is not defined in current scope  /usr/local/geant4.9.0/source/global/management/include/G4ios.hh:48:
 Error: Symbol ostreamG4cerr is not defined in current scope  /usr/local/geant4.9.0/source/global/management/include/G4ios.hh:49:
 Error: no such template numeric_limits<double> /usr/local/geant4.9.0/source/global/management/include/templates.hh:76:
 Error: no such template numeric_limits<double> /usr/local/geant4.9.0/source/global/management/include/templates.hh:80:
 Error: no such template numeric_limits<double> /usr/local/geant4.9.0/source/global/management/include/templates.hh:84:
 Error: no such template numeric_limits<double> /usr/local/geant4.9.0/source/global/management/include/templates.hh:88:
 Error: no such template numeric_limits<float> /usr/local/geant4.9.0/source/global/management/include/templates.hh:92:
 Error: no such template numeric_limits<float> /usr/local/geant4.9.0/source/global/management/include/templates.hh:96:
 Error: no such template numeric_limits<float> /usr/local/geant4.9.0/source/global/management/include/templates.hh:100:
 Error: no such template numeric_limits<float> /usr/local/geant4.9.0/source/global/management/include/templates.hh:104:
 Error: no such template numeric_limits<float> /usr/local/geant4.9.0/source/global/management/include/templates.hh:108:
 Error: cannot open file "CLHEP/Units/PhysicalConstants.h"  /usr/local/geant4.9.0/source/global/management/include/G4PhysicalConstants.hh:28:
 Error: cannot open file "CLHEP/Units/SystemOfUnits.h"  /usr/local/geant4.9.0/source/global/management/include/G4SystemOfUnits.hh:28:
 Error: cannot open file "CLHEP/Vector/ThreeVector.h"  /usr/local/geant4.9.0/source/global/management/include/G4ThreeVector.hh:42:
 Error: class,struct,union or type CLHEP not defined  /usr/local/geant4.9.0/source/global/management/include/G4ThreeVector.hh:43:
 Error: class,struct,union or type CLHEP not defined  /usr/local/geant4.9.0/source/global/management/include/G4ThreeVector.hh:43:
 Warning: Error occurred during reading source files
 Warning: Error occurred during dictionary source generation
 !!!Removing GEFInputDict.cc GEFInputDict.h !!!
 Error: rootcint: error loading headers...

I could at this point include another -I directory for the CLHEP headers, but that only introduces more CLHEP-related “undefined symbol” error messages.

Any tips?

More info:

If I leave out the “-c” flag I at least get a skeleton of a dictionary class, but I also get this error message:

Error: class,struct,union or type std::string::size_type not defined /usr/local/geant4.9.0/source/global/management/include/G4String.hh:56:

Interestingly enough, this is the only error message I get back…nothing about “no such template” or any other errors, just that one single line. I haven’t had any luck figuring out how to get around this.

Also, I try using my own GEFLinkDef.h file:

rootcint GEFInputDict.cc -I/usr/local/geant4.9.0/source/global/management/include GEFInput.hh GEFLinkDef.h

with the GEFLinkDef.h file looking like this:

#ifdef CINT
#pragma link C++ class GEFInput;
#endif

but the error message (saying that std::string::size_type isn’t defined) still persists.

[quote]If I leave out the “-c” flag I at least get a skeleton of a dictionary class, but I also get this error message: [/quote]That tells rootcint that your code is written in C (as opposed to C++). You need the -c :slight_smile:

You also need to tell rootcint where to find the header file! So you need to add the same -I options/flags as your using on your compilation line.

Also you should use (for Geant4) the external preprocessor (rather than CINT’s).

So use:rootcint -f GEFInputDict.cc -c -p -I/usr/local/geant4.9.0/source/global/management/include GEFInput.hh GEFLinkDef.h

Cheers,
Philippe.

Ah. Thank you for the clarification of the -c flag, Phillippe.

I ended up simply taking out anything other than standard C++ or ROOT variable types, even in the methods. This approach is working. If I’m feeling ambitious, I may go put them back in and try your suggestion.

Unfortunately, I spoke too fast.

I’ve attached the relevant files for what I’m trying to do (GEFInput.hh and .cc, GEFOutput.hh and .cc, GEFIOLinkDef.h, and the method where the ROOT file and TTrees are created). The dictionary files are created without any errors using the following command (remember that I am no longer dealing with any GEANT4 variables or methods in these input and output classes, just ROOT):

rootcint -f GEFIODict.cc -c GEFInput.hh GEFOutput.hh GEFIOLinkDef.h

rootcint returns no error or warning messages.

I compile and run the GEANT simulation in the standard GEANT way (i.e., with the GEFIODict.cc and GEFIODict.hh files in the appropriate src and include directories), and run the simulation with no error messages. When I open the resulting ROOT file I get this:

[quote]root [0] TFile *f = new TFile(“GEF_OUTPUT_835125592.root”)
Warning in TClass::TClass: no dictionary for class GEFInput is available
Warning in TClass::TClass: no dictionary for class GEFOutput is available
root [1] [/quote]

Interestingly enough, I can open the ROOT files just fine, and access all the variables except the TString variables (see root.cern.ch/phpBB2/viewtopic.php?t=5386).

Just for kicks, I have also tried including the following lines in GEFIOLinkDef.h:

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

I have also tried adding a plus sign to the end of the “class” lines in the same file (I didn’t think this would help, but I’m trying out everything I can think of).

As usual, any help is greatly appreciated. I’m very curious to know what the difference might be between, say, the Event.h example in the test directory and what I’m doing.
kkazkaz_test.zip (26.7 KB)

More information:

When I use this as my GEFIOLinkDef.h:

#ifdef __CINT__ #pragma link C++ class TString+; #pragma link C++ class GEFInput+; #pragma link C++ class GEFOutput+; #endif

I can create the dictionary file with no error or warning messages. I can also compile the simulation with no error messages, though I get about three dozen warning messages of the type

[quote]src/GEFIODict.cc:2948: warning: ISO C++ forbids casting between pointer-to-function and pointer-to-object
[/quote]

Finally, when I run the simulation, I get an Illegal Instruction when I get to this line:

inputTree->Branch( "GEFInput", "GEFInput", &input, 32000, 1 );

At this point, I am wondering if the problem is with the GEFIODict file, but debugging that is a bit of a black art to me.

[quote]#pragma link C++ class TString+; [/quote]Do NOT generate the dictionary for TString :slight_smile:. There is already one provide and this dictionary is preventing the proper streaming of TString.

Also in your zip file you did not have the trailing ‘+’ in your linkdef. Those ‘+’ are important without them you get the old I/O mechanism which does not support well many of the new features and in particular prevents proper reading of the file without your shared library (In order word, the + means that you file becomes self describing).

Cheers,
Philippe

Thank you very much for all your patience.

It turns out that using a variable declaration like

Int_t num; TString *theString; // [num]
in a TTree class definition confuses rootcint. The resulting ROOT files have no variable called theString. I assume this is because ROOT thinks theString is a pointer to a TString, rather than an array of num TStrings, and just gets confused.

I ended up using TString vectors instead of TString arrays. I have tested the output file, and everything seems to be working well.

One little issue, though, is that when I open the resulting ROOT output file I still get the messages

[quote]Warning in TClass::TClass: no dictionary for class GEFInput is available
Warning in TClass::TClass: no dictionary for class GEFOutput is available
[/quote]
I don’t understand how there could be no dictionary for the classes, and yet the ROOT files have the proper structure as defined by the classes themselves.

Thanks again for all the help. I’ve learned a lot.

[quote]One little issue, though, is that when I open the resulting ROOT output file I still get the messages [/quote]Those message indicates that you have not loaded the C++ library implementing those classes. You can still read the data via the ROOT tools, but you will not be able to call any of your member functions. So it is most likely safe for you to ignore this message.

Cheers,
Philippe

Hello,
I am also receiving the error

src/EventDict.cc:1880: warning: ISO C++ forbids casting between pointer-to-function and pointer-to-object.

This compilation warning did not occur until an update from gcc-3.1 to gcc-4.1. I have learned that, yes, ISO C++ does forbid the casting, and I don’t see how the error described above, the TString* definition, would cause a problem in my case. In the interest of not posting huge amounts of code, I have included a only a bit. Event.h below is shown below.

[code]class Event : public TObject {

private:
EventHeader fEvtHdr;
Primary fPrimary[NUM_PRIMARY];
Detector fDetector[NUM_DETECTOR];

public:
Event();
virtual ~Event();
void Build(Int_t ev, Int_t arg5=600, Float_t ptmin=1);
void Clear(Option_t *option ="");

void SetHeader(Int_t eventNum, Int_t run, Int_t date, Double_t beta,
Double_t EmisPosX, Double_t EmisPosY, Double_t EmisPosZ,
Double_t ProjMomX, Double_t ProjMomY, Double_t ProjMomZ);
void SetPrimary(Int_t index, Double_t beta,
Double_t energy, Double_t theta, Double_t phi,
Double_t pfEnergy, Double_t pfTheta, Double_t pfPhi);
void SetDetector(Int_t index, Int_t numHits,
Double_t firstHitX, Double_t firstHitY, Double_t firstHitZ,
Double_t energyDep, Double_t energyUp, Double_t energyDn,
Double_t energyRec, Double_t energyRecDop, Double_t positionRec);

EventHeader* GetHeader();
Primary* GetPrimary(Int_t index);
Detector* GetDetector(Int_t index);

ClassDef(Event,1) //Event structure
};
[/code]

The errors come from the lines marked with an ! in EventDict.cc, and are repeated for the similar lines in the EventHeader, Primary, and Detector classes. In function ‘void G__setup_memfuncEvent()’:

! G__memfunc_setup("Class",502,G__EventDict_478_0_10, 85, G__get_linked_tagnum(&G__EventDictLN_TClass), -1, 0, 0, 3, 1, 0, "", (char*)NULL, (void*) (TClass* (*)())(&Event::Class), 0); ! G__memfunc_setup("Class_Name",982,G__EventDict_478_0_11, 67, -1, -1, 0, 0, 3, 1, 1, "", (char*)NULL, (void*) (const char* (*)())(&Event::Class_Name), 0); ! G__memfunc_setup("Class_Version",1339,G__EventDict_478_0_12, 115, -1, G__defined_typename("Version_t"), 0, 0, 3, 1, 0, "", (char*)NULL, (void*) (Version_t (*)())(&Event::Class_Version), 0); ! G__memfunc_setup("Dictionary",1046,G__EventDict_478_0_13, 121, -1, -1, 0, 0, 3, 1, 0, "", (char*)NULL, (void*) (void (*)())(&Event::Dictionary), 0); G__memfunc_setup("IsA",253,G__EventDict_478_0_14, 85, G__get_linked_tagnum(&G__EventDictLN_TClass), -1, 0, 0, 1, 1, 8, "", (char*)NULL, (void*) NULL, 1); G__memfunc_setup("ShowMembers",1132,G__EventDict_478_0_15, 121, -1, -1, 0, 2, 1, 1, 0, "u 'TMemberInspector' - 1 - insp C - - 0 - parent", (char*)NULL, (void*) NULL, 1); G__memfunc_setup("Streamer",835,G__EventDict_478_0_16, 121, -1, -1, 0, 1, 1, 1, 0, "u 'TBuffer' - 1 - b", (char*)NULL, (void*) NULL, 1); G__memfunc_setup("StreamerNVirtual",1656,G__EventDict_478_0_17, 121, -1, -1, 0, 1, 1, 1, 0, "u 'TBuffer' - 1 - b", (char*)NULL, (void*) NULL, 0); ! G__memfunc_setup("DeclFileName",1145,G__EventDict_478_0_18, 67, -1, -1, 0, 0, 3, 1, 1, "", (char*)NULL, (void*) (const char* (*)())(&Event::DeclFileName), 0); ! G__memfunc_setup("ImplFileLine",1178,G__EventDict_478_0_19, 105, -1, -1, 0, 0, 3, 1, 0, "", (char*)NULL, (void*) (int (*)())(&Event::ImplFileLine), 0); ! G__memfunc_setup("ImplFileName",1171,G__EventDict_478_0_20, 67, -1, -1, 0, 0, 3, 1, 1, "", (char*)NULL, (void*) (const char* (*)())(&Event::ImplFileName), 0); ! G__memfunc_setup("DeclFileLine",1152,G__EventDict_478_0_21, 105, -1, -1, 0, 0, 3, 1, 0, "", (char*)NULL, (void*) (int (*)())(&Event::DeclFileLine), 0);

Thank you for your help.

[quote]src/EventDict.cc:1880: warning: ISO C++ forbids casting between pointer-to-function and pointer-to-object[/quote]This is a known warning. You can ignore it for now.

Cheers,
Philippe.