Re: Who says we can't call C++ constructors?

2018-04-24 Thread kinke via Digitalmars-d-announce

On Tuesday, 24 April 2018 at 11:19:59 UTC, Atila Neves wrote:
Odd then that dmd doesn't try to correctly mangle constructors 
and destructors since they're perfectly callable.


For normal constructors, that only works in the 
C++-ctor-called-from-D direction, with suboptimal performance. 
Reason being that the D compiler blits the T.init value over the 
instance before calling a ctor, and the D ctor not having to 
define all members.
So for a C++ ctor called from D, the pre-construction blit is 
useless extra work, and calling a D ctor from C++ is unsafe as 
the D ctor may not set all fields. The latter could be simply 
implemented by C++ ctor wrappers for all D ctors (for extern(C++) 
structs and classes), performing the T.init blit and then calling 
the corresponding D ctor, see 
https://forum.dlang.org/post/nqxsdehlydizatopr...@forum.dlang.org.


For the dtor, IIRC the problem was that it's usually virtual in 
C++ (at least when planning to allow subtyping) but isn't in D.


Re: Who says we can't call C++ constructors?

2018-04-24 Thread Atila Neves via Digitalmars-d-announce

On Tuesday, 24 April 2018 at 12:27:30 UTC, Uknown wrote:

On Tuesday, 24 April 2018 at 11:19:59 UTC, Atila Neves wrote:

On Monday, 23 April 2018 at 20:40:47 UTC, Manu wrote:
On 23 April 2018 at 07:27, Atila Neves via 
Digitalmars-d-announce  
wrote:

On Saturday, 21 April 2018 at 18:11:09 UTC, Manu wrote:


On 21 April 2018 at 05:41, Atila Neves via 
Digitalmars-d-announce 
 wrote:


[...]

Sure.

I thought about generating D wrappers for everything, but in 
TDD fashion I tried slapping a pragma(mangle) on the copy 
constructor and things just worked. Odd then that dmd doesn't 
try to correctly mangle constructors and destructors since 
they're perfectly callable.


[...]


This is very cool.


Thanks!

Is it possible to fix the mangling issues in DMD for Copy 
constructors and destructors?


Yes. And I'm going to have to (see below).


It seems like it would be less code for dpp,


Eh, it'd be a tiny change.


and better C++ interop for D in general.


Well, that's the real issue. There are C++ mangling bugs in the 
dmd frontend, and they need to be fixed because of templates. 
They don't have a mangling until they're instantiated, and I 
can't know that ahead of time where the templates are being 
declared. The easiest way to move forward is to just fix the dmd 
frontend. Unless I come up with some crazy idea. Which I wouldn't 
put past me.


Atila


Re: Who says we can't call C++ constructors?

2018-04-24 Thread Uknown via Digitalmars-d-announce

On Tuesday, 24 April 2018 at 11:19:59 UTC, Atila Neves wrote:

On Monday, 23 April 2018 at 20:40:47 UTC, Manu wrote:
On 23 April 2018 at 07:27, Atila Neves via 
Digitalmars-d-announce  
wrote:

On Saturday, 21 April 2018 at 18:11:09 UTC, Manu wrote:


On 21 April 2018 at 05:41, Atila Neves via 
Digitalmars-d-announce 
 wrote:


[...]

Sure.

I thought about generating D wrappers for everything, but in 
TDD fashion I tried slapping a pragma(mangle) on the copy 
constructor and things just worked. Odd then that dmd doesn't 
try to correctly mangle constructors and destructors since 
they're perfectly callable.


[...]


This is very cool. Is it possible to fix the mangling issues in 
DMD for Copy constructors and destructors? It seems like it would 
be less code for dpp, and better C++ interop for D in general.


Re: Who says we can't call C++ constructors?

2018-04-24 Thread Atila Neves via Digitalmars-d-announce

On Monday, 23 April 2018 at 20:40:47 UTC, Manu wrote:
On 23 April 2018 at 07:27, Atila Neves via 
Digitalmars-d-announce  
wrote:

