(de)serialization of templated classes

Hello,

For some reason, I am not able to (de)serialize templated classes in ROOT. Pretty much identical code works fine for a regular class, but converted into a template, it either produces incorrect results or crashes ROOT altogether :frowning:

Could you please help me to understand what I am doing incorrectly?

Here is a working example:

// File: TestClass.h
#ifndef TEST_CLASS_H
#define TEST_CLASS_H

#include "Rtypes.h"

#include <map>
#include <set>

/******************************************************************************/
class TestClass
{
public:

 Int_t x;
 int y;

 using Map1 = std::map<int, double>;
 Map1 map1;

 using Set2 = std::set<double>;
 using Map2 = std::map<int, Set2>;
 Map2 map2;

 ClassDef(TestClass,1)
};
/******************************************************************************/

#if !defined(__CLING__)
ClassImp(TestClass);
#endif

#ifdef __ROOTCLING__
#pragma link C++ class TestClass;
#endif

#endif
// File: test.C
// root.exe -b -l -q test.C+

#include "TestClass.h"

#include "TFile.h"

#include <iostream>
using std::cout;
using std::endl;

/******************************************************************************/
#if defined(__ACLIC__)
int test(void)
#else
int main(void)
#endif
{

 // create object
 TestClass tc;
 tc.x = 888;
 tc.y = 777;
 tc.map1.insert( TestClass::Map1::value_type(111, 222.2) );
 TestClass::Set2 s2; s2.insert(333.3); s2.insert(555.5);
 tc.map2.insert( TestClass::Map2::value_type(444, s2) );

 const char * const fileName = "test.root";

 // write object
 TFile oFile(fileName, "RECREATE");
 oFile.WriteObject(&tc, "tc");
 oFile.Close();

 // read object
 TFile iFile(fileName, "READ");
 TestClass * tc2 = (TestClass*) iFile.Get("tc");
 cout<<"tc2->x = "<<tc2->x<<endl;
 cout<<"tc2->y = "<<tc2->y<<endl;
 cout<<"tc2->map1[111] = "<<tc2->map1[111]<<endl;
 cout<<"tc2->map2[444] = "<<*tc2->map2[444].begin()<<endl;
 cout<<"tc2->map2[444] = "<<*tc2->map2[444].rbegin()<<endl;
 iFile.Close();

 delete tc2;

 return 0;
 
}// end of function
/******************************************************************************/

Here is a not working template example:

// File: TestClassT.h
#ifndef TEST_CLASS_H
#define TEST_CLASS_H

#include "Rtypes.h"

#include <map>
#include <set>

/******************************************************************************/
template <class T>
class TestClassT
{
public:

 TestClassT(void) {}

 Int_t x;
 T y;

 using Map1 = std::map<T, double>;
 Map1 map1;

 using Set2 = std::set<double>;
 using Map2 = std::map<T, Set2>;
 Map2 map2;

 ClassDef(TestClassT,1)
};
/******************************************************************************/

#if !defined(__CLING__)
ClassImp(TestClassT<int>);
//templateClassImp(TestClassT);
#endif

#ifdef __ROOTCLING__
#pragma link C++ class TestClassT<int>;
#endif

#endif
// File: testT.C
// root.exe -b -l -q testT.C+

#include "TestClassT.h"

#include "TFile.h"

#include <iostream>
using std::cout;
using std::endl;

/******************************************************************************/
#if defined(__ACLIC__)
int testT(void)
#else
int main(void)
#endif
{
 // create object
 using TestClass = TestClassT<int>;
 TestClass tc;
 tc.x = 888;
 tc.y = 777;
 tc.map1.insert( TestClass::Map1::value_type(111, 222.2) );
 TestClass::Set2 s2; s2.insert(333.3); s2.insert(555.5);
 tc.map2.insert( TestClass::Map2::value_type(444, s2) );

 const char * const fileName = "testT.root";

 // write object
 TFile oFile(fileName, "RECREATE");
 oFile.WriteObject(&tc, "tc");
 oFile.Close();

 // read object
 TFile iFile(fileName, "READ");
 TestClass * tc2 = (TestClass*) iFile.Get("tc");
 cout<<"tc2->x = "<<tc2->x<<endl;
 cout<<"tc2->y = "<<tc2->y<<endl;
 cout<<"tc2->map1[111] = "<<tc2->map1[111]<<endl;
 //cout<<"tc2->map2[444] = "<<*tc2->map2[444].begin()<<endl;  // XXX: if this code is added, ROOT crashes
 //cout<<"tc2->map2[444] = "<<*tc2->map2[444].rbegin()<<endl; // XXX: if this code is added, ROOT crashes
 iFile.Close();

 delete tc2;

 return 0;

}// end of function
/******************************************************************************/

The working example output:

The template example output:

If the two lines marked with XXX are added to the 2nd example, I get (a lot of errors and) [quote]Segmentation fault (core dumped)[/quote]

Could you please let me know what I am doing wrong with (de)serialization of templates in ROOT?

ROOT version: 6.06.04

Thank you very much for your help,
Siarhei.

Hi,

By using:#ifdef __ROOTCLING__ #pragma link C++ class TestClass; #endif you are (likely unintentionally) using the old (back to v3 of ROOT) version of the I/O which does not support well STL collection. Instead use:

#ifdef __ROOTCLING__ #pragma link C++ class TestClass+; #endifIā€¦e adding a trailing + at the end of the class name to request the new (StreamerInfo based) I/O implementation.

Cheers,
Philippe

Hi Philippe,

Thank you so much for your reply!!!

Indeed, it fixes the problem, and I did not realize that the + sign has such an important meaning. My only suggestion is to make the + sign effect the default behavior.

One other solution I found by experimenting with the code is to instantiate the class explicitly:

template class TestClassT<int>;

but, of course, your solution is much better.

Thank you very much for your help,
Siarhei.