I have a ROOT-unaware class that I use for loading and applying configurations (let’s call it Configurable). Now, I’d like to make ROOT I/O capable classes that can use this base class. I plan on having a common base class for these (let’s call it TConfigurable) that will take care of interactions between more derived classes and Configurable.
I’m using the cmake command reflex_generate_dictionary to create the dictionary files.
What I have at the moment is something like
// TConfigurable.h
#include "Configurable.h"
class TConfigurable : public TNamed, Configurable {
public:
TConfigurable() {}
ClassDef(TConfigurable, 1)
};
//TConfigurable.cxx
ClassImp(TConfigurable)
At the moment when I call TConfigurable::Write I, unsurprisingly get an error message like
TStreamerInfo::Build:0: RuntimeWarning: TConfigurable: base class Configurable has no streamer or dictionary it will not be saved
and a whole host of issues if I then try and use the file…
Configurable has various member variables, none of which I want written out to a file. I’m less fussed over whether or not they are usable in an interactive session (I’d probably lean on the side of not wanting them to appear there either). I’d like some way of telling the dictionary generation procedure to ignore the Configurable base class, so that as far as ROOT is concerned, TConfigurable is all it ever sees.
Is there a good way to do this?
Cheers,
Jon
ROOT Version: 6.14/04 Platform: Not Provided Compiler: g++ (GCC) 6.2.0
if I understand correctly, you would like to have in memory a representation of your class, say class A, while on disk, you’d like to see a different representation, say class B (" I’d like some way of telling the dictionary generation procedure to ignore the Configurable base class, so that as far as ROOT is concerned, TConfigurable is all it ever sees.")
Kind of… Basically, the data held in Configurable is all transient and calculated either at compile-time or at run-time once the configuration file has been loaded. I could make this class ROOT aware and then prevent those data members from being added to the streamer but that seems somewhat inelegant - for example, it prevents me from re-using the Configurable class in a project which does not use ROOT.
I could make this class ROOT aware and then prevent those data members from being added to the streamer but that seems somewhat inelegant - for example, it prevents me from re-using the Configurable class in a project which does not use ROOT.
I do not see why.
Irrespective of that, perhaps you may rething your design. Why don’t you write on disk a struct per configurable, say, TProtoConfigurable and initialise your TConfigurable with it? You would then completely decouple the IO layer from the in memory layer.
when using genreflex. (And if you need to mark only some of the member transient you can do that we a innocuous looking comment for rootcling and some xml statements for genreflex).
And remember that you can do I/O on any class, they do not need to inherit from TObject nor TNamed (unless you need that for some other reasons) and do not need a ClassDef (albeit the I/O is marginally faster with a ClassDef).
That works almost perfectly! I can now create and write the objects fine.
However, there’s still an error message when I read one of my objects from file
Error in <TBufferFile::CheckByteCount>: object of class RDFPlotting::ConfigurableObject read too few bytes: 2 instead of 6
(RDFPlotting::ConfigurableObject is what I was referring to as Configurable before).
The creation of ROOT dictionaries etc is still somewhat alien to me and I’ve not found much more documentation than in the user’s guide (Chapter: Addinga Class).
I should also add that (as far as can tell with the very limited tests that I’ve run so far) this error message doesn’t actually matter too much. The data from TConfigurable is still usable.
Also @Danilo, I had thought that in order to prevent Configurable from being written out I might need to use ClassDef in its header, thankfully this isn’t the case . I’m not sure what you mean by your second comment though, so long as I inherit from Configurable I need to have some way to tell ROOT not to try and stream it right?
Note that IF a class inherits from TObject (directly or indirectly), it must have a ClassDef macro to be stored properly. (So in your original example Configurable does not need a ClassDef but TConfigurable does.
It turns out that ‘class version 0’ is not available for classes with ClassDef and we have not yet implemented an xml tag to mark the class as defaulting to transient member. So instead of setting the class to 0, you ‘just’ need to enumerate all the data members like: