Problem with std::map and structure when stored in ROOT I/O streamer dictionary

We have the following class definition, where we have a structure, RelevantQuantity and a std::map <std::string, RelevantQuantity>.

We manage to write to disk the class using TRestDataSet::Export, that implements the folowing:

        fDataSet.Snapshot("AnalysisTree", filename);

        TFile* f = TFile::Open(filename.c_str(), "UPDATE");

where fDataSet is a ROOT::RDataFrame.

The problem is that when trying to reopen the generated file we get an error:

root [0] TFile *f = new TFile("BabyIAXO_Micromegas_TrueWolter_Vacuum.root")
Fatal in <InitializeEx>: Could not load nor generate the dictionary for "pair<string,RelevantQuantity>", some element might be missing their dictionary (eg. enums)
#0  0x00007f1d971e42d7 in __GI___waitpid (pid=4610, stat_loc=stat_loc
entry=0x7ffec0071808, options=options
entry=0) at ../sysdeps/unix/sysv/linux/waitpid.c:30
#1  0x00007f1d9716270f in do_system (line=<optimized out>) at ../sysdeps/posix/system.c:149
#2  0x00007f1d99e68153 in TUnixSystem::StackTrace() () from /programas/root/6.26.06/lib/
#3  0x00007f1d99d48ef2 in DefaultErrorHandler(int, bool, char const*, char const*) () from /programas/root/6.26.06/lib/
#4  0x00007f1d99dfa82f in ErrorHandler () from /programas/root/6.26.06/lib/
#5  0x00007f1d99dfb2c2 in Fatal(char const*, char const*, ...) () from /programas/root/6.26.06/lib/
#6  0x00007f1d99919fcf in TGenCollectionProxy::InitializeEx(bool) () from /programas/root/6.26.06/lib/
#7  0x00007f1d99915df3 in TGenCollectionProxy::GetValueClass() const () from /programas/root/6.26.06/lib/
#8  0x00007f1d99e0e867 in TClass::GetClass(char const*, bool, bool, unsigned long, unsigned long) () from /programas/root/6.26.06/lib/
#9  0x00007f1d99931a9b in TStreamerInfo::BuildCheck(TFile*, bool) () from /programas/root/6.26.06/lib/
#10 0x00007f1d998e0201 in TFile::ReadStreamerInfo() () from /programas/root/6.26.06/lib/
#11 0x00007f1d998eb124 in TFile::Init(bool) () from /programas/root/6.26.06/lib/
#12 0x00007f1d998ecad5 in TFile::TFile(char const*, char const*, char const*, int) () from /programas/root/6.26.06/lib/
#13 0x00007f1d8f52905e in ?? ()
#14 0x000055c1a5ce47d0 in ?? ()
#15 0x000055c1a7bfb050 in ?? ()
#16 0x000055c1a530dc00 in ?? ()
#17 0x00007f1d8f52803b in ?? ()
#18 0x00007f1d998ec200 in ?? () from /programas/root/6.26.06/lib/
#19 0x00007f1d915d3c1a in cling::IncrementalExecutor::runStaticInitializersOnce(cling::Transaction&) () from /programas/root/6.26.06/lib/
#20 0x00007f1d9155b9e8 in cling::Interpreter::executeTransaction(cling::Transaction&) () from /programas/root/6.26.06/lib/
#21 0x00007f1d915e479a in cling::IncrementalParser::commitTransaction(llvm::PointerIntPair<cling::Transaction*, 2u, cling::IncrementalParser::EParseResult, llvm::PointerLikeTypeTraits<cling::Transaction*>, llvm::PointerIntPairInfo<cling::Transaction*, 2u, llvm::PointerLikeTypeTraits<cling::Transaction*> > >&, bool) () from /programas/root/6.26.06/lib/
#22 0x00007f1d915e60cb in cling::IncrementalParser::Compile(llvm::StringRef, cling::CompilationOptions const&) () from /programas/root/6.26.06/lib/
#23 0x00007f1d9155eeaa in cling::Interpreter::EvaluateInternal(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, cling::CompilationOptions, cling::Value*, cling::Transaction**, unsigned long) [clone .constprop.548] () from /programas/root/6.26.06/lib/
#24 0x00007f1d9155f1de in cling::Interpreter::process(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, cling::Value*, cling::Transaction**, bool) () from /programas/root/6.26.06/lib/
#25 0x00007f1d91630048 in cling::MetaProcessor::process(llvm::StringRef, cling::Interpreter::CompilationResult&, cling::Value*, bool) () from /programas/root/6.26.06/lib/
#26 0x00007f1d91475b3c in HandleInterpreterException(cling::MetaProcessor*, char const*, cling::Interpreter::CompilationResult&, cling::Value*) () from /programas/root/6.26.06/lib/
#27 0x00007f1d9148ac0f in TCling::ProcessLine(char const*, TInterpreter::EErrorCode*) () from /programas/root/6.26.06/lib/
#28 0x00007f1d99d27728 in TApplication::ProcessLine(char const*, bool, int*) () from /programas/root/6.26.06/lib/
#29 0x00007f1d98b89d80 in TRint::ProcessLineNr(char const*, char const*, int*) () from /programas/root/6.26.06/lib/
#30 0x00007f1d98b8a173 in TRint::HandleTermInput() () from /programas/root/6.26.06/lib/
#31 0x00007f1d99e69c7b in TUnixSystem::CheckDescriptors() () from /programas/root/6.26.06/lib/
#32 0x00007f1d99e6b7b8 in TUnixSystem::DispatchOneEvent(bool) () from /programas/root/6.26.06/lib/
#33 0x00007f1d99d8bb21 in TSystem::Run() () from /programas/root/6.26.06/lib/
#34 0x00007f1d99d24a22 in TApplication::Run(bool) () from /programas/root/6.26.06/lib/
#35 0x00007f1d98b8bb76 in TRint::Run(bool) () from /programas/root/6.26.06/lib/
#36 0x000055c1a3befda3 in main ()

