TTreeReaderArray with enum type

Dear experts,
I have a TTree, that has a branch containing vector of structures, with structure containing enum type called someEnum. I am trying to read it with selector in maybe a bit unconventional way using the TTreeReaderArray<someEnum> (which works for basic types), but i keep getting following error:

Error in <TTreeReaderArrayBase::CreateProxy()>: The template argument type T of T accessing branch test_struct.e1 (which contains data of type Int_t) is not known to ROOT. You will need to create a dictionary for it.

I created a minimal example that mimics the way I am trying to use it, you can find it on github. Please correct me if I am wrong, but I believe that I am producing the dictionary for the enum types, which is why I am able to store these objects in a tree in a first place.

I reproduced this issue also when branch contains vector<someEnum> and I am trying to read it with the TTreeReaderArray<someEnum>.

(I also don’t quite understand why enum objects are stored as Int_t in a tree even when I ask them to be unsigned int, but I assume that decision is up to the compiler.)

Thank you for your time :slight_smile:


ROOT Version: 6.26/10
Platform: Ubuntu under WSL2
Compiler: g++


The implementation for storing enums in ROOT files was done many years before the C++ standard introduce the ability to select the underlying storage. We have not yet ( :frowning: ) update the code to properly handle this features (So all enums are stored as int no matter what).

Try using:

#pragma link C++ enum someEnum;
#pragma link C++ enum anotherEnum;

in your linkdef file.

Actually I have those lines in my LinkDef.h…
To explain the weird logic in provided example, I have two executables:

  • one that generates the root file (in real-life application its GEANT)
  • other is the selector that does the cherry picking of the data

The structures, enums etc., that are stored in the trees/files are treated as separate module with their own LinkDef file, so that then they can be later just linked to the executables, or just loaded as libraries if one needs to check the root files manually.
In the provided example, the separate “module” is just the enum.hh file which has its own LinkDef.h. (I just push new version that mirror this intention, enum.hh and LinkDef.h are in separate directory with their own CMakeLists.txt.)

The selector executable, the one that fails, is linked to the shared library generated by the enum.hh, so it has access to the dictionary of the enums. Since it inherits from TSelector, it has its own SelectorLinkDef.h and indeed, if I add the pragmas you suggested to linkdef, it will complain that they are already known.

I apologize for convoluted example, but I wanted to reproduce the problem as I am experiencing it in my real application, because I am convinced that it is my bug, hidden in the linkdefs. Other possibility might be, that there is an issue with the “data_structures” library is somehow being loaded too late to provide function template for the TTreeReaderArray, but I admit I know nothing about compilers and linkers…

The implementation for storing enums in ROOT files was done many years before the C++ standard introduce the ability to select the underlying storage. We have not yet ( :frowning: ) update the code to properly handle this features (So all enums are stored as int no matter what).

I intent to use those enums in bit-wise manner anyway, so storage type does not really matter to me. It was just a curiosity.

@pcanal do you have any other suggestion I could try?

So, the issue is a missing enum support in TTreeReader (will be solved in next releases). To work around the issue, read int (or Int_t) instead:

@@ -43,8 +43,9 @@ public :
    TTreeReaderValue<testStruct_t> test_struct = {fReader, "test_struct"};
    TTreeReaderArray<int> test_foo = {fReader, "test_struct.foo"};
    // TTreeReaderArray<someEnum> vector_enum_someEnum = {fReader, "vector_enum"};
-   TTreeReaderArray<someEnum> test_someEnum = {fReader, "test_struct.e1"};
-   TTreeReaderArray<anotherEnum> test_anotherEnum = {fReader, "test_struct.e2"};
+   TTreeReaderValue<int> test_someEnum = {fReader, "test_struct.e1"};
+   TTreeReaderArray<int> test_anotherEnum = {fReader, "test_struct.e2"};

    // enum_tree() {};
    enum_tree(TTree * /*tree*/ =0) :TSelector() { };

Cheers,
Philippe.

PS. valgrind pointed to another unrelated issue due to order of deletion. The fix for it is:

diff --git a/main_selector.cpp b/main_selector.cpp
index 195d634..c457003 100644
--- a/main_selector.cpp
+++ b/main_selector.cpp
@@ -7,10 +7,10 @@ int main()
 {
     testStruct_t test_struct;

-    enum_tree selector(0);


     TFile file("enum_test.root","read");
+    enum_tree selector(0);
     TTree* tree = (TTree*)file.Get("enum_tree");
     tree->Process(&selector);
     return 0;

I.e. Since the selector does hold on to a reference to the tree and/or file, it need to be deleted before them.

1 Like

FYI: Support for TTreeReader(s) of enums has been added in the development branch (and will be part of v6.30/00).

1 Like

Thank you @pcanal !