www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Passing string from D to c++

reply "Milvakili" <maliy.kayit gmail.com> writes:
I'm linking D with C++ lib.a file.  When the C++ function has 
compatible data types I can call them from D.  But when I changed 
the parameter to string I got bunch of errors.

Data Type Compatibility table does not include strings.  Is there 
a way of passing strings?
Jul 30 2013
next sibling parent reply "Dicebot" <public dicebot.lv> writes:
On Tuesday, 30 July 2013 at 19:52:44 UTC, Milvakili wrote:
 I'm linking D with C++ lib.a file.  When the C++ function has 
 compatible data types I can call them from D.  But when I 
 changed the parameter to string I got bunch of errors.

 Data Type Compatibility table does not include strings.  Is 
 there a way of passing strings?

http://dlang.org/phobos/std_string.html#.toStringz
Jul 30 2013
next sibling parent Walter Bright <newshound2 digitalmars.com> writes:
On 7/30/2013 3:01 PM, John Colvin wrote:
 $ dmd -c -m64 inter.d
 $ g++ -c -m64 inter.cpp -ointercpp.o
 $ g++ inter.o intercpp.o -ointer -lphobos2
 inter.o: In function `_Dmain':
 inter.d:(.text._Dmain+0x48): undefined reference to `printString(CppString*)'
 collect2: error: ld returned 1 exit status

You might want to look at the name mangling of the D printString vs the name mangling of the C++ one.
Jul 30 2013
prev sibling parent =?UTF-8?B?QWxpIMOHZWhyZWxp?= <acehreli yahoo.com> writes:
On 07/30/2013 02:17 PM, JS wrote:

 A C++ string is null terminated while a D string is not.

I think the OP means std::string, which also is not zero-terminated. (On the other hand, C strings are zero-terminated.)
 If you pass a D string to C++ then it will not have the same length 

 undesirable behavior.

That's an important consideration. char is not UTF-8 code unit in C and C++. Ali
Jul 30 2013
prev sibling next sibling parent "Milvakili" <maliy.kayit gmail.com> writes:
On Tuesday, 30 July 2013 at 20:02:51 UTC, Dicebot wrote:
 On Tuesday, 30 July 2013 at 19:52:44 UTC, Milvakili wrote:
 I'm linking D with C++ lib.a file.  When the C++ function has 
 compatible data types I can call them from D.  But when I 
 changed the parameter to string I got bunch of errors.

 Data Type Compatibility table does not include strings.  Is 
 there a way of passing strings?

http://dlang.org/phobos/std_string.html#.toStringz

So I need to pass them as char*, I can not pass them as string?
Jul 30 2013
prev sibling next sibling parent "John Colvin" <john.loughran.colvin gmail.com> writes:
On Tuesday, 30 July 2013 at 20:09:01 UTC, Milvakili wrote:
 On Tuesday, 30 July 2013 at 20:02:51 UTC, Dicebot wrote:
 On Tuesday, 30 July 2013 at 19:52:44 UTC, Milvakili wrote:
 I'm linking D with C++ lib.a file.  When the C++ function has 
 compatible data types I can call them from D.  But when I 
 changed the parameter to string I got bunch of errors.

 Data Type Compatibility table does not include strings.  Is 
 there a way of passing strings?

http://dlang.org/phobos/std_string.html#.toStringz

So I need to pass them as char*, I can not pass them as string?

Correct. Just thinking off the top of my head: you could probably hack something together though with a struct and casting.
Jul 30 2013
prev sibling next sibling parent "Milvakili" <maliy.kayit gmail.com> writes:
On Tuesday, 30 July 2013 at 20:17:34 UTC, John Colvin wrote:
 On Tuesday, 30 July 2013 at 20:09:01 UTC, Milvakili wrote:
 On Tuesday, 30 July 2013 at 20:02:51 UTC, Dicebot wrote:
 On Tuesday, 30 July 2013 at 19:52:44 UTC, Milvakili wrote:
 I'm linking D with C++ lib.a file.  When the C++ function 
 has compatible data types I can call them from D.  But when 
 I changed the parameter to string I got bunch of errors.

 Data Type Compatibility table does not include strings.  Is 
 there a way of passing strings?

http://dlang.org/phobos/std_string.html#.toStringz

So I need to pass them as char*, I can not pass them as string?