On Saturday, 21 April 2018 at 18:11:09 UTC, Manu wrote:


On 21 April 2018 at 05:41, Atila Neves via 
Digitalmars-d-announce  
wrote:


[...]



Paste the pre-processed D code?
Did you generate the C++ mangled symbol name and call it from 
a D

wrapper? I've attempted that before in 'normal' D ;)



Mostly just constructors with `pragma(mangle)` but having move 
work correctly involved two wrappers, one taking by value and 
one taking by non-const ref.


Can you explain the move issue? That's interesting.


Sure.

I thought about generating D wrappers for everything, but in TDD 
fashion I tried slapping a pragma(mangle) on the copy constructor 
and things just worked. Odd then that dmd doesn't try to 
correctly mangle constructors and destructors since they're 
perfectly callable.


But then there's the move constructor. There's no signature I can 
use for it exactly, so how do I hack that? Well, in D a move 
would be done by value, but that doesn't quite work, since:


this(ref const(T));
this(T);

And:

auto t1 = T(42);
auto t2 = T(t1);

Causes t2 to be constructed with the by value version instead of 
the copy constructor. Clearly not what we want. So I do this:


this(ref const(T));
this(ref T other) {
   this(*cast(const T*)());
}
this(T);

And now rvalues go to the by-value version, and lvalues to the 
copy constructor. What to do with the by-value constructor?


pragma(mangle, "") this(T* );  // C++ move constructor
this(T other) {
this();
other = T.init; // to avoid problems
}

And now rvalues go to the C++ move constructor. Since the D 
definition is in the same module it will probably be inlined as 
well.


The only thing left is to enable explicit moving of lvalues. The 
thing is that I'm already injecting code into the user's file 
anyway, so maybe I can use that to enable moves? I can't put the 
code into a different module (or maybe I can, I might do that 
later), so to namespace it I put it in a struct called dpp:


struct dpp {
static struct Move(T) {
T* ptr;
}

static auto move(T)(ref T value) { // by ref so only lvalues 
apply

return Move!T();
}
}

And we emit another constructor with code in it for the user:

this(Move!T wrapper) {
this(wrapper.ptr);  // calls the C++ move constructor
}

And Bob's your uncle.


Re: Who says we can't call C++ constructors?

2018-04-23 Thread Atila Neves via Digitalmars-d-announce

On Saturday, 21 April 2018 at 18:11:09 UTC, Manu wrote:
On 21 April 2018 at 05:41, Atila Neves via 
Digitalmars-d-announce  
wrote:

[...]


Paste the pre-processed D code?
Did you generate the C++ mangled symbol name and call it from a 
D

wrapper? I've attempted that before in 'normal' D ;)


Mostly just constructors with `pragma(mangle)` but having move 
work correctly involved two wrappers, one taking by value and one 
taking by non-const ref.


Re: Who says we can't call C++ constructors?

2018-04-23 Thread Laeeth Isharc via Digitalmars-d-announce

On Saturday, 21 April 2018 at 12:41:02 UTC, Atila Neves wrote:

From https://dlang.org/spec/cpp_interface.html:

"C++ constructors, copy constructors, move constructors and 
destructors cannot be called directly in D code".


O RLY?
Atila


https://www.reddit.com/r/programming/comments/8eat5o/calling_c_constructors_from_d/
https://github.com/atilaneves/dpp


Re: Who says we can't call C++ constructors?

2018-04-21 Thread Manu via Digitalmars-d-announce
On 21 April 2018 at 05:41, Atila Neves via Digitalmars-d-announce
 wrote:
