Problem generating dictionary with derived classes

Hello,

I’m having a problem successfully generating a dictionary for a group of classes I’ve made. I’ve observed the same problem on Root Version 4.02/00 on cygwin, Version 3.05/07 on linux with KAI, and Version 4.00/08 with GCC. I can tell that the dictionary generation isn’t completely successful because 1) it complains when I load in the shared library and 2) when I use the shared library, I don’t get information for some of the classes.

Basically, I’ve got an bunch of first “generation” objects (once removed from TObject), and then second and third generation objects that derive from the first generation objects. When I compile, I get no error messages and a shared object library is successfully created. When I load that object into root, I get an error message saying some of my second and third generation objects don’t have dictionaries (while other second and third level objects do). I put a gzipped tar ball on the web so anybody can try this (Note: the makefile assumes you have one of the three above situations (cygwin, Kai on linux, or GCC on linux). If you don’t simply load Root’s makefile.arch and stick in your compiler flags into the makefile).

cplager@fcdflnx6> wget http://www-cdf.fnal.gov/~cplager/CLPClasses.tgz
(stuff goes by)
cplager@fcdflnx6> tar xzf CLPClasses.tgz 
cplager@fcdflnx6> mkdir shlib
cplager@fcdflnx6> cd CLPClasses
cplager@fcdflnx6> gmake
(stuff goes by, but no errors)
cplager@fcdflnx6> root -l
root [1] gSystem->Load("../shlib/libCLPClasses") 
Warning in <TClass::TClass>: no dictionary for class CLPElectron is available
Warning in <TClass::TClass>: no dictionary for class CLPMuon is available
Warning in <TClass::TClass>: no dictionary for class CLPJet is available
(int)0

It has no problem finding the dictionary for other classes (e.g. CLP3Vector, CLPChargedParticle).

O.k. So did I screw up the inheritence or root macros? I wrote a perl script to check this (in the same directory we were just in, ‘CLPClasses/’):

cplager@fcdflnx6> ./showRootClassDepend.pl


Include file: CLP3Vector
     Included in LinkDef.h
     derives from: public TObject
     ClassDef (CLP3Vector, 1) // CLP 3 vector class
     ClassImp (CLP3Vector)


Include file: CLP4Vector
     Included in LinkDef.h
     derives from: public TObject
     ClassDef (CLP4Vector, 1) // CLP 4 vector class
     ClassImp (CLP4Vector)


Include file: CLPObject
     Included in LinkDef.h
     derives from: public TObject
     ClassDef (CLPObject, 1) // CLP Object class
     ClassImp (CLPObject)


Include file: CLPJet
     Included in LinkDef.h
     derives from: public CLPObject
     ClassDef (CLPJet, 1) // CLP Jet Class
     ClassImp (CLPJet)


Include file: CLPChargedParticle
     Included in LinkDef.h
     derives from: public CLPObject
     ClassDef (CLPChargedParticle, 1) // CLP Charged Particle Class
     ClassImp (CLPChargedParticle)


Include file: CLPElectron
     Included in LinkDef.h
     derives from: public CLPChargedParticle
     ClassDef (CLPElectron, 1) // CLP Electron Class
     ClassImp (CLPElectron)


Include file: CLPMuon
     Included in LinkDef.h
     derives from: public CLPChargedParticle
     ClassDef (CLPMuon, 1); // CLP Muon Class
     ClassImp (CLPMuon)


Include file: CLPClasses


Include file: CLPNamespace

The script complains if you mess up the name in either ClassDef or ClassImp. So everything inherits from either TObject or from CLPObject (or something that inherits from CLPObject). As far as I can tell everything looks fine. Note that CLPJet doesn’t have a successful dictionary, but that CLPChargedParticle does and they both inherit from CLPObject.

The rootcint command in the makefile only has the LinkDef.h file and CLPClasses.h (which has all other include files) in it.

        $(ROOTSYS)/bin/rootcint -f src/$(DICT).C -c $(INCFLAGS) $(MAIN).h LinkDef.h

