return string from c++ to c

  • Thread starter Michael Rasmussen
  • Start date
M

Michael Rasmussen

Hi all,

Not sure if this is OT?

I have a function in a library written in C++ which returns a
strdup(s.c_str()) to an application written i C. Running Valgrind on my
C-application shows this results in memory leaks due to the c_str is never
freed.

Example:

Foo.h

#ifdef __cplusplus

class Foo {
public:
std::string getString() { return s; }

private:
std::string s;
};

#else

typedef struct Foo Foo;

#endif

#ifdef __cplusplus

extern "C" {

#endif

extern char *cplusplus_getString(Foo *); // C++ call-back
extern char *get_string(Foo *); // C function

#ifdef __cplusplus

}

#endif

Foo.cc

char *cplusplus_getString(Foo *foo)
{
std::string s;

s = foo->getString();
return strdup(s.c_str()) // this is coursing memory leaks.
}

main.c

char *get_string(struct Foo *foo)
{
return cplusplus_getString(foo);
}

int main()
{
struct Foo *foo;

printf("%s\n", get_string());
return 0;
}
 
J

Jim Langston

Michael Rasmussen said:
Hi all,

Not sure if this is OT?

I have a function in a library written in C++ which returns a
strdup(s.c_str()) to an application written i C. Running Valgrind on my
C-application shows this results in memory leaks due to the c_str is never
freed.

Example:

Foo.h

#ifdef __cplusplus

class Foo {
public:
std::string getString() { return s; }

private:
std::string s;
};

#else

typedef struct Foo Foo;

#endif

#ifdef __cplusplus

extern "C" {

#endif

extern char *cplusplus_getString(Foo *); // C++ call-back
extern char *get_string(Foo *); // C function

#ifdef __cplusplus

}

#endif

Foo.cc

char *cplusplus_getString(Foo *foo)
{
std::string s;

s = foo->getString();
return strdup(s.c_str()) // this is coursing memory leaks.
}

Have you tried a simple
return s.c_str();

This will return a const char* to a buffer inside of s that remains until
changes are made to s. As long as your main.c doesn't try to change the
contents, everything should (read probably) work.
 
M

Michael Rasmussen

Have you tried a simple
return s.c_str();

This will return a const char* to a buffer inside of s that remains until
changes are made to s. As long as your main.c doesn't try to change the
contents, everything should (read probably) work.
Yes. It produces this output from Valgrind:
==7412== Invalid read of size 2
==7412== at 0x40BBA1A: mempcpy (in /lib/tls/i686/cmov/libc-2.3.5.so)
==7412== by 0x408D28B: vfprintf (in /lib/tls/i686/cmov/libc-2.3.5.so)
==7412== by 0x4092B02: printf (in /lib/tls/i686/cmov/libc-2.3.5.so)
==7412== by 0x804881C: main (main.c:56)
==7412== Address 0x429DB4C is 12 bytes inside a block of size 109 free'd
==7412== at 0x401C304: operator delete(void*) (vg_replace_malloc.c:246)
==7412== by 0x421733C: std::string::_Rep::_M_destroy(std::allocator<char> const&) (in /usr/lib/libstdc++.so.6.0.7)
==7412== by 0x42184E7: std::string::~string() (in
/usr/lib/libstdc++.so.6.0.7)

return s.c_str() and return strdup(s.c_str()) is coursing memory leaks.
Any other way to return a string as a C-string? Would returning a char
buffer not give me the same problems?
 
R

red floyd

Michael said:
Hi all,

Not sure if this is OT?

I have a function in a library written in C++ which returns a
strdup(s.c_str()) to an application written i C. Running Valgrind on my
C-application shows this results in memory leaks due to the c_str is never
freed.

Actually, the leak is due to the memory allocated by strdup() never
being freed.
 
J

Jim Langston

red floyd said:
Actually, the leak is due to the memory allocated by strdup() never being
freed.

Well, I guess you'll have to do it the ugly C style way then.

Pass a char* from C to the C++ function with preallocated space and strcpy
into that.
 
V

Victor Bazarov

Jim said:
Have you tried a simple
return s.c_str();

How is that going to help? You're returning a pointer to something
that comes out of a local object. You're essentially returning the
local object. This is called "a dangling pointer".
This will return a const char* to a buffer inside of s that remains
until changes are made to s. As long as your main.c doesn't try to
change the contents, everything should (read probably) work.

