Re: [Boost-users] [Bind] Unexpected behavior when binding constchar* to function
Piotr Jachowicz wrote:
"Cases like that promote stereotype "It's better to avoid Boost because it contains many traps"
Hi Piotr, I hope that when you have absorbed the comments of others, and understood that a const char* is a pointer and a std::string is object and so of course they have behave differently under copying, then you will come back and disown the above comment. Otherwise a false "slur" against Boost will remain on the internet forever, which is unfair to its authors. Pete
My point is that difference between binding to echo and echo2 (string vs. const char*) should not lead do different behavior. Otherwise user has to be very careful on signature of bind'ed function.
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. That code is safe - the lifetime of string literals is the lifetime of
Passing a std::string vs a const char* to boost::bind should have as close as possible to same behaviour as passing that parameter to a normal C++ function.. the program. Just to be clear, using char*s as strings is tricky and error prone - but that is a problem with C++, not a problem with boost.
On 28 April 2010 11:04, Joseph Gauterin <joseph.gauterin@googlemail.com> wrote:
My point is that difference between binding to echo and echo2 (string vs. const char*) should not lead do different behavior. Otherwise user has to be very careful on signature of bind'ed function.
Passing a std::string vs a const char* to boost::bind should have as close as possible to same behaviour as passing that parameter to a normal C++ function..
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. That code is safe - the lifetime of string literals is the lifetime of the program.
Good point! Yes, it's my mistake. I should have write something like that: void f1(const string& s) { cout << s << '\n'; } function<void ()> make1(const string& s) { return bind(f1, s); //ok, bind copies s } void f2(const char* s) { //old-style method. Legacy code we reuse cout << s << '\n'; } function<void()> make2(const string& s) { return bind(f2, s.c_str()); //ups, bind copies pointer } int main() { string s("abc"); function<void ()> g1 = make1(s); function<void ()> g2 = make2(s); s = "xyz"; g1(); //produces "abc" g2(); //produces "xyz" }
Just to be clear, using char*s as strings is tricky and error prone - but that is a problem with C++, not a problem with boost. _______________________________________________ Boost-users mailing list Boost-users@lists.boost.org http://lists.boost.org/mailman/listinfo.cgi/boost-users
-- Piotr Jachowicz
On 28 April 2010 10:02, Pete Bartlett <pete@pcbartlett.com> wrote:
Piotr Jachowicz wrote:
"Cases like that promote stereotype "It's better to avoid Boost because it contains many traps"
Hi Piotr,
I hope that when you have absorbed the comments of others, and understood that a const char* is a pointer and a std::string is object and so of course they have behave differently under copying, then you will come back and disown the above comment. Otherwise a false "slur" against Boost will remain on the internet forever, which is unfair to its authors.
Ok, it looks that my perception of "easy to be misused" differs from yours. Nothing wrong with that. I apologize if somebody feel offended by my comments. It was not my intention. Boost contributors do fantastic work. No doubt. I only wanted to express concern that when binding to function accepting pointers (which is misuse) would cause issue in production code, then it can be seen as "problem with boost". Similar as problems with copy/owning semantic in CORBA to C++ mapping are often summarized as "problem with CORBA". I've observed it at very high decision level in big IT company.
Pete
_______________________________________________ Boost-users mailing list Boost-users@lists.boost.org http://lists.boost.org/mailman/listinfo.cgi/boost-users
-- Piotr Jachowicz
Okay, finally in a place where I can respond, I have been kind of chuckling at this thread as I have been reading it from my phone. On Wed, Apr 28, 2010 at 2:55 PM, Piotr Jachowicz <pjachowi@gmail.com> wrote:
On 28 April 2010 10:02, Pete Bartlett <pete@pcbartlett.com> wrote:
Piotr Jachowicz wrote:
"Cases like that promote stereotype "It's better to avoid Boost because it contains many traps"
Hi Piotr,
I hope that when you have absorbed the comments of others, and understood that a const char* is a pointer and a std::string is object and so of course they have behave differently under copying, then you will come back and disown the above comment. Otherwise a false "slur" against Boost will remain on the internet forever, which is unfair to its authors.
Ok, it looks that my perception of "easy to be misused" differs from yours. Nothing wrong with that.
No, the perception of "easy to be misused" is based on the C++ standard, hence it is an issue with your erroneous perception of not knowing C++ rules. It is (as I recall) defined in the standard that the c_str() member returned by an std::string's lifespan is only until the next termination point (generally a semicolon in C++). This function returns a "const char*", hence the pointer it returns can be considered worthless after the next termination point (especially since if you set the string to a string that was, oh, 50 characters long, then what pointer it returns could be deallocated, hence you can access deallocated memory if you saved it somewhere and tried to access it). In real life, the pointer is valid as long as the std::string is not reassigned in every STL implementation that I know of. Thus, in this example: """ void f2(const char* s) { cout << s << '\n'; } function<void()> make2(const string& s) { return bind(f2, s.c_str()); // line A } """ On line A you create a bound struct, basically what you do is the same thing as this: """ struct myBoundOp { const char *ptr; function<void(const char*)> fun; void operator()() { fun(ptr); // line C } } void f2(const char* s) { cout << s << '\n'; } function<void()> make2(const string& s) { myBoundOp bound; bound.fun = f2; bound.ptr = s.c_str(); // line B return bound; } """ So, on line B, you assign the pointer to the buffer in the std::string to a long-term pointer. Now, when you call the returned function, it will call line C, if what the pointer is pointing to changed, well it should be obvious what happens, just normal C++ (or heck, even C or assembler, nothing special here). Now, let's see if it with f1, that takes an std::string, but still as a pointer: """ struct myBoundOp { std::string *ptr; function<void(std::string*)> fun; void operator()() { fun(ptr); } } void f2(std::string* s) { cout << *s << '\n'; } function<void()> make2(const string& s) { myBoundOp bound; bound.fun = f2; bound.ptr = &s; // line D return bound; } """ Now, on line D, you are storing the pointer to the std::string itself, instead of its buffer, but hey, if you change the string before you call the callback, same thing will occur as what is going on with the c_str method. Now, if we try to do this with a std::string *object*, instead of a pointer: """ struct myBoundOp { std::string str; function<void(std::string)> fun; void operator()() { fun(str); } } void f2(std::string s) { cout << s << '\n'; } function<void()> make2(const string& s) { myBoundOp bound; bound.fun = f2; bound.ptr = s; // line E return bound; } """ Hey now, line E just caused the copy constructor to be called, doing something very different now! If you look, it made a copy of the object, so if you edit the original, the function will still be called with the copy. On Wed, Apr 28, 2010 at 2:55 PM, Piotr Jachowicz <pjachowi@gmail.com> wrote:
I only wanted to express concern that when binding to function accepting pointers (which is misuse) would cause issue in production code, then it can be seen as "problem with boost". Similar as problems with copy/owning semantic in CORBA to C++ mapping are often summarized as "problem with CORBA". I've observed it at very high decision level in big IT company.
I do not believe that using pointers with function pointers is a mis-use, I use them all the time, including in fancy things like Boost.Phoenix, but I also understand C++, it might behoove you to study more about how C++ works and its rules on things, which are very much how assembler does it, if you learn assembler (even an old/easy dialect), then it will forever help you in C/C++.
On Apr 28, 2010, at 8:22 PM, OvermindDL1 wrote:
It is (as I recall) defined in the standard that the c_str() member returned by an std::string's lifespan is only until the next termination point (generally a semicolon in C++).
Not exactly. 21.3.6/2: ... Nor shall the program treat the returned value as a valid pointer value after any subsequent call to a non-const member function of the class basic_string that designates the same object as this. So anything that can modify the contents of the string (including things like its size and capacity, and also including destruction), and only those things, invalidates the c_str() result. But the general point that OvermindDL1 and others are making is correct: one needs to be aware of lifetime issues and what the interface one is using specifies in that regard.
On Wed, Apr 28, 2010 at 7:04 PM, Kim Barrett <kab.conundrums@verizon.net> wrote:
21.3.6/2: ... Nor shall the program treat the returned value as a valid pointer value after any subsequent call to a non-const member function of the class basic_string that designates the same object as this.
Yes, that, thanks, typing emails from my now malfunctioning phone is not easy, so I could not check what the standard says. I always treated it based on termination points anyway, so I was in the better-safe-then-sorry camp. :) On Wed, Apr 28, 2010 at 7:04 PM, Kim Barrett <kab.conundrums@verizon.net> wrote:
So anything that can modify the contents of the string (including things like its size and capacity, and also including destruction), and only those things, invalidates the c_str() result.
But the general point that OvermindDL1 and others are making is correct: one needs to be aware of lifetime issues and what the interface one is using specifies in that regard.
Yes, that is what I was demonstrating, really should learn C/C++ 101 first.
Yep - when using pointers in C++ you have to be very careful with object lifetime and ownership, regardless of the library used.
participants (5)
-
Joseph Gauterin
-
Kim Barrett
-
OvermindDL1
-
Pete Bartlett
-
Piotr Jachowicz