Template and share object

Dear rooters,

I recently wrote a C++ class that was not a priori dedicated to root.
This class is suppose to read from a stream and place the result in a STL vector.
Since this STL vector can be of any “simple” kind (int, float, …, string) I decided that the method of my class that will actually do this job will be a template.

Here a shorten version of the class definition (few more methods are in fact defined see below) :

[code]#include
#include

class ConfigReader{

public:
ConfigReader();
~ConfigReader();
template int Read(string, std::vector &) ;

};
[/code]

Now, I decided to use it within root (actually within a TTree class that is indirectly call from CINT via a macro). The compilation went fine and the creation of the *.so too (I believe I need a *.so… but this can be discussed later).

I load the generated libConfigReader.so – that contains the ConfigReader class – in CINT

 gSystem->Load("libConfigReader.so");      

Then I check with the auto-completion that ConfigReader is found : Ok.

root 0> ConfigReader:: ~ConfigReader ConfigReader AddFile FindAllToken FindTokenInFile Dump PrintListofFiles PrintListofToken GetListofFiles ConfigReader operator=

Concerning the methods all are found except the one based on a template, including Read

I concluded that I may need to instantiate Read with the possible types. I found many discussions on this forum about instantiating a class with #pragma but nothing about methods. And I must admit that I am a little bit lost with all the #pragma stuff.

Is there a (if possibly simple !) solution to this problem ?

Thank you in advance
Best
Julien

Hi,

You need to explicitly request the dictionary each instance you need:#ifdef __MAKECINT__ #pragma link C++ function ConfigReader::Read<int>; #endif

Cheers,
Philippe.

Dear Phillipe,

Thank you for the help, now I have the right #pragma syntax.
This compile well and I somehow could see the right methods in CINT, but: my code (means the call to the Read method of the ConfigReader) crashed.

Before getting into the debugging of the core of my code (which I suspect not being the main problem here), I would like to clearly understand the process in CINT.

Here are the step I have done and the results. Would you mind commenting?
[ol]
[li] I remove all loading of *.so (including the libraries that call ConfigReader) and I loaded only ConfigReader

Note that ConfigReader does not inheritate from any other class, and consequently not from TObject, and I did not generate the Dictionary. Thus I believe it is logical that I can not see this class at all in CINT.[/li]
[li] The generation of the Dictionary fails as is: normal I did not do a ClassDef (but I can’t since ConfigReader does not heritate from TObject), right ?[/li]
[li] Now I don’t change anything but only load a shared library that calls ConfigReader, and this shared library libMayaData.so contains
a class that inheritates from TObject, with all the right procedures (ClassDef, ClassImp) and Dictionary generation, tested independently before.

Now CINT can see ConfigReader, but of course a command like

fails because the actual libConfigReader.so was not loaded.[/li]
[li] Now if in addition I load libConfigReader.so:

a command like

fails again and I suspect because there is no Dictionary for ConfigReader, right ?[/li]
[li] Finally if I call the Method in the class MayaData that calls the Read method in ConfigReader (see details in the previous post) I have the message:

Any clue ?[/li][/ol]
Best
Julien

Hi Julien,

[quote=“gib”]I remove all loading of *.so (including the libraries that call ConfigReader) and I loaded only ConfigReader

Note that ConfigReader does not inheritate from any other class, and consequently not from TObject, and I did not generate the Dictionary. Thus I believe it is logical that I can not see this class at all in CINT.[/quote]
Correct.

[quote=“gib”]The generation of the Dictionary fails as is: normal I did not do a ClassDef (but I can’t since ConfigReader does not heritate from TObject), right ?
[/quote]
rootcint is able to produce dictionaries for classes without ClassDef; it should not fail. You must use ClassDef if your class derives from TObject, otherwise it’s optional, giving e.g. slightly better I/O performance.

[quote=“gib”]Now I don’t change anything but only load a shared library that calls ConfigReader, and this shared library libMayaData.so contains
a class that inheritates from TObject, with all the right procedures (ClassDef, ClassImp) and Dictionary generation, tested independently before.

Now CINT can see ConfigReader, but of course a command like