What are you talking about? As soon as the closing curly brace is
reached, the 's' is _destroyed_.

AFAIK, 'strdup' is not a standard function, but using 'strdup' is
probably the only way to preserve that string. You can, of course,
do 'malloc' and 'strcpy', which probably what 'strdup' does under
the covers anyway.

V
 
V

Victor Bazarov

Jim said:
Well, I guess you'll have to do it the ugly C style way then.

Pass a char* from C to the C++ function with preallocated space and
strcpy into that.

I suppose it might be a simpler solution. Of course, the C program
could always simply call 'free' for that pointer after it's done with
it, no? I guess, "man strdup" is in order...

V
 
J

Jim Langston

Victor Bazarov said:
I suppose it might be a simpler solution. Of course, the C program
could always simply call 'free' for that pointer after it's done with
it, no? I guess, "man strdup" is in order...

V

Best I could come up with is this:
const char* f(const char* text)
{
static char returnthis[1000];
std::string tempstring(text);
strcpy( returnthis, tempstring.c_str() );
return returnthis;
}
 
V

Victor Bazarov

Jim said:
Victor Bazarov said:
I suppose it might be a simpler solution. Of course, the C program
could always simply call 'free' for that pointer after it's done with
it, no? I guess, "man strdup" is in order...

V

Best I could come up with is this:
const char* f(const char* text)
{
static char returnthis[1000];
std::string tempstring(text);
strcpy( returnthis, tempstring.c_str() );
return returnthis;
}

Uh... What exactly is the point of all this? If you pass a char const*
in and you expect a char const* out, why do you need all this dancing
around with a static array, with an automatic std::string? It seems that
the intention here is

const char* useless_function(const char* text)
{
return text;
}

Of course, the OP had a C++ object with a _member_ of type 'std::string'
and needed to extract the contents somewhere... You must have simplified
the original intention just a tad too much.

V
 
M

Michael Rasmussen

Best I could come up with is this:
const char* f(const char* text)
{
static char returnthis[1000];
std::string tempstring(text);
strcpy( returnthis, tempstring.c_str() ); return returnthis;
}
}
I was given a more "clean" solution on a Linux list. The solution is to
create a string as a member of the class and then use this string as a
temporary storage. From this member you are allowed to return s.c_str()
since this string will be alive as long as the instance of the class. This
is also, in my opinion, a bether solution since it keeps encapsulation -
You will have to call the class if you need any change. More correct OOP
style.

Correct example:

Foo.h

#ifdef __cplusplus

class Foo {
public:
std::string cstring;
std::string getString() { return s; }

private:
std::string s;
};

#else

typedef struct Foo Foo;

#endif

#ifdef __cplusplus

extern "C" {

#endif

extern const char *cplusplus_getString(Foo *); // C++ call-back extern
const char *get_string(Foo *); // C function

#ifdef __cplusplus


}
#endif

Foo.cc

const char *cplusplus_getString(Foo *foo)
{
cstring = foo->getString();
return cstring.c_str() // no memory leak since it is destroyed from
the class' destructor.
}

main.c

const char *get_string(struct Foo *foo)
{
return cplusplus_getString(foo);
}

int main()
{
struct Foo *foo;

printf("%s\n", get_string());
return 0;
}
 
V

Victor Bazarov

Michael said:
Best I could come up with is this:
const char* f(const char* text)
{
static char returnthis[1000];
std::string tempstring(text);
strcpy( returnthis, tempstring.c_str() ); return returnthis;
}
}
I was given a more "clean" solution on a Linux list. The solution is
to create a string as a member of the class and then use this string
as a temporary storage. From this member you are allowed to return
s.c_str() since this string will be alive as long as the instance of
the class. This is also, in my opinion, a bether solution since it
keeps encapsulation - You will have to call the class if you need any
change. More correct OOP style.

Correct example:

Foo.h

#ifdef __cplusplus

class Foo {
public:
std::string cstring;
std::string getString() { return s; }

private:
std::string s;
};

#else

typedef struct Foo Foo;

#endif

#ifdef __cplusplus

extern "C" {

#endif

extern const char *cplusplus_getString(Foo *); // C++ call-back
extern const char *get_string(Foo *); // C function

#ifdef __cplusplus


}
#endif

Foo.cc

const char *cplusplus_getString(Foo *foo)
{
cstring = foo->getString();

What's "cstring"? I don't see it declared anywhere. The fact
that 'Foo' has a member with the same name notwithstanding.
return cstring.c_str() // no memory leak since it is destroyed from
the class' destructor.
}

