Thread overview
Using C++ Classes From D: dmd cannot link, while ldc segfault
Jun 19, 2023
mw
Jun 19, 2023
mw
Jun 19, 2023
mw
Jun 19, 2023
mw
Jun 19, 2023
mw
Jun 19, 2023
mw
June 19, 2023

Hi,

I'm following this example:

https://dlang.org/spec/cpp_interface.html#using_cpp_classes_from_d

and try to wrap a std::list

base.cpp

#include <iostream>
#include <list>

using namespace std;

class Base
{
    public:
        virtual void print3i(int a, int b, int c) = 0;
};

class Derived : public Base
{
    public:
        int field;
        Derived(int field) : field(field) {}

        void print3i(int a, int b, int c)
        {
            cout << "a = " << a << endl;
            cout << "b = " << b << endl;
            cout << "c = " << c << endl;
        }

        int mul(int factor);
};

template<class T> class std_list : public std::list<T> {};
void getInts(std_list<int>* list);

int Derived::mul(int factor)
{
    return field * factor;
}

Derived *createInstance(int i)
{
    // get list of ints from D side
    std_list<int> list;
    list.push_back(911);
    ::getInts(&list);
    for (int i : list) {
            cout << "i = " << i << endl;
    }

    return new Derived(i);
}

void deleteInstance(Derived *&d)
{
    delete d;
    d = 0;
}

main.c

extern(C++)
{
    abstract class Base
    {
        void print3i(int a, int b, int c);
    }

    class Derived : Base
    {
        int field;
        @disable this();
        override void print3i(int a, int b, int c);
        final int mul(int factor);
    }

    Derived createInstance(int i);
    void deleteInstance(ref Derived d);
}

extern(C++) {

class std_list(T) {
  @disable this();
  void push_back(const ref T value);
}

void getInts(std_list!(int) list) {
  foreach (int i; 0 .. 10) {
    list.push_back(i);
  }
}

}

void main()
{
    import std.stdio;

    auto d1 = createInstance(5);
    writeln(d1.field);
    writeln(d1.mul(4));

    Base b1 = d1;
    b1.print3i(1, 2, 3);

    deleteInstance(d1);
    assert(d1 is null);

    auto d2 = createInstance(42);
    writeln(d2.field);

    deleteInstance(d2);
    assert(d2 is null);
}

Makefile

c2d:
	g++ -c -g -ggdb base.cpp
	ldmd2 -g main.d base.o -L-lstdc++ && ./main

if I use LCD (ldmd2), it segfaults:

$ make
g++ -c -g -ggdb base.cpp
ldmd2 -g main.d base.o -L-lstdc++ && ./main
Segmentation fault (core dumped)

and gdb shows it fails at: main.d:29 list.push_back(i);

if I use DMD, it cannot link:

$ make
g++ -c -g -ggdb base.cpp
dmd -g main.d base.o -L-lstdc++ && ./main
/usr/bin/ld: main.o:(.data._D4main__T8std_listTiZQm6__vtblZ+0x0): undefined reference to `std_list<int>::push_back(int const&)'
collect2: error: ld returned 1 exit status
Error: linker exited with status 1

Looks like something wrong with the push_back().

So, how to fix this DMD link error, and LDC segfaults?

Thanks.

June 19, 2023
This is just a guess, but I think the problem is the vtable is incomplete.

Because of this the offsets are wrong. So you wouldn't be calling push_back.
June 19, 2023
On Monday, 19 June 2023 at 05:32:23 UTC, Richard (Rikki) Andrew Cattermole wrote:
> This is just a guess, but I think the problem is the vtable is incomplete.
>
> Because of this the offsets are wrong. So you wouldn't be calling push_back.

So, you mean on the D side, it need to list all the fields and methods of the C++ class?

```d
class std_list(T) {
  @disable this();
  void push_back(const ref T value);
}
```

Then it will be very tedious, esp. for such library class std::list.

Is there a tool that can automate this?

Thanks.

June 19, 2023
On Monday, 19 June 2023 at 05:39:51 UTC, mw wrote:
> Then it will be very tedious, esp. for such library class std::list.
>
> Is there a tool that can automate this?


A related question: basically I want to pass an array of objects from D side to the Cpp side, is there any example showing how to do this?

On this page

https://dlang.org/spec/cpp_interface.html#cpp-templates

I did not find any information about passing array (containers) between D and Cpp.

Thanks.
June 19, 2023
On 19/06/2023 5:39 PM, mw wrote:
> Then it will be very tedious, esp. for such library class std::list.

Yes, you would also need to verify it with every compiler you need (MSVC, vs linux gcc).

There could be a reason why it isn't in https://github.com/dlang/dmd/tree/master/druntime/src/core/stdcpp

> Is there a tool that can automate this?

Not that I know of.

June 19, 2023
On Monday, 19 June 2023 at 05:46:13 UTC, Richard (Rikki) Andrew Cattermole wrote:
>
> On 19/06/2023 5:39 PM, mw wrote:
>> Then it will be very tedious, esp. for such library class std::list.
>
> Yes, you would also need to verify it with every compiler you need (MSVC, vs linux gcc).
>
> There could be a reason why it isn't in https://github.com/dlang/dmd/tree/master/druntime/src/core/stdcpp

Ha, I saw vector.d there, So I can use this vector.d as the D side of C++'s std::vector?


June 19, 2023
On 19/06/2023 5:54 PM, mw wrote:
> Ha, I saw vector.d there, So I can use this vector.d as the D side of C++'s std::vector?

Probably, I just don't know how well tested it is.

But worth a go!
June 19, 2023
On Monday, 19 June 2023 at 05:56:54 UTC, Richard (Rikki) Andrew Cattermole wrote:
> On 19/06/2023 5:54 PM, mw wrote:
>> Ha, I saw vector.d there, So I can use this vector.d as the D side of C++'s std::vector?
>
> Probably, I just don't know how well tested it is.
>
> But worth a go!

```
import core.stdcpp.vector;

extern(C++) {

void getInts(core.stdcpp.vector.vector!(int) vec) {
  foreach (int i; 0 .. 10) {
    vec.push_back(i);
  }
}

}
```

dmd v2.104.0 failed:

```
/usr/include/dmd/druntime/import/core/stdcpp/vector.d(58): Error: undefined identifier `size`, did you mean alias `size_t`?
/usr/include/dmd/druntime/import/core/stdcpp/vector.d(33): Error: template instance `core.stdcpp.vector.vector!(int, allocator!int)` error instantiating
main.d(32):        instantiated from here: `vector!int`
```

LDC - the LLVM D compiler (1.32.2):
```
main.d(32): Error: undefined identifier `vector` in module `core.stdcpp.vector`, did you mean enum member `MIctor`?
```

So what's wrong the LDC? how do I write `core.stdcpp.vector.vector` then?
June 19, 2023
If I use array:

```
extern(C++) {

void getInts(core.stdcpp.array.array!(int, 10) vec) {
  foreach (int i; 0 .. 10) {
    vec.at(i) = i;
  }
}

}
```

```
#include <array>
using namespace std;

void getInts(array<int,10>* vector);
```

Both DMD and LDC has link error:

base.cpp:42: undefined reference to `getInts(std::array<int, 10ul>*)'