calling c++ virtual functions

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
5 messages Options
Reply | Threaded
Open this post in threaded view
|

calling c++ virtual functions

Martin Rubi
Hi everyone.

I'm having trouble trying to call a c++ virtual function on a dll from
Dolphin, and can't see what am i doing wrong.
The function is beeing called, but what should be the first parameter, it's
a 32bit constant value (i think it's not *this* pointer, though).
i've tried changing the calling convention in both Dolphin and the dll, but
didnt work ... any tips ?

The example is pretty simple.

this is my dolphin wrapper:

ExternalStructure subclass: #IExperimenting

IExperimenting>>inc: i
    "virtual int Inc(int i)"

    <virtual cdecl: sdword 2 sdword>
    ^self invalidCall

and i'm obtaining an instance with

ExperimentingLibrary>>makeInstanceOfIExperimenting
    "__declspec(dllexport) IExperimenting* MakeInstanceOfIExperimenting();"

    <cdecl: IExperimenting*
'?MakeInstanceOfIExperimenting@@YAPAVIExperimenting@@XZ'>
    ^self invalidCall


and this is my c++ class in the dll:

__declspec(dllexport) IExperimenting* MakeInstanceOfIExperimenting();
class IExperimenting
{
public:
 IExperimenting();
 virtual ~IExperimenting();
 virtual int Inc(int i);
};

thanks in advance
Martin


Reply | Threaded
Open this post in threaded view
|

Re: calling c++ virtual functions

Schwab,Wilhelm K
Martin,

I won't try to fumble for an answer to your problem.  Dolphin can call
some types of vtable functions (COM interfaces come to mind), though
I've never done it directly.

Since you seem to have control over the DLL, can you create some extern
"C" functions for Dolphin to call?  Then it becomes easy (by
comparison).  Note that you can use all the C++ you want inside the
C-callable function; you simply cannot pass C++ specific types
(references, etc.) to it, but that won't bother you, because Dolphin
does not much deal in those.

Have a good one,

Bill

--
Wilhelm K. Schwab, Ph.D.
[hidden email]


Reply | Threaded
Open this post in threaded view
|

Re: calling c++ virtual functions

Chris Uppal-3
In reply to this post by Martin Rubi
Martin wrote:

> I'm having trouble trying to call a c++ virtual function on a dll from
> Dolphin, and can't see what am i doing wrong.

> __declspec(dllexport) IExperimenting* MakeInstanceOfIExperimenting();
> class IExperimenting
> {
> public:
>  IExperimenting();
>  virtual ~IExperimenting();
>  virtual int Inc(int i);
> };

I think the problem is that MSVC++ defaults to the 'thiscall' argument passing
convention for virtual methods (which passes 'this' in a register, and then
handles the rest of the arguments the same as __stdcall).  Dolphin, according
to the docs, cannot (yet) handle 'thiscall' -- you can only use stdcall or
cdecl -- so the C++ program sees garbage where it expects to find the address
of  'this' and 'this' where it expects to find the integer argument.

If you change your example class to:
    ...
     virtual int __stdcall Inc(int i);
    ...
or:
    ...
     virtual int __cdecl Inc(int i);
    ...
and set the definition of IExperimenting>>inc: to:
    <virtual stdcall: sdword 2 sdword>
or:
    <virtual cdecl: sdword 2 sdword>
respectively, then you should be OK.


BTW, apparently you can't use __cdecl for a virtual destuctor; I don't know
why.


BTW2.  (I know this is only an example, but just in case) don't forget to
include a DestroyInstance() function to parallel MakeInstance().


BTW3.  If you declare your MakeInstance()/DestroyInstance() functions inside an
extern "C" { ... } block, then you won't suffer from the name mangling.  If
they are declared __cdecl then there won't be any mangling at all, if they are
__stdcall then there will be very little.  Alternatively/additionally you could
use a .DEF file to control the name exported by the DLL (which is what I do).

    -- chris


Reply | Threaded
Open this post in threaded view
|

Re: calling c++ virtual functions

Martin Rubi
Bill, Chris

> so the C++ program sees garbage where it expects to find the address
> of  'this' and 'this' where it expects to find the integer argument.

Indeed. After you pointed this out, i discovered that my actual parameter
was pushed as if it was the second one.

> If you change your example class to:
>     ...
>      virtual int __stdcall Inc(int i);
>     ...
> respectively, then you should be OK.

I am, works like a charme now.

> BTW2.  (I know this is only an example, but just in case) don't forget to
> include a DestroyInstance() function to parallel MakeInstance().

Thanks for the tip. I've made instances of IExperimenting finalizable,
redefined IExperimenting>>needsFree to answer true and call
DestroyInstance() from IExperimenting>>basicFree. This calls c++ destructor
when IExperimenting instances are garbage collected ... it's  working, but
could be something wrong in anything of what i've done ?

> Alternatively/additionally you could
> use a .DEF file to control the name exported by the DLL (which is what I
do).

Thanks for the tip2. I've followed this advice too.

Thanks again.
Best regards.
Martin.

PD: I think the tutorial on vitual calls from
http://www.object-arts.com/Lib/EducationCentre4/htm/virtualcalls.c...olecominterface..htm
may have a little error.
Where it says "<virtual cdecl: lpstr 1>", should it say "<virtual cdecl:
lpstr 2>" ? Because in the example, the first virtual function is the
destructor, and the vt index seems to be 1-based.
I think the example is clear enough, anyway.


Reply | Threaded
Open this post in threaded view
|

Re: calling c++ virtual functions

Chris Uppal-3
Martin wrote:

> Thanks for the tip. I've made instances of IExperimenting finalizable,
> redefined IExperimenting>>needsFree to answer true and call
> DestroyInstance() from IExperimenting>>basicFree. This calls c++
> destructor
> when IExperimenting instances are garbage collected ... it's  working, but
> could be something wrong in anything of what i've done ?

I can't think of anything special.

You'd need to be a bit careful about ensuring that your finaliser didn't do
anything (or wasn't called at all) for instances that had already been released
(including instances that persist over an image save/restore).  You also want
to make sure that you don't release instances that haven't been created by
#MakeInstance -- but that applies whether or not you use finalisation.  You
also need to ensure that the instances that are created implicitly by the
MakeInstance() wrapper method do actually become finalisable.  All that's
straightforward.

One other point to check is that your application (that uses these objects) is
such that the finaliser thread will actually get a chance to run reasonably
often.  My guess is that that won't be a problem for you, but it can be
sometimes.  (E.g. if a program is in a tight loop doing Database stuff, then
the finaliser may not get a chance to clean up old queries, etc -- which can
impact the system significantly.)


> PD: I think the tutorial on vitual calls from
>
http://www.object-arts.com/Lib/EducationCentre4/htm/virtualcalls.c...olecominterface..htm
> may have a little error.
> Where it says "<virtual cdecl: lpstr 1>", should it say "<virtual cdecl:
> lpstr 2>" ?

It looks that way to me too.

    -- chris