Loading custom class into CINT: "declared but not defined"

Fair enough. (albeit in that context, the type is then just a compile-time concept that is un-used outside the compilation step (either via an invocation of the compiler or via interpretation (CINT/Cling)).

do you mean that within the trampoline function

Yes.

with a series of ProcessLine calls

No just a single one, containing a call to the (templated) function you are wrapping with the trampoline function.

It should not be much more complicated than what I wrote … unless you are a variable number of arguments.

i’ll put my post on my current progress on hold for the moment.
I want to call MyClass::Set, which is a templated member function.
More explicitly it has two overloads:

template <typename T>
bool Set(std::string name, T in);
template<typename T>
bool Set(std::string name, T* in);

Within the implementation of those functions, they each call other templated member functions, both general, from MyClass, and from other custom classes:

template <typename T> Set(std::string name, T in){
    if(typeid(T).name()==typeid(std::string).name()){
         // do something
    }
    // Twist is a templated member function of MyClass
    Twist(in);
    // myclassB is a member of MyClass of type MyClassB
    // which implements a templated function DoIt
    myclassB.DoIt(in);
    // ... and so on
}

I would be (pleasantly!) surprised if i could simply do:

static int G__ManualMyClass2_126_0_190(G__value* result7, G__CONST char* funcname, struct G__param* libp, int hash){
    /* This function is wrapping MyClass::Set(string name, T in) */
    TString invoke;
    // assuming e.g.
    // libp->para[0] is a pointer to the instance of MyClass whose Set function is being invoked, and
    // libp->para[1] is a pointer to a string representing the name argument, and
    // libp->para[2] is a pointer to a string representing the type of the val argument, and
    // libp->para[3] is a pointer to the val argument
    invoke.Form("((MyClass*)(%p))->Set( (*((std::string*)(%p))), (*((%s*)(%p))));", libp->para[0], libp->para[1], libp->para[2], libp->para[3]);
    gROOT->ProcessLine(invoke);
}

which is how i would interpret “just a single [ProcessLine call], containing a call to the (templated) function you are wrapping with the trampoline function” to mean…

right … I would too … but I got myself confused :frowning: … If that worked, then the whole thing wouldn’t be necessary in the first place :frowning:

Okay … new plan (ish).

What we need here is a set of templated classes and functions that are (mostly) only known to the interpreter and call non-template compiled functions (if needed).

The idea is that since we can not enumerate all the template functions instances (because of the “need to work on any type”) and we can not (really) go through the meta-classes (because the implementation is inside all calling function instances).

i.e. in the case you shown, you would need a ‘free standing’ version of Set which call ‘free standing’ version of Twist and DoIt all the way down to the new templated version.

right … I would too … but I got myself confused :frowning:

I can certainly understand that, it’s taken a lot for me to wrap my head around!
I’m not quite following the “free standing” version - you mean, outside of a class? or with a concrete type…? of…?

OK, my previously alluded to progress was a much more straight forward (and crude) method - if we really need a dictionary with specific #pragma lines for every type, then let’s build one on the fly. So i’ve implemented code that, on call to Set<Type>, will check the linkdef for a suitable #pragma ... Set<Type> line, and if not present, will add one and invoke system("make libMyClass_RootDict.so"); to recompile it! :sweat_smile:
We can then instantiate a new TInterpreter, load the dictionary, pass it our objects and call our Set function. It’s pretty roundabout, but it works… mostly.

At this point the problem has changed, so perhaps its worth moving to a new thread, but in short the problem is trying to build the dictionary with:

  #pragma link C++ function MyClass::Set(std::string, TVector3);

returns

rootcint -f MyClass_RootDict.cxx -c -p -I./include -fPIC `root-config --cflags` include/MyClass.h MyClass_Linkdef.hh
Error: class,struct,union or type TVector3 not defined  MyClass_Linkdef.hh:29:
Error: Ambiguous overload resolution (2,2) MyClass_Linkdef.hh:29:
Calling : MyClass::Set(string,void*&);
Match rank: file     line  signature
*        2 include/MyClass.h 164 bool MyClass::Set(string,void*,bool);
*        2 include/MyClass.h 138 bool MyClass::Set(string,void*);

if i #include "TVector3.h" at the top of my Linkdef file (uughhh… and how do i do that automatically?) i get a slew of errors about TVector3 not being defined in the Dict.cxx

MyClass_RootDict.cxx: In function 'int G__MyClass_RootDict_435_0_37(G__value*, const char*, G__param*, int)':
MyClass_RootDict.cxx:717:115: error: 'TVector3' was not declared in this scope
       G__letint(result7, 103, (long) ((MyClass*) G__getstructoffset())->Set(*((string*) G__int(libp->para[0])), *((TVector3*) G__int(libp->para[1]))));

Incidentally that last error looks a whole lot like that one-liner I said would pleasantly surprise me if it could work! :thinking:

humm … pass it as an argument to rootcint ?

rootcint -f MyClass_RootDict.cxx -c -p -I./include TVector3.h include/MyClass.h MyClass_Linkdef.hh

There is also:

#pragma extra_include "TVector3.h"

if (and only if) the header is only needed at dictionary compilation (as opposed to rootcint parsing).

Aha, that does it! A combination of

#include TVector3.h
#pragma extra_include TVector3.h

in the Linkdef file does the trick. (as does adding the header to the rootcint line, although I prefer to keep the process to just updating the Linkdef file). Thanks Philippe

My remark about doing it automatically was about deducing the required header from the type name. TVector3 is from TVector3.h, but is it always true that <Type> comes from <Type>.h? (Maybe, for ROOT types?) Or is there a way to work out which header file defines a class? ( I guess not… :confused: )

If calling TClass::GetDeclFileName on the TClass object represented the C++ class you interested in is not empty then it contains the information you need. (Albeit for class template instance, it would be the header file for the class template, you would also need the header for the template arguments).

Perfect! I think that’s (finally!) all I need. Thanks so much for your help Philippe.

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.