Correct. Just thinking off the top of my head: you could probably hack something together though with a struct and casting.

Thanks. Is there any work in progress related to string passing? I think string passing should be solved automatically. Coz otherwise one need to write wrapper for each c++ function that has a string parameter. thanks.
Jul 30 2013
prev sibling next sibling parent "Adam D. Ruppe" <destructionator gmail.com> writes:
On Tuesday, 30 July 2013 at 20:09:01 UTC, Milvakili wrote:
 So I need to pass them as char*, I can not pass them as string?

I would pass it as a char* and a size_t for length. extern(C++) void cpp_func(char* strptr, size_t strlen); void main() { string foo = "test"; cpp_func(foo.ptr, foo.length); } It'd be important on the C++ side to remember that it is NOT zero terminated, and always use that length parameter instead of normal C functions. But that'd give the most efficiency, since that's using the same representation as D itself.
Jul 30 2013
prev sibling next sibling parent "Dicebot" <public dicebot.lv> writes:
On Tuesday, 30 July 2013 at 20:22:46 UTC, Milvakili wrote:
 Thanks.  Is there any work in progress related to string 
 passing?
 I think string passing should be solved automatically.

 Coz otherwise one need to write wrapper for each c++ function 
 that has a string parameter.

 thanks.

c_function(toStringz("hello").ptr) does not seem that bad, what is the issue here?
Jul 30 2013
prev sibling next sibling parent "Jesse Phillips" <Jesse.K.Phillips+D gmail.com> writes:
On Tuesday, 30 July 2013 at 20:09:01 UTC, Milvakili wrote:

 So I need to pass them as char*, I can not pass them as string?

C++ doesn't have an immutable(char)[], it has char* and its own string class. To communicate with it one must choose a type both languages can understand, char* is that. D may be able to get away with providing a C++ string, but I'm not familiar with the integration layer.
Jul 30 2013
prev sibling next sibling parent "JS" <js.mdnq gmail.com> writes:
On Tuesday, 30 July 2013 at 20:09:01 UTC, Milvakili wrote:
 On Tuesday, 30 July 2013 at 20:02:51 UTC, Dicebot wrote:
 On Tuesday, 30 July 2013 at 19:52:44 UTC, Milvakili wrote:
 I'm linking D with C++ lib.a file.  When the C++ function has 
 compatible data types I can call them from D.  But when I 
 changed the parameter to string I got bunch of errors.

 Data Type Compatibility table does not include strings.  Is 
 there a way of passing strings?

http://dlang.org/phobos/std_string.html#.toStringz

So I need to pass them as char*, I can not pass them as string?

You can't pass them as string because they have different representations. A C++ string is null terminated while a D string is not. If you pass a D string to C++ then it will not have the same length and give undesirable behavior. If you really want to use strings, then wrap each C++ function with one that converts the string to a C string. If D used both pascal and C++ style strings as one(a waste of 1 extra byte per string) then life would be easier.
Jul 30 2013
prev sibling next sibling parent "John Colvin" <john.loughran.colvin gmail.com> writes:
On Tuesday, 30 July 2013 at 20:22:46 UTC, Milvakili wrote:
 On Tuesday, 30 July 2013 at 20:17:34 UTC, John Colvin wrote:
 On Tuesday, 30 July 2013 at 20:09:01 UTC, Milvakili wrote:
 On Tuesday, 30 July 2013 at 20:02:51 UTC, Dicebot wrote:
 On Tuesday, 30 July 2013 at 19:52:44 UTC, Milvakili wrote:
 I'm linking D with C++ lib.a file.  When the C++ function 
 has compatible data types I can call them from D.  But when 
 I changed the parameter to string I got bunch of errors.

 Data Type Compatibility table does not include strings.  Is 
 there a way of passing strings?

http://dlang.org/phobos/std_string.html#.toStringz

So I need to pass them as char*, I can not pass them as string?

Correct. Just thinking off the top of my head: you could probably hack something together though with a struct and casting.

Thanks. Is there any work in progress related to string passing? I think string passing should be solved automatically. Coz otherwise one need to write wrapper for each c++ function that has a string parameter. thanks.

