Writing map<enum class, custom Root class> to tree

The model for my project is the following:

enum class Weights {NORM, PU};

struct WeightRecord: public TObject
{
  WeightRecord();
  ~WeightRecord();
  map<Vari, double> _weights;
  ClassDef(WeightRecord, 1);
};

class WeightMap: public TObject
{
  map<Weights, WeightRecord> _weightmap;
public:
  WeightMap();
  ~WeightMap();
  ClassDef(WeightMap, 1);
};

with the linkdef:

// some C++ header definition                                                                                                                                                    
#ifdef __MAKECINT__
// turns off dictionary generation for all                                                                                                                                       

#pragma link off all globals;
#pragma link off all classes;
#pragma link off all functions;
#pragma link off all namespaces;
#pragma link C++ enum class Weights;

#pragma link C++ class WeightRecord;
#pragma link C++ std::map<Definitions::Weights, WeightRecord>;
#pragma link C++ class WeightMap;


#endif

I write the objects of class WeightMap to tree. Unfortunately, when read from tree Weights in map<Weights, WeightRecord> _weightmap are converted to 0.

What might be wrong?


Please read tips for efficient and successful posting and posting code

ROOT Version: Not Provided
Platform: Not Provided
Compiler: Not Provided


Try with:

#pragma link C++ class WeightRecord+;
#pragma link C++ class WeightMap+;

