Errors when using a dictionary with nested STL collections

I’m having trouble with a ROOT dictionary generated via ROOT_GENERATE_DICTIONARY. The behavior is peculiar: When using the dictionary in a compiled .cxx file or loaded in pyroot via gSystem->Load, the dictionary works fine. But when used interactively, I’m getting errors.

As a clarifying point: elsewhere in my class/dictionary structure I’m including std::set and std::tuple objects. They work. It’s when they’re nested, as shown below, that I encounter difficulties.

I’m having this problem with several classes, but to keep this post simple as I can I’ll only illustrate it with this single class setup. The base class, ReadoutID, is used as a map key to a collection of tuples (which are in turn used as keys to a different map, but that’s not relevant to this question). Those keys are in the form of a set of tuples:

Here’s the code:

namespace grams {
   class ReadoutID {
     public:
       ReadoutID( int a_x_index, int a_y_index )
         : x_index( a_x_index )
         , y_index( a_y_index )
    {}
  private:
       int x_index;
       int y_index;
   };
   typedef std::set< std::tuple<int,int,int> > ClusterKeys;
   typedef std::map< ReadoutID, ClusterKeys > ReadoutMap;
}

Here’s the LinkDef.hh lines for that code:

#ifdef __CLING__

#pragma link C++ class std::set< std::tuple<int,int,int> >+;
#pragma link C++ class grams::ReadoutID+;
#pragma link C++ class grams::ClusterKeys+;
#pragma link C++ class grams::ReadoutMap+;

#pragma link C++ nestedclass;                                                                                          
#pragma link C++ nestedtypedef;                                                                                        

#endif // __CLING__                                                                                                      

I’ll add at this point that I’ve tried several variations in that LinkDef file; e.g., omitted the “nested” lines, omitting the potential duplicate in #pragma link C++ class grams::ClusterKeys+;, using typedef instead of class for those types that are typedefs. It doesn’t seem to change anything.

So what is the problem? There are two symptoms. The first is that when I start up ROOT and load the dictionary, I get an error message that depends on the order I refer to the classes:

# root
root [0] gSystem->Load("libDictionary.so");
root [1] auto readoutID = new grams::ReadoutID;
root [2] auto readoutMap = new grams::ReadoutMap;

That’s fine; everything is behaving as it should. But if I try to load ReadoutMap first:

# root
root [0] gSystem->Load("libDictionary.so");
root [1] auto readoutMap = new grams::ReadoutMap;
ROOT_prompt_0:1:30: error: no type named 'ReadoutMap' in namespace 'grams'
auto readoutMap = new grams::ReadoutMap;
                      ~~~~~~~^

root [2] auto readoutID = new grams::ReadoutID;
root [3] auto readoutMap = new grams::ReadoutMap;

So “invoking” ReadoutID makes ReadoutMap accessible.

Although I’m showing just ReadoutID, I’ll add that this behavior extends to other classes I’ve defined in the dictionary. If I load any other class that’s consists of a map first, I get the same error. If I load any class that’s not a map class, loading any other class now works.

The other symptom has to do with accessing the fields within the std::set. This quickest way to see this is with the TBrowser, but since that’s not convenient here, I’ll show a text version instead. I’m omitting some lines of a simple class that I don’t think is relevant to this problem:

root [2] ReadoutSim->Print()
******************************************************************************
*Tree    :ReadoutSim: ReadoutSim                                             *
*Entries :     1000 : Total =         1239876 bytes  File  Size =     252675 *
*        :          : Tree compression factor =   4.89                       *
******************************************************************************
*Br    2 :ReadoutMap : Int_t ReadoutMap_                                     *
*Entries :     1000 : Total  Size=      11686 bytes  File Size  =       2829 *
*Baskets :        1 : Basket Size=      32000 bytes  Compression=   2.86     *
*............................................................................*
*Br    3 :ReadoutMap.first.x_index : Int_t x_index[ReadoutMap_]              *
*Entries :     1000 : Total  Size=      55509 bytes  File Size  =      18128 *
*Baskets :        2 : Basket Size=      32000 bytes  Compression=   3.03     *
*............................................................................*
*Br    4 :ReadoutMap.first.y_index : Int_t y_index[ReadoutMap_]              *
*Entries :     1000 : Total  Size=      55509 bytes  File Size  =      16208 *
*Baskets :        2 : Basket Size=      32000 bytes  Compression=   3.39     *
*............................................................................*
*Br    5 :ReadoutMap.second : set<tuple<int,int,int> > second[ReadoutMap_]   *
*Entries :     1000 : Total  Size=    1085785 bytes  File Size  =     208914 *
*Baskets :       35 : Basket Size=      32000 bytes  Compression=   5.19     *
*............................................................................*

When I use TBrowser, I can see that the elements of ReadoutMap.second are _2, _1, and _0, which is what I’d expect. If I try to look at ReadoutMap.first fields, there’s no problem:

root [4] ReadoutSim->Draw("ReadoutMap.first.x_index")

But:

root [5] ReadoutSim->Draw("ReadoutMap.second._2")

 *** Break *** segmentation violation

followed by the backtrace, which I can include if anyone asks.

I don’t know if this is relevant, and it’s probably the wrong forum to point this out, but this problem also extends to uproot reading these same fields from the same files. The error I get (NotImplemented) can mean an issue with using member-wise splitting, but I’ve played with splitlevels (0, 1, 2, 99) and the symptoms do not change.

I know this is a lot. But I’ve been pounding on this for several days, trying every trick I know (rebuilding the dictionary with different #pragmas, trying different splitlevels, casting magic spells) without success.

Can anyone spot anything obvious that I’m doing wrong?


ROOT Version: 6.32.00
Platform: AlmaLinux 9
Compiler: gcc 12.3.0-13


Hello,
Thanks for reporting this problem. @pcanal might help you on this problem

Best wishes
Lorenzo

I can cross one part of the issue off the list: While I thought I’d tested splitlevel=0 for all facets of the problem, it turns out that I tested it only for a similar uproot issue (again, I know that’s not in the domain of this forum). When did ROOT-only tests, and I set splitlevel=0, I found that a command like this now works:

ReadoutSim->Draw("ReadoutMap.second._2")

However, the dictionary problem remains:

auto readoutMap = new grams::ReadoutMap;
ROOT_prompt_0:1:30: error: no type named 'ReadoutMap' in namespace 'grams'
auto readoutMap = new grams::ReadoutMap;
                      ~~~~~~~^

Whether the splitlevel=1 (or higher) failure is a bug or a feature I leave to you!

I know that “bumping” posts in a forum such as this is generally discouraged, but I didn’t want the question to fall off the radar. Does anyone have any insight as to why a dictionary would behave in this way?