[Bind] Unexpected behavior when binding const char* to function

Hello,
This code proceces "xyz", contrary to user intention to bind "abc":
void echo(const char* s) {
cout << s << '\n';
}
int main()
{
string s("abc");
function

Hello,
Igor R
function
f = boost::bind(echo, s.c_str()); The expression x.c_str() returns a pointer that becomes invalid as soon as any non-const member function of std::string is called for x.
I know reason behind. But use is right to think "I've bind >>abc<<.
Binder should store it and use on f() invocation".
In real code the situation could be even worse - if f can be stored
for future use and invoked when s has already been destroyed.
Passing string instead of const char* behaves exactly how expected
void echo2(const string& s) {
cout << s << '\n';
}
int main() {
string s2 = "abc";
function
_______________________________________________ Boost-users mailing list Boost-users <at> lists.boost.org http://lists.boost.org/mailman/listinfo.cgi/boost-users
-- Piotr Jachowicz

I know reason behind. But use is right to think "I've bind >>abc<<. Binder should store it and use on f() invocation".
Note tht you do not bind c_str member function, but the *result* it produces. The result is *invalid* after the std::string changes -- according to the Standard (IIRC). If you wish to bind c_str and invoke it in a "lazy" manner - you can do this as well.

----- Original Message -----
From: "Piotr Jachowicz"
Hello,
Igor R
writes: function
f = boost::bind(echo, s.c_str()); The expression x.c_str() returns a pointer that becomes invalid as soon as any non-const member function of std::string is called for x.
I know reason behind. But use is right to think "I've bind >>abc<<. Binder should store it and use on f() invocation".
Actually what you told bind to bind is an address of a buffer. So it did exactly what you asked. It is not really bind's fault that you changed the contents. If you pass a mutatable object to anything it isn't really fair to complain that it was mutated and that change affected everthing that referenced that object. Unless some special interface was designed to allow you to specify that you want a copy made of the buffer it wouldn't be reasonable for the implementation to assume it should do that. The buffer could be massive. That kind of copying is what is specifically avoided intentionally by using pointers. ...perhap boost::bind could try and determine the size of the buffer and make an arbitrary decision on whether to copy or not but, that sounds even worse.
In real code the situation could be even worse - if f can be stored for future use and invoked when s has already been destroyed.
Passing string instead of const char* behaves exactly how expected
I suspect because a temporary object is created, like any function that takes a std::string receiving a char*. I also suspect the same happens as a result of binding any fundemental type. But when you use pointers ( to plain buffers or user-defined types) you have to be responisble for the object's lifetime. <snip>>> _______________________________________________

Piotr Jachowicz wrote:
Hello,
Igor R
writes: function
f = boost::bind(echo, s.c_str()); The expression x.c_str() returns a pointer that becomes invalid as soon as any non-const member function of std::string is called for x.
I know reason behind. But use is right to think "I've bind >>abc<<. Binder should store it and use on f() invocation".
Piotr - I don't think this is "unexpected behavior" to most. You have *not* bound abc. You have bound a pointer. What that pointer points to may change and when you use the pointer it may very well point to something different than at the time of the bind. This isn't "unexpected". Your bind is to a pointer. You need to either ensure that the value pointed at doesn't change or you can pass an object containing the data itself. michael -- ---------------------------------- Michael Caisse Object Modeling Designs www.objectmodelingdesigns.com

On 27 April 2010 20:55, Michael Caisse
Piotr -
I don't think this is "unexpected behavior" to most. You have *not* bound abc. You have bound a pointer. What that pointer points to may change and when you use the pointer it may very well point to something different than at the time of the bind.
This isn't "unexpected". Your bind is to a pointer. You need to either ensure that the value pointed at doesn't change or you can pass an object containing the data itself.
I've called it "unexpected", because f1() and f2() in code below
behaves differently:
void echo1(const char* c) {
cout << c << '\n';
}
void echo2(const string& s) {
cout << s << '\n';
}
int main() {
boost::function
michael
--
---------------------------------- Michael Caisse Object Modeling Designs www.objectmodelingdesigns.com
_______________________________________________ Boost-users mailing list Boost-users@lists.boost.org http://lists.boost.org/mailman/listinfo.cgi/boost-users
-- Piotr Jachowicz

On Tue, Apr 27, 2010 at 11:35:19PM +0200, Piotr Jachowicz wrote:
I've called it "unexpected", because f1() and f2() in code below behaves differently:
void echo1(const char* c) { cout << c << '\n'; }
void echo2(const string& s) { cout << s << '\n'; }
int main() { boost::function
f1 = boost::bind(echo1, "abc"); //ups! binding to address of temporary boost::function f2 = boost::bind(echo2, "abc"); //ok, bind copies temporary string("abc") f1(); f2(); }
First off, your first bind comment is incorrect, as string literals have static storage duration, and both examples behave as intended. So, according to you, no-one should ever be able to bind a function parameter without it magically being cloned, in a way I can't remotely imagine how to implement. If a developer does not understand strings, they should learn how to use strings, not horribly mutilate unrelated APIs. The bind rules are very simple. It copies whatever arguments you bind to it, and stores them internally. If you feed it a raw pointer, it copies the raw pointer value. If you pass it an object, it copies the object. It's all very simple, and if you want different lifetime semantics for say pointers, use a smart pointer like shared_ptr. -- Lars Viklund | zao@acc.umu.se

----- Original Message -----
From: "Piotr Jachowicz"
On 27 April 2010 20:55, Michael Caisse
wrote: Piotr -
I don't think this is "unexpected behavior" to most. You have *not* bound abc. You have bound a pointer. What that pointer points to may change and when you use the pointer it may very well point to something different than at the time of the bind.
This isn't "unexpected". Your bind is to a pointer. You need to either ensure that the value pointed at doesn't change or you can pass an object containing the data itself.
I've called it "unexpected", because f1() and f2() in code below behaves differently:
void echo1(const char* c) { cout << c << '\n'; }
void echo2(const string& s) { cout << s << '\n'; }
int main() { boost::function
f1 = boost::bind(echo1, "abc"); //ups! binding to address of temporary boost::function f2 = boost::bind(echo2, "abc"); //ok, bind copies temporary string("abc") f1(); f2(); }
Does this really behave differently? Because you should get the behavior you expect. Here you use a string literal to bind and although it is techically a pointer, the type in the strictest sense is char[4] and in order to change the memory where the string literal is stored you would be mucking around with the data segment and in most compilations you would change the literal for any place in your program that literal was used thereafter. Not to be too pedantic but, f1() and f2() don't behave differently - they each get different data (in your original case).
Incoming book "How to code with C++0x and not commit suicide" will have to contain advise "When you bind, ensure that function signature have no pointers; otherwise you are in trouble".
Using care with pointers has been a tenet since C.
To be serious: library should naturally promote its proper usage. Code like "boost::bind(echo1, "abc")" looks natural, does not emit any warning, and is dangerous. Cases like that promote stereotype "It's better to avoid Boost because it contains many traps". I think that compiler warning would save hours of debugging for many programmers.
The compiler is not going detect the problem. I have been of the opinion that boost has a cost that sometimes may be overlooked. It, like template programming in general, is has a pretty steep learning curve and, IMHO, challenges what may be gaps in basic knowledge. I would say because template programming is rooted in strong typing sensibilties even though it works to achieve type flexibilty. I can say this for myself - I have be a C++ programming for over 15 years and used templates, like the STL, but boost programming and other material of the generic programming meta-programming variety have challenged me to introspective of my knowledge. I would just observe the old saying "It is a poor craftsman who blames his tools" ;-)
michael
--
---------------------------------- Michael Caisse Object Modeling Designs www.objectmodelingdesigns.com
_______________________________________________ Boost-users mailing list Boost-users@lists.boost.org http://lists.boost.org/mailman/listinfo.cgi/boost-users
-- Piotr Jachowicz _______________________________________________ Boost-users mailing list Boost-users@lists.boost.org http://lists.boost.org/mailman/listinfo.cgi/boost-users

Incoming book "How to code with C++0x and not commit suicide" will have to contain advise "When you bind, ensure that function signature have no pointers; otherwise you are in trouble".
c_str() shouldn't be used outside the expression it's part of. This issue is unrelated either to bind or to C++0x. On the contrary, c_str() is a "bridge" from C++ to old plain C.
participants (5)
-
Eric Whitcombe
-
Igor R
-
Lars Viklund
-
Michael Caisse
-
Piotr Jachowicz