Possible extension of a generated skeleton class?

Dear ROOTers,

I’m working with a skeleton class generated by the TTree::MakeClass method.
For flexibility, I’d decided to use this skeleton class as a base class for
my descendant class. Everything is OK, but I’ve met one problem that can be
overpassed only by extending the TTree::MakeClass method. The problem is
associated with a name of file used to generate the skeleton class. If I
work only with one root file, everything is OK. But if I need to read
another root file, then my descendant class can not inherit successfully
the skeleton class (due to the explicit definition of a source root file).
So it looks like I need to generate a skeleton class each time when my
source root file changes. This barrier can be overcome painlessly by
adding some statements (inside the skeleton constructor) like these

#include <TSystem.h>

if (!f) {
  if (!gSystem->AccessPathName("filename.root",kFileExists)) {
    f = new TFile("filename.root");
  }
  else {
    fChain = 0;
  }
}

instead of these

if (!f) {
  f = new TFile("filename.root");
}

By applying the above mentioned statements we may obligate a descendant
class to open a root file (if there is no a source root file in a current
directory). Also I’ve added “fChain = 0;” because there may arise situations
when a user need to inherit more then one skeleton class (in case of two or
more trees). In that case (and when there is no a source root file) there
may be a situation when no root file is opened but the skeleton destructor
will try to delete a poiter to it:

delete fChain->GetCurrentFile();

For a backward compatibility, I would suggest to add one additional
parameter to the TTree::MakeClass method (it looks like the 3rd one)
with a default value not allowing the method to implement their extending
version. But if we would put the 3rd parameter to be different from the
default value, then the extending version of a skeleton class were created.

If there is a better way to overcome the above mentioned situations, then
give me please your advice. I would appreciate it. Thank you.

Best regards,
Alexander

Hi Alexander,

You don’t need an extension of TClass to do this. Just do
TFile f(“myfile.root”);
TTree T = (TTree)f.Get(“mytree”);
MyClass a(T);

by default if you do MyClass a; the constructor will use the file used
to generate the file MyClass.h by MakeClass.

Rene

Hi Rene,

Thank you for your advice. I’ll try to implement it.

Best regards,
Alexander

Hi Rene,

I was trying to develop your advice when inheriting two skeleton
classes. (I have two trees.) Everything is OK, if I create an object
by default (there will be a dummy test output produced in constructors
and destructors)

root [0] .L libKopioMCntuple1.so
root [1] .L libKopioMCntuple5.so
root [2] .L libKopioMCLoader.so
root [3] KopioMCLoader ld;
KopioMCntuple1::KopioMCntuple1: 1st test output
KopioMCntuple1::KopioMCntuple1: 2nd test output
KopioMCntuple5::KopioMCntuple5: 1st test output
root [4] ld.FillMCLoader();
root [5] .q
KopioMCLoader::~KopioMCLoader: 1st test output
KopioMCLoader::~KopioMCLoader: 2nd test output
KopioMCLoader::~KopioMCLoader: 3rd test output
KopioMCLoader::~KopioMCLoader: 4th test output
KopioMCntuple5::~KopioMCntuple5: 1st test output
KopioMCntuple5::~KopioMCntuple5: 2nd test output
KopioMCntuple5::~KopioMCntuple5: 3rd test output
KopioMCntuple1::~KopioMCntuple1: 1st test output
KopioMCntuple1::~KopioMCntuple1: 2nd test output
KopioMCntuple1::~KopioMCntuple1: 3rd test output