> From https://dlang.org/spec/cpp_interface.html:
>
> "C++ constructors, copy constructors, move constructors and destructors
> cannot be called directly in D code".
>
> O RLY?
>
> // hdr.hpp
> struct Struct {
> void *data;
> Struct(int i);
> Struct(const Struct&);
> Struct(Struct&&);
> ~Struct();
> int number() const;
> };
>
>
> // cpp.cpp
> #include "hdr.hpp"
> #include 
>
> using namespace std;
>
> Struct::Struct(int i) {
> cout << "  C++:  int ctor " << i << endl;
> data = new int(i);
> }
>
> Struct::Struct(const Struct& other) {
> cout << "  C++: copy ctor " << other.number() << endl;
> data = new int(*reinterpret_cast(other.data));
> }
>
> Struct::Struct(Struct&& other) {
> cout << "  C++: move ctor " << other.number() << endl;
> data = other.data;
> other.data = nullptr;
> }
>
> Struct::~Struct() {
> cout << "  C++ dtor " << number() << endl;
> delete reinterpret_cast(data);
> }
>
> int Struct::number() const {
> return data == nullptr ? 0 : *reinterpret_cast(data);
> }
>
>
> // ctors.dpp
> #include "hdr.hpp"
> import std.stdio;
>
> void main() {
> writeln("D: int ctor");
> const cs = const Struct(2);
> auto  ms = Struct(3);
> writeln;
>
> writeln("D: copy ctor");
> auto ccs = Struct(cs); assert(ccs.number() == 2);
> auto cms = Struct(ms); assert(cms.number() == 3);
> writeln;
>
> writeln("D: move ctor");
> auto tmp = Struct(4);
> // dpp.move causes the move ctor be called instead of the copy ctor
> auto mv1 = Struct(dpp.move(tmp)); assert(mv1.number() == 4);
> // moved from, now T.init (even if the C++ code doesn't do that)
> assert(tmp.data is null);
>
> // This last line doesn't work with dmd due to issue 18784.
> // It works fine with ldc though.
> auto mv2 = Struct(Struct(5)); assert(mv2.number() == 5);
> writeln;
> }
>
>
> % clang++ -c cpp.cpp
> % d++ --compiler=ldc2 ctors.dpp cpp.o -L-lstdc++
> % ./ctors
>
> D: int ctor
>   C++:  int ctor 2
>   C++:  int ctor 3
>
> D: copy ctor
>   C++: copy ctor 2
>   C++: copy ctor 3
>
> D: move ctor
>   C++:  int ctor 4
>   C++: move ctor 4
>   C++ dtor 0
>   C++:  int ctor 5
>   C++: move ctor 5
>   C++ dtor 0
>
>   C++ dtor 5
>   C++ dtor 4
>   C++ dtor 0
>   C++ dtor 3
>   C++ dtor 2
>   C++ dtor 3
>   C++ dtor 2
>
>
>
> Atila
>

Paste the pre-processed D code?
Did you generate the C++ mangled symbol name and call it from a D
wrapper? I've attempted that before in 'normal' D ;)


Re: Who says we can't call C++ constructors?

2018-04-21 Thread 12345swordy via Digitalmars-d-announce

On Saturday, 21 April 2018 at 12:41:02 UTC, Atila Neves wrote:

From https://dlang.org/spec/cpp_interface.html:

"C++ constructors, copy constructors, move constructors and 
destructors cannot be called directly in D code".


O RLY?

// hdr.hpp
struct Struct {
void *data;
Struct(int i);
Struct(const Struct&);
Struct(Struct&&);
~Struct();
int number() const;
};


// cpp.cpp
#include "hdr.hpp"
#include 

using namespace std;

Struct::Struct(int i) {
cout << "  C++:  int ctor " << i << endl;
data = new int(i);
}

Struct::Struct(const Struct& other) {
cout << "  C++: copy ctor " << other.number() << endl;
data = new int(*reinterpret_cast(other.data));
}

Struct::Struct(Struct&& other) {
cout << "  C++: move ctor " << other.number() << endl;
data = other.data;
other.data = nullptr;
}

Struct::~Struct() {
cout << "  C++ dtor " << number() << endl;
delete reinterpret_cast(data);
}

int Struct::number() const {
return data == nullptr ? 0 : 
*reinterpret_cast(data);

}


// ctors.dpp
#include "hdr.hpp"
import std.stdio;

