John Smith said:
Hi,
I'm writing a library in C++ which is supposed to be used by people using C.
One function I have must return a string to users which is arbitrary length.
The user must be able to use this string as "char *" so I was wondering if
the following construct is safe:
char *Func()
{
static string str;
str = "foobar...";
return (char *) str.c_str();
}
The problem is that before the function call the length of the string is
unknown.
An alternative is to use malloc or new and provide that pointer to the user.
The memory allocated could then be deallocated later on.
Thanks in advance.
-- John
Well .. I think passing a char * allocated from inside the C++ to an
external C routine is fundamentally unsafe .. it would be much better
to pass in an allocated char * and an int describing the length as
suggested by another user. However I understand you might not have
much choice if you are dealing with legacy code. Lets assume you can
guarantee that the C code will not try to access beyond the end of the
string .. otherwise you are sunk. Lets also assume that the C-code
will not try to de-allocate the memory for the char *.
If both of the above are true, I think a factory approach like the
following might be useful:
#include <vector>
#include <string>
using std::string;
using std::vector;
class string_maker : vector<char *> {
public:
char * new_string(string s) { // you could also use const char
char *p = new char[s.length()+1];
s.copy(p,string::npos);
p[s.length()] = 0; // add C-style string terminator
push_back(p);
return p;
}
~string_maker() {
// free memory
vector<char *>::iterator I=begin();
for ( ; I!=end(); ++I)
delete [] *I;
}
};
// put this in the same translation unit as Func below.
namespace {
string_maker repository;
}
// now for your function
int Func(char *&p) {
string return_string;
// dynamic generation of return_string contents
p=repository.new_string(return_string);
return return_string.length()+1;
}
Notice I have changed the declaration of Func so that it returns the
length of the string as an int, which is essential so that the C-code
calling the function can make sure not to overrun the result. The
(unallocated) char * addressing the result is passed *by reference* to
Func as an argument. (Leaving out the reference means only the local
copy is modified).
Now, while I would really not advise doing things this way, I guess
this might work for you, given the caveats above. At least the
allocation and deallocation of memory is handled in a well defined
way, and you can be fairly sure that the char *'s being passed between
C and C++ will not be invalidated before the program exit.
Of course, many improvements could be made to the string_maker class
above .. it is just a (hopefully) helpful idea.
HTH, Dave Moore