I had a crack at making this work, but I'm stuck on some basics and I don't know a great deal about c++ really //inter.cpp #include<string> #include<iostream> using namespace std; void printString(string* s) { cout << s << "\n"; } string *strD2Cpp(char *ptr, unsigned long long length) { return new std::string(ptr, length); } //inter.d extern(C++) { interface CppString{} CppString strD2Cpp(char* ptr, size_t length); void printString(CppString s); } void main() { string s = "2432qreafdsa"; auto s_cpp = strD2Cpp(s.dup.ptr, s.length); printString(s_cpp); } $ dmd -c -m64 inter.d $ g++ -c -m64 inter.cpp -ointercpp.o $ g++ inter.o intercpp.o -ointer -lphobos2 inter.o: In function `_Dmain': inter.d:(.text._Dmain+0x48): undefined reference to `printString(CppString*)' collect2: error: ld returned 1 exit status Is this an incompatibility between dmd and g++? I'm on linux x64 How come it's happy to have CppString as the return type of strD2Cpp but not as a parameter to printString? Also, using size_t in inter.cpp caused linker errors, hence the unsigned long long.
Jul 30 2013
prev sibling next sibling parent "bsd" <slackovsky gmail.com> writes:
I'm using toStringz() and it works fine if you're c++ function 
parms are const ref or by value:

void funk(const std::string& val) {}
void funk(std::string val) {}
Jul 30 2013
prev sibling next sibling parent "bsd" <slackovsky gmail.com> writes:
(damn, no edit!)

I'm using toStringz() and it works fine if you're c++ function
parms are const ref or by value:

void funk(const std::string& val) {}
void funk(std::string val) {}


then use a very simple D script to auto-generated C wrapper 
functions from headers. All the script does is:

void funkD(const char* c) {funk(c);}

It wouldn't work for functions taking a string by ref or pointer, 
that would require too much effort in the generating script for 
my liking.

You may baulk at the idea of writing a script for auto-generating 
the code but really it is very simple.
Jul 30 2013
prev sibling next sibling parent "deadalnix" <deadalnix gmail.com> writes:
On Tuesday, 30 July 2013 at 19:52:44 UTC, Milvakili wrote:
 I'm linking D with C++ lib.a file.  When the C++ function has 
 compatible data types I can call them from D.  But when I 
 changed the parameter to string I got bunch of errors.

 Data Type Compatibility table does not include strings.  Is 
 there a way of passing strings?

When it come to C++, passing via C string is always an option, but kind of a waste as you'll allocate a temporary string and go through it twice. My solution of choice it to create a function in C++ that take a const char* and a size_t length as parameter (and other C++ parameters of the function and forward to C++ . Doing so, you only allocate the C++ string as C++ mandate and don't need to go through it. But you need a wrapper.
Jul 30 2013
prev sibling next sibling parent "deadalnix" <deadalnix gmail.com> writes:
On Tuesday, 30 July 2013 at 20:57:55 UTC, Jesse Phillips wrote:
 On Tuesday, 30 July 2013 at 20:09:01 UTC, Milvakili wrote:

 So I need to pass them as char*, I can not pass them as string?

C++ doesn't have an immutable(char)[], it has char* and its own string class. To communicate with it one must choose a type both languages can understand, char* is that. D may be able to get away with providing a C++ string, but I'm not familiar with the integration layer.

const char*, char const* are equivalent to const(char)* in D.
Jul 30 2013
prev sibling next sibling parent "Jesse Phillips" <jessek.phillips+D gmail.com> writes:
On Wednesday, 31 July 2013 at 05:44:38 UTC, deadalnix wrote:
 On Tuesday, 30 July 2013 at 20:57:55 UTC, Jesse Phillips wrote:
 On Tuesday, 30 July 2013 at 20:09:01 UTC, Milvakili wrote:

 So I need to pass them as char*, I can not pass them as 
 string?

C++ doesn't have an immutable(char)[], it has char* and its own string class. To communicate with it one must choose a type both languages can understand, char* is that. D may be able to get away with providing a C++ string, but I'm not familiar with the integration layer.

const char*, char const* are equivalent to const(char)* in D.

