Storing object of user defined class in ROOT Tree branch

Hello rooters,
Is it possible to store the object of My own class in ROOT Tree.

When i am trying to do that, i am getting following message while running
Error in TTree::Branch: The pointer specified for InComingTrack is not of a class or type known to ROOT

where inComingTrack is the ojbect of my class, Track.h

Thanks

Hi Raman,

You need to generate a ROOT dictionary for your class.

Cheers,

Hi,

as Guilherme said, you need a dictionary. Now, here you can see how to generate it:
selection.xml

<rootdict>
<class name = "inComingTrack" />
</rootdict>

Note that you’ll have to select “vector” or other collections in case you are not persistifying inComingTrack directly.

commands to generate the library associated to the dictionary. Make sure this library, the pcm file and the rootmap file are in the LD_LIBRARY_PATH.

genreflex Track.h -s selection.xml --rootmap track.rootmap --rootmap-lib libTrack.so
g++ -o libTrack.so Track_dict.cxx -shared -fPIC `root-config --cflags --libs` 

Let us know how it goes.

Cheers,
D

Thanks dpiparo and amadio.
that worked.

But i would like to do the same thing using CMake, which i am not able to do.

Any suggestions !!

Hi Raman, if you are doing this for VecGeom and GeantV, have a look at the CMake system of GeantV, as there are examples there of how to do this in CMake. Search for ROOT_GENERATE_DICTIONARY. Cheers,

Thanks Guilherme
I will try that.

Hello,
Now after creating the dictionary for my class, i am able to read data from Root Tree branches that stores object of my class.
But in some branches i have stored some typedef which are defined in my MyDefs.h file and one of which are as follows

typedef std::vector < unsigned int > Channel ;

getting following error
Error in TTreeReaderValueBase::CreateProxy(): The template argument type T of T accessing branch TEST_BRANCH (which contains data of type vector) is not known to ROOT. You will need to create a dictionary for it.
Error in <Get()>: Value reader not properly initialized, did you remember to call TTreeReader.Set(Next)Entry()?

i think again i have to create dictionary of for the typedefs defined in MyDefs.h
and i don’t know how to write selection.xml for that.

Could you please help me in that.

I also read that the same thing can be done using LinkDef.h file, still no success in that.

Regards,

Hi,

ROOT already provides by default dictionaries for vector. What is the type of the branch? If you want to build a dictionary for it, there is no magic:

<rootdict>
<class name = "vector<MyShinyClass>" />
</rootdict>

Type of branch is std::vector< unsigned int >
and is defined in MyDef.h as
typedef std::vector < unsigned int > Channel ;

Hi,

I am confused.
You can check by yourself that the dictionary for vector is there

root [0] TClass::GetClass("vector<unsigned int>")->HasDictionary()
(bool) true

To be more complete, have a look to this example which writes and reads a vector with the default dictionary, i.e. no need to generate anything:

// Write
ROOT::Experimental::TDataFrame in(10);
int seed = 1;
auto genfunc = [&seed](){vector<unsigned int> v; for (int i=0;i<10;++i) v.push_back(seed++); return v; };
in.Define("vector_of_uint", genfunc).Snapshot("in","in.root");

// Read
ROOT::Experimental::TDataFrame out("in","in.root");
auto printfcn = [](const std::vector<unsigned int> v){for (auto e:v) std::cout << e << " "; std::cout << std::endl;};
out.Foreach(printfcn, {"vector_of_uint"});

Can you share with us a minimal reproducer (input file, analysis code) which shows the issue?

Cheers,
D

1 Like

Thanks a lot.
Its workings, there was some bug in my compilation script.

Thanks again.

Marking it as SOLVED

1 Like

Sorry to come back again at the same topic.

I am having a tree that contains branches as follows
branch0 = (vector< unsigned int >)
branch1 = (vector< unsigned int >
)
branch2 = (vector< unsigned int >*)
.
.

I have checked that ROOT does not contains dictionaries for such,
I think there are no dictionaries for any types of pointers,
(Please correct me if i am wrong at some place.)
so even if i have something like
testbranch = (int *),
i have to create dictionary.

Coming back to my Tree,
i have a typedef like
typedef std::vector< unsigned int > Channel;
So basically the branches are of type ( Channel* )

TDataFrame is really very useful and helpful
Now i want to read the Tree that has branches of type (vector< unsigned int >*) using TDataFrame.
Could you please suggest me how to handle this.

Regards,

Hi,

there is nothing like a dictionary of a “pointer type”, only for “types”, i.e. one has a dictionary for the class MyClass and those allow to do I/O for MyClass* as well. What counts is the layout in memory of the MyClass instances (objects).
I am not sure I understand. I propose that you try this:

ROOT::Experimental::TDataFrame out("in","in.root");
auto printfcn = [](const std::vector<unsigned int> v){for (auto e:v) std::cout << e << " "; std::cout << std::endl;};
out.Foreach(printfcn, {"vector_of_uint"});

and if this does not work, you share the file with us.

Cheers,
D

Hi ,
I have just tried using your recipe, and it works perfectly.
but my requirement is to get only the desired entry.

So when i try to use it (as suggested by Enrico Guiraud ) as

ROOT::Experimental::TDataFrame d(“BSC_DATA_TREE”, “hello.root”);
auto vecTrack = d.Range(10,11).Take< std::vector< unsigned int > >(“branch0”);

so that i can get only the required entry of branch0, then its not getting compiled.
Please find below the attached file .

hello.root (6.4 KB)

Hi rasehgal,
thank you for trying TDataFrame. It is a new feature and we need all the feedback we can get.
I can reproduce your problem, I will come back with more info asap.

Hi rasehgal,
you found a bug :slightly_smiling_face:
We are not dealing correctly with Takeing columns that are themselves collections.

This is now fixed in the master branch and will be fixed in the 6.10-02 patch release, coming very soon – i.e. in the next days. In the meanwhile, as a workaround, you can use Foreach:

// from the prompt (i.e. at global scope)
ROOT::Experimental::TDataFrame d("BSC_DATA_TREE", "hello.root");
std::vector<std::vector<unsigned int>> vecTrack;
d.Range(10,11).Foreach([](std::vector<unsigned int> &b0) { vecTrack.emplace_back(b0); }, {"branch0"});

// from a compiled `main` (we need to capture `vecTrack` from the lambda)
#include "ROOT/TDataFrame.hxx"
int main() {
  ROOT::Experimental::TDataFrame d("BSC_DATA_TREE", "hello.root");
  std::vector<std::vector<unsigned int>> vecTrack;
  d.Range(10,11).Foreach([&vecTrack](std::vector<unsigned int> &b0) { vecTrack.emplace_back(b0); }, {"branch0"});
  return 0;
}

A vector<vector>> is not a very performant data structure, so it’s up to you whether you prefer doing something different there :slight_smile:

Again thank you for the feedback, and let me know if you have further questions!

Hi Enrico
A Simple question, in your code snippet above you have something like

std::vector< std::vector< unsigned int > > vecTrack;

Here vecTrack represent full tree or what ?? because the data type of my branch is
std::vector< unsigned int >

What data structure for the branches would you suggest to make it faster.

Cheers for your quick bug fix.

Regards,

You don’t need a vector<vector> if you just take one entry. The vector<vector> is in case you change your Range to take more than one entry.

Instead of a vector<vector<unsigned int>> it is usually better to use a bigger vector<unsigned int> that you index as if it was a matrix.

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.