void main() {
writeln("D: int ctor");
const cs = const Struct(2);
auto  ms = Struct(3);
writeln;

writeln("D: copy ctor");
auto ccs = Struct(cs); assert(ccs.number() == 2);
auto cms = Struct(ms); assert(cms.number() == 3);
writeln;

writeln("D: move ctor");
auto tmp = Struct(4);
// dpp.move causes the move ctor be called instead of 
the copy ctor
auto mv1 = Struct(dpp.move(tmp)); assert(mv1.number() 
== 4);
// moved from, now T.init (even if the C++ code doesn't 
do that)

assert(tmp.data is null);

// This last line doesn't work with dmd due to issue 
18784.

// It works fine with ldc though.
auto mv2 = Struct(Struct(5)); assert(mv2.number() == 5);
writeln;
}


% clang++ -c cpp.cpp
% d++ --compiler=ldc2 ctors.dpp cpp.o -L-lstdc++
% ./ctors

D: int ctor
  C++:  int ctor 2
  C++:  int ctor 3

D: copy ctor
  C++: copy ctor 2
  C++: copy ctor 3

D: move ctor
  C++:  int ctor 4
  C++: move ctor 4
  C++ dtor 0
  C++:  int ctor 5
  C++: move ctor 5
  C++ dtor 0

  C++ dtor 5
  C++ dtor 4
  C++ dtor 0
  C++ dtor 3
  C++ dtor 2
  C++ dtor 3
  C++ dtor 2



Atila


You are a mad man!


Who says we can't call C++ constructors?

2018-04-21 Thread Atila Neves via Digitalmars-d-announce

From https://dlang.org/spec/cpp_interface.html:

"C++ constructors, copy constructors, move constructors and 
destructors cannot be called directly in D code".


O RLY?

// hdr.hpp
struct Struct {
void *data;
Struct(int i);
Struct(const Struct&);
Struct(Struct&&);
~Struct();
int number() const;
};


// cpp.cpp
#include "hdr.hpp"
#include 

using namespace std;

Struct::Struct(int i) {
cout << "  C++:  int ctor " << i << endl;
data = new int(i);
}

Struct::Struct(const Struct& other) {
cout << "  C++: copy ctor " << other.number() << endl;
data = new int(*reinterpret_cast(other.data));
}

Struct::Struct(Struct&& other) {
cout << "  C++: move ctor " << other.number() << endl;
data = other.data;
other.data = nullptr;
}

Struct::~Struct() {
cout << "  C++ dtor " << number() << endl;
delete reinterpret_cast(data);
}

int Struct::number() const {
return data == nullptr ? 0 : 
*reinterpret_cast(data);

}


// ctors.dpp
#include "hdr.hpp"
import std.stdio;

void main() {
writeln("D: int ctor");
const cs = const Struct(2);
auto  ms = Struct(3);
writeln;

writeln("D: copy ctor");
auto ccs = Struct(cs); assert(ccs.number() == 2);
auto cms = Struct(ms); assert(cms.number() == 3);
writeln;

writeln("D: move ctor");
auto tmp = Struct(4);
// dpp.move causes the move ctor be called instead of the 
copy ctor
auto mv1 = Struct(dpp.move(tmp)); assert(mv1.number() == 
4);
// moved from, now T.init (even if the C++ code doesn't 
do that)

assert(tmp.data is null);

// This last line doesn't work with dmd due to issue 
18784.

// It works fine with ldc though.
auto mv2 = Struct(Struct(5)); assert(mv2.number() == 5);
writeln;
}


% clang++ -c cpp.cpp
% d++ --compiler=ldc2 ctors.dpp cpp.o -L-lstdc++
% ./ctors

D: int ctor
  C++:  int ctor 2
  C++:  int ctor 3

D: copy ctor
  C++: copy ctor 2
  C++: copy ctor 3

D: move ctor
  C++:  int ctor 4
  C++: move ctor 4
  C++ dtor 0
  C++:  int ctor 5
  C++: move ctor 5
  C++ dtor 0

  C++ dtor 5
  C++ dtor 4
  C++ dtor 0
  C++ dtor 3
  C++ dtor 2
  C++ dtor 3
  C++ dtor 2



Atila