I was referring to string types: http://www.cplusplus.com/reference/string/string/ If his C++ functions takes a const char* he should be passing it a const/immutable(char)* as you say, not an immutable(char)[]
Jul 30 2013
prev sibling next sibling parent "deadalnix" <deadalnix gmail.com> writes:
On Wednesday, 31 July 2013 at 06:07:07 UTC, Jesse Phillips wrote:
 On Wednesday, 31 July 2013 at 05:44:38 UTC, deadalnix wrote:
 On Tuesday, 30 July 2013 at 20:57:55 UTC, Jesse Phillips wrote:
 On Tuesday, 30 July 2013 at 20:09:01 UTC, Milvakili wrote:

 So I need to pass them as char*, I can not pass them as 
 string?

C++ doesn't have an immutable(char)[], it has char* and its own string class. To communicate with it one must choose a type both languages can understand, char* is that. D may be able to get away with providing a C++ string, but I'm not familiar with the integration layer.

const char*, char const* are equivalent to const(char)* in D.

I was referring to string types: http://www.cplusplus.com/reference/string/string/ If his C++ functions takes a const char* he should be passing it a const/immutable(char)* as you say, not an immutable(char)[]

In C++, std::string own its own copy of the string. So it is safe to pass a D const char* to the constructor.
Jul 31 2013
prev sibling next sibling parent "monarch_dodra" <monarchdodra gmail.com> writes:
On Wednesday, 31 July 2013 at 05:56:13 UTC, Ali Çehreli wrote:
 On 07/30/2013 02:17 PM, JS wrote:

 A C++ string is null terminated while a D string is not.

I think the OP means std::string, which also is not zero-terminated. (On the other hand, C strings are zero-terminated.)

You can obtain a 0 terminated "const char*" from an std::string in 0(1) using c_str. While the C++98 standard doesn't mandate it, the above requirement means that 99% of string implementations are using arraysinternally. The C++11 standard *does* mandate that the implementation use arrays internally. So that means you can get a valid char* from &myString[0]. That string will not be 0 terminated, but all the major compiler providers (AFAIK) make that guarantee as an extension.
Jul 31 2013
prev sibling next sibling parent "monarch_dodra" <monarchdodra gmail.com> writes:
On Tuesday, 30 July 2013 at 20:53:55 UTC, Dicebot wrote:
 On Tuesday, 30 July 2013 at 20:22:46 UTC, Milvakili wrote:
 Thanks.  Is there any work in progress related to string 
 passing?
 I think string passing should be solved automatically.

 Coz otherwise one need to write wrapper for each c++ function 
 that has a string parameter.

 thanks.

c_function(toStringz("hello").ptr) does not seem that bad, what is the issue here?

