Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Error reading custom class object from file with ROOT 6.22,24 #8268

Open
1 task done
viktorklochkov opened this issue May 27, 2021 · 3 comments
Open
1 task done

Error reading custom class object from file with ROOT 6.22,24 #8268

viktorklochkov opened this issue May 27, 2021 · 3 comments
Assignees

Comments

@viktorklochkov
Copy link

  • 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

  1. Reproduced with ROOT 6.22.08, 6.24 (today's version from the branch with patches)
  2. Operating system Fedora 33 / centos7
  3. binary download / you built it yourself.
@pcanal
Copy link
Member

pcanal commented May 27, 2021

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);
};

@viktorklochkov
Copy link
Author

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

@pcanal
Copy link
Member

pcanal commented May 28, 2021

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants