Class with a pointer to an abstract class as datamember

Hello,

I am having “the same” problem as the one reported here:
root.cern.ch/phpBB2/viewtopic.php?t=3157

I am using root v 5.16.00 on lxplus.

I’d like to be able to save in a file and/or in a TTree objects of a class C which has as a data member a pointer to a class Base from which other classes A1, A2, … inherit.

What happens is the following:

  1. I can write one class C object in a TFile with no error.
  2. If I open the TFile in a new root session without loading the libraries of my classes C, Base, A1, A2,… I receive a warning that "
    Warning in TClass::TClass: no dictionary for class C is available
    Warning in TClass::TClass: no dictionary for class Base is available
    Warning in TClass::TClass: no dictionary for class A1 is available
    "
    where A1 is the class of the object pointed to by the Base pointer data member of C in my example. Therefore it looks like that the original class was not lost when the object was written in the file.
  3. If I load the libraries and I try to read the C class object from the file I got this error message:
    Error in TClass::New: cannot create object of class Base

I think I have defined properly all the default constructors of the classes C, Base and A1,A2,…

I have tried to have a look at the Streamer automatically created by rootcint for C, Base and A1 and I got a little bit confused:
regardless of the fact if I was put , or not, “//->” after the declaration of the data member
Base* _baseobject;
in the C class definition, the C::Streamer(s) are identical and they both contain:
R__b << _baseobject ;

Is it normal? Is there a way to have a look of the operator<< and operator>> used in the Streamer for these classes?
According to the answer given in the post I have linked at the beginning what I am trying to do should be possible so, likely, I am doing something wrong. Could you help me?

                     Thanks in advance and regards

                                   Andrea

Update:

I have transformed all the pure virtual methods of class Base into “dummy” methods.

In this way I have a different error when I try to read a C object from a TFile:

Error in TBufferFile::CheckByteCount: object of class Base read too few bytes: 16 instead of 66

I am wondering if 66 is the right size of objects of class A1 while 16 is the size of objects of class Base (which inherits from TObject). A1 has two float array of six elements has data members.

Suggestions?

                                 Andrea

Hi,

I am a bit confused on the exact details of your case :slight_smile: I would be best if you posted an example reproducing the example (both the writing and reading part).

From your description it looks like you are using the old I/O (which uses generated streamers with operator>> and does not support automatic schema evolution) instead of the new I/O (based on TStreamerInfo(s)). To use the new I/O append + before the ; in your pragma link statement:#pragma link C++ class myclass+;

Cheers,
Philippe.

Thanks Philippe,

indeed after my post I have read (again) the user guide chapter 11 and 15 and I had the feeling that what I am using is not what is in the documentation. The Streamer method lines I have quoted in my post have been obtained by executing rootcint WITHOUT the pragma link statement you have suggested. But the actual code I have used is the one generated by gSystem->CompileMacro(…) WITHOUT any XXX_linkdef.h file . Do you know which is the default in this case? old I/O or new I/O ? In any case which is your suggestion? To use the new I/O because with the old I/O I cannot do what I want to do or I should be able to do it even with the old I/O scheme? Let me know, in the meantime I’ll try to prepare a working example of my problem.

                                   cheers

                                    Andrea

Hi,

The default for ACliC (aka CompileMacro) is to use the new I/O.
A priori you should have been able to do what you want with both the new and old I/O given that you respect the convention and requirements for pointers and abstract classes … now that said, I do not have enough information to actually know what you meant to do :slight_smile:

Cheers,
Philippe

Hello,

I think I managed to create a simple example of my problems. You can find the relevant files on lxplus in ~venturia/public/root_test

There are three *.C and *.h files. They can be compiled by executing
.x CompileAll.C in root. The libraries will be saved in ~venturia/public/root_test/51600 directory. In the following root session it will be enough to execute
.x LoadAll.C
to load all the libraries. First comment: when I load the library I got this message:
Warning in TClass::TClass: no dictionary for class Measurement is available
Probably it is related to my problem but I do not know what is causing this message

Once the libraries are loaded the macro example.C can be executed. A TempMeas object is created and saved in the file test.root.
Then if you try to run example2.C in a fresh new root session you should get this message:
Error in TBufferFile::CheckByteCount: object of class Measurement read too few bytes: 16 instead of 66
furthermore if I try to execute:
meas->GetMeas() which should access the method GetValue() of Measurement which is specialised in APVNoise I got the dummy value w.r.t. the original datamember of the APVNoise object I have created in example.C

                                                cheers

                                               Andrea

