Using TClass::New(...) on a fairly complicated class

Hi,

I have quite a complicated problem, but since I’m getting lost in it myself, I’ll try to summarise:

I’m trying to extend the TSelector class by writing my own class inheriting from it, and then my analysis classes inherit from this extended class rather than TSelector. (I think this is a general question though, that’s why I didn’t write in the PROOF forum.) The problem is that my “extension” to TSelector is pretty complicated by now. I try to re-use an older analysis framework, in which this inheritance tree worked pretty well (The inheritance tree is attached to the post.), it just used TObject instead of TSelector previously. (The actual analysis classes inherit from SCycleBase in my code.)

In the code that I run locally, I instantiate my analysis classes like this:

[code]TClass* testClass = gROOT->GetClass( “FirstCycle”, true );
if( ! testClass || ! testClass->InheritsFrom( “SCycleBase” ) ) {
cout << “FirstCycle could not be loaded!” << endl;
return;
} else {
cout << “FirstCycle class loaded” << endl;
}

SCycleBase* cycle = reinterpret_cast< SCycleBase* >( testClass->New() );[/code]

With this I get a perfectly working instance of the FirstCycle class that I can access through its SCycleBase interface, including the interface provided by TSelector. I can even write:

SCycleBaseBase* cycle = reinterpret_cast< SCycleBaseBase* >( testClass->New() );

and that still lets me use the TSelector interface of the newly created object. If however I try to instantiate the object like:

TSelector* cycle = reinterpret_cast< TSelector* >( testClass->New() );

everything breaks… The cycle pointer is not valid in this case, and any usage of it results in a segmentation fault.

I tried playing with the order of the inheritance in the base classes, but to no avail so far. In my local code I can of course create a valid pointer to a TSelector object by creating a pointer to let’s say an SCycleBaseBase object first and then casting it to a TSelector object pointer. But the problem comes of course when I try to use my analysis class on PROOF. Since PROOF tries to instantiate my analysis class similarly to what I wrote, it always crashes.

Any ideas on the matter would be greatly appreciated. I know the inheritance tree could be simplified and that could solve the issue, but that would take a lot of work, and would disable a lot of nice features the code has currently.

Cheers,
Attila

Hi,

[Correction Added]

You must NOT use:TSelector* cycle = dynamic_cast< TSelector* >( testClass->New() );To be able to handle the intricacies of multiple inheritance.

since TClass::New returns void*, instead you ought to use:[code]TSelector* cycle = reinterpret_cast< testclass* >( testClass->New() );[/cod]

Cheers,
Philippe.

Hi,

Let me expand a little bit on the problem…

I hope no one understood the last message like I was blaming TClass::New(…). This is purely a class hierarchy question. The problem comes from the virtual inheritance that I have to use.

In “normal” inheritance, if let’s say Class1 <- Class2 <- Class3, then the following would be working code:

void* class3 = new Class3(); Class2* class2 = reinterpret_cast< Class2* >( class3 ); Class1* class1 = reinterpret_cast< Class1* >( class3 );

Such code always relies on the fact that the memory layout of objects is such, that direct ancestors are put first. But of course if let’s say Class3 was descending from Class1 and Class2 (in this order) at the same time, then only the last line of the above code would work. (The “Class2 part” of Class3 would have an offset compared to the class3 pointer.)

This is all crystal clear to me when working with normal inheritance. But I have no idea how memory is managed when using virtual inheritance. I played a lot with this inheritance tree today, right now I’m at a version that is attached to this post. In this version SCycleBase descends directly from 4 classes (no virtual inheritance!), I have two pure virtual classes called ISCycleBaseConfig and ISCycleBaseNTuple that should not influence the memory layout, and all the middle-level classes inherit virtually from SCycleBaseBase. SCycleBaseBase in the end descends directly from TSelector.

If I now create a new class (let’s call it FirstCycle) that inherits SCycleBase directly, I get the following:

void* vcycle = new FirstCycle(); // This works since SCycleBase is the direct ancestor of FirstCycle: SCycleBase* bcycle = dynamic_cast< SCycleBase* >( vcycle ); // This works since SCycleBaseHist is the first ancestor of SCycleBase: SCycleBaseHist* hcycle = dynamic_cast< SCycleBaseHist* >( vcycle );

