Re: How to pass a class by (const) reference to C++
On Tuesday, 14 December 2021 at 06:21:39 UTC, Tejas wrote: Hey, evilrat, I've seen people make claims that our C++ interop has reached phenomenal levels and that going any further would basically require a C++ compiler ala ImportC++, the issue is just that the docs haven't been updated yet to reflect it. What do you think about this? Is this really true? Because it sure doesn't look that way to me :( Unfortunately it is mostly true. There is some missing features like above tail ref and const, there is minor mangling issues that requires pragma mangle sometimes, and other annoying little details. Aside from that there is things that requires actual C++ compiler OR at least part of it to enable certain C++ features - like the example above with pass-by-value for classes certainly requires real C++ copy constructor, some operator overloads, use of SFINAE instead of CTFE, and I'm sure there is more of such nuances. All this not going to happen, D spec clearly states it allows limited C++ interop by relying on linker mechanics rather than being C++ compatible language. It is now abandoned but there was LDC fork called "Calypso", it was a mixed clang/LDC compiler that aimed to achieve seamless D/C++ interop and from what I've heard it was working just fine without all this hiccups as above.
Re: Passing a derived class where base class is defined as ref parameter
On Monday, 13 December 2021 at 22:06:45 UTC, chopchop wrote: If I remove the ref, it works as expected, that is to say I can give a derived class as parameter. I have an idea why it does not work, but I think a c++ reference would work, ie incr(A& console) would accept a B as parameter. What the logic here? TL:DR it's because there are two levels of indirection. What's happening here is that `ref A` in D *is not* equivalent to `A&` in C++. That's because D classes are reference types like Java classes, not value types like C++ classes. Your `b` is a handle to an instance, not an instance itself. It's more akin to `B*` in C++. So that means that your `ref A` is like `A**` in C++. And I believe you'll find that `B**` in C++ is not implicitly convertible to `A**`. Since `ref` in D is just a pointer under the hood, we can be more explicit like so: ```d void incr(A* a) { writeln(a.i); } B b = new B(); incr(&b); ``` In this case, compilation also fails. `B*` is not implicitly convertible to `A*`. Again, this is equivalent to `B**` and `A**` in C++. In this case, you can explicitly do the conversion with a cast: `incr(cast(A*)&b);` But consider what happens in this case: ```d void incr(A* a) { *a = new A; } B b = new B(); incr(cast(A*)&b); writeln(b.j); ``` Your `b` is no longer a `B`, but still thinks it is. This is my understanding of why implicit conversion is disallowed when multiple levels of indirection are involved.
Re: How to pass a class by (const) reference to C++
On Monday, 13 December 2021 at 12:08:30 UTC, evilrat wrote: On Monday, 13 December 2021 at 11:13:12 UTC, Tejas wrote: On Monday, 13 December 2021 at 09:21:26 UTC, Jan wrote: [...] You'll have to use something called a [shim](https://en.wikipedia.org/wiki/Shim_(computing)), it seems. For example: `main.d` : ```d extern(C++) class A{} extern(C++) void cppFunc_shim(A arg); void main(){ A a = new A(); cppFunc_shim(a); } ``` `cppShim.cpp` : ```c++ class A{}; extern void cppFunc(A const &arg); void cppFunc_shim(A *param){ const A forwardingVar = A(*param); cppFunc(forwardingVar); } ``` `cppFunc.cpp` : ```c++ #include "iostream" class A{}; void cppFunc(A const &arg){ //std::cout << arg << std::endl; std::cout << "Called cppFunc :D" << std::endl; } ``` Then pass the following on the command line(assuming all files are in the same directory): `ldmd2 main.d cppFunc.o cppShim.o -L-lstdc++` That's what it took to make it work for me, dunno if more convenient methods exist. Hope it helps :D Yeah but it sucks to have making C++ wrapper just for this. I think either pragma mangle to hammer it in place or helper dummy struct with class layout that mimics this shim logic is a better solution in such cases. Literally anything but building C++ code twice for a project. Hey, evilrat, I've seen people make claims that our C++ interop has reached phenomenal levels and that going any further would basically require a C++ compiler ala ImportC++, the issue is just that the docs haven't been updated yet to reflect it. What do you think about this? Is this really true? Because it sure doesn't look that way to me :(
Re: Passing a derived class where base class is defined as ref parameter
On Monday, 13 December 2021 at 22:30:59 UTC, Adam D Ruppe wrote: On Monday, 13 December 2021 at 22:06:45 UTC, chopchop wrote: If I remove the ref, it works as expected, that is to say I can give a derived class as parameter. Why are you using the ref to begin with? What the logic here? Consider this: class C : A {} void incr(ref A a) { a = new C; } B b = new B; incr(b); // oops b now got rebound to a C instead of to a B, which breaks everything But `B` is not a child of `A`, why should it be accepted in a function that accepts `A` as a parameter? It's not implicitly convertible to `A`
Re: Why code failed to compile for foo2?
On Monday, 13 December 2021 at 20:43:51 UTC, Steven Schveighoffer wrote: On 12/11/21 10:02 PM, apz28 wrote: On Sunday, 12 December 2021 at 00:02:25 UTC, Stanislav Blinov wrote: @apz28, I can't figure out the intent here. To convert result of abs to an unsigned? The function logic works only for unsigned type and the parameter value can be altered in body hence Unqual. If not Unqual, must declare a local var and make a copy. Just asking if this can be done to avoid a cast by caller To translate a bit here, what apz28 is looking for is, given ANY value type that implicitly converts from the qualified value to the unqualified value, generate *one* function that only accepts the unqualified value. You can wrap like: ```d void foo2(T)(T v) if (!is(Unqual!T == T)) { foo(Unqual!T(t)); } ``` This will inline as if you called it directly (which should just whittle down to a direct call to the correct foo2). But D isn't currently smart enough to see through aliases, so IFTI will not work for what you are doing, even in a case like: ```d template foo2(T) if (!isUnqual!T == T) { alias foo2 = .foo2!(Unqual!T); } ``` It would be nice if IFTI gave some hooks between the parameter and the deduction, but it currently doesn't. -Steve Is there anything wrong with the answer I posted? Can you please tell me if there's anything dissatisfactory about it? I feel like it does everything the OP wants. Also, am I wrong in using `Unconst` over `Unqual`? Isn't `Unqual` overkill if you just want to cast away `const`?
Re: Why code failed to compile for foo2?
On Saturday, 11 December 2021 at 23:44:59 UTC, Adam Ruppe wrote: On Saturday, 11 December 2021 at 23:17:17 UTC, Stanislav Blinov wrote: ? No. If it was unsatisfied constraint, the error would've shown that. And if you try to instantiate it, you'll see it is an unsatisfied constraint anyway. There's two layers of failure here. Using Unqual there is pretty iffy, i wouldn't bother with it at all, but if you do anything, instead qualify it const. But either way, then the constraint still fails since int isn't unsigned. I'd really recommend simplifying this a lot. 1. This is why there is a diverse logic for language rule vs template rule. The overload functions enjoy all benefits of implicit conversion rule but none for template 2. This is why template implementation create more function bloats more than it needs to be as compilable example below import std.traits : isUnsigned, Unqual; void foo2(T, alias UT1 = Unqual!T)(T x) // You can create alias UT1 but not able to use it in function parameter if(isUnsigned!T) { alias UT2 = Unqual!T; pragma(msg, T.stringof); pragma(msg, UT1.stringof); pragma(msg, UT2.stringof); } void main() { import std.math; int s = 1; foo2(cast(uint)abs(s)); foo2(cast(const(uint))abs(s)); } Output as below uint uint uint const(uint) uint uint
Re: A debug class has started
On Mon, Dec 13, 2021 at 10:43:14PM +, forkit via Digitalmars-d-learn wrote: > On Monday, 13 December 2021 at 21:13:25 UTC, H. S. Teoh wrote: > > > > What you should be doing is: > > > > return to!string(str[0 .. len]); > > > > Or just: > > > > return str[0 .. len].idup; [...] > oh.. so many different ways...(to both produce the same bug, and also > to produce the correct output). > > ... it's a little mind boggling ;-) [...] It's very simple. In C, an array decays to a pure pointer. In D, an array is a pointer + length pair. Given a D array, if you want a pointer to its first element, you use .ptr. Given a D pointer, if you want an array, you slice it with [0 .. length]. That's all there is to it. T -- Almost all proofs have bugs, but almost all theorems are true. -- Paul Pedersen
Re: A debug class has started
On Monday, 13 December 2021 at 21:13:25 UTC, H. S. Teoh wrote: What you should be doing is: return to!string(str[0 .. len]); Or just: return str[0 .. len].idup; T oh.. so many different ways...(to both produce the same bug, and also to produce the correct output). ... it's a little mind boggling ;-) // -- module test; import std : writeln, writefln, assumeUnique; import std.conv : to; import core.stdc.string : strdup; import std.string : toStringz; void main() { string str = "abc;def;ab"; //char* w = cast(char*)str; // nope. a pointer to a string constant is // (supposed to be) immutable, so expect undefined behaviour. char* w = strdup(cast(char*)str); // ok //char* w = cast(char*)str.toStringz; // also ok //char* w = cast(char*)str.dup; // also ok //char* w = str.dup.ptr; // also ok writeln(replaceChar(w, str.length, ';', 'X')); } immutable(char)[] replaceChar(char* str, ulong len, char ch1, char ch2) { for (ulong i = 0; i < len; i++) { if (str[i] == ch1) { writefln("Found %c at str[%d]", ch1, i); // fine str[i] = ch2; } } //return to!(immutable(char)[])(str); // nope .. issue with null terminator perhaps ?? return str[0 .. len].idup; // ok //return str[0 .. len].dup; // also ok //return to!string(str[0 .. len]); // also ok //return assumeUnique(str[0..len]); // also ok } // -
Re: Passing a derived class where base class is defined as ref parameter
On Monday, 13 December 2021 at 22:06:45 UTC, chopchop wrote: If I remove the ref, it works as expected, that is to say I can give a derived class as parameter. Why are you using the ref to begin with? What the logic here? Consider this: class C : A {} void incr(ref A a) { a = new C; } B b = new B; incr(b); // oops b now got rebound to a C instead of to a B, which breaks everything
Passing a derived class where base class is defined as ref parameter
Hi guys, below a small piece of code, which does not compile because "b" of type B can not be passed as ref parameter to incr(ref A a). If I remove the ref, it works as expected, that is to say I can give a derived class as parameter. I have an idea why it does not work, but I think a c++ reference would work, ie incr(A& console) would accept a B as parameter. What the logic here? Thanks ``` void main() { import std.stdio: write, writeln, writef, writefln; class A { int i = 2; } class B : A { int j= 3; } void incr(ref A a) { writeln(a.i); } B b = new B(); incr(b); } ```
Re: How to pass a class by (const) reference to C++
On Monday, 13 December 2021 at 21:17:49 UTC, Jan wrote: Unfortunately no. Maybe the cast would even make it work, but I can't have "A" and "_A" in D as separate types, because the class is supposed to link to C++ functions and thus renaming it from A to _A breaks that. On the other hand I can't give the struct another name either, because that's how it is linked to in "CppFunc". The easiest workaround is probably to set the mangling manually: pragma(mangle, "_Z7CppFuncRK1A") extern(C++) void CppFunc(const A arg); This mangling is for the Itanium ABI, which is used by Linux and other operating systems. It needs to be different for Windows.
Re: A debug class has started
On Monday, 13 December 2021 at 20:58:42 UTC, forkit wrote: immutable(char)[] replaceChar(char* str, ulong len, char ch1, char ch2) //snip return to!(immutable(char)[])(str); } You're calling a `to` on a char pointer, which, ostensibly, would look for null terminator. Which there may not be any if you do a .dup.
Re: How to pass a class by (const) reference to C++
On Monday, 13 December 2021 at 12:16:03 UTC, Ola Fosheim Grøstad wrote: On Monday, 13 December 2021 at 12:08:30 UTC, evilrat wrote: Yeah but it sucks to have making C++ wrapper just for this. I think either pragma mangle to hammer it in place or helper dummy struct with class layout that mimics this shim logic is a better solution in such cases. Literally anything but building C++ code twice for a project. Does something like this work? ``` class _A {} struct A {} extern(C++) void CppFunc(ref const(A) arg); void func(_A a){ CppFunc(*cast(A*)a); } ``` Unfortunately no. Maybe the cast would even make it work, but I can't have "A" and "_A" in D as separate types, because the class is supposed to link to C++ functions and thus renaming it from A to _A breaks that. On the other hand I can't give the struct another name either, because that's how it is linked to in "CppFunc".
Re: A debug class has started
On Mon, Dec 13, 2021 at 08:58:42PM +, forkit via Digitalmars-d-learn wrote: [...] > immutable(char)[] replaceChar(char* str, ulong len, char ch1, char ch2) > { > for (ulong i = 0; i < len; i++) > { > if (str[i] == ch1) > { > writefln("Found %c at str[%d]", ch1, i); // fine > str[i] = ch2; > } > } > return to!(immutable(char)[])(str); This line is your problem: you have a raw pointer `str` and force-casting it to an array without specifying its length. Do not expect anything good to come of this. (In fact I'm surprised it .to even accepts such a thing!) What you should be doing is: return to!string(str[0 .. len]); Or just: return str[0 .. len].idup; T -- Being forced to write comments actually improves code, because it is easier to fix a crock than to explain it. -- G. Steele
Re: A debug class has started
On Mon, Dec 13, 2021 at 08:47:12PM +, forkit via Digitalmars-d-learn wrote: > On Monday, 13 December 2021 at 20:28:26 UTC, H. S. Teoh wrote: > > On Mon, Dec 13, 2021 at 08:04:24PM +, forkit via Digitalmars-d-learn > > wrote: [...] > > > (this produces an unpredictable result??) > > > char* w = cast(char*)str.dup; > > > > Shouldn't you be using: > > > > char* w = str.dup.ptr; > > > > instead?? [...] > that also produces the same unpredictable result. > > i.e (an extra character from 'somewhere' appears in the output from > line below) > writeln(replaceChar(w, str.length, ';', 'X')); [...] That's weird. Can you post the minimal code that exhibits this problem? T -- Prosperity breeds contempt, and poverty breeds consent. -- Suck.com
Re: ImportC std support
On Sunday, 12 December 2021 at 08:25:09 UTC, Dave P. wrote: ImportC is not ready for general use yet. I think you nailed it ;-) https://dlang.org/changelog/2.098.1.html
Re: A debug class has started
On Monday, 13 December 2021 at 20:28:26 UTC, H. S. Teoh wrote: On Mon, Dec 13, 2021 at 08:04:24PM +, forkit via Digitalmars-d-learn wrote: On Monday, 13 December 2021 at 12:06:53 UTC, WebFreak001 wrote: > > You should really use `.dup` if you want to mutate your > string. (You would need to duplicate anyway if you don't > want an unsafe cast) (this produces an unpredictable result??) char* w = cast(char*)str.dup; Shouldn't you be using: char* w = str.dup.ptr; instead?? T so here are all the possible options I've tried. only 2 of these actually produce the expected result. // -- module test; import std : writeln, writefln; import std.conv : to; import core.stdc.string : strdup; import std.string : toStringz; void main() { string str = "abc;def;ab"; //char* w = cast(char*)str; // NOPE! A string constant is immutable, so expect undefined behaviour. char* w = strdup(cast(char*)str); // ok //char* w = cast(char*)str.toStringz; // also ok // all these below result in an extra character from 'somewhere' appearing in the writeln output //char* w = cast(char*)str.dup; // nope //char* w = str.dup.ptr; // nope //char* w = &dup(cast(const(char)[])str)[0]; // nope writeln(replaceChar(w, str.length, ';', 'X')); } immutable(char)[] replaceChar(char* str, ulong len, char ch1, char ch2) { for (ulong i = 0; i < len; i++) { if (str[i] == ch1) { writefln("Found %c at str[%d]", ch1, i); // fine str[i] = ch2; } } return to!(immutable(char)[])(str); } //
Re: unit test broken [DUB bug?]
On 12/12/21 12:54 AM, Chris Katko wrote: Running 64-bit Linux ``` dmd --version DMD64 D Compiler v2.098.0-beta.2 dub --version DUB version 1.27.0-beta.2, built on Sep 7 2021 ``` the following code 'compiles' in one project. ```d unittest { gasdindgaslkdgansklnasgdlknaglkgansklsdg; } void main(){} // compiles, links, and 'runs unit tests' ``` dub [shows compiling, linking, then runs] dub test ``` Running dfile-test-library All unit tests have been run successfully. ``` `dub test` adds its own main file, and removes yours. So essentially, it's not building your app.d file at all. `dub test` is a utility used to test libraries, which typically do not have a main function. It's definitely suspect for dub to do this, and it's based on historical baggage. To test an application with dub, use `dub -b unittest`, which builds the application the same as always, just with the `-unittest` switch. -Steve
Re: A debug class has started
On Monday, 13 December 2021 at 20:28:26 UTC, H. S. Teoh wrote: On Mon, Dec 13, 2021 at 08:04:24PM +, forkit via Digitalmars-d-learn wrote: On Monday, 13 December 2021 at 12:06:53 UTC, WebFreak001 wrote: > > You should really use `.dup` if you want to mutate your > string. (You would need to duplicate anyway if you don't > want an unsafe cast) (this produces an unpredictable result??) char* w = cast(char*)str.dup; Shouldn't you be using: char* w = str.dup.ptr; instead?? T that also produces the same unpredictable result. i.e (an extra character from 'somewhere' appears in the output from line below) writeln(replaceChar(w, str.length, ';', 'X'));
Re: Why code failed to compile for foo2?
On 12/11/21 10:02 PM, apz28 wrote: On Sunday, 12 December 2021 at 00:02:25 UTC, Stanislav Blinov wrote: @apz28, I can't figure out the intent here. To convert result of abs to an unsigned? The function logic works only for unsigned type and the parameter value can be altered in body hence Unqual. If not Unqual, must declare a local var and make a copy. Just asking if this can be done to avoid a cast by caller To translate a bit here, what apz28 is looking for is, given ANY value type that implicitly converts from the qualified value to the unqualified value, generate *one* function that only accepts the unqualified value. You can wrap like: ```d void foo2(T)(T v) if (!is(Unqual!T == T)) { foo(Unqual!T(t)); } ``` This will inline as if you called it directly (which should just whittle down to a direct call to the correct foo2). But D isn't currently smart enough to see through aliases, so IFTI will not work for what you are doing, even in a case like: ```d template foo2(T) if (!isUnqual!T == T) { alias foo2 = .foo2!(Unqual!T); } ``` It would be nice if IFTI gave some hooks between the parameter and the deduction, but it currently doesn't. -Steve
Re: ImportC std support
On 12/11/21 4:42 PM, ManKey wrote: What implementations of the C standard library does importC support? ImportC isn't really a way to import C. It's a C compiler built into the D compiler. It only compiles C11 code *without* any headers (i.e. you can't use C standard library). In order to properly import C headers, you have to first preprocess a C file (with a C preprocessor not supplied by D), and then use the output from that to build with your D project. And in that case, you likely will find it probably doesn't work yet, since it's not a ready feature. I recommend instead just using D's hand-crafted bindings in `core.stdc` package, which do work. -Steve
Re: A debug class has started
On Mon, Dec 13, 2021 at 08:04:24PM +, forkit via Digitalmars-d-learn wrote: > On Monday, 13 December 2021 at 12:06:53 UTC, WebFreak001 wrote: > > > > You should really use `.dup` if you want to mutate your string. (You > > would need to duplicate anyway if you don't want an unsafe cast) > > (this produces an unpredictable result??) > char* w = cast(char*)str.dup; Shouldn't you be using: char* w = str.dup.ptr; instead?? T -- It is impossible to make anything foolproof because fools are so ingenious. -- Sammy
Re: A debug class has started
On Monday, 13 December 2021 at 12:06:53 UTC, WebFreak001 wrote: You should really use `.dup` if you want to mutate your string. (You would need to duplicate anyway if you don't want an unsafe cast) (this produces an unpredictable result??) char* w = cast(char*)str.dup; (but this seems to work - as expected) char* w = strdup(cast(char*)str); // import core.stdc.string : strdup; pro-tip for bugs like this: just slap `@safe:` at the start of every file, the compiler will tell you everything that is risky I like this idea. Thanks ;-)
Re: How to pass a class by (const) reference to C++
On Monday, 13 December 2021 at 16:29:12 UTC, Tim wrote: I made a pull request, which changes the mangling to tail const for classes passed directly as parameter or return type, but now think this would break too much code: https://github.com/dlang/dmd/pull/13369 The proposal to add a deref-type-constructor, would also allow to have tail const classes. A 'deref' keyword sounds interesting. I'm not an expert on compilers, but thinking about this a bit more, to me it looks like the fundamental problem is, that D tries to apply its class pointer/reference semantics to C++, even though it could do this differently. 'Deref' would only solve one (common) issue. However, in C++ it is also very common to treat classes as value types. Maybe one could give such a hint to the D compiler instead. If I have this C++ code: ```cpp class A { ... }; void AsValue(A value); void AsPtr(A* value); void AsConstPtr(const A* value); void AsRef(A& value); void AsConstRef(const A& value); ``` Afaik today I can really only bind to functions of the form 'AsPtr' and 'AsConstPtr'. And if I declare A in D as a struct, I could also bind to all the others, but as mentioned above that's not always possible. How about in D I could declare that A should be used like a value type to pass it to a function, using the 'struct' keyword: ```cpp extern(C++) class A { ... } extern(C++) void AsValue(struct A value); extern(C++) void AsPtr(A value); // could stay as previously, OR extern(C++) void AsPtr(struct A* value); // same as above extern(C++) void AsConstPtr(const(A) value); extern(C++) void AsConstPtr(const(struct A*) value); // same as above extern(C++) void AsRef(ref struct A value); extern(C++) void AsConstRef(const(ref struct A) value); // same as above ``` So here the 'struct' keyword would tell the compiler to treat A like a value type, just as in C++ and thus apply pointer, const and reference semantics like in C++. Additionally, if a pure 'struct A' is encountered, the compiler would need to create a copy of the object on the stack, just as it would do for structs, to pass it to C++ (which might modify the temporary). I guess this would be trickier to implement but then you would be able to pass classes to C++ under all circumstances. The added benefit would be, that this shouldn't change existing behavior and thus not break anything. Unfortunately I have neither the time nor expertise to change DMD myself.
Re: unit test broken [DUB bug?]
On Sunday, 12 December 2021 at 05:54:44 UTC, Chris Katko wrote: Running 64-bit Linux ``` dmd --version DMD64 D Compiler v2.098.0-beta.2 [...] I really recommend always using dub configurations, especially when you want to use unit tests. The name of the first configuration doesn't matter. It is used by default for commands dub, dub build... The second configuration you name "unittest". This configuration is automatically used by command dub test. In configuration "unittest" you additionally specify "mainSourceFile". Kind regards Andre
Re: How to pass a class by (const) reference to C++
On Monday, 13 December 2021 at 15:21:19 UTC, Jan wrote: On Monday, 13 December 2021 at 13:02:50 UTC, Ola Fosheim Grøstad wrote: Yes, I wouldn't want to use it, maybe manual mangling is better, but still painful. ```const A&``` is so common in C++ API's that it really should be supported out-of-the-box. All it takes is adding a deref-type-constructor to the D language spec, e.g. ```ref const(@deref(A))``` I fully agree. This pattern is so common in C++, that I am surprised D doesn't have a way to do this already. The whole idea of linking against C++ is to interop easily, with little friction and high performance. Needing to build any shims or redesign the C++ side is very much contrary to this goal. Does anyone know whether such issues have been discussed before? I can't imagine I'm the first one to run into this. A similar issue about tail const classes has already been discussed: https://digitalmars.com/d/archives/digitalmars/D/const_Class_is_mangled_as_Class_const_const_299139.html I made a pull request, which changes the mangling to tail const for classes passed directly as parameter or return type, but now think this would break too much code: https://github.com/dlang/dmd/pull/13369 The proposal to add a deref-type-constructor, would also allow to have tail const classes.
Re: How to pass a class by (const) reference to C++
On Monday, 13 December 2021 at 13:02:50 UTC, Ola Fosheim Grøstad wrote: On Monday, 13 December 2021 at 12:51:17 UTC, evilrat wrote: That example is still looks very conspicuous because it is very likely does nothing on the caller side in C++ as it passes a copy. Yes, I wouldn't want to use it, maybe manual mangling is better, but still painful. ```const A&``` is so common in C++ API's that it really should be supported out-of-the-box. All it takes is adding a deref-type-constructor to the D language spec, e.g. ```ref const(@deref(A))``` I fully agree. This pattern is so common in C++, that I am surprised D doesn't have a way to do this already. The whole idea of linking against C++ is to interop easily, with little friction and high performance. Needing to build any shims or redesign the C++ side is very much contrary to this goal. Does anyone know whether such issues have been discussed before? I can't imagine I'm the first one to run into this.
Re: How to pass a class by (const) reference to C++
On Monday, 13 December 2021 at 12:51:17 UTC, evilrat wrote: That example is still looks very conspicuous because it is very likely does nothing on the caller side in C++ as it passes a copy. Yes, I wouldn't want to use it, maybe manual mangling is better, but still painful. ```const A&``` is so common in C++ API's that it really should be supported out-of-the-box. All it takes is adding a deref-type-constructor to the D language spec, e.g. ```ref const(@deref(A))```
Re: How to pass a class by (const) reference to C++
On Monday, 13 December 2021 at 12:16:03 UTC, Ola Fosheim Grøstad wrote: On Monday, 13 December 2021 at 12:08:30 UTC, evilrat wrote: Yeah but it sucks to have making C++ wrapper just for this. I think either pragma mangle to hammer it in place or helper dummy struct with class layout that mimics this shim logic is a better solution in such cases. Literally anything but building C++ code twice for a project. Does something like this work? ``` class _A {} struct A {} extern(C++) void CppFunc(ref const(A) arg); void func(_A a){ CppFunc(*cast(A*)a); } ``` Only if this struct matches class memory layout, the only potential problem is ctor on C++ side. Also C++ class will likely be NOT zero initialized and have byte gaps due to alignment, this can mess up many things including (default) equality operators and such. That example is still looks very conspicuous because it is very likely does nothing on the caller side in C++ as it passes a copy. Such things may indicate that the library author have no idea what he is doing, and it works on occasion. All this will require extra care or it will explode in your face, and is quite hard to debug without such low-level knowledge of details.
Re: How to pass a class by (const) reference to C++
On Monday, 13 December 2021 at 12:08:30 UTC, evilrat wrote: Yeah but it sucks to have making C++ wrapper just for this. I think either pragma mangle to hammer it in place or helper dummy struct with class layout that mimics this shim logic is a better solution in such cases. Literally anything but building C++ code twice for a project. Does something like this work? ``` class _A {} struct A {} extern(C++) void CppFunc(ref const(A) arg); void func(_A a){ CppFunc(*cast(A*)a); } ```
Re: How to pass a class by (const) reference to C++
On Monday, 13 December 2021 at 12:16:03 UTC, Ola Fosheim Grøstad wrote: class _A {} struct A {} With ```extern(C++)``` on these…
Re: A debug class has started
On Monday, 13 December 2021 at 11:09:18 UTC, drug wrote: On 13.12.2021 13:49, forkit wrote: On Monday, 13 December 2021 at 09:49:05 UTC, forkit wrote: char* w = cast(char*)str.toStringz; // this seems to be the solution class has ended ;-) That's because `str` is initialized by a literal and you can not change it by definition. When you call `toStringz` it duplicates that literal (adding terminating zero at the end) and the duplicate is mutable. I would recommend do not use `toStringz` and just make duplicate of the literal - https://run.dlang.io/is/vaosW0 important: toStringz _may_ do a copy with the current implementation but nothing in the docs states it actually does so. In fact there is commented out code where it [in the past](https://github.com/dlang/phobos/commit/bc412e7c3fa3f124d7f2785223318b45edd4b3e6#diff-b94766ba288f9b4b05ef1a4874e26724750e614afdcddaf4c2071d0f19d91595L217) just dereferenced the memory after the string and checked if it was 0. You should really use `.dup` if you want to mutate your string. (You would need to duplicate anyway if you don't want an unsafe cast) pro-tip for bugs like this: just slap `@safe:` at the start of every file, the compiler will tell you everything that is risky and the bug will 9/10 times just sort itself out by fixing what the compiler complains about. (just recently helped someone with this again, was a 3 minute fix for a big code-base where manually searching the issue would have taken much longer)
Re: How to pass a class by (const) reference to C++
On Monday, 13 December 2021 at 11:13:12 UTC, Tejas wrote: On Monday, 13 December 2021 at 09:21:26 UTC, Jan wrote: On Monday, 13 December 2021 at 07:48:34 UTC, evilrat wrote: On Sunday, 12 December 2021 at 21:24:39 UTC, Jan wrote: In D I have an extern(C++) class: ```cpp extern(C++) class A { ~this(); // other stuff } ``` An a function that takes A by const reference: ```cpp void CppFunc(const A& arg); ``` But how do I bind this in D ? ```cpp extern(C++) void CppFunc(A arg); // tries to pass as 'A*' extern(C++) void CppFunc(ref const(A) arg); // tries to pass as 'A const * const &' ``` I have solved similar problems with other classes by declaring them as struct in D, but that only works for classes that have no virtual functions. I now have a class where I do need to use a class on the D side, and now I have problems passing these objects to C++. You can tell compiler to mangle it as struct/class using extern(C++, struct). ```d extern (C++, struct) // will use struct mangling even though it's a class class SomeDClass { ... } ``` I tried this, but it doesn't work, because it seems D decides how to pass the object by whether it is a class or struct in D, not in C++. So even with the change as you suggested it, it still tries to pass the object as a pointer to begin with. You'll have to use something called a [shim](https://en.wikipedia.org/wiki/Shim_(computing)), it seems. For example: `main.d` : ```d extern(C++) class A{} extern(C++) void cppFunc_shim(A arg); void main(){ A a = new A(); cppFunc_shim(a); } ``` `cppShim.cpp` : ```c++ class A{}; extern void cppFunc(A const &arg); void cppFunc_shim(A *param){ const A forwardingVar = A(*param); cppFunc(forwardingVar); } ``` `cppFunc.cpp` : ```c++ #include "iostream" class A{}; void cppFunc(A const &arg){ //std::cout << arg << std::endl; std::cout << "Called cppFunc :D" << std::endl; } ``` Then pass the following on the command line(assuming all files are in the same directory): `ldmd2 main.d cppFunc.o cppShim.o -L-lstdc++` That's what it took to make it work for me, dunno if more convenient methods exist. Hope it helps :D Yeah but it sucks to have making C++ wrapper just for this. I think either pragma mangle to hammer it in place or helper dummy struct with class layout that mimics this shim logic is a better solution in such cases. Literally anything but building C++ code twice for a project.
Re: A debug class has started
On 13.12.2021 14:26, ag0aep6g wrote: On 13.12.21 12:09, drug wrote: That's because `str` is initialized by a literal and you can not change it by definition. When you call `toStringz` it duplicates that literal (adding terminating zero at the end) and the duplicate is mutable. I would recommend do not use `toStringz` and just make duplicate of the literal - https://run.dlang.io/is/vaosW0 From the link: string str = "abc;def;ab".dup; // allocates the string in the heap char* w = cast(char*)str; writeln(replaceChar(w, str.length, ';', 'X')); That still has undefined behavior. You cannot mutate the characters in a `string`. It doesn't matter if it came from a literal or `.dup`. Use `char[]` instead of `string`. You're right. I forget to change `string str` to `auto str` or `char[] str`.
Re: A debug class has started
On 13.12.21 12:09, drug wrote: That's because `str` is initialized by a literal and you can not change it by definition. When you call `toStringz` it duplicates that literal (adding terminating zero at the end) and the duplicate is mutable. I would recommend do not use `toStringz` and just make duplicate of the literal - https://run.dlang.io/is/vaosW0 From the link: string str = "abc;def;ab".dup; // allocates the string in the heap char* w = cast(char*)str; writeln(replaceChar(w, str.length, ';', 'X')); That still has undefined behavior. You cannot mutate the characters in a `string`. It doesn't matter if it came from a literal or `.dup`. Use `char[]` instead of `string`.
Re: How to pass a class by (const) reference to C++
On Monday, 13 December 2021 at 09:21:26 UTC, Jan wrote: On Monday, 13 December 2021 at 07:48:34 UTC, evilrat wrote: On Sunday, 12 December 2021 at 21:24:39 UTC, Jan wrote: In D I have an extern(C++) class: ```cpp extern(C++) class A { ~this(); // other stuff } ``` An a function that takes A by const reference: ```cpp void CppFunc(const A& arg); ``` But how do I bind this in D ? ```cpp extern(C++) void CppFunc(A arg); // tries to pass as 'A*' extern(C++) void CppFunc(ref const(A) arg); // tries to pass as 'A const * const &' ``` I have solved similar problems with other classes by declaring them as struct in D, but that only works for classes that have no virtual functions. I now have a class where I do need to use a class on the D side, and now I have problems passing these objects to C++. You can tell compiler to mangle it as struct/class using extern(C++, struct). ```d extern (C++, struct) // will use struct mangling even though it's a class class SomeDClass { ... } ``` I tried this, but it doesn't work, because it seems D decides how to pass the object by whether it is a class or struct in D, not in C++. So even with the change as you suggested it, it still tries to pass the object as a pointer to begin with. You'll have to use something called a [shim](https://en.wikipedia.org/wiki/Shim_(computing)), it seems. For example: `main.d` : ```d extern(C++) class A{} extern(C++) void cppFunc_shim(A arg); void main(){ A a = new A(); cppFunc_shim(a); } ``` `cppShim.cpp` : ```c++ class A{}; extern void cppFunc(A const &arg); void cppFunc_shim(A *param){ const A forwardingVar = A(*param); cppFunc(forwardingVar); } ``` `cppFunc.cpp` : ```c++ #include "iostream" class A{}; void cppFunc(A const &arg){ //std::cout << arg << std::endl; std::cout << "Called cppFunc :D" << std::endl; } ``` Then pass the following on the command line(assuming all files are in the same directory): `ldmd2 main.d cppFunc.o cppShim.o -L-lstdc++` That's what it took to make it work for me, dunno if more convenient methods exist. Hope it helps :D
Re: A debug class has started
On 13.12.2021 13:49, forkit wrote: On Monday, 13 December 2021 at 09:49:05 UTC, forkit wrote: char* w = cast(char*)str.toStringz; // this seems to be the solution class has ended ;-) That's because `str` is initialized by a literal and you can not change it by definition. When you call `toStringz` it duplicates that literal (adding terminating zero at the end) and the duplicate is mutable. I would recommend do not use `toStringz` and just make duplicate of the literal - https://run.dlang.io/is/vaosW0
Re: A debug class has started
On Monday, 13 December 2021 at 09:49:05 UTC, forkit wrote: char* w = cast(char*)str.toStringz; // this seems to be the solution class has ended ;-)
Re: A debug class has started
On Monday, 13 December 2021 at 09:49:05 UTC, forkit wrote: oh... Windows - dmd version is 2.098.0-dirty - ldc2 version is 1.28 (based on dmd v2.098.0) Linux - dmd version is 2.098 - ldc2 version is 1.20.1 (based on dmd v2.090.1)
A debug class has started
ok. one line in this code is causing a problem (as noted in comments) an explanation as to the cause, is welcome ;-) // module test; import std : writeln, writefln; import std.conv : to; void main() { string str = "abc;def;ab"; char* w = cast(char*)str; writeln(replaceChar(w, str.length, ';', 'X')); } immutable(char)[] replaceChar(char* str, ulong len, char ch1, char ch2) { for (ulong i = 0; i < len; i++) { if (str[i] == ch1) { writefln("Found %c at str[%d]", ch1, i); // fine // problem is with this next line: // the line works fine using DMD on windows - but crashes if using LDC on windows // On Linux, both DMD and LDC -> sigsegv //str[i] = ch2; // seems simple enough // - just replace the char currently in element str[i]) with 'X' } } return to!(immutable(char)[])(str); } //
Re: template ctor overload Segmentation fault
On Sunday, 12 December 2021 at 19:17:53 UTC, vit wrote: On Sunday, 12 December 2021 at 18:32:28 UTC, Imperatorn wrote: On Sunday, 12 December 2021 at 11:57:43 UTC, vit wrote: Hello, why does this code fail to compile? ```d struct Foo(T){ this(Rhs, this This)(scope Rhs rhs){ } this(ref scope typeof(this) rhs){ } } struct Bar{ Foo!int foo; } void main(){ } ``` error: Segmentation fault (core dumped) What are you trying to accomplish? Something like this: ```d import std.traits : CopyConstness; struct UniquePtr(T){ alias Type = T; this(Rhs, this This)(scope Rhs rhs) if(is(CopyConstness!(Rhs, Rhs.Type*) : CopyConstness!(This, This.Type*))){ //... } //one of copy ctors: this(ref scope typeof(this) rhs){ //... } static UniquePtr make(Args...)(Args args){ return UniquePtr.init; } } void main(){ const UniquePtr!(int) cui = UniquePtr!(const int).make(1); const UniquePtr!(const int) cuci = UniquePtr!(const int).make(1); UniquePtr!(const int) uci = UniquePtr!(int).make(1); UniquePtr!(int) ui = UniquePtr!(int).make(1); const UniquePtr!(int) xcui = UniquePtr!(immutable int).make(1); const UniquePtr!(const int) xcuci = UniquePtr!(immutable int).make(1); } ``` This work but UniquePtr canno't be inside struct because Segmentation fault. This made your previous snippet work: ```d struct Foo(T){ this(Rhs, this This)(scope Rhs rhs){ } this(scope Foo!(T)* rhs){ } } struct Bar{ Foo!int foo; } void main(){ import std.stdio:writeln; Bar bar = Bar(); auto BAR = new Bar(); writeln(bar, "\t", BAR, "\t", *BAR); } ``` Definitely something funky going on behind the scenes. I also think you should post a bug, like the above user said.
Re: How to loop through characters of a string in D language?
On Friday, 10 December 2021 at 18:47:53 UTC, Stanislav Blinov wrote: Threshold could be relative for short strings and absolute for long ones. Makes little sense reallocating if you only waste a couple bytes, but makes perfect sense if you've just removed pages and pages of semicolons ;) Like this? ``` @safe: string prematureoptimizations(string s, char stripchar) @trusted { import core.memory; immutable uint flags = GC.BlkAttr.NO_SCAN|GC.BlkAttr.APPENDABLE; char* begin = cast(char*)GC.malloc(s.length+1, flags); char* end = begin + 1; foreach(c; s) { immutable size_t notsemicolon = c != stripchar; // hack: avoid conditional by writing semicolon outside buffer *(end - notsemicolon) = c; end += notsemicolon; } immutable size_t len = end - begin - 1; begin = cast(char*)GC.realloc(begin, len, flags); return cast(string)begin[0..len]; } void main() { import std.stdio; string str = "abc;def;ab"; writeln(prematureoptimizations(str, ';')); } ```
Re: How to pass a class by (const) reference to C++
On Monday, 13 December 2021 at 07:48:34 UTC, evilrat wrote: On Sunday, 12 December 2021 at 21:24:39 UTC, Jan wrote: In D I have an extern(C++) class: ```cpp extern(C++) class A { ~this(); // other stuff } ``` An a function that takes A by const reference: ```cpp void CppFunc(const A& arg); ``` But how do I bind this in D ? ```cpp extern(C++) void CppFunc(A arg); // tries to pass as 'A*' extern(C++) void CppFunc(ref const(A) arg); // tries to pass as 'A const * const &' ``` I have solved similar problems with other classes by declaring them as struct in D, but that only works for classes that have no virtual functions. I now have a class where I do need to use a class on the D side, and now I have problems passing these objects to C++. You can tell compiler to mangle it as struct/class using extern(C++, struct). ```d extern (C++, struct) // will use struct mangling even though it's a class class SomeDClass { ... } ``` I tried this, but it doesn't work, because it seems D decides how to pass the object by whether it is a class or struct in D, not in C++. So even with the change as you suggested it, it still tries to pass the object as a pointer to begin with.
Re: template ctor overload Segmentation fault
On Sunday, 12 December 2021 at 11:57:43 UTC, vit wrote: Hello, why does this code fail to compile? ```d struct Foo(T){ this(Rhs, this This)(scope Rhs rhs){ } this(ref scope typeof(this) rhs){ } } struct Bar{ Foo!int foo; } void main(){ } ``` error: Segmentation fault (core dumped) Firstly, report the crash with the keyword "ice". Then, there might be something not allowed (in some situations the compiler cant decide which ctor to use) but you cant see it for now.
Re: Why code failed to compile for foo2?
On Sunday, 12 December 2021 at 03:02:28 UTC, apz28 wrote: On Sunday, 12 December 2021 at 00:02:25 UTC, Stanislav Blinov wrote: @apz28, I can't figure out the intent here. To convert result of abs to an unsigned? The function logic works only for unsigned type and the parameter value can be altered in body hence Unqual. If not Unqual, must declare a local var and make a copy. Just asking if this can be done to avoid a cast by caller AFAIK stuff like `const int` implicitly converts to `int` since `int` is passed by value so the `const`ness of the original value is not violated. That's why code like: ```d void main(){ const int a = 55; int b = a; } ``` compiles and executes. So, in a similar vein, your code will transform to: ```d void foo1(ubyte x) {} void foo1(ushort x) {} void foo1(uint x) {} void foo1(ulong x) {} import std.traits : Unconst; import std.stdio : writeln; void foo2(T)(T x) if(is(Unconst!(T) : ulong)) {//You don't need Unqual for this foo3(x); } void foo3(ulong param){ //I'm assuming this is your function that you will pass your uint to writeln(param); } void main() { import std.math; int s = int.min + 1; //abs returns int.min for abs(int.min) lol XD foo1(abs(s)); foo2(abs(s)); //failed? //Not anymore :D } ``` Again, please remember that `std.math.algebraic:abs` doesn't return `uint` for `int` and so on, please see [the documentation](https://dlang.org/phobos/std_math_algebraic.html#.abs): Returns: The absolute value of the number. If floating-point or integral, the __return type__ will be the __same as the input__. Limitations Does not work correctly for signed intergal types and value Num.min.