Interfacing with basic C++ class

2022-09-28 Thread Riccardo M via Digitalmars-d-learn
I think I am stuck in the easiest of the issues and yet it seems 
I cannot get around this.

I have a C++ file:
```
class MyClass {
public:
int field;
MyClass(int a) : field(a) {}

int add(int asd) {
return asd + 1;
}
};

MyClass* instantiate(int asd) {
return new MyClass(asd);
}
```
and a D file:
```
extern(C++) {
class MyClass {
public: 
int field;
@disable this();
final int add(int asd);
}

MyClass instantiate(int asd);
}

void main()
{
import std : writeln;
auto myclass = instantiate(100);
writeln(myclass.field);
}
```
This is a very simplified version of the dlang official example 
of interfacing with C++ classes.


When I compile, I can't correctly read 'field'. What I do:
```
g++ -c cpp.cpp
dmd app.d cpp.o -L-lstdc++
./app
```
What I get:
```
0
```
Instead of the expected 100.

Care to help me understand what am I doing wrong?

Side question: it seems that I need to declare every class method 
as "final" otherwise I get  undefined references during linking. 
Is this expected behaviour?


Thanks


Re: Interfacing with basic C++ class

2022-09-28 Thread Ali Çehreli via Digitalmars-d-learn

On 9/28/22 12:57, Riccardo M wrote:

> class MyClass {
> public:
>  int field;
>  MyClass(int a) : field(a) {}

Make the following function 'virtual':

>  int add(int asd) {

virtual int add(int asd) {

I think the C++ class does not get a vptr without a virtual function and 
apparently D expects this.


>  final int add(int asd);

You don't need that 'final' anymore.

> This is a very simplified version of the dlang official example of
> interfacing with C++ classes.

The example there uses virtual functions. That must be the difference.

> I need to declare every class method as
> "final" otherwise I get  undefined references during linking. Is this
> expected behaviour?

Apparently, not with that added 'virtual'.

Ali




Re: Interfacing with basic C++ class

2022-09-29 Thread Riccardo M via Digitalmars-d-learn
On Wednesday, 28 September 2022 at 20:41:13 UTC, Ali Çehreli 
wrote:

[...]

Ali


Thank you, that is perfect!

However that begs the following observation: it would be rather 
hard to link to a C++ library only by means of 'extern (C++)' if 
one should slightly rearrange C++ code as well. This sounds like 
it is rather obvious to the community, actually, but I am a 
recent addition to D language :)


Do you know that this is documented somewhere? The examples in 
the official docs are rather limited, maybe studying a github 
with previous work might help.


Cheers


Re: Interfacing with basic C++ class

2022-09-29 Thread Ogi via Digitalmars-d-learn

On Wednesday, 28 September 2022 at 19:57:10 UTC, Riccardo M wrote:
I think I am stuck in the easiest of the issues and yet it 
seems I cannot get around this.

I have a C++ file:
```
class MyClass {
public:
int field;
MyClass(int a) : field(a) {}

int add(int asd) {
return asd + 1;
}
};
```


Ali is correct. However, you don’t have to change anything on the 
C++ side, just map C++ class to D struct:

```D
extern(C++, class) {
struct MyClass {
public: 
int field;
@disable this();
int add(int asd); //struct fields are always `final`
}

MyClass* instantiate(int asd); //mind the *
}
```


Re: Interfacing with basic C++ class

2022-09-29 Thread Ali Çehreli via Digitalmars-d-learn

On 9/29/22 01:28, Riccardo M wrote:

> if one should
> slightly rearrange C++ code as well.

Right. Additionally, the order of members must match (I am pretty sure, 
which means I am not :p).


> I am a recent addition to D
> language :)

Welcome! :)

> Do you know that this is documented somewhere? The examples in the
> official docs are rather limited

Manu Evans said at DConf 2022 that the documentation is very lacking 
compared to actual capability. People who know should update the docs.


Ali




Re: Interfacing with basic C++ class

2022-09-29 Thread Riccardo M via Digitalmars-d-learn

On Thursday, 29 September 2022 at 11:13:15 UTC, Ogi wrote:
Ali is correct. However, you don’t have to change anything on 
the C++ side, just map C++ class to D struct:

[...]


Thanks, this works perfectly.
Now i see that I can even instantiate directly from D, calling 
the default ctor, calling the custom ctor or even overriding the 
custom ctor in D (the fact that ctors could be called, in 
contrast with docs, was in fact hinted at DConf 2022 as suggested 
by Ali).


So it turns out that D's structs are a much better match for 
C++'s classes in this case. But why is this? Can you elaborate? 
It must have to do with the fact that D structs are passed by 
value?


