I have trouble saving/reading of TObjArray of my custom class to a TTree. I suspect the problem is with a dictionary, however, I can’t find anywhere how to do it properly.
The code of my script is:
#include "myclass.h"
int clones_test()
{
gROOT->ProcessLine(".L myclass.C+");
TObjArray a(1);
a[0] = new myclass();
((myclass*)a[0])->track_length = 3;
((myclass*)a[0])->v1 = new Float_t[3]{1.5,2,3};
((myclass*)a[0])->v2 = new Float_t[3]{1.5,2,3};
((myclass*)a[0])->v3 = new Float_t[3]{1.5,2,3};
((myclass*)a[0])->v4 = new Float_t[3]{1.5,2,3};
((myclass*)a[0])->v5 = new Float_t[3]{1.5,2,3};
((myclass*)a[0])->v6 = new Float_t[3]{1.5,2,3};
cout << ((myclass*)(a[0]))->v1[2] << endl;
auto t = new TTree("t", "t");
t->Branch("a", &a);
t->Fill();
t->Scan();
return 0;
}
And I attach myclass.h and .C. After executing of the script, on Scan() I get: Error in <Streamer>: Cannot stream interpreted class.
Thanks, that works. So I understand that for interpreted scripts libraries need to be loaded before their execution. It would be great if this was added to documentation or… fixed, if possible?
Still, after that I get a crash, and I am not sure why…
Error in <Streamer>: Cannot stream interpreted class.
is due to the header being parsed before the library loaded. An alternative to Wile’s solution is, instead of the ProcessLine to add a the start of the script (before any parsing, for example on the first first line).
R__LOAD_LIBRARY(myclass.C+)
The crash is due to missing part in the declaration of myclass, you need to use:
or write a default constructor that initialize them. [The I/O uses the default constructor when it needs to create new object].
Third and likely most important, this is currently unlikely to be constructing what you need. Rather than distributing the content of the TObjArray with one branch per object, it creates a single branch (TObjArray can not even be split member-wise).
Instead you might have meant:
t->Branch(&a);
which will create one branch per element of TObject array. In addition if you use something like:
class myclass : public TNamed
....
myclass(const char *name) : TNamed(name, "") {}
....
a[0] = new myclass("a0.");
a[1] = new myclass("a1.");
the branch will have nice names (instead of all being “myclass” ).
An additional question is: it seems I do not need to init the class members in the constructor. I may as well just declare Int_t track_length=0;. Is there any reason to initialize track_length to 0 in a constructor?
Be warned that this problem is still unsolved, even in the newest ROOT 6.22/08:
@pcanal That’s why I started to “advertise” the usage of “rootlogon.C” files (and they also work in both ROOT 5 and 6 versions, if one needs to maintain this kind of compatibility for one’s macros).
BTW. For safety reasons, I would do both in the “default constructor”, set all pointers and the length to zero (well, you never know when something will try to be clever and use or “delete” them). Also, make sure that you do not allocate anything in the “default constructor” (this space will be lost by the ROOT I/O).
My point is, that (from what I read) you can initalise variables outside of constructor since C++11 - member initialisation. Just declare it as int track_length=0;. I wonder if intialising inside a constructor instead of “member initialisation” outside of it has any advantages.