www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Why can't we use strings in C++ methods?

reply Dadoum <dadoum protonmail.com> writes:
I was wondering why C++ linkage forbids strings as arguments 
while we can with the C one.

With C linkage, it's translated to a template that's defined in 
the automatically generated header, but it just doesn't compile 
in C++.
Nov 03 2023
next sibling parent reply Imperatorn <johan_forsberg_86 hotmail.com> writes:
On Saturday, 4 November 2023 at 03:00:49 UTC, Dadoum wrote:
 I was wondering why C++ linkage forbids strings as arguments 
 while we can with the C one.

 With C linkage, it's translated to a template that's defined in 
 the automatically generated header, but it just doesn't compile 
 in C++.
Can you provide an example of exactly what you want to do?
Nov 04 2023
parent reply Dadoum <dadoum protonmail.com> writes:
On Saturday, 4 November 2023 at 10:08:20 UTC, Imperatorn wrote:
 On Saturday, 4 November 2023 at 03:00:49 UTC, Dadoum wrote:
 I was wondering why C++ linkage forbids strings as arguments 
 while we can with the C one.

 With C linkage, it's translated to a template that's defined 
 in the automatically generated header, but it just doesn't 
 compile in C++.
Can you provide an example of exactly what you want to do?
```d extern (C) void hello(string arg) { import std.stdio; writeln(arg); } ``` Compiles fine with dmd, ldc2 and gdc. ```d extern (C++) void hello(string arg) { import std.stdio; writeln(arg); } ``` Doesn't compile. DMD: `Internal Compiler Error: type `string` cannot be mapped to C++` GDC and LDC2: `function 'example.hello' cannot have parameter of type 'string' because its linkage is 'extern(C++)'` And I am wondering why the type can be mapped to a template in C but not in C++. (you can see the template used when you compile with `-H --HCf=./header.h`
Nov 04 2023
next sibling parent reply Emmanuel Danso Nyarko <emmankoko519 gmail.com> writes:
On Saturday, 4 November 2023 at 11:18:02 UTC, Dadoum wrote:
 On Saturday, 4 November 2023 at 10:08:20 UTC, Imperatorn wrote:
 On Saturday, 4 November 2023 at 03:00:49 UTC, Dadoum wrote:
 I was wondering why C++ linkage forbids strings as arguments 
 while we can with the C one.

 With C linkage, it's translated to a template that's defined 
 in the automatically generated header, but it just doesn't 
 compile in C++.