I had a look here in case it was something related with LinkDef,

but we include the line #pragma link C++ struct RelevantQuantity; inside our LinkDef file.

Also, if I open the file in the same ROOT session as the session where the file was generated, there are no issues!

root [0] TRestDataSet d("dataset2.rml")
(TRestDataSet &) Name: BabyIAXO Title: Default TRestDataSet
root [1] d.Initialize()
root [2] d.Export("BabyIAXO_Micromegas_TrueWolter_Vacuum.root")
root [3] TFile *f = new TFile("BabyIAXO_Micromegas_TrueWolter_Vacuum.root")
(TFile *) 0x7f1bec001b10
root [4] .ls
TFile**		BabyIAXO_Micromegas_TrueWolter_Vacuum.root	BabyIAXO_Micromegas_TrueWolter_Vacuum.root
 TFile*		BabyIAXO_Micromegas_TrueWolter_Vacuum.root	BabyIAXO_Micromegas_TrueWolter_Vacuum.root
  KEY: TTree	AnalysisTree;1	AnalysisTree
  KEY: TRestDataSet	BabyIAXO;1	Default TRestDataSet
root [5] BabyIAXO->PrintMetadata()
                                            ||                                      TRestDataSet content                                      ||                                            
                                            ||                                   Config file : dataset2.rml                                   ||                                            
                                            ||                                        Name : BabyIAXO                                         ||                                            
                                            ||                                  Title : Default TRestDataSet                                  ||                                            
                                            ||                                     REST Version : 2.3.14                                      ||                                            
                                            ||                                   REST Official release: Yes                                   ||                                            
                                            ||                                        Clean state: Yes                                        ||                                            
                                            ||                                     REST Commit : 5f19f747                                     ||                                            
                                            ||                                    REST Library version : 0                                    ||                                            
                                            ||                                       - StartTime : 16.85                                      ||                                            
                                            ||                                       - EndTime : 9.36111                                      ||                                            
                                            ||                         - Path : /storage/iaxo/simulations/ray-tracing                         ||                                            
                                            ||                  - File pattern : run_006*rayTrac*_Vacuum_jgalan_V2.3.14.root                  ||                        

At least:

#pragma link C++ class RelevantQuantity+;
#pragma link C++ class std::pair<std::string, RelevantQuantity>+;
#pragma link C++ class std::map<std::string, RelevantQuantity>+;

That didn’t help. It should be class or struct?

Also, even if the error message states pair, the data member is of type std::map.

We have used previously data members such as std::vector <MyStructure> and we did not experienced problems. EDIT: Ok, perhaps we did not have problems because the member was marked as //! :slight_smile:

Well, I forgot the “map” (now added in my previous post).

For the “vector”, you would need:

#pragma link C++ class MyStructure+;
#pragma link C++ class std::vector<MyStructure>+;

