Returning strings from a dll and freeing C strings

BlitzMax Forums/BlitzMax Programming/Returning strings from a dll and freeing C strings

JoshK(Posted 2007) [#1]
I return strings from dlls like this:

global ReturnString:Byte ptr

Function GetString:Byte Ptr()
GCEnter()
ReturnString="Hello".tocstring()
return ReturnString
EndFunction


Now for the same of memory management, I believe it should be like this:
global ReturnString:Byte ptr

Function GetString:Byte Ptr()
GCEnter()
If ReturnString<>Null MemFree ReturnString
ReturnString="Hello".tocstring()
return ReturnString
EndFunction


However, this causes a crash in my program. Do I need to manually free the byte ptr like this?


skidracer(Posted 2007) [#2]
I find it is best to think symmetrical with memory. Usually this means the code that allocates the memory owns it and has the job of freeing it.

However there are often times where the object becomes owned by the function the value is passed / returned to, usually when a copy of the pointer is stored in some form that exists beyond the life of the function.

To keep things simple it is often best for any function to make and use it's own copy of the object to avoid a reference to the original string existing beyond the life of said function.


Koriolis(Posted 2007) [#3]
Yes you should.
However I wonder why you never actually return "ReturnString" here.
edit: @skidracer: in this case the ownership of ReturnString is clear, it is owned by the dll. I don't see any problem in this regard. The temporary ("ReturnString") is here, AFAICT, only to allow the buffer to survive until the caller has made a copy of it.


JoshK(Posted 2007) [#4]
The reason I returned a global is because once the function is over, a local byte ptr would not be guaranteed to exist.

The main program does this:
s$=stringfromcstring(GetString())

I'm still not clear on how this should be handled.


skidracer(Posted 2007) [#5]
The life time of the pointer itself is not an issue unless you are returning a reference to it.

As K says the logical analysis of your code is that the returned memory pointed to by the pointer is owned by the caller so logically it is up to the caller to delete it, a design which I don't particularly care for due to it's unsymmetrical nature.


marksibly(Posted 2007) [#6]
It looks like GetString() is missing a 'Return ReturnString'?

Currently it will return Null by default and s$=String.FromCString( GetString() ) will crash and burn...

Apart from that, it should work.


Koriolis(Posted 2007) [#7]
As K says the logical analysis of your code is that the returned memory pointed to by the pointer is owned by the caller so logically it is up to the caller to delete it, a design which I don't particularly care for due to it's unsymmetrical nature.

I said exactly the opposite. It's owned by the DLL, not the caller. The caller *copies* the buffer into a fresh new string.
Nothing unsymetrical. The DLL allocates the buffer, the DLL frees it. Fine. As I said the only problem I can see here is the string is not actually returned, which Josh might just have forgot to put in the post (but hopefully not in his actual code).


grable(Posted 2007) [#8]
This is what i use to copy data from a String directly to a buffer, bypassing ToCString()
i really wish the String class could do this ;)
#include "E:\Program Files\BlitzMax\mod\brl.mod\blitz.mod\blitz_string.h"

int CopyToCString( BBString *str, char *dst, int maxlen) {
	int i, sz;
	if( str->length+1 >= maxlen)
		sz = maxlen - 1;
	else
		sz = str->length;
	for( i=0; i<sz; i++) 
		dst[i] = str->buf[i];
	dst[sz] = 0;
	return sz;
}

Then you could alternatively do this:
Function GetString:Int( buffer:Byte Ptr, maxlen:Int)
   Return CopyToCString( "Hello World!", buffer, maxlen)
EndFunction



JoshK(Posted 2007) [#9]
I found a few spots where I was attempting to MemFree the pointer returned to the main program. Removing those errors fixed everything.

Again, I believe returning a local variable is a no-no here. For example, in PureBasic you had to return a global variable, because the moment the function exits, that variable no longer exists. It is probably still there, and will probably still work most of the time, but it will cause random memory errors here and there.