Can you provide an example of exactly what you want to do?
```d extern (C) void hello(string arg) { import std.stdio; writeln(arg); } ``` Compiles fine with dmd, ldc2 and gdc. ```d extern (C++) void hello(string arg) { import std.stdio; writeln(arg); } ``` Doesn't compile. DMD: `Internal Compiler Error: type `string` cannot be mapped to C++` GDC and LDC2: `function 'example.hello' cannot have parameter of type 'string' because its linkage is 'extern(C++)'` And I am wondering why the type can be mapped to a template in C but not in C++. (you can see the template used when you compile with `-H --HCf=./header.h`
So C-strings are just an array of characters that are governed by simple functions and D strings also defined the same. So you could see that D strings are possibly built on the architecture of C strings. In C++, string is a complex standard template library that generates complex symbols and cannot be mapped as it is based on a standard library implementation.
Nov 04 2023
next sibling parent reply Johan <j j.nl> writes:
On Saturday, 4 November 2023 at 12:01:11 UTC, Emmanuel Danso 
Nyarko wrote:
 On Saturday, 4 November 2023 at 11:18:02 UTC, Dadoum wrote:
 ```d
 extern (C) void hello(string arg) {
     import std.stdio;
     writeln(arg);
 }
 ```

 Compiles fine with dmd, ldc2 and gdc.


 ```d
 extern (C++) void hello(string arg) {
     import std.stdio;
     writeln(arg);
 }
 ```

 Doesn't compile.

 DMD: `Internal Compiler Error: type `string` cannot be mapped 
 to C++`
 GDC and LDC2: `function 'example.hello' cannot have parameter 
 of type 'string' because its linkage is 'extern(C++)'`

 And I am wondering why the type can be mapped to a template in 
 C but not in C++. (you can see the template used when you 
 compile with `-H --HCf=./header.h`
So C-strings are just an array of characters that are governed by simple functions and D strings also defined the same.
This is not true. D string (=slice) variables store the length of the string in addition to the reference to the array of characters. The reason this "works" with `extern (C)` is because the C mangling of a function name does not include the type of the parameters. Note that C does not have a `string` type, so to call the function from C you will have to write a different function signature in C (you'll see that `char[]` will not work). It does not work with `extern(C++)` because the C++ mangling of a function _does_ include the type of the parameters, and there is no built-in C++ type that is equivalent to D's `string`. -Johan
Nov 04 2023
next sibling parent Dadoum <dadoum protonmail.com> writes:
On Saturday, 4 November 2023 at 12:21:45 UTC, Johan wrote:
 On Saturday, 4 November 2023 at 12:01:11 UTC, Emmanuel Danso 
 Nyarko wrote:
 On Saturday, 4 November 2023 at 11:18:02 UTC, Dadoum wrote:
 ```d
 extern (C) void hello(string arg) {
     import std.stdio;
     writeln(arg);
 }
 ```

 Compiles fine with dmd, ldc2 and gdc.


 ```d
 extern (C++) void hello(string arg) {
     import std.stdio;
     writeln(arg);
 }
 ```

 Doesn't compile.

 DMD: `Internal Compiler Error: type `string` cannot be mapped 
 to C++`
 GDC and LDC2: `function 'example.hello' cannot have parameter 
 of type 'string' because its linkage is 'extern(C++)'`

 And I am wondering why the type can be mapped to a template 
 in C but not in C++. (you can see the template used when you 
 compile with `-H --HCf=./header.h`
So C-strings are just an array of characters that are governed by simple functions and D strings also defined the same.
This is not true. D string (=slice) variables store the length of the string in addition to the reference to the array of characters. The reason this "works" with `extern (C)` is because the C mangling of a function name does not include the type of the parameters. Note that C does not have a `string` type, so to call the function from C you will have to write a different function signature in C (you'll see that `char[]` will not work). It does not work with `extern(C++)` because the C++ mangling of a function _does_ include the type of the parameters, and there is no built-in C++ type that is equivalent to D's `string`. -Johan
What I don't understand is why it cannot use the template it defines in the header. Here it is: ```c++ // Automatically generated by LDC Compiler #pragma once #include <assert.h> #include <math.h> #include <stddef.h> #include <stdint.h> #ifdef CUSTOM_D_ARRAY_TYPE #define _d_dynamicArray CUSTOM_D_ARRAY_TYPE #else /// Represents a D [] array template<typename T> struct _d_dynamicArray final { size_t length; T *ptr; _d_dynamicArray() : length(0), ptr(NULL) { } _d_dynamicArray(size_t length_in, T *ptr_in) : length(length_in), ptr(ptr_in) { } T& operator[](const size_t idx) { assert(idx < length); return ptr[idx]; } const T& operator[](const size_t idx) const { assert(idx < length); return ptr[idx]; } }; #endif extern "C" void hello(_d_dynamicArray< const char > arg); ``` And the D compiler can generate templates in the mangled name, as this D code is translated to C++: ```d class Foo(T) {} extern (C++) void hello2(Foo!char arg2) { } ``` ```c++ // Automatically generated by LDC Compiler #pragma once #include <assert.h> #include <math.h> #include <stddef.h> #include <stdint.h> #ifdef CUSTOM_D_ARRAY_TYPE #define _d_dynamicArray CUSTOM_D_ARRAY_TYPE #else /// Represents a D [] array template<typename T> struct _d_dynamicArray final { size_t length; T *ptr; _d_dynamicArray() : length(0), ptr(NULL) { } _d_dynamicArray(size_t length_in, T *ptr_in) : length(length_in), ptr(ptr_in) { } T& operator[](const size_t idx) { assert(idx < length); return ptr[idx]; } const T& operator[](const size_t idx) const { assert(idx < length); return ptr[idx]; } }; #endif template <typename T> class Foo; extern void hello2(Foo<char >* arg2); ```
Nov 04 2023
prev sibling parent Emmanuel Danso Nyarko <emmankoko519 gmail.com> writes:
On Saturday, 4 November 2023 at 12:21:45 UTC, Johan wrote:
 On Saturday, 4 November 2023 at 12:01:11 UTC, Emmanuel Danso 
 Nyarko wrote:
 On Saturday, 4 November 2023 at 11:18:02 UTC, Dadoum wrote:
 ```d
 extern (C) void hello(string arg) {
     import std.stdio;
     writeln(arg);
 }
 ```

 Compiles fine with dmd, ldc2 and gdc.


 ```d
 extern (C++) void hello(string arg) {
     import std.stdio;
     writeln(arg);
 }
 ```

 Doesn't compile.

 DMD: `Internal Compiler Error: type `string` cannot be mapped 
 to C++`
 GDC and LDC2: `function 'example.hello' cannot have parameter 
 of type 'string' because its linkage is 'extern(C++)'`

 And I am wondering why the type can be mapped to a template 
 in C but not in C++. (you can see the template used when you 
 compile with `-H --HCf=./header.h`
So C-strings are just an array of characters that are governed by simple functions and D strings also defined the same.
This is not true. D string (=slice) variables store the length of the string in addition to the reference to the array of characters. The reason this "works" with `extern (C)` is because the C mangling of a function name does not include the type of the parameters. Note that C does not have a `string` type, so to call the function from C you will have to write a different function signature in C (you'll see that `char[]` will not work). It does not work with `extern(C++)` because the C++ mangling of a function _does_ include the type of the parameters, and there is no built-in C++ type that is equivalent to D's `string`. -Johan
You're right but that's not what he's looking for I think. He wants to understand why it doesn't compile at all. One major cause of failed compilation is syntax disagreements And my main point is that because C++ doesn't know independent 'string' and that the string in C++ is a standard template library, the D compiler decides to stop any symbol generated interaction with string because C++ syntatically doesn't know 'string'. Maybe the compiler team could provide a better answer to him but that's what I think.
Nov 04 2023
prev sibling parent reply Dadoum <dadoum protonmail.com> writes:
On Saturday, 4 November 2023 at 12:01:11 UTC, Emmanuel Danso 
Nyarko wrote:
 [...]

 So C-strings are just an array of characters that are governed 
 by simple functions and D strings also defined the same. So you 
 could see that D strings are possibly built on the architecture 
 of C strings. In C++, string is a complex standard template 
 library that generates complex symbols and cannot be mapped as 
 it is based on a standard library implementation.
I don't think the strings are being translated in any way when we change the linkage. Strings in D are dynamic arrays of chars, the C binding code is giving us a template representing D's dynamic arrays, and so I thought that we could also use it in C++ for interoperability with D dynamic arrays.
Nov 04 2023
parent reply Emmanuel Danso Nyarko <emmankoko519 gmail.com> writes:
On Saturday, 4 November 2023 at 12:34:28 UTC, Dadoum wrote:
 On Saturday, 4 November 2023 at 12:01:11 UTC, Emmanuel Danso 
 Nyarko wrote:
 [...]

 So C-strings are just an array of characters that are governed 
 by simple functions and D strings also defined the same. So 
 you could see that D strings are possibly built on the 
 architecture of C strings. In C++, string is a complex 
 standard template library that generates complex symbols and 
 cannot be mapped as it is based on a standard library 
 implementation.
I don't think the strings are being translated in any way when we change the linkage. Strings in D are dynamic arrays of chars, the C binding code is giving us a template representing D's dynamic arrays, and so I thought that we could also use it in C++ for interoperability with D dynamic arrays.
There is a syntax disagreement here that's why the D compiler is instantly stopping you from doing any symbol generated interaction with string in C++ interop. C++ doesn't know 'string' and C++ mangles with parameters and so passing string will make string get involved with the symbol generation and since string(std::string) in C++ is a template library, the D compiler stops you from engaging with 'string'
Nov 04 2023
parent reply Dadoum <dadoum protonmail.com> writes:
On Saturday, 4 November 2023 at 13:45:56 UTC, Emmanuel Danso 
Nyarko wrote:
 [...]

 There is a syntax disagreement here that's why the D compiler 
 is instantly stopping you from doing any symbol generated 
 interaction with string in C++ interop. C++ doesn't know 
 'string' and C++ mangles with parameters and so passing string 
 will make string get involved with the symbol generation and 
 since string(std::string) in C++ is a template library, the D 
 compiler stops you from engaging with 'string'
I don't think it's related to the existence of std::string at all since all dynamic array types are forbidden. ```d extern (C++) void hello(ubyte[] arg) { import std.stdio; writeln(arg); } ``` also fails to compile while this works: ```d extern (C) void hello(ubyte[] arg) { import std.stdio; writeln(arg); } ```
Nov 04 2023
next sibling parent Emmanuel Danso Nyarko <emmankoko519 gmail.com> writes:
On Saturday, 4 November 2023 at 13:51:20 UTC, Dadoum wrote:
 On Saturday, 4 November 2023 at 13:45:56 UTC, Emmanuel Danso 
 Nyarko wrote:
 [...]

 There is a syntax disagreement here that's why the D compiler 
 is instantly stopping you from doing any symbol generated 
 interaction with string in C++ interop. C++ doesn't know 
 'string' and C++ mangles with parameters and so passing string 
 will make string get involved with the symbol generation and 
 since string(std::string) in C++ is a template library, the D 
 compiler stops you from engaging with 'string'
I don't think it's related to the existence of std::string at all since all dynamic array types are forbidden. ```d extern (C++) void hello(ubyte[] arg) { import std.stdio; writeln(arg); } ``` also fails to compile while this works: ```d extern (C) void hello(ubyte[] arg) { import std.stdio; writeln(arg); } ```
Here too you will get the same error, cannot be mapped to C++. C++ mangles dynamic arrays as pointer arrays(uses the 'P' symbol), which is supported by D for interfacing with C. Another disagreement here using it in c++ interop. Hence will not compile. Once again, I think the compiler team will be the best to give you what you're looking for.
Nov 04 2023
prev sibling parent reply Basile B. <b2.temp gmx.com> writes:
On Saturday, 4 November 2023 at 13:51:20 UTC, Dadoum wrote:
 On Saturday, 4 November 2023 at 13:45:56 UTC, Emmanuel Danso 
 Nyarko wrote:
 [...]

 There is a syntax disagreement here that's why the D compiler 
 is instantly stopping you from doing any symbol generated 
 interaction with string in C++ interop. C++ doesn't know 
 'string' and C++ mangles with parameters and so passing string 
 will make string get involved with the symbol generation and 
 since string(std::string) in C++ is a template library, the D 
 compiler stops you from engaging with 'string'
I don't think it's related to the existence of std::string at all since all dynamic array types are forbidden. ```d extern (C++) void hello(ubyte[] arg) { import std.stdio; writeln(arg); } ``` also fails to compile while this works: ```d extern (C) void hello(ubyte[] arg) { import std.stdio; writeln(arg); } ```
The type simply cannot be mangled using the C++ mangler as it does not exist over there. You might have the impression that this should be allowed, e.g as an extension, but keep in mind that `extern(C++)` is firstly designed to link against object produced by a C++ compiler. Now why this works in `extern(C)` ? Because C does not mangle the parameters, a function linkage name is simply its unqualified name, so linking will work even if the parameter types are specific to D. Now there is still the question whether the `extern(C)` code will work as expected or not.
Nov 04 2023
next sibling parent Imperatorn <johan_forsberg_86 hotmail.com> writes:
On Saturday, 4 November 2023 at 14:33:56 UTC, Basile B. wrote:
 On Saturday, 4 November 2023 at 13:51:20 UTC, Dadoum wrote:
 [...]
The type simply cannot be mangled using the C++ mangler as it does not exist over there. You might have the impression that this should be allowed, e.g as an extension, but keep in mind that `extern(C++)` is firstly designed to link against object produced by a C++ compiler. Now why this works in `extern(C)` ? Because C does not mangle the parameters, a function linkage name is simply its unqualified name, so linking will work even if the parameter types are specific to D. Now there is still the question whether the `extern(C)` code will work as expected or not.
Could the mangling be provided with pragma and made to work?
Nov 04 2023
prev sibling parent Dadoum <dadoum protonmail.com> writes:
On Saturday, 4 November 2023 at 14:33:56 UTC, Basile B. wrote:
 [...]
 Now there is still the question whether the `extern(C)` code 
 will work as expected or not.
So with few patches could we make it work? DMD can write the C++ function prototype (as it does it with `extern (C)`) and then mangle it as the template it uses in C?
Nov 04 2023
prev sibling parent reply Imperatorn <johan_forsberg_86 hotmail.com> writes:
On Saturday, 4 November 2023 at 11:18:02 UTC, Dadoum wrote:
 On Saturday, 4 November 2023 at 10:08:20 UTC, Imperatorn wrote:
 On Saturday, 4 November 2023 at 03:00:49 UTC, Dadoum wrote:
 I was wondering why C++ linkage forbids strings as arguments 
 while we can with the C one.

 With C linkage, it's translated to a template that's defined 
 in the automatically generated header, but it just doesn't 
 compile in C++.
Can you provide an example of exactly what you want to do?
```d extern (C) void hello(string arg) { import std.stdio; writeln(arg); } ``` Compiles fine with dmd, ldc2 and gdc. ```d extern (C++) void hello(string arg) { import std.stdio; writeln(arg); } ``` Doesn't compile. DMD: `Internal Compiler Error: type `string` cannot be mapped to C++` GDC and LDC2: `function 'example.hello' cannot have parameter of type 'string' because its linkage is 'extern(C++)'` And I am wondering why the type can be mapped to a template in C but not in C++. (you can see the template used when you compile with `-H --HCf=./header.h`
We can just assume what you're doing on the C++-side. Are you using std::string? You could try as a pointer + length and it might work, but without seeing your complete code it's quite hard to know what you want to do.
Nov 04 2023
parent Dadoum <dadoum protonmail.com> writes:
On Saturday, 4 November 2023 at 12:07:12 UTC, Imperatorn wrote:
 [...]

 We can just assume what you're doing on the C++-side. Are you 
 using std::string?

 You could try as a pointer + length and it might work, but 
 without seeing your complete code it's quite hard to know what 
 you want to do.
I will use whatever D gives me in the C++ header, because in fact I am using Swift on the other side, so using std::string isn't useful. I already made a conversion function from Swift strings to D strings when they are represented with C linkage.
Nov 04 2023
prev sibling parent reply Paul Backus <snarwin gmail.com> writes:
On Saturday, 4 November 2023 at 03:00:49 UTC, Dadoum wrote:
 I was wondering why C++ linkage forbids strings as arguments 
 while we can with the C one.

 With C linkage, it's translated to a template that's defined in 
 the automatically generated header, but it just doesn't compile 
 in C++.
`extern(C++)` functions use C++ name mangling, which includes the types of the parameters in the mangled name. However, since C++ does not have a built-in slice type like D's `T[]`, there is no valid C++ mangling for a D slice. Because of this, it is impossible to compile an `extern(C++)` function that has a D slice as a parameter. As a workaround, you can convert the slice to a `struct`: ```d struct DSlice(T) { T* ptr; size_t length; T[] opIndex() => ptr[0 .. length]; } DSlice!T toDslice(T)(T[] slice) { return DSlice!T(slice.ptr, slice.length); } extern(C++) void hello(DSlice!(const(char)) arg) { import std.stdio; writeln(arg[]); } void main() { const(char)[] greeting = "hello"; hello(greeting.toDslice); } ```
Nov 04 2023
next sibling parent Dadoum <dadoum protonmail.com> writes:
On Saturday, 4 November 2023 at 14:21:49 UTC, Paul Backus wrote:
 [...]

 `extern(C++)` functions use C++ name mangling, which includes 
 the types of the parameters in the mangled name. However, since 
 C++ does not have a built-in slice type like D's `T[]`, there 
 is no valid C++ mangling for a D slice. Because of this, it is 
 impossible to compile an `extern(C++)` function that has a D 
 slice as a parameter.

 As a workaround, you can convert the slice to a `struct`:

 [...]
I use another workaround myself: ```d extern (C++) struct List(T) { // or extern (C) T[] self; alias self this; } extern (C++) void hello(List!ubyte arg) { import std.stdio; writeln(arg); } ``` but it means adding a lot of edge cases to the mixins I use for bindings while showing that C++ can express D arrays. (translated to ```c++ template <typename T> struct List final { _d_dynamicArray< T > self; List() { } }; extern void hello(List<uint8_t > arg); ``` )
Nov 04 2023
prev sibling parent Emmanuel Danso Nyarko <emmankoko519 gmail.com> writes:
On Saturday, 4 November 2023 at 14:21:49 UTC, Paul Backus wrote:
 On Saturday, 4 November 2023 at 03:00:49 UTC, Dadoum wrote:
 [...]
`extern(C++)` functions use C++ name mangling, which includes the types of the parameters in the mangled name. However, since C++ does not have a built-in slice type like D's `T[]`, there is no valid C++ mangling for a D slice. Because of this, it is impossible to compile an `extern(C++)` function that has a D slice as a parameter. [...]
Simply add perfectly explained!
Nov 04 2023