Thread overview
Calling C++ "void foo(Klass&)"
Aug 08, 2017
Johan Engelen
Aug 08, 2017
Jacob Carlborg
Aug 09, 2017
Walter Bright
Aug 14, 2017
Johan Engelen
Aug 10, 2017
Mengu
Aug 10, 2017
Arjan
Aug 10, 2017
Mengu
August 08, 2017
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
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
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
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
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
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
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