I want to point out that one of the dangers (as I've heard it, I haven't actually run into this), is that since the garbage collector won't scan your C function's internals, the caller *must* keep a reference in his code to preserve the allocated array. The above scheme does not do that: If the garbage collector runs after the start of the call, but before the end of the function, then you are in trouble. I'd do it like this: //---- string lifeline = toStringz("hello"); //Preserve reference c_function(lifeline.ptr); //Make call lifeline = null; //Release reference //---- That's assuming c_function won't preserve the string somewhere. If it does, things get more complicated.
Jul 31 2013
prev sibling next sibling parent "deadalnix" <deadalnix gmail.com> writes:
On Wednesday, 31 July 2013 at 07:54:00 UTC, monarch_dodra wrote:
 I want to point out that one of the dangers (as I've heard it, 
 I haven't actually run into this), is that since the garbage 
 collector won't scan your C function's internals, the caller 
 *must* keep a reference in his code to preserve the allocated 
 array. The above scheme does not do that: If the garbage 
 collector runs after the start of the call, but before the end 
 of the function, then you are in trouble.

You have to work very hard to not find it either in the stack or in a register during the function call. So as long as the C code do not keep a reference, ou should be fine.
 I'd do it like this:
 //----
 string lifeline = toStringz("hello"); //Preserve reference
 c_function(lifeline.ptr); //Make call
 lifeline = null; //Release reference
 //----

This is plain useless. dead store elimination will remove the "release reference", the variable lifeline promoted to register (its ptr part anyway, it length part is likely to be trashed right away by the function call). On X86, the string will be returned in EAX and EDX (ptr in EDX if I'm not mistaken). So the length will be erased by copying EDX to EAX (now both contains the ptr, length is lost) and the call made right away (first argument is passed in EAX).
 That's assuming c_function won't preserve the string somewhere. 
 If it does, things get more complicated.

If it does, that means big trouble, even in pure C, as it is now unclear who is the owner of the string. A recipe for memory corruption/leak.
Jul 31 2013
prev sibling next sibling parent "monarch_dodra" <monarchdodra gmail.com> writes:
On Wednesday, 31 July 2013 at 08:20:48 UTC, deadalnix wrote:
 On Wednesday, 31 July 2013 at 07:54:00 UTC, monarch_dodra wrote:
 I want to point out that one of the dangers (as I've heard it, 
 I haven't actually run into this), is that since the garbage 
 collector won't scan your C function's internals, the caller 
 *must* keep a reference in his code to preserve the allocated 
 array. The above scheme does not do that: If the garbage 
 collector runs after the start of the call, but before the end 
 of the function, then you are in trouble.

You have to work very hard to not find it either in the stack or in a register during the function call. So as long as the C code do not keep a reference, ou should be fine.
 I'd do it like this:
 //----
 string lifeline = toStringz("hello"); //Preserve reference
 c_function(lifeline.ptr); //Make call
 lifeline = null; //Release reference
 //----

This is plain useless. dead store elimination will remove the "release reference", the variable lifeline promoted to register (its ptr part anyway, it length part is likely to be trashed right away by the function call). On X86, the string will be returned in EAX and EDX (ptr in EDX if I'm not mistaken). So the length will be erased by copying EDX to EAX (now both contains the ptr, length is lost) and the call made right away (first argument is passed in EAX).

I'll take your word for it then. As I said, I didn't run into this before, but I had read some other posts that mentioned this problem. I might have it confused with dynamic libs though? I don't know. Forget I said anything then.
 That's assuming c_function won't preserve the string 
 somewhere. If it does, things get more complicated.

If it does, that means big trouble, even in pure C, as it is now unclear who is the owner of the string. A recipe for memory corruption/leak.

Most probably.
Jul 31 2013
prev sibling next sibling parent "John Colvin" <john.loughran.colvin gmail.com> writes:
On Wednesday, 31 July 2013 at 02:53:47 UTC, Walter Bright wrote:
 On 7/30/2013 3:01 PM, John Colvin wrote:
 $ dmd -c -m64 inter.d
 $ g++ -c -m64 inter.cpp -ointercpp.o
 $ g++ inter.o intercpp.o -ointer -lphobos2
 inter.o: In function `_Dmain':
 inter.d:(.text._Dmain+0x48): undefined reference to 
 `printString(CppString*)'
 collect2: error: ld returned 1 exit status

You might want to look at the name mangling of the D printString vs the name mangling of the C++ one.

Woops, yeah that was never going to work. However, I did come across something interesting: gcc mangles std::string as "Ss", see here: https://github.com/mirrors/gcc/blob/master/gcc/cp/mangle.c#L472 It also doesn't bother mangling the length for these special case identifiers, e.g. void printString(string * s, BLAHBLAH fdsa) becomes _Z11printStringPSs8BLAHBLAH Any ideas how we can get around this? Some std c++ type instrinsics in dmd?
Jul 31 2013
prev sibling next sibling parent "Dicebot" <public dicebot.lv> writes:
On Wednesday, 31 July 2013 at 13:15:58 UTC, John Colvin wrote:
 Any ideas how we can get around this? Some std c++ type 
 instrinsics in dmd?

Maybe http://d.puremagic.com/issues/show_bug.cgi?id=10077 can help?
Jul 31 2013
prev sibling next sibling parent "Jesse Phillips" <Jesse.K.Phillips+D gmail.com> writes:
On Wednesday, 31 July 2013 at 07:17:15 UTC, deadalnix wrote:
 In C++, std::string own its own copy of the string. So it is 
 safe to pass a D const char* to the constructor.

I thought the discussion was about: void someFun(std::string arg) { ...} Pass D string to someFun. I do not believe const(char) * is valid.
Jul 31 2013
prev sibling parent "John Colvin" <john.loughran.colvin gmail.com> writes:
On Wednesday, 31 July 2013 at 13:39:29 UTC, Dicebot wrote:
 On Wednesday, 31 July 2013 at 13:15:58 UTC, John Colvin wrote:
 Any ideas how we can get around this? Some std c++ type 
 instrinsics in dmd?

Maybe http://d.puremagic.com/issues/show_bug.cgi?id=10077 can help?

Yikes that's quite a rabbit hole, it's all a bit beyond me tbh.
Jul 31 2013