I can’t cast it to anything else on the same “level” in the inheritance tree, since those all have an offset compared to the vcycle pointer. So that’s okay. But when I try to do:

SCycleBaseBase* bbcycle = dynamic_cast< SCycleBaseBase* >( vcycle );

or God forbid:

TSelector* sel = dynamic_cast< TSelector* >( vcycle );

that doesn’t work, and I’m not sure why. The first parent for all the “middle level” classes is SCycleBaseBase. It’s a virtual inheritance, but still.

Okay, I just see that there is an answer, so will stop writing this message…

Cheers,
Attila

[quote=“pcanal”]Hi,

You must use:TSelector* cycle = dynamic_cast< TSelector* >( testClass->New() );To be able to handle the intricacies of multiple inheritance.

Cheers,
Philippe.[/quote]

Dear Philippe,

Strangely enough this doesn’t help me much. I’ve been using dynamic_cast in the last few tries, but that didn’t get me closer…

Cheers,
Attila

P.S. Besides, I just realised that I can’t use dynamic_cast on void* pointers in compiled code. CINT allows me to do that, but it works just like a reinterpret_cast (so no offsetting of the pointer). This makes sense, since how would CINT know how to offset the pointer? It doesn’t know from the void* type what kind of pointer it is…

[quote]Strangely enough this doesn’t help me much. I’ve been using dynamic_cast in the last few tries, but that didn’t get me closer… [/quote]My apologies, I was incorrect :frowning:

TClass::New return a void* that holds the return value of the call to operator new for the class in question. So to get correct result you must use something like:

But since ClassOfCl is by definition the most derived class. you should be able to always use:

Cheers,
Philippe.

Also note that virtual inheritance is not well supported (i.e. don’t use it, if can avoid it).

Hi Philippe,

If the creation and casting of the new objects was entirely under my jurisdiction, then this wouldn’t be a problem. The problem is that I’m trying to get PROOF to create and handle these objects for me. PROOF assumes that all the classes whose names are given to it, can be cast directly into a TSelector pointer from a void pointer. But this is not the case here unfortunately.

So that’s why I was trying to learn about how virtually inherited classes are placed in memory in an object’s imprint, but didn’t find anything useful yet. It seems quite obvious that they are not placed first… :frowning: I’m especially clueless what happens if there are multiple virtually inherited classes in the hierarchy. (The interface classes I use are also virtually inherited.)

So it seems I’ll do have to re-think these classes (from the ground up) after all. :frowning:

     Attila

[quote]So it seems I’ll do have to re-think these classes (from the ground up) after all[/quote]From a very short look at your hiearchy, it looks like you would actually be better off using composition rather inheritance from most (if not all) your base class (except TSelector).

[quote]PROOF assumes that all the classes whose names are given to it, can be cast directly into a TSelector pointer from a void pointer.[/quote]A priori, just having TSelector be the left most base class should work …

Cheers,
Philippe.

They do depend a little bit on each other. For instance SCycleBaseNTuple has to know the configuration of the object from SCycleBaseConfig. ISCycleBaseConfig is introduced to remove the direct dependence between these two. Also, SCycleBaseExec needs to access features of both SCycleBaseNTuple and SCycleBaseConfig.

The idea with the interfaces comes from a different angle. In the analysis framework that I’m trying to adapt to running on PROOF, one can freely exchange any parts of SCycleBase and substitute it with another one. For instance we wrote a version of SCycleBase that can be used to read ATLAS reconstructed data by only exchanging the SCycleBaseNTuple component but leaving everything else alone. (Even the SCycleBaseExec component which was responsible for the whole event loop.) Because of the big success of this design there, I would really like to keep it one way or another.

But all that aside, I agree that I should think about using composition as well… :-/

Cheers,
Attila

Hi,

Hmm… I just had a quick read of

http://www.phpcompiler.org/doc/virtualinheritance.html

From what that says (I never dived this deep into the inner workings of GCC, just used these features…), it seems pretty much impossible to achieve what I wanted to do. :confused: In order to access the TSelector component of such a complicated hierarchy, I do always have to access it as a “high level” object first. (An object where virtual inheritance has not kicked in yet.) So this is bad news. :frowning:

Seems like I’ll have to “sleep on it” to come up with some kind of solution…

Cheers,
Attila