main.c

const char *get_string(struct Foo *foo)
{
return cplusplus_getString(foo);
}

int main()
{
struct Foo *foo;

printf("%s\n", get_string());
return 0;
}

I don't think this is working code.

V
 
J

Jim Langston

Michael Rasmussen said:
Best I could come up with is this:
const char* f(const char* text)
{
static char returnthis[1000];
std::string tempstring(text);
strcpy( returnthis, tempstring.c_str() ); return returnthis;
}
}
I was given a more "clean" solution on a Linux list. The solution is to
create a string as a member of the class and then use this string as a
temporary storage. From this member you are allowed to return s.c_str()
since this string will be alive as long as the instance of the class. This
is also, in my opinion, a bether solution since it keeps encapsulation -
You will have to call the class if you need any change. More correct OOP
style.

Correct example:

Foo.h

#ifdef __cplusplus

class Foo {
public:
std::string cstring;
std::string getString() { return s; }

private:
std::string s;
};

#else

typedef struct Foo Foo;

#endif

#ifdef __cplusplus

extern "C" {

#endif

extern const char *cplusplus_getString(Foo *); // C++ call-back extern
const char *get_string(Foo *); // C function

#ifdef __cplusplus


}
#endif

Foo.cc

const char *cplusplus_getString(Foo *foo)
{
cstring = foo->getString();
return cstring.c_str() // no memory leak since it is destroyed from
the class' destructor.
}

main.c

const char *get_string(struct Foo *foo)
{
return cplusplus_getString(foo);
}

int main()
{
struct Foo *foo;

printf("%s\n", get_string());
return 0;
}

Okay, now I'm confused. Both cstring and s are members of the class Foo,
correct? The only difference is that s is declared private and cstring is
declared public. So all you are doing is copying s to a public variable so
you can call .c_str() on it. So, again like I first suggested, why don't
you just return s.c_str()? Just add a method to Foo:

const char* c_str() { return s.c_str(); } const;

then it's even simpler:

const char* cplusplus_getString(Foo* foo)
{
return Foo->c_str();
}

Same thing, just one level of indirection less. If calling it c_str() bugs
you (it might bug me) call it cstring() or something.
 
J

Jim Langston

Victor Bazarov said:
Jim said:
Victor Bazarov said:
Jim Langston wrote:
Michael Rasmussen wrote:
Hi all,

Not sure if this is OT?

I have a function in a library written in C++ which returns a
strdup(s.c_str()) to an application written i C. Running Valgrind
on my C-application shows this results in memory leaks due to the
c_str is never
freed.


Actually, the leak is due to the memory allocated by strdup() never
being freed.

Well, I guess you'll have to do it the ugly C style way then.

Pass a char* from C to the C++ function with preallocated space and
strcpy into that.

I suppose it might be a simpler solution. Of course, the C program
could always simply call 'free' for that pointer after it's done with
it, no? I guess, "man strdup" is in order...

V

Best I could come up with is this:
const char* f(const char* text)
{
static char returnthis[1000];
std::string tempstring(text);
strcpy( returnthis, tempstring.c_str() );
return returnthis;
}

Uh... What exactly is the point of all this? If you pass a char const*
in and you expect a char const* out, why do you need all this dancing
around with a static array, with an automatic std::string? It seems that
the intention here is

const char* useless_function(const char* text)
{
return text;
}

Of course, the OP had a C++ object with a _member_ of type 'std::string'
and needed to extract the contents somewhere... You must have simplified
the original intention just a tad too much.

No, the intention here is to return a cstyle string as a char* from a
std::string that stays alive.
 
M

Michael Rasmussen

What's "cstring"? I don't see it declared anywhere. The fact that 'Foo'
has a member with the same name notwithstanding.
Sorry, a typo:)
should be:
const char *cplusplus_getString(Foo *foo) {
foo->cstring = foo->getString();
return foo->cstring.c_str() // no memory leak since it is destroyed
from the class' destructor.
}
 
M

Michael Rasmussen

Okay, now I'm confused. Both cstring and s are members of the class Foo,
correct? The only difference is that s is declared private and cstring is
declared public. So all you are doing is copying s to a public variable
so you can call .c_str() on it. So, again like I first suggested, why
don't you just return s.c_str()? Just add a method to Foo:
s, as you mention, is a private member in which case you cannot access it
thrue the struct. Only public members are accessible thrue a struct. const
char *cplusplus_getString(Foo *foo) is a free function which is not a
member of class Foo in which case it will only have access to the members
of class Foo thrue the struct Foo (typedef struct Foo Foo)
 