fails because the actual libConfigReader.so was not loaded.
[/quote]
If your library libMayaData.so contains calls to ConfigReader it must depend on ./libConfigReader.so unless ConfigReader does not contain virtual functions (a vtable, to be more precise) and all the functions that are called are inlined. If libMayaData.so depends on ./libConfigReader.so you cannot load libMayaData.so without loading ./libConfigReader.so. The fact that the loading of libMayaData.so succeeded means that also ./libConfigReader.so got loaded; maybe because the dependency is noted at linktime via explicit linking: when building ./libConfigReader.so you tell ld that it depends on libMayaData.so.

CINT “sees” ConfigReader in the sense that the dictionary in libMayaData.so probably tells it that ConfigReader is a class - depending on what your Linkdef.h contains (does it contain a pragma link C++ class ConfigReader?) it might know nothing else about ConfigReader.

[quote=“gib”]Now if in addition I load libConfigReader.so:

a command like

fails again and I suspect because there is no Dictionary for ConfigReader, right ?[/quote]
As I explained before this should be a no-op; I expect libConfigReader.so to be loaded already before.

[quote=“gib”]Finally if I call the Method in the class MayaData that calls the Read method in ConfigReader (see details in the previous post) I have the message:

says that the missing function is

The part I don’t understand is why this error happens at call time; it should happen at load time of the library.

Is int ConfigReader::Read(…) inlined?

Cheers, Axel.

Hi Axel,

Thanks for the comments. I am starting to understand.
So to quickly comment back :

Yes it’s ok.

No. It does not contain any.

It should be the contrary no ?

This happen if I put the #pragma in ConfigReader.h. If I put it in LinkDef.h and use it in rootcint :

rootcint -f TMayaDataDict.cxx -c TMayaData.h ConfigReader.h LinkDef.h
then the error occurs at load time, yes.

No.

Best
Julien

Hi Alex,

I am back on this post after trying again. Nothing works.
I also realized I forgot to answer one or two of your questions :

Yes. I always load ./libConfigReader.so before ./libConfigReader.so

Yes. And I trying not too, it keeps crashing.

I tried (in LinkDef.h) and nothing changed.

Do you mean that I’d better make it inlined ?

Best
Julien

Hi,

if you make ConfigReader::Read(…) inlined you won’t have to create the necessary tempate instantiations by hand. If you have no idea what the heck I’m talking about then yes, please inline it. And maybe read about C++ and template instantiation :wink:

Cheers, Axel.

Axel,

Indeed I did not know much about inline and its relation to template instantiations. This is still quite abstract for me since I never but my heads in it before. For what I understand of your suggestions and what I could read elsewhere, technically speaking all I have to do is in the ConfigReader class definition :

 template<typename T> inline int Read(string, std::vector<T> &) ;

and when I call this function :

ConfigReader *cr = new ConfigReader();
  vector<float> test(10); 
  cr->Read<float>("label",test);

Then during the (pre)compilation all my Ts will be replaced by float and that’s it ?
So I tried and root still does not want it:

Sorry to insist but I indeed have some trouble getting this sorted.
Would you mind giving me a small example if I am wrong ?

Cheers
Julien

Dear Axel,

I have tried many ways of having: either root seeing the template or having them instantiated properly.

I finally did some first order hacking that works by adding a private method in the class ConfigReader :

[code]void ConfigReader::ForceInstantiation(){
std::vector iv1 ;
std::vector<std::vector > iv2 ;
Read("",iv1,-1);
Read("",iv2);

//
std::vector fv1 ;
std::vector<std::vector > fv2 ;
Read("",fv1,-1);
Read("",fv2);

//
std::vector dv1 ;
std::vector<std::vector > dv2 ;
Read("",dv1,-1);
Read("",dv2);

} [/code]

that instantiates the Read method. Note that there is no need to actually call it. Note also that no dictionary has been generate for ConfigReader but the dictionary for the classes that call ConfigReader has been generate with rootcint including the header for ConfigReader. Same for the *.so

I still think that the proper calls to #pragma must do it too. If I find it I will post it here.

Best
Julien