Error reading std::vectors of custom classes interactively with the ROOT console

Dear colleagues,

I have a CMake-compiled code that creates a tree of some custom classes and saves it in a ROOT file. The code works well, and the problem appears when I try to access some compiled functions interactively with the ROOT console. I tell ROOT where my libraries/dictionaries are by adding the folder to LD_LIBRARY_PATH. It recognizes the functions well (so it is working), however, if I try to load a tree, it troubles only the branches that are std::vectors or std::maps of my custom classes.
I get this error:

Error in TChain::SetBranchAddress: Unable to determine the type given for the address for “SEL”. This is probably due to a missing dictionary, the original data class for this branch is vector<SelectionBase*>.

However the dictionary is there since I used it to create the tree the first time. I declared both structures in the LinkDef.h, and I created the dictionaries with CMake with the root_generate_dictionary command. This is my LinkDef.h:

#ifdef __CLING__
#pragma link C++ class SelectionBase+;
#pragma link C++ class vector <SelectionBase*>+;
#endif

Is there something else I must consider? I know this code used to work with some older ROOT versions, and at some point, it does not work with the newer versions.

Any help is welcome.


_ROOT Version: ROOT 6.26/10
_Platform: Ubuntu 20.04
_Compiler: gcc 9.4 cmake 3.23.2


I think @pcanal can help you.

