Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to pass object from D to C++?

Tags:

c++

interop

d

dmd

ldc

I'm trying to make an interop with C++ and D. And the thing I've found today is really messing my mind: objects are not being passed correctly in my program.

It's better to show an example.

I have a C++ library, which I compile to an object file and the D program, which I link with my library and run.

Here they are:

#include <stdio.h>

class Color
{
public:
  Color(unsigned int _r, unsigned int _g, unsigned int _b) : r(_r), g(_g), b(_b) {}

  unsigned int r, g, b;
};

class Printer
{
public:
  Printer() {}
  ~Printer() {}
  static Printer* getInstance();
  void print(Color *c);
};

Printer* Printer::getInstance()
{
  return new Printer();
}

void Printer::print(Color *c)
{
  printf("(%d, %d, %d)\n", c->r, c->g, c->b);
}

And the D program:

import std.stdio;

extern(C++)
{
  class Color
  {
    uint r, g, b;

    this(uint _r, uint _g, uint _b)
    {
      r = _r;
      g = _g;
      b = _b;
    }
  }

  class Printer
  {
    @disable this();
    static Printer getInstance();
    final void print(Color c);
  }
}

void main()
{
  auto printer = Printer.getInstance();

  Color c = new Color(42, 7, 19);

  printer.print(c);
}

I compile them with these commands:

c++ -c my_core.cpp -o my_core.o
dmd main.d my_core.o -L-lstdc++

But when I run ./main, I got strange results:

(113244372, 1, 42)

What made me think objects are being passed incorrectly is just a simple experiment. First, I ran my program a couple of times and here's what I saw:

$ ./main
(266442332, 1, 42)
$ ./main
(234899036, 1, 42)
$ ./main
(109475420, 1, 42)

So the first number seems to be a pointer to a memory block. And my sixth feeling in couple with assembly knowledge make me think it's the pointer to this variable.

And now, just to confirm my data is still in place and those numbers are not just random ones, I've added two more fields to my class Color:

C++ lib:

#include <stdio.h>

class Color
{
public:
  Color(unsigned int _r, unsigned int _g, unsigned int _b, unsigned int _u, unsigned int _v) : r(_r), g(_g), b(_b), u(_u), v(_v) {}

  unsigned int r, g, b, u, v;
};

class Printer
{
public:
  Printer() {}
  ~Printer() {}
  static Printer* getInstance();
  void print(Color *c);
};

Printer* Printer::getInstance()
{
  return new Printer();
}

void Printer::print(Color *c)
{
  printf("(%d, %d, %d, %d, %d)\n", c->r, c->g, c->b, c->u, c->v);
}

And the D program:

import std.stdio;

extern(C++)
{
  class Color
  {
    this(uint _r, uint _g, uint _b, uint _u, uint _v)
    {
      r = _r;
      g = _g;
      b = _b;
      u = _u;
      v = _v;
    }

    uint r, g, b, u, v;
  }

  class Printer
  {
    @disable this();
    static Printer getInstance();
    final void print(Color c);
  }
}

void main()
{
  auto printer = Printer.getInstance();

  Color c = new Color(42, 7, 19, 499, 727);

  printer.print(c);
}

And the outputs are:

$ ./main                         
(90379876, 1, 42, 7, 19)
$ ./main
(79758948, 1, 42, 7, 19)
$ ./main
(74901092, 1, 42, 7, 19)
$ ./main
(217458276, 1, 42, 7, 19)
$ ./main
(238933604, 1, 42, 7, 19)

I've tried compiling my program with both DMD and LDC compilers, but both provided me with exactly the same behavior.

UPD: What's even more interesting and (probably) pointing to where the problem lies, is the fact objects, created in C++ lib are being passed between D and C++ correctly.

To prove this, I created "factory method" in my Color class:

static Color* create(unsigned int _r, unsigned int _g, unsigned int _b, unsigned int _u, unsigned int _v) {
    return new Color(_r, _g, _b, _u, _v);
}

And then, in D program:

Color c = Color.create(42, 7, 19, 499, 727);

printer.print(c);

So that the c object comes from C++ library, gets passed to the printer object, created in C++ library and this transfer is made in D program.

And the results are unexpectedly correct:

$ ./main
(42, 7, 19, 499, 727)

Am I missing the concepts of C++ and D interop or is this a bug in two D compilers (doubtfully)?

like image 889
shybovycha Avatar asked Oct 18 '22 13:10

shybovycha


1 Answers

You should't use Ds new to allocate C++ classes, if you create a Color::getInstance it works.

import std.stdio;

extern(C++)
{
  class Color
  {
    this(uint _r, uint _g, uint _b, uint _u, uint _v)
    {
      r = _r;
      g = _g;
      b = _b;
      u = _u;
      v = _v;
    }

    uint r, g, b, u, v;
    static Color getInstance(uint _r, uint _g, uint _b, uint _u, uint _v);
  }

  class Printer
  {
    @disable this();
    static Printer getInstance();
    final void print(Color c);
  }
}

void main()
{
  auto printer = Printer.getInstance();
  auto c = Color.getInstance(42, 7, 19, 499, 727);

  printer.print(c);
}

and

#include <stdio.h>

class Color
{
public:
  Color(unsigned int _r, unsigned int _g, unsigned int _b, unsigned int _u, unsigned int _v) : r(_r), g(_g), b(_b), u(_u), v(_v) {}

  unsigned int r, g, b, u, v;
  static Color* getInstance (unsigned int _r, unsigned int _g, unsigned int _b, unsigned int _u, unsigned int _v);
};

Color* Color::getInstance(unsigned int _r, unsigned int _g, unsigned int _b, unsigned int _u, unsigned int _v)
{
  return new Color(_r, _g, _b, _u, _v);
}

class Printer
{
public:
  Printer() {}
  ~Printer() {}
  static Printer* getInstance();
  void print(Color *c);
};

Printer* Printer::getInstance()
{
  return new Printer();
}

void Printer::print(Color *c)
{
  printf("(%d, %d, %d, %d, %d)\n", c->r, c->g, c->b, c->u, c->v);
}
like image 74
NotSpooky Avatar answered Oct 21 '22 06:10

NotSpooky