(to request the current I/O scheme rather than the very old one (which easily fails with STL collections).

Now Weights no longer become 0, but there is a crash:


#6  0x00007f19d4c865b0 in std::_Rb_tree<Weights, std::pair<Weights const, WeightRecord>, std::_Select1st<std::pair<Weights const, WeightRecord> >, std::less<Weights>, std::allocator<std::pair<Weights const, WeightRecord> > >::_M_begin (this=0xf) at /cvmfs/cms.cern.ch/slc7_amd64_gcc630/external/gcc/6.3.0/include/c++/6.3.0/bits/stl_tree.h:666
#7  0x00007f19d4c865ce in std::_Rb_tree<Weights, std::pair<Weights const, WeightRecord>, std::_Select1st<std::pair<Weights const, WeightRecord> >, std::less<Weights>, std::allocator<std::pair<Weights const, WeightRecord> > >::clear (this=0xf) at /cvmfs/cms.cern.ch/slc7_amd64_gcc630/external/gcc/6.3.0/include/c++/6.3.0/bits/stl_tree.h:1087
#8  0x00007f19d4c8543a in std::map<Weights, WeightRecord, std::less<Weights>, std::allocator<std::pair<Weights const, WeightRecord> > >::clear (this=0xf) at /cvmfs/cms.cern.ch/slc7_amd64_gcc630/external/gcc/6.3.0/include/c++/6.3.0/bits/stl_map.h:1044
#9  0x00007f19d4ca50fc in ROOT::Detail::TCollectionProxyInfo::Type<std::map<Weights, WeightRecord, std::less<Weights>, std::allocator<std::pair<Weights const, WeightRecord> > > >::clear (env=0x54a2850) at /cvmfs/cms.cern.ch/slc7_amd64_gcc630/cms/cmssw/CMSSW_9_4_11/external/slc7_amd64_gcc630/bin/../../../../../../../slc7_amd64_gcc630/lcg/root/6.10.08-fmblme2/include/TCollectionProxyInfo.h:306
#10 0x00007f19d5ecfd39 in TGenCollectionStreamer::ReadBufferGeneric(TBuffer&, void*, TClass const*) () from /cvmfs/cms.cern.ch/slc7_amd64_gcc630/cms/cmssw/CMSSW_9_4_11/external/slc7_amd64_gcc630/bin/../../../../../../../slc7_amd64_gcc630/lcg/root/6.10.08-fmblme2/lib/libRIO.so
#11 0x00007f19d5e6c1fa in TBufferFile::ReadFastArray(void*, TClass const*, int, TMemberStreamer*, TClass const*) () from /cvmfs/cms.cern.ch/slc7_amd64_gcc630/cms/cmssw/CMSSW_9_4_11/external/slc7_amd64_gcc630/bin/../../../../../../../slc7_amd64_gcc630/lcg/root/6.10.08-fmblme2/lib/libRIO.so
#12 0x00007f19d5f61544 in int TStreamerInfoActions::ReadSTL<&TStreamerInfoActions::ReadSTLMemberWiseSameClass, &TStreamerInfoActions::ReadSTLObjectWiseFastArray>(TBuffer&, void*, TStreamerInfoActions::TConfiguration const*) () from /cvmfs/cms.cern.ch/slc7_amd64_gcc630/cms/cmssw/CMSSW_9_4_11/external/slc7_amd64_gcc630/bin/../../../../../../../slc7_amd64_gcc630/lcg/root/6.10.08-fmblme2/lib/libRIO.so
#13 0x00007f19d5e6ba85 in TBufferFile::ApplySequence(TStreamerInfoActions::TActionSequence const&, void*) () from /cvmfs/cms.cern.ch/slc7_amd64_gcc630/cms/cmssw/CMSSW_9_4_11/external/slc7_amd64_gcc630/bin/../../../../../../../slc7_amd64_gcc630/lcg/root/6.10.08-fmblme2/lib/libRIO.so
#14 0x00007f19d575ae1d in TBranchElement::ReadLeavesMember(TBuffer&) () from /cvmfs/cms.cern.ch/slc7_amd64_gcc630/cms/cmssw/CMSSW_9_4_11/external/slc7_amd64_gcc630/bin/../../../../../../../slc7_amd64_gcc630/lcg/root/6.10.08-fmblme2/lib/libTree.so
#15 0x00007f19d574e07a in TBranch::GetEntry(long long, int) () from /cvmfs/cms.cern.ch/slc7_amd64_gcc630/cms/cmssw/CMSSW_9_4_11/external/slc7_amd64_gcc630/bin/../../../../../../../slc7_amd64_gcc630/lcg/root/6.10.08-fmblme2/lib/libTree.so
#16 0x00007f19d5766011 in TBranchElement::GetEntry(long long, int) () from /cvmfs/cms.cern.ch/slc7_amd64_gcc630/cms/cmssw/CMSSW_9_4_11/external/slc7_amd64_gcc630/bin/../../../../../../../slc7_amd64_gcc630/lcg/root/6.10.08-fmblme2/lib/libTree.so
#17 0x00007f19d5765fca in TBranchElement::GetEntry(long long, int) () from /cvmfs/cms.cern.ch/slc7_amd64_gcc630/cms/cmssw/CMSSW_9_4_11/external/slc7_amd64_gcc630/bin/../../../../../../../slc7_amd64_gcc630/lcg/root/6.10.08-fmblme2/lib/libTree.so
#18 0x00007f19d57a8cc1 in TTree::GetEntry(long long, int) () from /cvmfs/cms.cern.ch/slc7_amd64_gcc630/cms/cmssw/CMSSW_9_4_11/external/slc7_amd64_gcc630/bin/../../../../../../../slc7_amd64_gcc630/lcg/root/6.10.08-fmblme2/lib/libTree.so

What could be the problem?

The order matters:

#pragma link C++ class Vari+;
#pragma link C++ class std::map<Vari, double>+;
#pragma link C++ class WeightRecord+;
#pragma link C++ class Weights+;
#pragma link C++ class std::map<Weights, WeightRecord>+;
#pragma link C++ class WeightMap+;

What could be the problem?

Did you regenerate the file (the file produce with the old is not redable)?

Now I changed the order, I also changed enum class to class + (following @Wile_E_Coyote). Now I managed to write and read WeightMap from the file.

Please confirm that enum class should be changed to class +.

The question @pcanal is whether “enum class” is supported / recognized by the “rootcling” (and, if yes, whether a “+” is needed),

The only “up to date” (a bit outdated?) list of supported “#pragma” statements, that I know of, exists in (search also for “pragma” in the whole document):

ROOT User’s Guide → Adding a Class → Adding a Class with a Shared Library

The pragma is not needed for the enums (whether or not they are classes).

Well, “the complete list of pragma statements currently supported by Cling” explicitly shows:

#pragma link [C|C++|off] [class|struct|union|enum|namespace|protected] [name];
#pragma link [C|C++|off] [class|class+protected|struct|union|enum|namespace] [name];

Should I assume that “enum” was needed in ROOT 5, but it is redundant (harmful?) in ROOT 6?

Nevermind, I mis-remembered :(.

Yes, you should use

#pragma link C++ enum Weights;

enum class is not a supported spelling.
The trailing + is not necessary (I think it is just ignored if present).

Also by writing the pragma for enum class as #pragma link C++ enum Vari; there are crashes.

What is it possible to tell from this backtrace:

0x00007fa56d474702 in TUnixSystem::StackTrace() () from /cvmfs/cms.cern.ch/slc7_amd64_gcc630/cms/cmssw/CMSSW_9_4_11/external/slc7_amd64_gcc630/bin/../../../../../../../slc7_amd64_gcc630/lcg/root/6.10.08-fmblme2/lib/libCore.so
#4  0x00007fa56d476bac in TUnixSystem::DispatchSignals(ESignals) () from /cvmfs/cms.cern.ch/slc7_amd64_gcc630/cms/cmssw/CMSSW_9_4_11/external/slc7_amd64_gcc630/bin/../../../../../../../slc7_amd64_gcc630/lcg/root/6.10.08-fmblme2/lib/libCore.so
#5  <signal handler called>
#6  0x00007fa56d048566 in int TStreamerInfoActions::ReadBasicType<unsigned int>(TBuffer&, void*, TStreamerInfoActions::TConfiguration const*) () from /cvmfs/cms.cern.ch/slc7_amd64_gcc630/cms/cmssw/CMSSW_9_4_11/external/slc7_amd64_gcc630/bin/../../../../../../../slc7_amd64_gcc630/lcg/root/6.10.08-fmblme2/lib/libRIO.so
#7  0x00007fa56cf69a85 in TBufferFile::ApplySequence(TStreamerInfoActions::TActionSequence const&, void*) () from /cvmfs/cms.cern.ch/slc7_amd64_gcc630/cms/cmssw/CMSSW_9_4_11/external/slc7_amd64_gcc630/bin/../../../../../../../slc7_amd64_gcc630/lcg/root/6.10.08-fmblme2/lib/libRIO.so
#8  0x00007fa56c858e1d in TBranchElement::ReadLeavesMember(TBuffer&) () from /cvmfs/cms.cern.ch/slc7_amd64_gcc630/cms/cmssw/CMSSW_9_4_11/external/slc7_amd64_gcc630/bin/../../../../../../../slc7_amd64_gcc630/lcg/root/6.10.08-fmblme2/lib/libTree.so
#9  0x00007fa56c84c07a in TBranch::GetEntry(long long, int) () from /cvmfs/cms.cern.ch/slc7_amd64_gcc630/cms/cmssw/CMSSW_9_4_11/external/slc7_amd64_gcc630/bin/../../../../../../../slc7_amd64_gcc630/lcg/root/6.10.08-fmblme2/lib/libTree.so
#10 0x00007fa56c864011 in TBranchElement::GetEntry(long long, int) () from /cvmfs/cms.cern.ch/slc7_amd64_gcc630/cms/cmssw/CMSSW_9_4_11/external/slc7_amd64_gcc630/bin/../../../../../../../slc7_amd64_gcc630/lcg/root/6.10.08-fmblme2/lib/libTree.so
#11 0x00007fa56c863fca in TBranchElement::GetEntry(long long, int) () from /cvmfs/cms.cern.ch/slc7_amd64_gcc630/cms/cmssw/CMSSW_9_4_11/external/slc7_amd64_gcc630/bin/../../../../../../../slc7_amd64_gcc630/lcg/root/6.10.08-fmblme2/lib/libTree.so
#12 0x00007fa56c863fca in TBranchElement::GetEntry(long long, int) () from /cvmfs/cms.cern.ch/slc7_amd64_gcc630/cms/cmssw/CMSSW_9_4_11/external/slc7_amd64_gcc630/bin/../../../../../../../slc7_amd64_gcc630/lcg/root/6.10.08-fmblme2/lib/libTree.so
#13 0x00007fa56c8a6cc1 in TTree::GetEntry(long long, int) () from /cvmfs/cms.cern.ch/slc7_amd64_gcc630/cms/cmssw/CMSSW_9_4_11/external/slc7_amd64_gcc630/bin/../../../../../../../slc7_amd64_gcc630/lcg/root/6.10.08-fmblme2/lib/libTree.so
and from this backtrace:

#6  0x00007f36571a0656 in std::_Rb_tree<Weights, std::pair<Weights const, WeightRecord>, std::_Select1st<std::pair<Weights const, WeightRecord> >, std::less<Weights>, std::allocator<std::pair<Weights const, WeightRecord> > >::_M_begin (this=0xf) at /cvmfs/cms.cern.ch/slc7_amd64_gcc630/external/gcc/6.3.0/include/c++/6.3.0/bits/stl_tree.h:666
#7  0x00007f36571a0674 in std::_Rb_tree<Weights, std::pair<Weights const, WeightRecord>, std::_Select1st<std::pair<Weights const, WeightRecord> >, std::less<Weights>, std::allocator<std::pair<Weights const, WeightRecord> > >::clear (this=0xf) at /cvmfs/cms.cern.ch/slc7_amd64_gcc630/external/gcc/6.3.0/include/c++/6.3.0/bits/stl_tree.h:1087
#8  0x00007f365719f4e0 in std::map<Weights, WeightRecord, std::less<Weights>, std::allocator<std::pair<Weights const, WeightRecord> > >::clear (this=0xf) at /cvmfs/cms.cern.ch/slc7_amd64_gcc630/external/gcc/6.3.0/include/c++/6.3.0/bits/stl_map.h:1044
#9  0x00007f36571bf1be in ROOT::Detail::TCollectionProxyInfo::Type<std::map<Weights, WeightRecord, std::less<Weights>, std::allocator<std::pair<Weights const, WeightRecord> > > >::clear (env=0x50dca30) at /cvmfs/cms.cern.ch/slc7_amd64_gcc630/cms/cmssw/CMSSW_9_4_11/external/slc7_amd64_gcc630/bin/../../../../../../../slc7_amd64_gcc630/lcg/root/6.10.08-fmblme2/include/TCollectionProxyInfo.h:306
#10 0x00007f36583eed39 in TGenCollectionStreamer::ReadBufferGeneric(TBuffer&, void*, TClass const*) () from /cvmfs/cms.cern.ch/slc7_amd64_gcc630/cms/cmssw/CMSSW_9_4_11/external/slc7_amd64_gcc630/bin/../../../../../../../slc7_amd64_gcc630/lcg/root/6.10.08-fmblme2/lib/libRIO.so
#11 0x00007f365838b1fa in TBufferFile::ReadFastArray(void*, TClass const*, int, TMemberStreamer*, TClass const*) () from /cvmfs/cms.cern.ch/slc7_amd64_gcc630/cms/cmssw/CMSSW_9_4_11/external/slc7_amd64_gcc630/bin/../../../../../../../slc7_amd64_gcc630/lcg/root/6.10.08-fmblme2/lib/libRIO.so
#12 0x00007f3658480544 in int TStreamerInfoActions::ReadSTL<&TStreamerInfoActions::ReadSTLMemberWiseSameClass, &TStreamerInfoActions::ReadSTLObjectWiseFastArray>(TBuffer&, void*, TStreamerInfoActions::TConfiguration const*) () from /cvmfs/cms.cern.ch/slc7_amd64_gcc630/cms/cmssw/CMSSW_9_4_11/external/slc7_amd64_gcc630/bin/../../../../../../../slc7_amd64_gcc630/lcg/root/6.10.08-fmblme2/lib/libRIO.so
#13 0x00007f365838aa85 in TBufferFile::ApplySequence(TStreamerInfoActions::TActionSequence const&, void*) () from /cvmfs/cms.cern.ch/slc7_amd64_gcc630/cms/cmssw/CMSSW_9_4_11/external/slc7_amd64_gcc630/bin/../../../../../../../slc7_amd64_gcc630/lcg/root/6.10.08-fmblme2/lib/libRIO.so
#14 0x00007f3657c79e1d in TBranchElement::ReadLeavesMember(TBuffer&) () from /cvmfs/cms.cern.ch/slc7_amd64_gcc630/cms/cmssw/CMSSW_9_4_11/external/slc7_amd64_gcc630/bin/../../../../../../../slc7_amd64_gcc630/lcg/root/6.10.08-fmblme2/lib/libTree.so
#15 0x00007f3657c6d07a in TBranch::GetEntry(long long, int) () from /cvmfs/cms.cern.ch/slc7_amd64_gcc630/cms/cmssw/CMSSW_9_4_11/external/slc7_amd64_gcc630/bin/../../../../../../../slc7_amd64_gcc630/lcg/root/6.10.08-fmblme2/lib/libTree.so
#16 0x00007f3657c85011 in TBranchElement::GetEntry(long long, int) () from /cvmfs/cms.cern.ch/slc7_amd64_gcc630/cms/cmssw/CMSSW_9_4_11/external/slc7_amd64_gcc630/bin/../../../../../../../slc7_amd64_gcc630/lcg/root/6.10.08-fmblme2/lib/libTree.so
#17 0x00007f3657c84fca in TBranchElement::GetEntry(long long, int) () from /cvmfs/cms.cern.ch/slc7_amd64_gcc630/cms/cmssw/CMSSW_9_4_11/external/slc7_amd64_gcc630/bin/../../../../../../../slc7_amd64_gcc630/lcg/root/6.10.08-fmblme2/lib/libTree.so
#18 0x00007f3657cc7cc1 in TTree::GetEntry(long long, int) () from /cvmfs/cms.cern.ch/slc7_amd64_gcc630/cms/cmssw/CMSSW_9_4_11/external/slc7_amd64_gcc630/bin/../../../../../../../slc7_amd64_gcc630/lcg/root/6.10.08-fmblme2/lib/libTree.so

Can you please tell me what is the correct and sure way to provide a pragma for an enum class so that it can be a key in a map and can be written to a TTree?

In case I do #pragma link C++ enum .. shouldn’t I do also #pragma link off all enums?

The link off are left-over from a long time ago when the default was to generate the dictionary for everything seen. The default has long change to be off. You can remove all the link off lines.

There is no crash only when a pragma for enum class is stated like this:
#pragma link C++ class Vari+;

That is surprising. You have a work-around thus you should be able to move forward. However could you open an issue on github so that we either fix it and/or document this properly.

Thanks,
Philippe.

@pcanal , my reply has long since been updated.

So I am confused :). Do you have a working solution or do both options fails?

both options fail. Does the backtrace tell you anything?

When you modify the “linkdef” file, remember to recreate the shared library, and then you need to generate ROOT files from scratch (old files are not readable).

Unfortunately, no. (It “even” looks impossible to crash there).

One possibly is to run:

valgrind --suppressions=$ROOTSYS/etc/valgrind-root.supp your_command_name your_arguments

to get more information on the kind of failures.

Is the script that reads WeightMap from tree correct:

TChain chain("migration");
  chain.Add("root://eosuser.cern.ch//eos/user/v/vveckaln/test_output/migration/MC13TeV_TTJets/migration_MC13TeV_TTJets_0.root/migration");
  WeightMap *wmap;
  chain.SetBranchAddress("weight", &wmap);
  for (unsigned long event_ind = 0; event_ind < chain.GetEntries(); event_ind ++)
    {
      chain.GetEntry(event_ind);
      wmap -> ls();
                                                                                                                                                                    
    }

In place of WeightMap * wmap maybe I need WeightMap * wmap = new WeightMap? Actually when I do this there are no crashes.

or use Weightmap wmap and then && wmap?