I see, still the error is there, my LinkDef file looks as follows now:

#ifdef __CINT__
#pragma link off all globals;
#pragma link off all classes;
#pragma link off all functions;
#pragma link C++ nestedclasses;
#pragma link C++ nestedclasses;
#pragma link C++ class RelevantQuantity+;
#pragma link C++ class std::pair<std::string, RelevantQuantity>+;
#pragma link C++ class std::map<std::string, RelevantQuantity>+;
#pragma link C++ class TRestDataSet+;

But I don’t understand why when I open the file in the same session there is no problems, and if I go out .q and enter again, trying to open the file fails.

Ok, I found out why.

I had to #include <TRestDataSet.h> before opening the file.

Twice “nestedclasses”?
You probably wanted one “nestedtypedef” here.

Maybe, I will fix.

@pcanal However, it is kind of funny. Now I have removed the additional #pragma including the RelevantQuantity and it opens the file without problems as soon as I #include <TRestDataSet.h> header just before.

root [0] #include <TRestDataSet.h>
root [1] TFile *f = new TFile("BabyIAXO_Micromegas_TrueWolter_Vacuum.root")
(TFile *) 0x55b28e72ca50
root [2] 

So, finally are those pragma lines not necessary?

Ok, I was just not sourcing the most recent compilation. Adding the pragma lines solved the problem.

@pcanal But it seems that doing #include <TRestDataSet.h> solves also the problem without having to add the pragma lines.

Apriori you just need to use:

#ifdef __ROOTCLING__
#pragma link C++ class RelevantQuantity+;
#pragma link C++ class std::map<std::string, RelevantQuantity>+;
#pragma link C++ class TRestDataSet+;

Some older version of ROOT requires the addition of:

#pragma link C++ class std::pair<std::string, RelevantQuantity>+;

Then creating a dictionary and including in a shared library than is then accessible/visible from LD_LIBRARY_PATH, should be sufficient.

But it seems that doing `#include <TRestDataSet.h>

It should not solve the problem by itself (it would be missing the I/O information for the map).

Which version of ROOT are you using? With and then without the include, what does TClass::GetClass("TRestDataSet")->IsLoaded() return (you may also have to check against nullptr)

Compiling with LinkDef including #pragma link C++ class std::map<std::string, RelevantQuantity>+;, I get the following:

root [0] TClass::GetClass("TRestDataSet")
(TClass *) 0x55e5a24c2e60
root [1] TClass::GetClass("TRestDataSet")->IsLoaded()
(bool) true
root [2] 

Of course, same result if on top of that I add #include <TRestDataSet.h>, and opens the file properly.

If I remove the lines that contain RelevantQuantity and recompile, then same result, but I get problems reading the file.

root [0] TClass::GetClass("TRestDataSet")
(TClass *) 0x557708422a90
root [1] TClass::GetClass("TRestDataSet")->IsLoaded()
(bool) true
root [2] TFile *f = new TFile("BabyIAXO_Micromegas_TrueWolter_Vacuum.root")
Fatal in <InitializeEx>: Could not load nor generate the dictionary for "pair<string,RelevantQuantity>", some element might be missing their dictionary (eg. enums)

However, if I include #incllude <TRestDataSet.h> just before TFile, it opens the file without warnings.

root-config --version

Since you are storing a std::map<std::string, RelevantQuantity>, you need to have the dictionary for RelevantQuantity and thus those lines are required.

I understand, the problem is solved, just giving you feedback from the last question you asked. Still we don’t understand why when I #include <TRestDataSet.h> the dictionary is not necessary inside LinkDef.

I think I understand why " #include <TRestDataSet.h>" works.
There is the following inside:
std::map<std::string, RelevantQuantity> fQuantity; //<
so Cling will automatically JIT compile the needed dictionary info.

1 Like

The dictionary contains 2 types of information. One is a summary of the class layout information and the other are accelerator functions/accessors (also knows as CollectionProxy) for STL containers. With just the header files ROOT can (slight slower than with the dictionary) acquire the information but does not yet automatically generate the CollectionProxy. So currently I/O of “Interpreted classes” (having just loaded the header) works in a lot of cases but not all and thus is not officially supported yet.

1 Like

In case this is useful to any future root user, we fixed this by adding a piece of code at our MacroRootDict that automatically adds the required lines to our LinkDef automatically generated files.

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