When I check the dictionary file by hand, I see entries for both CLPChargedParticles (which has a good dictionary) and CLPJets (which doesn’t).

What am I doing wrong?

Cheers,
     Charles

Hi Charles,

I would need a way to reproduce your problem. It is surprising that the message [quote]Warning in TClass::TClass: no dictionary for class CLPElectron is available [/quote]is issue while loading a library. I wonder if you have any global funtion being executed when the library is loaded?

Philippe.

Well, I did, but I don’t anymore. :smiley:

In the example that you can still get, there are two important features:

  1. I made only one dictionary file for all of the classes
  2. In one of the classes, I had a static object of that class (specifically, I had a zero three vector so I could easily compare to that class).

I have a copy where I have changed both of these features (the static copy of my three vector is now a static pointer that I initialize on first use and I’ve changed the makefile so that it makes a different dictionary file for each class.

The first change made it so when I typed ‘.class CLPJet’ I don’t get only told about the static 3 vector. The second change made the warnings go away when I loaded the library.

Unfortunately, neither change helped the underlying problem - that when I try to save these classes in a tree I sometimes don’t get all of the member data that I should.

I’m trying to come up with another short(?) example of the problem.

I think I figured out what the cause of my missing variables problem was (or, at least, I made it go away).

I’m storing my objects in root trees using TClonesArrays. There are two different constructors (besides the default). One takes a C string with the name of the class that’s going to be contained, the other takes a poiinter to a TClass object which represents the contained class. Instead of using the former, I made constant TClasses for each class I wanted to store. I was using these constants.

When I switched to constructing all of my TClonesArrays with C strings, all of my problems with variables not being saved went away.

So why did I try using the constant TClasses in the first place? Some of my objects themselves have TClonesArrays in them. As a general rule, I want all of my objects to have copy constructors. Since I couldn’t find a copy constructor for a TClonesArray, I wrote my own copy routine. But I needed to know which of my classes was being stored in the TClonesArray in order to copy it. So, I figured that if I initialized them with a TClass variable, then I could check more easily (it turns out that this didn’t work reliably and I had to fall back to checking the equality of C strings).

So, long-story-short, I think I’ve fixed the problems I was having. Any
suggestions on what I should have done are appreicated.

Cheers,
   Charles

Note 1: Unlike the other problems, I’ve only verified this problem using Version 3.05/07 on linux with KAI.

Note 2: Unlike the Event class example where there is always only 1 Event object at a time, I can have multiple objects at once, so the trick of using 1 static TClonesArray wouldn’t work in my case.

[quote]I made constant TClasses for each class I wanted to store. I was using these constants.[/quote]The TClass object are not constant and can even be deleted. In ROOT 4.03/04, we introduced a new class, TClassRef, which should be used to stored pointers to a TClass object (instead of a straghts TClass pointer). This object are guaranteed to point to the correct, valid TClass object corresponding to the class.

Your change from “the static copy of my three vector is now a static pointer that I initialize on first use”, is a very good since it avoid problems with initialization order (i.e was the TClass pointer initialize before or after this initialization).

Cheers,
Philippe.

[quote=“pcanal”][quote]I made constant TClasses for each class I wanted to store. I was using these constants.[/quote]The TClass object are not constant and can even be deleted. In ROOT 4.03/04, we introduced a new class, TClassRef, which should be used to stored pointers to a TClass object (instead of a straghts TClass pointer). This object are guaranteed to point to the correct, valid TClass object corresponding to the class.
[/quote]

O.k. Long-story-short: Use a C string, NOT a TClass, to initialize a TClonesArray. I can live with that.

Yup. That’s what I found.

The last thing I want to point out (for others trying to do this) was that generating a dictionary file for each class instead of having one dictionary made the error messages (that I was getting in the original post) go away. While in this case, they only seemed to be error messages and didn’t affect performance, a local guru claims that having a single dictionary file for multiple classes has been the cause of serious problems in (older versions of) root.