But if I create an object by the second method, then I get the following result
[b]
linux_machine> gdb root.exe
(gdb) run -l
root [0] .L libKopioMCntuple1.so
root [1] .L libKopioMCntuple5.so
root [2] .L libKopioMCLoader.so
root [3] TFile f(“kopio_mc.root”); // this file was used to create skeleton classes
root [4] TTree *t1 = (TTree *) f.Get(“h1”);
root [5] TTree *t5 = (TTree *) f.Get(“h5”);
root [6] KopioMCLoader ld(t1,t5);
root [7] ld.FillMCLoader();
root [8] .q
KopioMCLoader::~KopioMCLoader: 1st test output
KopioMCLoader::~KopioMCLoader: 2nd test output
KopioMCLoader::~KopioMCLoader: 3rd test output
KopioMCLoader::~KopioMCLoader: 4th test output
KopioMCntuple5::~KopioMCntuple5: 1st test output
KopioMCntuple5::~KopioMCntuple5: 2nd test output
KopioMCntuple5::~KopioMCntuple5: 3rd test output
KopioMCntuple1::~KopioMCntuple1: 1st test output
KopioMCntuple1::~KopioMCntuple1: 2nd test output
KopioMCntuple1::~KopioMCntuple1: 3rd test output

Program received signal SIGSEGV, Segmentation fault.
0x4207aed2 in chunk_free () from /lib/i686/libc.so.6
[/b]
Probably there is my mistake somewhere, but I do not know how to find it.
Could you help me to find out it? I’ve put all needed claases at this
link: phy.bnl.gov/~artamonov/work/kopio.tar.gz

I will appreciate any help. Thanks in advance.
Best regards,
Alexander

I’m working on Red Hat Linux (7.3 2.96-113) and ROOT (Version 4.00/04).

P.S.
I was also trying to repeat the same steps in Debian prerelease with gcc
version 2.95.4 20011002 and ROOT (Version 4.00/04). The result is the
same but additionally I got the following warnings when greating default
or other object
[b]
root [0] .L libKopioMCntuple1.so
root [1] .L libKopioMCntuple5.so
root [2] .L libKopioMCLoader.so
root [3] KopioMCLoader ld;
KopioMCntuple1::KopioMCntuple1: 1st test output
Warning in TStreamerInfo::BuildCheck:
The StreamerInfo of class TAttLine read from file kopio_mc.root
has the same version (=1) as the active class but a different checksum.
You should update the version to ClassDef(TAttLine,2).
Do not try to write objects with the current class definition,
the files will not be readable.

Warning in TStreamerInfo::BuildCheck:
The StreamerInfo of class TAttMarker read from file kopio_mc.root
has the same version (=1) as the active class but a different checksum.
You should update the version to ClassDef(TAttMarker,2).
Do not try to write objects with the current class definition,
the files will not be readable.

KopioMCntuple1::KopioMCntuple1: 2nd test output
KopioMCntuple5::KopioMCntuple5: 1st test output
[/b]

Comrades,

I lost myself in 2 trees. :confused: I still do not understand what happens
wrong when inheriting two skeleton classes. I’ve made a reduction
the KopioMCLoader class keeping only 3 methods (constructor, copy
constructor and destructor). Please see the attachment. The problem
appears or disappears depending on last steps done before quiting
a ROOT session. For instance:

gdb root.exe
(gdb) run
root [0] .L libKopioMCntuple1.so
root [1] .L libKopioMCntuple5.so
root [2] .L libKopioMCLoader.so
root [3] TFile f(“kopio_mc_v075.root”);
root [4] TTree *t1 = (TTree *) f.Get(“h1”);
root [5] TTree *t5 = (TTree *) f.Get(“h5”);
root [6] KopioMCLoader ld(t1,t5);
root [7] .q
KopioMCLoader::~KopioMCLoader: Test output
KopioMCntuple5::~KopioMCntuple5: (1)
KopioMCntuple5::~KopioMCntuple5: (2)
KopioMCntuple5::~KopioMCntuple5: (3)
KopioMCntuple1::~KopioMCntuple1: (1)
KopioMCntuple1::~KopioMCntuple1: (2)
KopioMCntuple1::~KopioMCntuple1: (3)
Program received signal SIGINT, Interrupt.
[Switching to Thread 1024 (LWP 1673)]
0x4131ed1b in free () from /lib/libc.so.6

OK, then I was trying to do the following (after the line 6)