Those the header that you pass to root_generate_dictionary includes the definition of both SelectionBase and vector (i.e. #include <vector>). Was there any error or warnings during the build?

Hi @pcanal,
Yes, inside SelectionBase.hxx, where the class SelectionBase is defined, there is an #include<vector>. There is no errors during compilation, and it executes also without errors. The problem appears when reading the generated TFiles interactively in the ROOT terminal. For some reason, it does not recognize the vectors of the declared classes. Should I generate the dictionaries in a different manner, to be able to use it in the interactive terminal?

Thanks for your help!

Try:
root -l -q -b -e '.class SelectionBase'
root -l -q -b -e '.class std::vector<SelectionBase*>'

Hi @Wile_E_Coyote ,
This is the output that I obtained. I’m not sure what I should check with this, but it looks fine.

$ root -l -q -b -e '.class SelectionBase'

===========================================================================
class SelectionBase
SIZE: 5568 FILE: SelectionBase.hxx LINE: 84
List of member variables --------------------------------------------------
SelectionBase.hxx 501 0x8        protected: Int_t _selEnabledIndex
[same for many other variables]
List of member functions :---------------------------------------------------
filename     line:size busy function type and name
(compiled)     (NA):(NA) 0 public: SelectionBase();
(compiled)     (NA):(NA) 0 public: SelectionBase(bool forceBreak, Int_t eventBoxId = UNASSIGNEDID);
[same for many other functions]

$ root -l -q -b -e '.class std::vector<SelectionBase*>'

===========================================================================
class std::vector<class SelectionBase *, class std::allocator<class SelectionBase *> >
SIZE: 24 FILE: stl_vector.h LINE: -1
Base classes: --------------------------------------------------------
0x0        protected std::_Vector_base<class SelectionBase *, class std::allocator<class SelectionBase *> >
List of member variables --------------------------------------------------
List of member functions :---------------------------------------------------
filename     line:size busy function type and name
(compiled)     (NA):(NA) 0 public: vector();
(compiled)     (NA):(NA) 0 public: explicit vector(const std::vector<SelectionBase *, std::allocator<SelectionBase *> >::allocator_type &__a);
[same for other functions]

Thanks for you help!

Try:
root -l -q -b your_file.root -e 'gFile->ls(); your_tree->Print();'

Thanks @Wile_E_Coyote,
I get this:

$ root -l -q -b myfile.root -e 'gFile->ls(); config->Print();'

Attaching file myfile.root as _file0...
(TFile *) 0x562b98012bd0
TFile**		myfile.root	
 TFile*		myfile.root	
  OBJ: TTree	config	0 : 0 at: 0x562b980bb950
  KEY: TTree	ana;1	20
  KEY: TTree	truth;1	2
  KEY: TTree	config;1	0
  KEY: TTree	header;1	1
******************************************************************************
*Tree    :config    : 0                                                      *
*Entries :        1 : Total =           58081 bytes  File  Size =       8628 *
*        :          : Tree compression factor =   7.32                       *
******************************************************************************
/*Many branches*/
*............................................................................*
*Br    9 :SEL       : vector<SelectionBase*>                                 *
*Entries :        1 : Total  Size=       1191 bytes  One basket in memory    *
*Baskets :        0 : Basket Size=      32000 bytes  Compression=   1.00     *
*............................................................................*
/*Many other branches*/

No errors.

Try:

{
  TFile *f = TFile::Open("myfile.root");
  TTree *t; f->GetObject("config", t);
  // t->SetMakeClass(1); // may be needed
  std::vector<SelectionBase*> *v = 0;
  t->SetBranchAddress("SEL", &v);
  t->GetEntry(0);
  // ...
  // t->ResetBranchAddresses();  // disconnect from local variables
  delete f; // automatically deletes t, too
  if (v) for (unsigned long i = 0; i < v->size(); i++) delete v->at(i); // cleanup
  delete v; // cleanup
}

Hi @Wile_E_Coyote, thank you very much for your help. I see your point. If I do your test, I don’t get any errors and I’m able to recover the info in the tree interactively. So the problem I think is in the way the branch is declared in the compiled functions.

This works (your code):

  std::vector<SelectionBase*> *v = 0;
  t->SetBranchAddress("SEL", &v);
  t->GetEntry(0);

But in my code, it is declared like this, which doesn’t work, and provides the same error:

  std::vector<SelectionBase*> *v = 0;
  TBranch* _branch;
  void* _pointer;
  _pointer=v;
  t->SetBranchAddress("SEL", &_pointer, &_branch);

It is declared in this way since the branches are declared in a general function, used to set many types of branches. I still don’t see why this way is wrong, and I know this code used to work in old root versions.

Thank you again, your comments helped a lot!

Well, I think the “SetBranchAddress” MUST get the original address of the pointer “&v” (not the pointer value “v”) as ROOT will/may change its value.

Maybe @pcanal can come up with some “void” solution.
You could try (not tested):
void **_pointer = (void**)&v; t->SetBranchAddress("SEL", _pointer);

There are several potential solution. Wile’s last post is a one solution. Another, probably better, solution is to make you generic routine function template based on the actual type (For example, this is done for SetBranchAddress itself).

Note, with:

  std::vector<SelectionBase*> *v = 0;
  TBranch* _branch;
  void* _pointer;
  _pointer=v;
  t->SetBranchAddress("SEL", &_pointer, &_branch);

TTree records the address of the variable _pointer and will use (and update) its value. After the initilization:

  _pointer=v;

_pointer and v are completely disconnected and v is never change nor used by TTree.
Wile’s solution works as it records and passes the address of the variable v (instead of the value in your example).

Thanks @Wile_E_Coyote and @pcanal for your help. However, with this solution proposed by @Wile_E_Coyote:

  std::vector<SelectionBase*> *v = 0;
  TBranch* _branch;
  void** _pointer=(void**)&v;
  t->SetBranchAddress("SEL", _pointer, &_branch);

I still get this error:

Error in <TTree::SetBranchAddress>: Unable to determine the type given for the address for "SEL". This is probably due to a missing dictionary, the original data class for this branch is vector<SelectionBase*>.

About the template function proposed by @pcanal, it sounds reasonable, but this might imply many modifications in the code (I believe, since I’m not sure of how to do such a change).

About the template function proposed by @pcanal, it sounds reasonable, but this might imply many modifications in the code

It really depends. When I introduce the template version of SetBranchAddress it requires no user code change.

I still get this error:

Try with:

void* _pointer=(void*)&v;

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