www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Calling C++ "void foo(Klass&)"

reply Johan Engelen <j j.nl> writes:
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
Aug 08 2017
parent reply Jacob Carlborg <doob me.com> writes:
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
Aug 08 2017
next sibling parent reply Walter Bright <newshound2 digitalmars.com> writes:
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.
Aug 09 2017
parent Johan Engelen <j j.nl> writes:
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
Aug 14 2017
prev sibling parent reply Mengu <mengukagan gmail.com> writes:
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.
Aug 09 2017
parent reply Arjan <arjan ask.me.to> writes:
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
Aug 10 2017
parent Mengu <mengukagan gmail.com> writes:
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.
Aug 10 2017