J

Jim Langston

Michael Rasmussen said:
s, as you mention, is a private member in which case you cannot access it
thrue the struct. Only public members are accessible thrue a struct. const
char *cplusplus_getString(Foo *foo) is a free function which is not a
member of class Foo in which case it will only have access to the members
of class Foo thrue the struct Foo (typedef struct Foo Foo)

Hence, adding the method c_str() to Foo. Or is there a problem with C code
handling C++ structures that aren't POD?
 
A

Alf P. Steinbach

* Michael Rasmussen:
Not sure if this is OT?

It's always on topic to bring a little fun & confusion to the group.

I have a function in a library written in C++ which returns a
strdup(s.c_str()) to an application written i C. Running Valgrind on my
C-application shows this results in memory leaks due to the c_str is never
freed.

Example:

Foo.h

#ifdef __cplusplus

class Foo {
public:
std::string getString() { return s; }

private:
std::string s;
};

#else

typedef struct Foo Foo;

What's that meant to accomplish?

#endif

#ifdef __cplusplus

extern "C" {

#endif

extern char *cplusplus_getString(Foo *); // C++ call-back
extern char *get_string(Foo *); // C function

#ifdef __cplusplus

}

#endif

Better just do

#ifdef __cplusplus
# define EXTERN_C extern "C"
#else
# define EXTERN_C extern
#endif

then use EXTERN_C in your function declarations.

Foo.cc

char *cplusplus_getString(Foo *foo)
{
std::string s;

s = foo->getString();
return strdup(s.c_str()) // this is coursing memory leaks.
}

The function above does not cause a memory leak.

It is however needlessly complex & inefficient.

Just do

{
return strdup( foo->getString().c_str() );
}

(and be aware that 'strdup' is not a standard C or C++ function,
although I think it is Posix standard -- check it out).

main.c

char *get_string(struct Foo *foo)
{
return cplusplus_getString(foo);
}

int main()
{
struct Foo *foo;

Where do you initialize that pointer? You'll have to somehow get
it from the C++ code.

printf("%s\n", get_string());

Run that through 'lint' to discover the missing argument.

Also, here you have a memory leak.

The allocated memory is not freed.
 
M

Michael Rasmussen

What's that meant to accomplish?
See this example:
http://www.parashift.com/c++-faq-lite/mixing-c-and-cpp.html#faq-32.8
The function above does not cause a memory leak.

It is however needlessly complex & inefficient.

Just do

{
return strdup( foo->getString().c_str() );
}
}
(and be aware that 'strdup' is not a standard C or C++ function,
although I think it is Posix standard -- check it out).
Testing with Valgrind shows that strdup indeed courses memory leaks
unless you explicitely free the returned pointer (strdup returns a char*).
This is ok as long as you are doing both back-end and front-end, but not
ok if you are making are shared library.
Where do you initialize that pointer? You'll have to somehow get it
from the C++ code.
I know. For simplicity I left out parts which was not directly connected
to the problem.
Run that through 'lint' to discover the missing argument.
What argument?
 
A

Alf P. Steinbach

* Michael Rasmussen:

OK. I'd put the pure C++ stuff (the class definition) in a pure C++
header,. But perhaps that's more of a personal preference.

Testing with Valgrind shows that strdup indeed courses memory leaks

In that case you have a defective implementation, but I think you're
simply misinterpreting the results: strdup doesn't cause a memory leak,
but forgetting to deallocate can cause a memory leak.

unless you explicitely free the returned pointer (strdup returns a char*).

Yes, see the posting you replied to, where I pointed out that memory
leak in your original code.

This is ok as long as you are doing both back-end and front-end, but not
ok if you are making are shared library.

It's not OK in any context except when you're allocating globals (as
e.g. some runtime library implementations do).
I know. For simplicity I left out parts which was not directly connected
to the problem.

That's poor practice: you're asking for advice on some code A you don't
show, and show something else, B, instead, pretending it is A.

What argument?

Did you try 'lint'?

If not, and you absolutely don't want to do that (or cannot do that),
check the line that that comment applied to. There are two function
calls. One of them lacks an argument.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,772
Messages
2,569,593
Members
45,108
Latest member
AlbertEste
Top