root [7] delete t1;
root [8] delete t5;
root [9] .q
KopioMCLoader::~KopioMCLoader: Test output
KopioMCntuple5::~KopioMCntuple5: (1)
KopioMCntuple5::~KopioMCntuple5: (2)
KopioMCntuple5::~KopioMCntuple5: (3)
KopioMCntuple1::~KopioMCntuple1: (1)
KopioMCntuple1::~KopioMCntuple1: (2)
KopioMCntuple1::~KopioMCntuple1: (3)
Program exited normally.

The problem disappears also when I do the following

root [0] .L libKopioMCntuple1.so
root [1] .L libKopioMCntuple5.so
root [2] .L libKopioMCLoader.so
root [3] TFile *f = TFile::Open(“kopio_mc_v075.root”);
root [4] TTree *t1 = (TTree *) f->Get(“h1”);
root [5] TTree *t5 = (TTree *) f->Get(“h5”);
root [6] KopioMCLoader ld(t1,t5);
root [7] delete t1; // one may also omit this line
root [8] delete t5; // one may also omit this line
root [9] .q
KopioMCLoader::~KopioMCLoader: Test output
KopioMCntuple5::~KopioMCntuple5: (1)
KopioMCntuple5::~KopioMCntuple5: (2)
KopioMCntuple5::~KopioMCntuple5: (3)
KopioMCntuple1::~KopioMCntuple1: (1)
KopioMCntuple1::~KopioMCntuple1: (2)
KopioMCntuple1::~KopioMCntuple1: (3)

But it appears again if I do the following steps before exiting

root [0] .L libKopioMCntuple1.so
root [1] .L libKopioMCntuple5.so
root [2] .L libKopioMCLoader.so
root [3] TFile *f = TFile::Open(“kopio_mc_v075.root”);
root [4] TTree *t1 = (TTree *) f->Get(“h1”);
root [5] TTree *t5 = (TTree *) f->Get(“h5”);
root [6] KopioMCLoader ld(t1,t5);
root [7] if (f) cout << “f” << endl;
f
root [8] if (t1) cout << “t1” << endl;
t1
root [9] if (t5) cout << “t5” << endl;
t5
root [10] delete t1;
root [11] delete t5;
root [12] delete f;
root [13] if (f) cout << “f” << endl;
root [14] if (t1) cout << “t1” << endl;
root [15] if (t5) cout << “t5” << endl;
root [16] .q
KopioMCLoader::~KopioMCLoader: Test output
KopioMCntuple5::~KopioMCntuple5: (1)
KopioMCntuple5::~KopioMCntuple5: (2)
KopioMCntuple5::~KopioMCntuple5: (3)
KopioMCntuple1::~KopioMCntuple1: (1)
KopioMCntuple1::~KopioMCntuple1: (2)