Hi,

[quote]when I load the library I got this message:
Warning in TClass::TClass: no dictionary for class Measurement is available [/quote]I do not get this message and this message should not appear as a result of loading a shared library. It should only appear as a result of loading a root file.

[quote]Error in TBufferFile::CheckByteCount: object of class Measurement read too few bytes: 16 instead of 66 [/quote]I do see this message. It is a direct result to your mis-use of the I/O hint ‘->’:[code] Measurement* _meas; //->[code]explicitly tells the I/O that the enclosing class (TempMeas in your case) is responsible for allocating the Measurement object and the I/O should assume that _meas is non zero (in practice to avoid spurrious crash instead of assuming it, if _meas is null we do the best we can … which is usually wrong … and allocate an object of the type refer to by the pointer (aka Measurement in you case)

This also explain your earlier message (can not allocate Measurement object).

Simply removing the -> seems to make it work for me.

Cheers,
Philippe.

Dear Philippe,

thanks a lot!! Without //-> it works fine. I had not tried it because when I have generated the Streamer code I found no difference when //-> was present or not, so I thought I had not to expect any change. Likely I did something wrong there.

I have also tried a case where the _meas pointer is null and it works. Is this what you expect? I have also restored the methods of Maesurement as pure virtual vithout any problem.

Instead the message:
Warning in TClass::TClass: no dictionary for class Measurement is available
is still there. When you say that you do not get it, have you loaded the library I have compiled or, firstly, have you compiled the libraries by yourself? Maybe the difference is in what the compiler does in my or your case. Indeed in about 5 out of ~20 classes I have created I got this message when I load the library.

Finally a question related to Streamer and I/O. I have a class of mine for which I have to add a few lines of code in the Streamer. Since I have not used “+” to generate the Streamer code I have modified, I was given an old style Streamer that I have to modify everytime I change the class. I’d like to have a confirmation that if, instead, I use “+” to generate the Streamer code to be modified, I can add my lines of code once forever, like in the box on top of page 174 of the user guide, and then I don’t have to care about it when I change the class. Also the box in page 183 seems to go into this directions while at the end of page 180 it is said that if one has his/hew own Streamer, it has to be modified everytime the class is changed, even if the examples in the boxes in this page look all related to the old Streamer style (with << and >> operators). Any suggestions?

                                Thanks in advance

                                        Andrea

[quote]I have also tried a case where the _meas pointer is null and it works. Is this what you expect? I have also restored the methods of Maesurement as pure virtual vithout any problem. [/quote]Yes this is the expected behavior without the -> :slight_smile:

[quote]When you say that you do not get it, have you loaded the library I have compiled[/quote]Well I deleted our shared library since I doubt that we have the same kernel/gcc/processor version as you do. But I followed exactly your instruction:They can be compiled by executing .x CompileAll.C in root. The libraries will be saved in ~venturia/public/root_test/51600 directory. In the following root session it will be enough to execute .x LoadAll.C to load all the libraries.

I have a class of mine for which I have to add a few lines of code in the Streamer.In this case you actually need to keep the ‘-’. You can chose to either keep your code as is and will need to hand code any on file schema changes, or you can follow the instruction on page 183 (aka use ReadBuffer/Writebuffer for new version and your old code for old versions) and you will benefit from the automatic schema evolution.

Cheers,
Philippe.

Dear Philippe,

last question, at least for this thread, still related to I/O, inheritance from TObject and so on.

Because I am in the same situation depicted in this thread:
root.cern.ch/phpBB2/viewtopic.php?t=5119

I had, time ago, decided to solve it in this way:

  1. I have a class C which inherits from class A and B. Because of that and because of what is explained in the thread I have linked, A and B cannot inherit both from TObject
  2. Since sometime I am interested in using a A or B object in a TCollection or save it in a file I have defined new classes Ao and Bo which, respectively, inherits from A+TObject and B+TObject.

In the tests I have done in these days I found no problem when a Ao object was written in a file and when I have inspected it with a TBrowser I found all the methods and the data members of the class A perfectly working. Is this expected? Is it safe to save in a file an object like this? Could it be a problem the fact that the class A has no ClassDef and ClassImp ?

                            Thanks, Andrea

Hi,

What you tried is safe and correct. ClassDef and ClassImp are not required for I/O. (However ClassDef is required for any class inheriting from TObject).

Cheers,
Philippe