www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - [SPEC/DMD] Bug (?): extern( C ) function pointers (D1 and D2)

reply David Nadlinger <see klickverbot.at> writes:
As easily verified e.g. by compiling
---
extern(C) void foo() {}
pragma( msg, typeof( &foo ) );
---,
function pointers types include the linkage type (the code above prints 
»void C function()«).

However, there is no way to specify the linkage type e.g. in the 
signature of a function accepting a delegate, i.e.:
---
extern(C) void foo() {}
void bar( void function() func ) {} // How to correctly specify the full 
parameter type here?

void main() {
    bar( &foo );
}
---

This problem is currently somewhat hidden by the fact that DMD simply 
ignores the linkage when doing type checking. But e.g. LDC does strict 
type checking (probably because it needs to reflect the pointer types in 
the LLVM IR, but that's just guessing) today, and it will hopefully be 
added to DMD at some point.

Note: Simply adding »extern( C )« to the type specification does not 
work, but: At the first glance, I couldn't even find any section in the 
language spec for both D1 and D2 mentioning linkage annotations for 
function pointers. Has this part simply not been spec'd yet?

In any case, this currently breaks passing of function pointers to C 
functions resp. their SWIG-generated wrappers with LDC, as I could find 
no easy way to work around it – creating an alias for the function 
pointer type before might work, as extern( C ) seems to be accepted 
there, but this is a major annoyance if you are automatically generating 
code.


David
Dec 30 2010
next sibling parent reply Andrej Mitrovic <andrej.mitrovich gmail.com> writes:
Try this:

extern(C) void foo() {}
extern(C)
{
    alias void function() FooFunc;
    // alias typeof(foo) FooFunc;  // or try this one if it works
}

void bar(FooFunc func) { }

void main() {
  bar( &foo );
}

I've had a nasty bug where I forgot to put extern(C) on a function
type like that. I spend the entire day trying to debug the damn thing,
because for some reason calling C code worked, but C code trying to
call a delegate which I've passed failed with arbitrary types.
Basically, it was a communication mismatch with the calling
convention.



On 12/30/10, David Nadlinger <see klickverbot.at> wrote:
 As easily verified e.g. by compiling
 ---
 extern(C) void foo() {}
 pragma( msg, typeof( &foo ) );
 ---,
 function pointers types include the linkage type (the code above prints
 =BBvoid C function()=AB).

 However, there is no way to specify the linkage type e.g. in the
 signature of a function accepting a delegate, i.e.:
 ---
 extern(C) void foo() {}
 void bar( void function() func ) {} // How to correctly specify the full
 parameter type here?

 void main() {
     bar( &foo );
 }
 ---

 This problem is currently somewhat hidden by the fact that DMD simply
 ignores the linkage when doing type checking. But e.g. LDC does strict
 type checking (probably because it needs to reflect the pointer types in
 the LLVM IR, but that's just guessing) today, and it will hopefully be
 added to DMD at some point.

 Note: Simply adding =BBextern( C )=AB to the type specification does not
 work, but: At the first glance, I couldn't even find any section in the
 language spec for both D1 and D2 mentioning linkage annotations for
 function pointers. Has this part simply not been spec'd yet?

 In any case, this currently breaks passing of function pointers to C
 functions resp. their SWIG-generated wrappers with LDC, as I could find
 no easy way to work around it =96 creating an alias for the function
 pointer type before might work, as extern( C ) seems to be accepted
 there, but this is a major annoyance if you are automatically generating
 code.


 David

Dec 30 2010
parent reply David Nadlinger <see klickverbot.at> writes:
On 12/30/10 10:44 PM, Andrej Mitrovic wrote:
 Try this: [snip]

Thanks for your answer, but as I mentioned in my original post, I don't see why creating an alias for the function pointer type should be necessary – it is quite annoying when you are automatically creating code, you'd need a special case for function pointers then. David
Dec 30 2010
parent David Nadlinger <see klickverbot.at> writes:
On 12/30/10 11:29 PM, Andrej Mitrovic wrote:
 Try filing a feature request on bugzilla.

I don't quite see how this would be a feature request – »I'd like to be able to actually express the implicitly used types in D code«? The reason I brought this here instead of directly filing a specification bug to Bugzilla is that 1) I am not sure if I had missed a related part of the specification and 2) since resolving this issue will probably require a change/addition to the language specification, I think it deserves some extended discussion. So, to restate the question: Currently, the spec for both D1 and D2 apparently does not allow specifying the calling convention of function pointer types directly. This issue probably hasn't popped up so far just becasue DMD currently does not take the calling convention into account when type-checking function pointers, but this might not be the case for other compilers or future versions of DMD. How should we resolve this issue? David
Dec 30 2010
prev sibling next sibling parent Andrej Mitrovic <andrej.mitrovich gmail.com> writes:
I'm pretty sure this will work as well, because I have this defined in
a module where I'm passing a function pointer to C:
alias extern(C) size_t function(/*params*/) CallbackType;

