Problem with user defined class

Dear All,

since I recieved very fast and useful answer to my previous post, let me try again on a different topic.

We are building a reconstruction program that produces a root file and we are using our user-defined event class, following the procedure indicated on user’s guide and on many examples (i.e. inheritance from TObject, rootcint, shared libriry, etc.).
Our event has several sub-event classes referring to different detector portions.
We want to provide the user the possibility to produce light or full event, where the first one doesn’t hold low level info. This is attempted by defining a light and a full version of every sub-event class where the latest inherits from the first in each case.
The event class has pointers to light sub-event objects and its constructor recieves a flag as argument to decide whether to create light or full sub-objects.
Writing is done via a std tree with no splitting:

p_root_tree = new TTree ("etree", "Events Tree"); p_root_tree->Branch ("events", "bx_root_event", &p_root_event);

When the event is light there is no problem.
When the event is full, it is indeed written to file as full but the pointers in the event object keeps being to light sub-objects.
Then I want to read them from file in CINT and macros.
Using SetBranchAddress() and GetEntry() I can access the extra methods&members of the full event, provided that I explicitly do the necessary casting on the sub-ojects.
But more basic and absolutely useful things like my_tree->Draw("sub_object.memeber_of_full") of course fail miserably since ROOT recognizes sub_object only as light.
Is there a way to have root knowing the type of these subojects?

Thanks in advance for the help.
Cheers,
Davide

p_root_tree = new TTree ("etree", "Events Tree"); p_root_tree->Branch ("events", "bx_root_event", &p_root_event);
Selects the default splitting (99). If any splitting is requested (any value greater than 0), then the type of the object is fixed to exactly the type passed as argument. In order to benefit from polymorphism (ie. avoid the splicing I just described), you need to disable splitting.

Alternatively, you could use a different implementation for your heavy/light implementation. Instead of using inheritance, you could have only one user visible class which contain a pointer to the ‘heavy’ part which would be null or not depending on the case.

Cheers,
Philippe.

Hello Davide,

in my opinion an implementation of light/heavy classes is unnecessary.
With the use of the SetBranchStatus() method the user can do the same, even in a more flexible way. For wildcards are supported, you can disable/enable groups of branches with a single statement. It is very powerful.
I’m using it for my event classes too and I’m very happy with it.

Cheers,
Oliver

Dear Phillippe,

thank you for your reply.

My event object has pointers to sub objects, so I guess the splitting is not done in any case, am I right? I cannot use the browser on sub-object.
Anyhow I tried to explicitly disable splitting and got no difference, i.e. the pointer is a pointer to light version (of course) and with casting it can access also full members&getters which was also the case with splitting enabled.
Using references wouldn’t be feaseble since at obj initialization I don’t know the type of sub-ojects yet (I have to ask it to a parameter interface).

Having the heavy part pushed to an ineer level could be a solution, I’m thinking about it.

In the meantime I discovered that the TTree::Draw() works also with methods. So I can define the getters for memebrs of full class also in the light one as virtual and returning 0.
In this way calling:

on a full event, thanks to the virtual table resolution, accesses the right getter and works fine.

This preserves somehow the functionality of the tree.
What do you think of this solution?

Thanks, bye
Davide

This is a good solution.
I am not sure what your exact TTree layout is, however the following might also work (assuming no splitting)

Cheers,
Philippe.

Dear Philippe,

I tried the casting inside the TTree::Draw() but it doesn’t seem to work for me. Anyhow using getters is handy enough.
What some users would like instead is to use TBrowser which presently doesn’t allow to look inside the sub-objects since they are included as pointers.
How does the splitting work in case of pointers to objects?
The user’s guide is quite brief on it (pag 206):

To my experience of these days, changing the splitting level in the TTRee::Branch() method has no effect for pointers to objects:
[ul]
In both cases RTTI is available.
In both cases the browser doesn’t look inside the object.
In both cases casting inside TTree::Draw() is not allowed.
[/ul]

My tree structure is fairly simple in the end, I have a custom Event class and I save it in a single TBranch. The Event class has pointers to polymorfic objects. That’s all.

Let me also thank Oliver for his reply. It seems to me that your suggestion doesn’t apply directly since we would like to use a single branch, splitted or unsplitted.
Going to more branches would imply getting rid of the top level event class which is very useful both inside the program and in the macros.
You also need to do SetBranchAddress individually for every branch, which could be annoying for macro writers. Anyhow I’ll thinkmore deeply into this possibility. thank you.

that’s it for tonight, bye
Davide

Hi,

Object with are refered to as with a pointer are never split. This is because of the possible use of polymorphism. (It would be technically possible to split the object if the class did not have a virtual table … but this is not implemented). TClonesArray are an exception to this rule (but it is their content which is split … not the TClonesArray itself).

At least partially, this could be implemented but is not available yet.

I am surprised. Could you send me an example? (I would need the ROOT file and the command you try to issue).

Cheers,
Philippe.

Hi Philippe,

since I was not happy about my pointers to unsplit objects, I worked out a version that uses (const) references, changing a lot the design of my code.
I first found out the the _cint.cc file generated by rootcint doesn’t compile due to “double reference” (i.e. a reference to a reference) in the Streamer method, which is of course illegal.

[code] void bx_root_event::Streamer(TBuffer &R__b) {
// Stream an object of class bx_root_event.

UInt_t R__s, R__c;
if (R__b.IsReading()) {
   Version_t R__v = R__b.ReadVersion(&R__s, &R__c); if (R__v) { }
   TObject::Streamer(R__b);
   R__b >> run;
   R__b >> evnum;
   const_cast<  bx_root_light_trigger_event& &>( trigger ).Streamer(R__b);
   R__b.CheckByteCount(R__s, R__c, bx_root_event::IsA());
} else {
   R__c = R__b.WriteVersion(bx_root_event::IsA(), kTRUE);
   TObject::Streamer(R__b);
   R__b << run;
   R__b << evnum;
   const_cast<  bx_root_light_trigger_event& &>( trigger ).Streamer(R__b);
   R__b.SetByteCount(R__c, kTRUE);
}

}[/code]

By editing the file and getting rid of this double reference (simpy cutting out a ‘&’) I managed to have the program compiling and running, but not to give out sensible results.
What happens is that the reference is just not there as a member and the getter that returns it, returns a reference to some junk area that in the best case holds random value and in the worst one (virtual methods) gets a seg violation.
I wonder whether I’m doing something terribly wrong or ROOt doesn’t support reference to objects at all (uhm… I haven’t seen any example around in fact …).
If the last is true this means that there is no way to hold polymorphic objects && benefit from splitting in ROOT.
I really hope it’s my mistake…

Cheers,
Davide

Hi,

There is no real way to the I/O system to deal with references (beside syntax difficulties). What should reading back a reference mean? Should it change the referenced object, should it create a new object and set the reference to it, should it ignore it?

The part of the object that is varying can NOT be split. Otherwise some event would have either too much or too little information.
One solution is to have something like:

class Event { ... first the variable that are always present ... OptionalValues *values; // value that might or might not be present. }; Cheers,
Philippe.