Thread overview | |||||||||
---|---|---|---|---|---|---|---|---|---|
|
August 08, 2017 Calling C++ "void foo(Klass&)" | ||||
---|---|---|---|---|
| ||||
Hi all, Currently, it is not possible to call the C++ function "void foo(Klass&)" when Klass is an extern(C++) _class_ on the D side. You have to declare Klass as a D _struct_, otherwise there is no way to get the correct mangling. When Klass has virtual functions, you're hosed. For more context (involving "const"), see: https://forum.dlang.org/post/tvohflgtaxlynpzedqky@forum.dlang.org Is this problem on anybody's radar? What are the ideas to resolve this issue, or are we content never to solve it? At the very least, we should add information about this to the C++ interfacing page, https://dlang.org/spec/cpp_interface.html . - Johan |
August 08, 2017 Re: Calling C++ "void foo(Klass&)" | ||||
---|---|---|---|---|
| ||||
Posted in reply to Johan Engelen | On 2017-08-08 20:51, Johan Engelen wrote: > Hi all, > Currently, it is not possible to call the C++ function "void > foo(Klass&)" when Klass is an extern(C++) _class_ on the D side. You > have to declare Klass as a D _struct_, otherwise there is no way to get > the correct mangling. When Klass has virtual functions, you're hosed. > > For more context (involving "const"), see: > https://forum.dlang.org/post/tvohflgtaxlynpzedqky@forum.dlang.org > > Is this problem on anybody's radar? > What are the ideas to resolve this issue, or are we content never to > solve it? One way to do it, that might be a bit confusing, is to force the declaration of the function to explicitly specify a pointer or a reference. Currently it looks like it's an implicit pointer. extern (C++) class Klass {} void foo(Klass*); // ok void foo(ref Klass); // ok void foo(Klass); // error Of course, there's always pragma(mangle) as well. -- /Jacob Carlborg |
August 09, 2017 Re: Calling C++ "void foo(Klass&)" | ||||
---|---|---|---|---|
| ||||
Posted in reply to Jacob Carlborg | On 8/8/2017 2:04 PM, Jacob Carlborg wrote:
> On 2017-08-08 20:51, Johan Engelen wrote:
>> Hi all,
>> Currently, it is not possible to call the C++ function "void
>> foo(Klass&)" when Klass is an extern(C++) _class_ on the D side. You
>> have to declare Klass as a D _struct_, otherwise there is no way to get
>> the correct mangling. When Klass has virtual functions, you're hosed.
>>
>> For more context (involving "const"), see:
>> https://forum.dlang.org/post/tvohflgtaxlynpzedqky@forum.dlang.org
>>
>> Is this problem on anybody's radar?
>> What are the ideas to resolve this issue, or are we content never to
>> solve it?
>
> One way to do it, that might be a bit confusing, is to force the declaration of the function to explicitly specify a pointer or a reference. Currently it looks like it's an implicit pointer.
>
> extern (C++) class Klass {}
> void foo(Klass*); // ok
> void foo(ref Klass); // ok
> void foo(Klass); // error
>
> Of course, there's always pragma(mangle) as well.
As Jacob hints at, the C++ name mangling for Klass* and Klass& is different. D classes are implicitly by reference. So which mangling to choose? D chose the Klass*.
The best way to deal with that is, on the C++ side, add:
void foo(Klass* k) { foo(*k); }
On a more philosophical note, D makes a hard distinction between struct and class - struct is a value type, class is a reference type. In C++ the characteristics of each can be mixed and matched, not so in D. Interfacing D to such chimera types is going to need a bit of flexibility on the C++ side, such as writing a trampoline like the above.
|
August 10, 2017 Re: Calling C++ "void foo(Klass&)" | ||||
---|---|---|---|---|
| ||||
Posted in reply to Jacob Carlborg | On Tuesday, 8 August 2017 at 21:04:23 UTC, Jacob Carlborg wrote: > On 2017-08-08 20:51, Johan Engelen wrote: >> Hi all, >> Currently, it is not possible to call the C++ function "void >> foo(Klass&)" when Klass is an extern(C++) _class_ on the D side. You >> have to declare Klass as a D _struct_, otherwise there is no way to get >> the correct mangling. When Klass has virtual functions, you're hosed. >> >> For more context (involving "const"), see: >> https://forum.dlang.org/post/tvohflgtaxlynpzedqky@forum.dlang.org >> >> Is this problem on anybody's radar? >> What are the ideas to resolve this issue, or are we content never to >> solve it? > > One way to do it, that might be a bit confusing, is to force the declaration of the function to explicitly specify a pointer or a reference. Currently it looks like it's an implicit pointer. > > extern (C++) class Klass {} > void foo(Klass*); // ok > void foo(ref Klass); // ok > void foo(Klass); // error > > Of course, there's always pragma(mangle) as well. sorry for hijacking the thread but i have a similar question: i was wondering if i could write a wrapper for a C++11 library called cpr. in one of its header files (https://github.com/whoshuu/cpr/blob/master/include/cpr/auth.h#L13) it has a generic constructor that initializes its member fields. i had no idea as to how to do it. then i came up with the following line: extern (C++, cpr) { this(UT, PT)(ref UT username, ref PT password) { ... } } when i compiled it with the .a lib given, it worked. do you guys think i did it right? the & my second question is: i have no idea what's going on in this file: https://github.com/whoshuu/cpr/blob/master/include/cpr/body.h i'd appreciate some pointers. |
August 10, 2017 Re: Calling C++ "void foo(Klass&)" | ||||
---|---|---|---|---|
| ||||
Posted in reply to Mengu | On Thursday, 10 August 2017 at 00:32:40 UTC, Mengu wrote:
> my second question is: i have no idea what's going on in this file: https://github.com/whoshuu/cpr/blob/master/include/cpr/body.h i'd appreciate some pointers.
A new 'type' named Body which IS-A std::string is defined.
To construct a Body there are various options:
The ctors 'default': Body(), 'copy': Body(const Body&) and 'move': Body(Body&&) ctors are using the compiler generated default implementation.
The same is true for the assignment operators =
Then a few explicit conversion ctors are defined to construct a Body from a const char* string and std::string. Explicit means the compiler is not allowed to implicit convert to std::string or const char* for provide args not being a const char* or std::string but for which a conversion exists.
Since the h file also contains the definitions, the compiler must inline the code for the Body ctors and assignment operator. It also means not C/cpp file is needed since the function bodies are already in the h file.
HTH
|
August 10, 2017 Re: Calling C++ "void foo(Klass&)" | ||||
---|---|---|---|---|
| ||||
Posted in reply to Arjan | On Thursday, 10 August 2017 at 07:58:55 UTC, Arjan wrote: > On Thursday, 10 August 2017 at 00:32:40 UTC, Mengu wrote: >> my second question is: i have no idea what's going on in this file: https://github.com/whoshuu/cpr/blob/master/include/cpr/body.h i'd appreciate some pointers. > > A new 'type' named Body which IS-A std::string is defined. i think we can mimic this with an alias this for a string (or const char*) property. is that right? > To construct a Body there are various options: > The ctors 'default': Body(), 'copy': Body(const Body&) and 'move': Body(Body&&) ctors are using the compiler generated default implementation. > The same is true for the assignment operators = how can i check what compiler generates for default so i can add them to my extern C++ clause? > Then a few explicit conversion ctors are defined to construct a Body from a const char* string and std::string. Explicit means the compiler is not allowed to implicit convert to std::string or const char* for provide args not being a const char* or std::string but for which a conversion exists. > i'll give these converters a try. > Since the h file also contains the definitions, the compiler must inline the code for the Body ctors and assignment operator. It also means not C/cpp file is needed since the function bodies are already in the h file. i realized that when i saw the member initialization syntax in header files. > > HTH thank you very much for the detailed explanation. |
August 14, 2017 Re: Calling C++ "void foo(Klass&)" | ||||
---|---|---|---|---|
| ||||
Posted in reply to Walter Bright | On Wednesday, 9 August 2017 at 18:43:27 UTC, Walter Bright wrote: > On 8/8/2017 2:04 PM, Jacob Carlborg wrote: >> >> [snip] >> Of course, there's always pragma(mangle) as well. Yes these work: ``` pragma(mangle, convertMangleToCppRef(g.mangleof)) void g(Klass); pragma(mangle, mangleAsCpp("void Klass::g(Klass&)") void g(Klass); ``` `convertMangleToCppRef` and `mangleAsCpp` are not going to be easy to implement, but perhaps it can be made to work relatively easily for common cases. (hardcoded mangling won't work in a template class) Any takers for a dub package? ;-) On Wednesday, 9 August 2017 at 18:43:27 UTC, Walter Bright wrote: > > As Jacob hints at, the C++ name mangling for Klass* and Klass& is different. D classes are implicitly by reference. So which mangling to choose? D chose the Klass*. Let's keep this discussion focussed on solutions. We all know what the cause of the problem is. > The best way to deal with that is, on the C++ side, add: > > void foo(Klass* k) { foo(*k); } If this is the accepted solution (i.e. don't improve status quo), it means the only way to bind to common C++ libs is to set up a C++ build step for your project. This is already needed for instantiating templated C++ code, so perhaps it's not so bad, but definitely painful if things would have worked easily if only one could annotate things to slightly adjust the C++mangling. Also, automatically generating the trampolines will be a "fun" challenge for a binding tool writer. By the way, UFCS does help nicely for class method trampolines. ``` // C++ class B {}; class A { void foo(B&); }; // in C++ bindings file void foo(A* a, B* b) { a->foo(*b); } ``` ``` // D bindings file extern (C++) class A {} extern (C++) class B {} extern (C++) void foo(A a, B b); // D user code void g(A a, B b) { a.foo(b); } ``` I think this is important information to provide clarity on in the "Interfacing to C++" documentation. -Johan |
Copyright © 1999-2021 by the D Language Foundation