Here's a snippet of how I used the OS to get a pointer to a C
function, which I've used to pass my own callback, and get a struct
back (or a pointer to one anyway):

alias extern(C) size_t function(AEffect* effect, int opcode, int
index, size_t value, void* ptr, float opt) audioMasterCallback;

extern(C) size_t HostCallback(AEffect* effect, int opcode, int index,
size_t value, void* ptr, float opt)
{
    // code..
}

extern(C)
{
    alias AEffect* function(audioMasterCallback) EntryProc;
}

EntryProc getMainEntry()
{
    auto MainEntry =3D cast(EntryProc)GetProcAddress(VSTModule,
        "VSTPluginMain");

    // Older VSTs use "main" entry point
    if (!MainEntry)
        MainEntry =3D cast(EntryProc)GetProcAddress(VSTModule, "main");

    if (MainEntry is null)
        throw new Exception("Entry function 'VSTPluginMain' or 'main'
not found.");

    return MainEntry;
}

AEffect* getEffect()
{
    EntryProc VSTMain =3D getMainEntry();
    auto effect =3D VSTMain(&HostCallback);  // Passing the function addres=
s to C,
                                           // and getting back a struct

    if (effect is null)
        throw new Exception("VST failed to return the effect struct.");

    return effect;
}

It was code to load DLLs with a defined standard, all DLLs that
conform to the spec need to have an entry point function with a
specific name, and then I can pass a callback and in turn I get back
this nice structure where I can figure things out the functionality of
that "VST" plugin.


On 12/30/10, Andrej Mitrovic <andrej.mitrovich gmail.com> wrote:
 Try this:

 extern(C) void foo() {}
 extern(C)
 {
     alias void function() FooFunc;
     // alias typeof(foo) FooFunc;  // or try this one if it works
 }

 void bar(FooFunc func) { }

 void main() {
   bar( &foo );
 }

 I've had a nasty bug where I forgot to put extern(C) on a function
 type like that. I spend the entire day trying to debug the damn thing,
 because for some reason calling C code worked, but C code trying to
 call a delegate which I've passed failed with arbitrary types.
 Basically, it was a communication mismatch with the calling
 convention.



 On 12/30/10, David Nadlinger <see klickverbot.at> wrote:
 As easily verified e.g. by compiling
 ---
 extern(C) void foo() {}
 pragma( msg, typeof( &foo ) );
 ---,
 function pointers types include the linkage type (the code above prints
 =BBvoid C function()=AB).

 However, there is no way to specify the linkage type e.g. in the
 signature of a function accepting a delegate, i.e.:
 ---
 extern(C) void foo() {}
 void bar( void function() func ) {} // How to correctly specify the full
 parameter type here?

 void main() {
     bar( &foo );
 }
 ---

 This problem is currently somewhat hidden by the fact that DMD simply
 ignores the linkage when doing type checking. But e.g. LDC does strict
 type checking (probably because it needs to reflect the pointer types in
 the LLVM IR, but that's just guessing) today, and it will hopefully be
 added to DMD at some point.

 Note: Simply adding =BBextern( C )=AB to the type specification does not
 work, but: At the first glance, I couldn't even find any section in the
 language spec for both D1 and D2 mentioning linkage annotations for
 function pointers. Has this part simply not been spec'd yet?

 In any case, this currently breaks passing of function pointers to C
 functions resp. their SWIG-generated wrappers with LDC, as I could find
 no easy way to work around it =96 creating an alias for the function
 pointer type before might work, as extern( C ) seems to be accepted
 there, but this is a major annoyance if you are automatically generating
 code.


 David


Dec 30 2010
prev sibling parent Andrej Mitrovic <andrej.mitrovich gmail.com> writes:
Yes, I agree. This will not work either but I'd like it to:

extern(C) void foo() { }
void bar(typeof(foo) func) { }

 Error: variable main.bar.func cannot be declared to be a function

Try filing a feature request on bugzilla. On 12/30/10, David Nadlinger <see klickverbot.at> wrote:
 On 12/30/10 10:44 PM, Andrej Mitrovic wrote:
 Try this: [snip]

Thanks for your answer, but as I mentioned in my original post, I don't see why creating an alias for the function pointer type should be necessary =96 it is quite annoying when you are automatically creating code, you'd need a special case for function pointers then. David

Dec 30 2010