Boost logo

Boost Users :

Subject: Re: [Boost-users] [Bind] Unexpected behavior when binding constchar* to function
From: OvermindDL1 (overminddl1_at_[hidden])
Date: 2010-04-28 20:22:56


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_at_[hidden]> wrote:
> On 28 April 2010 10:02, Pete Bartlett <pete_at_[hidden]> 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_at_[hidden]> 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++.


Boost-users list run by williamkempf at hotmail.com, kalb at libertysoft.com, bjorn.karlsson at readsoft.com, gregod at cs.rpi.edu, wekempf at cox.net