After an entire evening wasted trying to get IDL to interface with a
DLL I made, I thought I'd jot down the things that I wish I had known
at the beginning, in the hope that it will be useful to someone,
somewhere, sometime. I was using IDL 6.1 on Windows and Microsoft's
Visual C++ Express 2008 compiler; the same procedure will work on
other OSes, mutatis mutadis.
OK, here we go. Suppose we have two functions, written in C++, that we
wish to use from within IDL. We naively start with the following
header:
#ifndef NORMALS_H
#define NORMALS_H
namespace Normals
{
__declspec(dllexport) double InverseCumulative(double x);
__declspec(dllexport) double Cumulative(double x);
}
#endif
After building the DLL and moving it to IDL's working directory, we
type the following into IDL:
x = call_external("MyLib.dll","Cumulative",double(0.5),/all_value,/
d_value,value=[0])
It can't find the function! Why? Because the polymorphism and
overloading features of C++ are usually implemented by mangling your
nice function names into something that looks like a core dump.
Examine your DLL with a program like PEDUMP to figure out what
Normals::Cumulative() is now known as; I get ?
Cumulative@Normals@@YANN@Z . That line noise encodes precise
information about the argument types accepted by the function, believe
it or not. Armed with this information, we type the following into
IDL:
x = call_external("MyLib.dll","?
Cumulative@Normals@@YANN@Z",double(0.5),/all_value,/d_value,value=[0])
Alarmingly IDL crashes! Why? Well, because IDL assumes all functions
follow the prototype
RETURN_TYPE function_name (int argc, void *argv[])
so Normals::Cumulative() received a totally different argument list to
what it was expecting. So different that it went haywire and took down
IDL. To overcome this difficulty we have to provide "glue" functions
that take IDL's argument list and mangle it appropriately for our
functions. It is important to know how IDL's arg list works. The int
argc bit is easy enough; it tells you how many arguments you have been
passed. The array of pointers may not actually be an array of
pointers, however: if you set the /all_values keyword in
call_external, or set some elements of its values keyword to non-zero
numbers, then the appropriate arguments will be passed by value, and
the appropriate elements of argv[] will not be pointers but values!
Now before we were trying to pass a double by value. However a double
is (on this computer at least) bigger than a pointer, and so we can't
pass that argument by value, because doing so would corrupt some
following pointers. So we must pass the double by reference. We are
now ready to proceed.
Our glue functions have the prototypes
__declspec(dllexport) double __cdecl InverseCumulative_IDL(int argc,
void *argv[]);
__declspec(dllexport) double __cdecl Cumulative_IDL(int argc, void
*argv[]);
within the Normals namespace (the __cdecl bit would allow you to use
the more powerful LINKIMAGE IDL procedure instead of call_external).
Their implementation is
__declspec(dllexport) double __cdecl Cumulative_IDL(int argc, void
*argv[])
{
double *ptr = (double*) argv[0];
return Cumulative(*ptr);
}
__declspec(dllexport) double __cdecl InverseCumulative_IDL(int argc,
void *argv[])
{
double *ptr = (double*) argv[0];
return InverseCumulative(*ptr);
}
and their mangled names are (from PEDUMP)
?InverseCumulative_IDL@Normals@@YANHQAPAX@Z
?Cumulative_IDL@Normals@@YANHQAPAX@Z
Now call_external has an "auto_glue" faclity that can build the glue
functions for you; it is instructive to write them yourself, at least
at first, I feel. Also note that our glue functions are ferociously
fragile; passing anything other than a single double by reference will
cause problems. This isn't so much of a problem if you clearly
document the fact that the glue function must be called with precisely
correct arguments. Nonetheless we are now in a position to actually
use our DLL from within IDL. After building the DLL with the glue
functions and moving it to where IDL can see it, we can type e.g.
x = call_external("MyLib.dll","?
Cumulative_IDL@Normals@@YANHQAPAX@Z",double(0.3),/d_value,/
cdecl,values=[0])
and everything works.
|
|
0
|
|
|
|
Reply
|
mark.t.douglas (7)
|
5/29/2008 9:40:01 AM |
|
mark.t.douglas@gmail.com wrote:
> After an entire evening wasted trying to get IDL to interface with a
> DLL I made, I thought I'd jot down the things that I wish I had known
> at the beginning, in the hope that it will be useful to someone,
> somewhere, sometime. I was using IDL 6.1 on Windows and Microsoft's
> Visual C++ Express 2008 compiler; the same procedure will work on
> other OSes, mutatis mutadis.
>
> OK, here we go. Suppose we have two functions, written in C++, that we
> wish to use from within IDL. We naively start with the following
> header:
>
> #ifndef NORMALS_H
> #define NORMALS_H
>
> namespace Normals
> {
> __declspec(dllexport) double InverseCumulative(double x);
> __declspec(dllexport) double Cumulative(double x);
> }
>
> #endif
>
> After building the DLL and moving it to IDL's working directory, we
> type the following into IDL:
>
> x = call_external("MyLib.dll","Cumulative",double(0.5),/all_value,/
> d_value,value=[0])
>
> It can't find the function! Why? Because the polymorphism and
> overloading features of C++ are usually implemented by mangling your
> nice function names into something that looks like a core dump.
> Examine your DLL with a program like PEDUMP to figure out what
> Normals::Cumulative() is now known as; I get ?
> Cumulative@Normals@@YANN@Z . That line noise encodes precise
> information about the argument types accepted by the function, believe
> it or not. Armed with this information, we type the following into
> IDL:
>
> x = call_external("MyLib.dll","?
> Cumulative@Normals@@YANN@Z",double(0.5),/all_value,/d_value,value=[0])
Wouldn't it be simpler to disable the name mangling by declaring the
functions as 'extern "C"' ? You can still use any feature of C++ that
you want, inside the definition of the function. Of course, you can't
use any C++ features in the function interface of an 'extern "C"'
function that are not also supported by C, but CALL_EXTERNAL probably
couldn't handle those features anyway.
|
|
0
|
|
|
|
Reply
|
jameskuyper (5141)
|
5/29/2008 11:20:29 AM
|
|
I would recommend making your own DLM's in preference to using
CALL_EXTERNAL and /AUTO_GLUE. Making your own DLM is a bit more
tedious but gives you more control in the way that IDL variables are
type cast.
If your keen to get dirty with C++ then I would recommend having a
look at my examples of converting Boost::MultiArray objects to and
from IDL Variables.
I take advantage of template specialization so that you only need to
ever use two functions:
const IDL_TYPE i = idl_cast_in<IDL_TYPE>(argv[0]);
and
idl_cast_out(argv[1],i);
Source code available from
http://barnett.id.au/idl/
Robbie
|
|
0
|
|
|
|
Reply
|
retsil (153)
|
5/30/2008 12:46:33 AM
|
|
On 29 May, 12:20, James Kuyper <jameskuy...@verizon.net> wrote:
> mark.t.doug...@gmail.com wrote:
> > After an entire evening wasted trying to get IDL to interface with a
> > DLL I made, I thought I'd jot down the things that I wish I had known
> > at the beginning, in the hope that it will be useful to someone,
> > somewhere, sometime. I was using IDL 6.1 on Windows and Microsoft's
> > Visual C++ Express 2008 compiler; the same procedure will work on
> > other OSes, mutatis mutadis.
>
> > OK, here we go. Suppose we have two functions, written in C++, that we
> > wish to use from within IDL. We naively start with the following
> > header:
>
> > #ifndef NORMALS_H
> > #define NORMALS_H
>
> > namespace Normals
> > {
> > __declspec(dllexport) double InverseCumulative(double x);
> > __declspec(dllexport) double Cumulative(double x);
> > }
>
> > #endif
>
> > After building the DLL and moving it to IDL's working directory, we
> > type the following into IDL:
>
> > x = call_external("MyLib.dll","Cumulative",double(0.5),/all_value,/
> > d_value,value=[0])
>
> > It can't find the function! Why? Because the polymorphism and
> > overloading features of C++ are usually implemented by mangling your
> > nice function names into something that looks like a core dump.
> > Examine your DLL with a program like PEDUMP to figure out what
> > Normals::Cumulative() is now known as; I get ?
> > Cumulative@Normals@@YANN@Z . That line noise encodes precise
> > information about the argument types accepted by the function, believe
> > it or not. Armed with this information, we type the following into
> > IDL:
>
> > x = call_external("MyLib.dll","?
> > Cumulative@Normals@@YANN@Z",double(0.5),/all_value,/d_value,value=[0])
>
> Wouldn't it be simpler to disable the name mangling by declaring the
> functions as 'extern "C"' ? You can still use any feature of C++ that
> you want, inside the definition of the function. Of course, you can't
> use any C++ features in the function interface of an 'extern "C"'
> function that are not also supported by C, but CALL_EXTERNAL probably
> couldn't handle those features anyway.
That would have worked fine and made life simpler for the two
functions I outlined here, certainly. However there are other things
in the DLL which are "proper" C++ so I elected not to use extern "C"
for the sake of consistency, as the DLL was designed as a C++ library
in the first instance. I probably should have mentioned this in the
original post!
|
|
0
|
|
|
|
Reply
|
mark.t.douglas (7)
|
5/30/2008 10:26:42 AM
|
|
On 30 May, 01:46, Robbie <ret...@iinet.net.au> wrote:
> I would recommend making your own DLM's in preference to using
> CALL_EXTERNAL and /AUTO_GLUE. Making your own DLM is a bit more
> tedious but gives you more control in the way that IDL variables are
> type cast.
>
> If your keen to get dirty with C++ then I would recommend having a
> look at my examples of converting Boost::MultiArray objects to and
> from IDL Variables.
>
> I take advantage of template specialization so that you only need to
> ever use two functions:
>
> const IDL_TYPE i = idl_cast_in<IDL_TYPE>(argv[0]);
>
> and
>
> idl_cast_out(argv[1],i);
>
> Source code available from
>
> http://barnett.id.au/idl/
>
> Robbie
Making a DLM is overkill for this specific DLL - all I really want to
do is plot f(x) for f in the DLL, and I can wrap the call_externals
using stub functions to give a modicum of type safety:
function f,x
if ~ n_elements(x) eq 1 then return,0.d0
return,call_external('my.dll','function_name',double(x),/
d_value,values=[0])
end
That boost stuff, however, is very neat: thanks for the pointer :)
|
|
0
|
|
|
|
Reply
|
mark.t.douglas (7)
|
5/30/2008 10:45:05 AM
|
|
mark.t.douglas@gmail.com wrote:
> On 29 May, 12:20, James Kuyper <jameskuy...@verizon.net> wrote:
....
>> Wouldn't it be simpler to disable the name mangling by declaring the
>> functions as 'extern "C"' ? You can still use any feature of C++ that
>> you want, inside the definition of the function. Of course, you can't
>> use any C++ features in the function interface of an 'extern "C"'
>> function that are not also supported by C, but CALL_EXTERNAL probably
>> couldn't handle those features anyway.
>
> That would have worked fine and made life simpler for the two
> functions I outlined here, certainly. However there are other things
> in the DLL which are "proper" C++ so I elected not to use extern "C"
> for the sake of consistency, as the DLL was designed as a C++ library
> in the first instance. I probably should have mentioned this in the
> original post!
I think that hard-coding the name-mangling scheme of one particular
implementation of C++ in your IDL code is a bad idea. It makes your IDL
code harder to read, and it might have to be changed if you use a
different C++ compiler, or even a different version of the same C++
compiler. Declaring the function 'extern "C"' is a lot cleaner and more
portable.
Don't let your concerns about "consistency" make your job harder than it
needs to be. There's nothing wrong with using C++-specific features in
the body of a C++ function with "C" language linkage. This is quite
normal, because such functions usually serve as the interface between
C++ code and non-C++ code. Nor is there any problem with having
functions with "C" language linkage in the same translation unit as
functions with "C++" language linkage.
|
|
0
|
|
|
|
Reply
|
jameskuyper (5141)
|
5/30/2008 11:12:39 AM
|
|
|
5 Replies
32 Views
(page loaded in 0.107 seconds)
|