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