Now this D code runs as expected:
```
// D
extern(C++, class) {
struct MyClass {
public: 
int field;
this(int a);
int add(int asd);
}

MyClass* instantiate(int asd);
}

void main()
{
import std : writeln;

	auto myclass = instantiate(100); //actually, instantiation 
through C++ is not required any longer

assert(myclass.field == 100);

auto myclass2 = new MyClass;
assert(myclass2.field == 0);

auto myclass3 = new MyClass(50);
assert(myclass3.field == 50);

assert(myclass3.add(40) == 90);
}
```
However the 'add' function only links correctly if C++ has the 
function body defined outside of its class.

```
// C++
int MyClass::add(int asd) {
return field + asd;
}
```
If the function is defined inside its class, it is an undefined 
reference at link time. Once again I ask for clarifications and 
workarounds, if possible.


Thanks


Re: Interfacing with basic C++ class

2022-09-30 Thread Ogi via Digitalmars-d-learn

On Thursday, 29 September 2022 at 12:49:06 UTC, Riccardo M wrote:

On Thursday, 29 September 2022 at 11:13:15 UTC, Ogi wrote:
So it turns out that D's structs are a much better match for 
C++'s classes in this case. But why is this? Can you elaborate? 
It must have to do with the fact that D structs are passed by 
value?




In C++, class and struct are basically the same thing (AFAIK the 
only difference is that struct members are public by default). In 
D, they are two very different things: classes are reference 
types — structs are value types, classes support inheritance — 
structs only support `alias this`, and so on.


When interfacing to C++, disregard the keyword and look at the 
implementation instead. If all its member functions are 
non-virtual, map it to struct. Otherwise map it to class. If it 
defines at least one pure virtual member function, map it to 
abstract class. If all its member functions are either pure 
virtual or non-virtual and it contains no fields, map it to 
interface. Sounds complicated? Well, that’s because C++ is 
complicated.


However the 'add' function only links correctly if C++ has the 
function body defined outside of its class.

```
// C++
int MyClass::add(int asd) {
return field + asd;
}
```
If the function is defined inside its class, it is an undefined 
reference at link time. Once again I ask for clarifications and 
workarounds, if possible.


In C++, member functions defined inside its class are called 
*inline* member functions. In contrast to normal functions which 
must be defined once and only once in your program, inline 
functions must be defined in every translation unit that uses 
them. Let’s replicate your linking error in C++:


```C++
//c.h
class C {
public:
int foo() {
return 42;
}
void bar();
};
```

```C++
//c.cpp
#include "c.h"

void C::bar() { /* ... */ }
```

```C++
//main.cpp
#include 

//Let’s see what happens if we forget `C::foo` definition:
class C {
public:
int foo();
void bar();
};

int main() {
auto c = new C();
printf("%d\n", c->foo());
c->bar();
return 0;
}
```

```
$ clang++ -c c.cpp
$ clang++ -c main.cpp
$ clang++ main.o c.o
main.o : error LNK2019: unresolved external symbol "public: int 
__cdecl C::foo(void)" (?foo@C@@QEAAHXZ) referenced in function 
main

a.exe : fatal error LNK1120: 1 unresolved externals
clang++: error: linker command failed with exit code 1120 (use -v 
to see invocation)

```

Copying `C::foo` definition to `main.cpp` will fix this. Of 
course, we could just include `c.h`.


Same goes for D. `MyClass.add` must be defined in your D module.


Re: Interfacing with basic C++ class

2022-10-02 Thread Riccardo M via Digitalmars-d-learn

On Friday, 30 September 2022 at 22:56:06 UTC, Ogi wrote:
On Thursday, 29 September 2022 at 12:49:06 UTC, Riccardo M 
wrote:
When interfacing to C++, disregard the keyword and look at the 
implementation instead. If all its member functions are 
non-virtual, map it to struct. Otherwise map it to class. If it 
defines at least one pure virtual member function, map it to 
abstract class. If all its member functions are either pure 
virtual or non-virtual and it contains no fields, map it to 
interface. Sounds complicated? Well, that’s because C++ is 
complicated.


Ok, in layman terms, is it correct to say that I should match the 
underlining structure of the object (e.g in terms of vtbl) so 
that C++ side and D side can work with each other correctly?


In C++, member functions defined inside its class are called 
*inline* member functions. In contrast to normal functions 
which must be defined once and only once in your program, 
inline functions must be defined in every translation unit that 
uses them. Let’s replicate your linking error in C++:


Well, I didn't know the implications of inlining member 
functions: basically when a member function is inlined, it has no 
linkage so I am pretty much done with using D in such case. While 
in C++ you can import the header and call the member function 
anyway.
The only solution would be reimplementing the offending function 
on D side.