How to write a branch of `RVec<std::array<`?

Hi all:
I want to save a std::vector<std::array<float, 3>

#include <TFile.h>
#include <TTree.h>
#include <ROOT/RVec.hxx>
#include <array>

void p1_tree() {
  TFile f("p1_tree.root", "RECREATE");
  TTree t("t", "t");
  std::array<float, 3> a = {1, 2, 3};
  t.Branch("a", &a);
  ROOT::RVec<std::array<float, 3>> b = {{1, 2, 3}, {4, 5, 6}};
  t.Branch("b", &b);
  t.Fill();
  b.push_back({7, 8, 9});
  t.Fill();
  t.Write();
  f.Close();
}

it cause,

Error in <TTree::Branch>: The class requested (ROOT::VecOps::RVec<array<float,3> >) for the branch "b" is an instance of an stl collection and does not have a compiled CollectionProxy. Please generate the dictionary for this collection (ROOT::VecOps::RVec<array<float,3> >) to avoid to write corrupted data.

I’m not sure which one is the best solution for both simple and safe of using TTree.

  • using TCloneArray?
  • using RVec<TArray?>
  • build a class contain std::array member and build a dict?
    Could any one give me some advice, a tutorial world be the best

Hi @cxwx1,

thanks for reaching out!

This is another case of missing dictionaries for your custom collection class, as it has also happened in the past, for example here and here.

So you need to generate dictionaries for your class. I did it like this:

#include <TFile.h>
#include <TTree.h>
#include <ROOT/RVec.hxx>
#include <array>
#include <vector>
#include <TInterpreter.h>

void p1_tree() {
  TFile f("p1_tree.root", "RECREATE");
  TTree t("t", "t");
  std::vector<float> a = {1,2,3};
  t.Branch("a", &a);
  gInterpreter->GenerateDictionary("ROOT::RVec<vector<float> >","vector;ROOT/RVec.hxx");
  ROOT::RVec<std::vector<float>> b = {{1,2,3},{4,5,6}};
  t.Branch("b", &b);
  t.Fill();
  b.push_back({7, 8, 9});
  t.Fill();
  t.Write();
  f.Close();
}
2 Likes

Hi @mdessole,
using vector is OK,
but what if I want to use a std::array

  gInterpreter->GenerateDictionary("ROOT::RVec<array<float, 3> >","array;ROOT/RVec.hxx");

It cause

Error in <TBranchElement::InitializeOffsets>: Could not find the real data member '__elems_[3]' when constructing the branch 'b' [Likely missing ShowMember].

and the data member _elems[3] is missing

I solved this by specifying leaflist in the Branch definition. It is the concatenation of the variable name and the variable type are separated by a slash (/). The variable type must be 1 character. In your case, i.e. a float array with fixed size: b[3]/F.

#include <TFile.h>
#include <TTree.h>
#include <ROOT/RVec.hxx>
#include <array>
#include <vector>
#include <TInterpreter.h>

void p1_tree() {
  TFile f("p1_tree.root", "RECREATE");
  TTree t("t", "t");
  std::array<float, 3> a = {1, 2, 3};
  t.Branch("a", &a);
  gInterpreter->GenerateDictionary("ROOT::RVec<array<float,3>>", "array;ROOT/RVec.hxx");
  ROOT::RVec<std::array<float, 3>> b = {{1, 2, 3}, {4, 5, 6}};
  t.Branch("b", &b, "b[3]/F");
  t.Fill();
  b.push_back({7, 8, 9});
  t.Fill();
  t.Write();
  f.Close();
}

1 Like

Thanks @mdessole, but still has bugs.
It seems the data in b is not correct.
and:

#include <iostream>
#include <TFile.h>
#include <TTree.h>
#include <ROOT/RVec.hxx>
#include <ROOT/RDataFrame.hxx>
#include <array>
#include <TInterpreter.h>

void p1_tree() {
  TFile f("p1_tree.root", "RECREATE");
  TTree t("t", "t");
  std::array<float, 3> a = {1, 2, 3};
  t.Branch("a", &a);
  gInterpreter->GenerateDictionary("ROOT::RVec<array<float, 3> >","array;ROOT/RVec.hxx");
  ROOT::RVec<std::array<float, 3>> b = {{1, 2, 3}, {4, 5, 6}};
  t.Branch("b", &b, "b[3]/F");
  t.Fill();
  b.push_back({7, 8, 9});
  b.push_back({7, 8, 9});
  b.push_back({7, 8, 9});
  b.push_back({7, 8, 9});
  t.Fill();
  t.Write();
  f.Close();

  ROOT::RDataFrame df("t", "p1_tree.root");
  df.Foreach([](const ROOT::RVec<std::array<float, 3>> &a) {
    std::cout
      << a.size()
      << a[0][0] << " "
      << a[0][1] << " "
      << a[0][2] << " "
      <<std::endl;
  }, {"b"});
}

cause

Error in <TTreeReaderArrayBase::CreateContentProxy()>: The branch b contains data of type float. It cannot be accessed by a TTreeReaderArray<array<float,3>>

 *** Break *** bus error

Note that this is not yet supported directly but you can had a in between layer to solve the problem:

template <typename T, int N>
struct MyArray
{
   std::array<T, N> fValues;
};

and use in std::vector<MyArray<float,3>> after generating a dictionary for both MyArray<float,3> and std::vector<MyArray<float,3>>

1 Like

Thanks for template solution.
and I have another question.
If I use a class from TObject, not a struct.

class MyArray: public TObject
{
   std::array<T, N> fValues;
   ClassDef(MyArray, 1)
};

How much system overhead will be increased?

What do you hope to gain from using TObject there? (to answer the direct question it would increase the file by adding fBits and fUniqueID for each individual array instances.

Thanks @pcanal
What do you hope to gain from using TObject there?
I’m not sure whether it is safer to use a TObject. For This time I think there is no need to use it.

Indeed for this case, it is completely unnecessary.