Error reading custom class object from file with ROOT 6.22,24
Closed this issue · 4 comments
- Checked for duplicates
Describe the bug
Error reading object from file. Please find a short example to reproduce below.
Minimal example to reproduce
Below I refer to it as test.cpp
#include "map"
#include "array"
#include "iostream"
#include "TObject.h"
#include "TFile.h"
class TestClass : public TObject {
public:
TestClass(){
std::array<std::string, 2> test_array{"aaaa", "bbbbbb"};
test_map_[test_array] = "cccc";
}
void Print(Option_t *option="") const {
for(const auto& element : test_map_){
std::cout << element.first[0] << " " << element.first[1] << " " << element.second << std::endl;
}
}
private:
std::map<std::array<std::string, 2>, std::string> test_map_{};
ClassDef(TestClass, 1);
};
ClassImp(TestClass)
void test(){
auto* test_obj = new TestClass;
test_obj->Print();
auto* file = TFile::Open("test.root", "recreate");
test_obj->Write("obj");
file->Close();
delete file;
delete test_obj;
file = TFile::Open("test.root", "read");
test_obj = file->Get<TestClass>("obj");
test_obj->Print();
file->Close();
delete file;
}
int main(int argc, char* argv[]) {
test();
return 0;
}
Running the example
With a compiled code everything works as expected:
root -l
root [0] .L test.cpp+
root [1] test()
gives correct output:
aaaa bbbbbb cccc
aaaa bbbbbb cccc
But if I try to read again the same file:
root -l test.root
root [0] gSystem->Load("test_cpp")
root [1] obj->Print()
Error in <TBufferFile::ReadVersion>: Could not find the StreamerInfo with a checksum of 0x6b3ba626 for the class "string" in test.root.
Error in <TBufferFile::CheckByteCount>: object of class string read too many bytes: 72 instead of 24
Warning in <TBufferFile::CheckByteCount>: string::Streamer() not in sync with data on file test.root, fix Streamer()
aaaabbbbbb@ cccc�i�� cccc
With an older version of ROOT (6.18), everything works as expected.
Some additional information
I tried to compare StreamerInfo for 2 ROOT versions and they are different (last item):
root 6.18
root [2] _file0->ShowStreamerInfo()
OBJ: TList TList Doubly linked list : 0
StreamerInfo for class: TestClass, version=1, checksum=0x84f55819
TObject BASE offset= 0 type=66 Basic ROOT object
map<array<string,2>,string> test_map_ offset= 0 type=300 (nodelete) ,stl=4, ctype=61,
StreamerInfo for class: pair<array<string,2>,string>, version=1, checksum=0x64321048
string first [2] offset= 0 type=320 ,stl=365, ctype=365,
string second offset= 0 type=300 ,stl=365, ctype=365,
root 6.22,24
root [3] _file0->ShowStreamerInfo()
OBJ: TList TList Doubly linked list : 0
StreamerInfo for class: TestClass, version=1, checksum=0x84f55819
TObject BASE offset= 0 type=66 Basic ROOT object
map<array<string,2>,string> test_map_ offset= 0 type=300 (nodelete) ,stl=4, ctype=61,
StreamerInfo for class: pair<array<string,2>,string>, version=1, checksum=0xb5fb752
array<string,2> first offset= 0 type=62 Emulation
string second offset= 0 type=300 ,stl=365, ctype=365, Emulation
StreamerInfo for class: array<string,2>, version=1, checksum=0x6b3ba626
string _M_elems offset= 0 type=320 ,stl=365, ctype=365
Unfortunately, I don't how to proceed further.
Setup
- Reproduced with ROOT 6.22.08, 6.24 (today's version from the branch with patches)
- Operating system Fedora 33 / centos7
- binary download / you built it yourself.
The I/O for STL collection containing directly an std::array is not yet supported.
Workaround:
struct StringArray : public std::array<std::string, 2>
{};
class TestClass : public TObject {
...
private:
std::map<StringArray, std::string> test_map_{};
ClassDef(TestClass, 1);
};
Dear Philippe,
Thank you for the solution!
Is there a way to keep backward compatibility with old files created with ROOT 6.18? With this workaround, I'm getting warnings
Warning in <TStreamerInfo::BuildOld>: Cannot convert test_map_ from type: map<array<string,2>,string> to type: map<StringArray,string>, skip element
Cheers,
Viktor
There is a chance (you need to verify that it reads the data correctly) that adding
#pragma read sourceClass="array<string,2>" targetClass="StringArray";
or
#pragma read sourceClass="map<array<string,2>,string>" targetClass="map<StringArray,string>";
is sufficient.
Given the time that passed since the last comment, I am closing the issue assuming it has been addressed. Of course, I invite the original author to submit a new one related to the same topic in case the problem is not fixed.