views:

602

answers:

8

I have a dll that must be useable from C etc, so I cant use string objects etc as a normal would, but I'm not sure on how to do this safely..

const char *GetString()
{
    std::stringstream ss;
    ss << "The random number is: " << rand();
    return ss.str().c_str();
}

could the c string be destroyed when ss falls off the stack? I'm assuming so...

Another option may be to create a new string on the heap, but what is going to deallocate that?

const char *GetString()
{
    std::stringstream ss;
    ss << "The random number is: " << rand();
    char *out = new char[ss.str().size()];
    strcpy(ss.str().c_str(), out);
    return out;//is out ever deleted?
}

The same goes for pointers to other things as well as strings.

+1  A: 

The first would actually not work because the stringstream deallocates it's space on destruction. So if you try to de-reference that pointer there is a good chance that your program would crash.

The second option you mention is how it's usually done and the user of the function is required to deallocate the space. IIf this is a C program which uses the function make sure you allocate with malloc() and free with free()

Another option is to return an address of a static char array. This is relevant if you know in advance a good upper bound to the length. More importantly this should be used ONLY if there is no chance that the function is going to be called from two different threads at the same time because using a static array essentially makes your function non-reentrant.

shoosh
Ok, well my dll is actauly for python, so whats the best way to do this? Should I wrap the dll functions up in python functions that say "Call dll function; Call dll deallocate function"? I'm assuming python makes a completly new string, rather than just wrapping an object around it?
Fire Lancer
+1  A: 

Well obviously anytime you are returning pointers to memory allocated inside a function the deallocating must come externally, unless you are using garbage collection. If you don't want to do this, allocate a character buffer b efore calling GetString() and change the prototype to

int get_string(const char* buffer);

Then fill up the buffer. But returning a point to malloced data is fine.

and please, pass also the size of the buffer!
quinmars
int get_string(const char* buffer, int bufferSize); <-- like so...
xan
A: 

If you declare ss as static you can avoid the problem. This could be a good solution if your program runs on a single-thread enviroment.

jab
But the result gets invalid, once the function is called a second time. From that moment on, only the result from the second call is valid.
Wimmel
making the scream static will make thing much worse. Are they going to share teh string? Does the stream guarantee the pointer won't change (no)? Will it delete the pointer on a re-assignment (yes)? The first caller will be left with a dead pointer after a second call, etc. No go
n-alexander
A: 

You have to allocate the string on the heap if you want to safely return it, also allocate with malloc() i.s.o. new() when writing C functions.

When you return pointers (and, unlike in C++, in C you have no real choice many times), deallocation is always a concern. There isn't really a definitive solution.

One way of handling this I've seen in quite some API's is calling all function either

CreateString()

When memory needs to be deallocated by the caller, and

GetString()

when that's not an issue.

This is anything but foolproof of course, but given enough discipline it's the best method I've seen to be honest...

Pieter
+4  A: 

The first variant doesn't work because you're returning a pointer into a stack object, which will get destroyed. (More presisely, you return a pointer to a heap memory, whch will have been deleted().) Worse still, it may even work for some time, if nobody's overwriting the memory, making it very hard to debug.

Next, you can not return a const char* unless you return a pointer to a static string like this:

const char *GetString()
{
    return "a static string in DATA segment - no need to delete";
}

You second variant has the problem of returning memory allocated with new() into a C program that will call free(). Those may not be compatible.

If you return a string to C, there are 2 way to do that:

char *GetString()
{
    std::stringstream ss;
    ss << "The random number is: " << rand();
    return strdup( ss.str().c_str() ); // allocated in C style with malloc()
}

void foo()
{
    char *p = GetString();
    printf("string: %s", p));
    free( p ); // must not forget to free(), must not use delete()
}

or:

char *GetString(char *buffer, size_t len)
{
    std::stringstream ss;
    ss << "The random number is: " << rand();
    return strncpy(buffer, ss.str().c_str(), len); // caller allocates memory
}

void foo()
{
    char buffer[ 100 ];
    printf("string: %s", GetString(buffer, sizeof( buffer ))); // no memory leaks
}

depending on you memory handling policy.

As a rule, you can NOT ever return a pointer or a reference to an automatic object in C++. This is one of common mistakes analyzed in many C++ books.

n-alexander
A: 

If thread-safety is not important,

const char *GetString()
{
    static char *out;
    std::stringstream ss;
    ss << "The random number is: " << rand();
    delete[] out;
    char *out = new char[ss.str().size()];
    strcpy(ss.str().c_str(), out);
    return out;//is out ever deleted?
}

Then the function can take over the responsibility of deallocating the string.

If thread-safety is important,

Then the best method is to pass it in as an argument, as in,

void GetString(char *out, int maxlen);

I observe this is what happens when the old non thread-safe APIs are changed to thread-safe.

sep
1) what is the reason to make out static?2) you can't return const char*3) you mastn't return new-ed memory into a C program
n-alexander
1) So that the address of the last new-ed memory is retained in between calls to GetString.2) Ok, agreed that C does not have const. Return char * then.3) It doesn't matter as long as the C program doesn't call free on it.
sep
A: 

After the function is called, you will want the caller to be responsible for the memory of the string (and especially for de-allocating it). Unless you want to use static variables, but there be dragons! The best way to do this cleanly is to have the caller do the allocation of the memory in the first place:

void foo() {
  char result[64];
  GetString(result, sizeof(result));
  puts(result);
}

and then GetString should look like this:

int GetString(char * dst, size_t len) {
  std::stringstream ss;
  ss << "The random number is: " << rand();
  strncpy(ss.str().c_str(), dst, len);
}

Passing the maximum buffer length and using strncpy() will avoid accidentally overwriting the buffer.

Enno
+1  A: 

Over the years C boiled this down to 2 standard methods:

  • Caller passes in buffer.
    There are three versions of this.
    Version 1: Pass a buffer and a length.
    Version 2: Documentation specifies an expected min buffer size.
    Version 3: Pre-Flight. Function returns the min buffer required. caller calls twice first time with a NULL buffer.
    • Example: read()
  • Use a static buffer that is valid until the next call.
    • Example: tmpname()

A few non standard ones returned memory that you had to explicitly free

  • strdup() pops to mind.
    Common extension but not actually in the standard.
Martin York