*** Break *** segmentation violation
Generating stack trace…
0x412d96b8 in from /lib/libc.so.6
0x4195d4ba in KopioMCntuple1::~KopioMCntuple1(void) + 0x6e from
/data08/artamon/kopio/root/dev/KopioMCLoader/v00_3/tmp3/./libKopioMCntuple1.so
0x4196aa57 in KopioMCLoader::~KopioMCLoader(void) + 0x6b from
/data08/artamon/kopio/root/dev/KopioMCLoader/v00_3/tmp3/./libKopioMCLoader.so
0x4196bab9 in from
/data08/artamon/kopio/root/dev/KopioMCLoader/v00_3/tmp3/./libKopioMCLoader.so
0x4079e18e in G__call_cppfunc + 0x28a from
/data08/artamon/root/root_v4.00.06/root/lib/libCint.so
0x4078c71a in G__interpret_func + 0x7ea from
/data08/artamon/root/root_v4.00.06/root/lib/libCint.so
0x40772449 in G__getfunction + 0x17b5 from
/data08/artamon/root/root_v4.00.06/root/lib/libCint.so
0x407db885 in G__destroy_upto + 0x3a9 from
/data08/artamon/root/root_v4.00.06/root/lib/libCint.so
0x407db52a in G__destroy_upto + 0x4e from
/data08/artamon/root/root_v4.00.06/root/lib/libCint.so
0x407db4ce in G__scratch_globals_upto + 0x2a from
/data08/artamon/root/root_v4.00.06/root/lib/libCint.so
0x401cefac in TCint::ResetGlobals(void) + 0x24 from
/data08/artamon/root/root_v4.00.06/root/lib/libCore.so
0x4013e87e in TApplication::ProcessLine(char const *, bool, int *) +
0x7e from /data08/artamon/root/root_v4.00.06/root/lib/libCore.so
0x41203bfa in TRint::HandleTermInput(void) + 0x1ea from
/data08/artamon/root/root_v4.00.06/root/lib/libRint.so
0x41202cd2 in TTermInputHandler::Notify(void) + 0x2a from
/data08/artamon/root/root_v4.00.06/root/lib/libRint.so
0x41204399 in TTermInputHandler::ReadNotify(void) + 0x25 from
/data08/artamon/root/root_v4.00.06/root/lib/libRint.so
0x4023a581 in TUnixSystem::CheckDescriptors(void) + 0x159 from
/data08/artamon/root/root_v4.00.06/root/lib/libCore.so
0x40239ab0 in TUnixSystem::DispatchOneEvent(bool) + 0x16c from
/data08/artamon/root/root_v4.00.06/root/lib/libCore.so
0x4019ab47 in TSystem::InnerLoop(void) + 0x2f from/data08/artamon/root/root_v4.00.06/root/lib/libCore.so
0x4019aacb in TSystem::Run(void) + 0x6f from
/data08/artamon/root/root_v4.00.06/root/lib/libCore.so
0x4013f7f3 in TApplication::Run(bool) + 0x33 from
/data08/artamon/root/root_v4.00.06/root/lib/libCore.so
0x412035fb in TRint::Run(bool) + 0x323 from
/data08/artamon/root/root_v4.00.06/root/lib/libRint.so
0x080487c7 in main + 0x87 from
/data08/artamon/root/root_v4.00.06/root/bin/root.exe
0x412c914f in __libc_start_main + 0xbb from /lib/libc.so.6
0x08048681 in __register_frame_info + 0x3d from
/data08/artamon/root/root_v4.00.06/root/bin/root.exe

This is a very strange behaviour since if I omit the lines 7,8 and 9,
then the exiting procedure completes successfully.

Could you advise me what I’m doing wrong? Or maybe this is a ROOT
problem? Maybe you advise me how to investigate this problem.

I will appreciate any help. Thank you in advance.

Cheers,
Alexander

P.S
I’m working on Debian prerelease with gcc version 2.95.4 20011002
and using ROOT (Version 4.00/06)
file.tar.gz (363 KB)
file.tar.gz (363 KB)

Hi,

Your problem comes from the destructor of you skeletons. They both contain something similar to:

KopioMCntuple1::~KopioMCntuple1() { if (!fChain) return; delete fChain->GetCurrentFile(); }
Since fChain in the KopioMCntuple1 and 2 point to a TTree that is located in the same file, the file is deleted twice resulting in ‘random’ behavior (depending on the circunstance, nothing wrong or crash).
So need to make that the file in only delete ONCE. (For example since you pass the tree pointer, you might delegate the deleting file to the main script and just remove the lines

Cheers,
Philippe.

Hi Philippe,

For the test I’ve commented one skeleton destructor

KopioMCntuple1::~KopioMCntuple1() 
{ 
// if (!fChain) return; 
// delete fChain->GetCurrentFile(); 
} 

keeping another skeleton destructor

KopioMCntuple5::~KopioMCntuple5()  
{  
   if (!fChain) return;  
   delete fChain->GetCurrentFile();  
}  

and unfortunately this did not help. It has the same problem.

Cheers,
Alexander

Hi,

Since you allocate the TFile on the stack it will be deleted at the end. Hence you have to remove the delete current file from BOTH destructors (or you still have a double deletion (i.e. you have a triple deletion before).

Cheers,
Philippe.