Change type of member of a class stored in a tree

Hi,

suppose I have some class A, and I want to change the type of one of its members. How can I make sure that old versions of this class can still be read? Consider the following example of class A with a member variable content of type std::vector.

class A : public TObject {
    public:
        std::vector<TComplex> content;
    ClassDef(A, 1);
};

Now I want to change the type of content to std::vector<std::complex >:

class A : public TObject {
    public:
        std::vector<std::complex<double> > content;
    ClassDef(A, 2);
};

As expected without any further modifications, if an old version of this class is loaded content is empty. The error messages I get are:

Warning in <TStreamerInfo::BuildOld>: Cannot convert A::content from type:vector<TComplex> to type:vector<complex<double> >, skip element
Error in <TBranchElement::SetAddress>: For content can not convert vector<complex<double> > into vector<complex<double> >

But, to my understanding, I should in principle be able to write some code to convert content by putting something along this line in my LinkDef.h:

#pragma read sourceClass="A" \
             source="std::vector<TComplex> content" \
             version="[1]" \
             targetClass="A" \
             target="content" \
             include="iostream, TComplex.h" \
             code="{ std::cout << "hello world" << std::endl; }"

Obviously the printing of “hello world” would need to be replaced by some proper conversion, however, “hello world” is not printed when I try to read an old version. But at least one of the error message from above vanishes, the one remaining is:

Error in <TBranchElement::SetAddress>: For content can not convert vector<complex<double> > into vector<complex<double> >

Which additional steps to I need to take to make this work?

In the attached archive you will find the code I used for the example above, A_v1.h and A_v2.h contain the two versions of the definition of class A, A_v1LinkDef.h and A_v2LinkDef.h are the corresponding LinkDef files. write.cxx is a simple program writing a single entry with an object of class A (version 1) to a tree, read_v1.cxx successfully reads the object back using version 1 of class A. read_v2.cxx should do the same with version 2, but does not work (yet(?)).

Cheers,
Sebastian
test.tgz (1.23 KB)

Hi Sebastian,

thanks for providing such a complete example.

in cases analogous to the one you describe, i.e. transformations of collection of objects, the rule is to be written for the types of the contained objects. For example, a class which has a data member which is a vector in version 1 and a vector in version 2 will require a rule for the transformation of A into B.

Now, coming to your specific case, the usage of std::complex is not yet supported. Indeed ROOT does not yet provide a custom streamer for it. The custom streamer is needed in this case since the layout of this class can be implementation dependent. I would not reccomend to persistify std::complex instances for the moment.

As a side note, looking at your code I saw you have datamodel classes which inherit from TObject without needing it. Probably you are already aware of this optimisation: root.cern.ch/drupal/content/addi … t-classdef last paragraph.

Cheers,
Danilo

The “Adding Your Class to ROOT: ClassDef” web page contains a bit not-up-to-date informations.
See:

See also: ROOT User’s Guide → Adding a Class