Weak functor: Interested? (How to boost::bind to a weak_ptr)

Hi! I saw people asking how to do boost::bind using a weak pointer. Generally it's not possible without first specifying what should happen if the weak_ptr cannot be converted to shared_ptr. The "weak_fn" I wrote is a small class holding a member pointer, a weak pointer and a "policy": * It is callable so you can insert it to boost::bind. * You can construct it using free function templates. * The policy says what should happen when the object is called yet the weak_ptr cannot be converted to shared_ptr. * It's implemented both using variadic templates and Boost.Preprocessor. Copy of the help comment: /** Returns a callback that can be used eg. in boost::bind. When called, it * tries to lock weak_ptr to get a shared_ptr. If successful, it calls * given member function with given arguments. If not successful, it calls given * policy functor. Built-in policies are: * * "ignore_if_invalid" - does nothing * "throw_if_invalid" - throws "bad_weak_ptr" * "return_default_if_invalid" - returns given value * * Example: * * struct Foo { * void bar(int i) { * std::cout << i << std::endl; * } * }; * * struct do_something { * void operator()() { * std::cout << "outdated reference" << std::endl; * } * }; * * int main() * { * boost::shared_ptr<Foo> sp(new Foo()); * boost::weak_ptr<Foo> wp(sp); * * boost::bind(boost::weak_fn(&Foo::bar, wp), _1)(1); * sp.reset(); * boost::bind(boost::weak_fn(&Foo::bar, wp), 1)(); * boost::bind(boost::weak_fn(&Foo::bar, wp, do_something()), 1)(); * } */ What's missing: * Cannot point to a free function. Only member functions. * ? Wdys? Vladimir.

2012/1/10 Vladimír Třebický <vladimir.trebicky@gmail.com>
Hi!
I saw people asking how to do boost::bind using a weak pointer. Generally it's not possible without first specifying what should happen if the weak_ptr cannot be converted to shared_ptr. The "weak_fn" I wrote is a small class holding a member pointer, a weak pointer and a "policy":
* It is callable so you can insert it to boost::bind. * You can construct it using free function templates. * The policy says what should happen when the object is called yet the weak_ptr cannot be converted to shared_ptr. * It's implemented both using variadic templates and Boost.Preprocessor.
Copy of the help comment:
/** Returns a callback that can be used eg. in boost::bind. When called, it * tries to lock weak_ptr to get a shared_ptr. If successful, it calls * given member function with given arguments. If not successful, it calls given * policy functor. Built-in policies are: * * "ignore_if_invalid" - does nothing * "throw_if_invalid" - throws "bad_weak_ptr" * "return_default_if_invalid" - returns given value * * Example: * * struct Foo { * void bar(int i) { * std::cout << i << std::endl; * } * }; * * struct do_something { * void operator()() { * std::cout << "outdated reference" << std::endl; * } * }; * * int main() * { * boost::shared_ptr<Foo> sp(new Foo()); * boost::weak_ptr<Foo> wp(sp); * * boost::bind(boost::weak_fn(&Foo::bar, wp), _1)(1); * sp.reset(); * boost::bind(boost::weak_fn(&Foo::bar, wp), 1)(); * boost::bind(boost::weak_fn(&Foo::bar, wp, do_something()), 1)(); * } */
What's missing:
* Cannot point to a free function. Only member functions. * ?
Wdys?
Vladimir.
I beleive people want to use std::weak_ptr with std::bind when they want to store a callback, not just passing a functor to an algorithm, and when people do that, that usually means storingin in an std::function instance. We already have similar pattern for shared/weak pointers, so wouldn't it be better to adopt an existing desing and make weak_fn noncallable, but with the ability to obtain an std::function instance from it, that would store a strong reference to the object via shared_ptr?

Hi!
I saw people asking how to do boost::bind using a weak pointer. Generally it's not possible without first specifying what should happen if the weak_ptr cannot be converted to shared_ptr. The "weak_fn" I wrote is a small class holding a member pointer, a weak pointer and a "policy":
* It is callable so you can insert it to boost::bind. * You can construct it using free function templates. * The policy says what should happen when the object is called yet the weak_ptr cannot be converted to shared_ptr. * It's implemented both using variadic templates and Boost.Preprocessor.
Copy of the help comment:
/** Returns a callback that can be used eg. in boost::bind. When called, it * tries to lock weak_ptr to get a shared_ptr. If successful, it calls * given member function with given arguments. If not successful, it calls given * policy functor. Built-in policies are: * * "ignore_if_invalid" - does nothing * "throw_if_invalid" - throws "bad_weak_ptr" * "return_default_if_invalid" - returns given value * * Example: * * struct Foo { * void bar(int i) { * std::cout << i << std::endl; * } * }; * * struct do_something { * void operator()() { * std::cout << "outdated reference" << std::endl; * } * }; * * int main() * { * boost::shared_ptr<Foo> sp(new Foo()); * boost::weak_ptr<Foo> wp(sp); * * boost::bind(boost::weak_fn(&Foo::bar, wp), _1)(1); * sp.reset(); * boost::bind(boost::weak_fn(&Foo::bar, wp), 1)(); * boost::bind(boost::weak_fn(&Foo::bar, wp, do_something()), 1)(); * } */
What's missing:
* Cannot point to a free function. Only member functions. * ?
Wdys?
Vladimir.
I beleive people want to use std::weak_ptr with std::bind when they want to store a callback, not just passing a functor to an algorithm, and when people do that, that usually means storingin in an std::function instance. We already have similar pattern for shared/weak pointers, so wouldn't it be better to adopt an existing desing and make weak_fn noncallable, but with the ability to obtain an std::function instance from it, that would store a strong reference to the object via shared_ptr?
I think there's a slight misunderstanding. The way it is designed doesn't only allow you to pass it as a functor to an algorithm. Of course you can do that. But you can also pass it to boost::bind and/or from boost bind to boost::function as you propose: boost::function<void(int)> f = boost::bind(boost::weak_fn(&Foo::bar, some_weak_ptr), _1); f(1); (Plus all the other examples above.) Similarly to boost::bind, boost::fn is a function that returns an instance of weak_fn_storage whose operator() mimics the prototype of the bound member function (ie. returns the same type and can be invoked with identical arguments). You can then invoke it directly or -- more typically -- pass it to boost::bind for which it appears as a "callable" to which it can pass the bound arguments when invoked. The result of boost::bind can then be converted to boost::function if you like, of course. Vladimir.

2012/1/10 Vladimír Třebický <vladimir.trebicky@gmail.com>
boost::function<void(int)> f = boost::bind(boost::weak_fn(&Foo::bar, some_weak_ptr), _1); f(1);
It's it better to bind &Foo::bar directly? boost::function<void(int)> f = boost::bind(&Foo::bar, some_weak_ptr, _1); Much simpler in my opinion. Roman Perepelitsa.

On Tue, Jan 10, 2012 at 10:34 PM, Roman Perepelitsa <roman.perepelitsa@gmail.com> wrote:
2012/1/10 Vladimír Třebický <vladimir.trebicky@gmail.com>
boost::function<void(int)> f = boost::bind(boost::weak_fn(&Foo::bar, some_weak_ptr), _1); f(1);
It's it better to bind &Foo::bar directly?
boost::function<void(int)> f = boost::bind(&Foo::bar, some_weak_ptr, _1);
Much simpler in my opinion.
That cannot be done, boost::bind() indeed accepts references, pointers and shared_ptrs for member functions but doesn't accept weak pointers, because a weak pointer cannot be trivially converted to shared_ptr (or anything that leads to the corresponding class instance).
Roman Perepelitsa.
V.

2012/1/10 Vladimír Třebický <vladimir.trebicky@gmail.com>
On Tue, Jan 10, 2012 at 10:34 PM, Roman Perepelitsa
boost::function<void(int)> f = boost::bind(boost::weak_fn(&Foo::bar, some_weak_ptr), _1); f(1);
It's it better to bind &Foo::bar directly?
boost::function<void(int)> f = boost::bind(&Foo::bar, some_weak_ptr, _1);
Much simpler in my opinion.
That cannot be done, boost::bind() indeed accepts references, pointers and shared_ptrs for member functions but doesn't accept weak pointers, because a weak pointer cannot be trivially converted to shared_ptr (or anything that leads to the corresponding class instance).
Indeed, I've needed similar functionality in a system designed to support dynamically loaded modules. I have: template <class T> void register_function( boost::shared_ptr<function_directory> const &, name const &, boost::function<T> const &, boost::weak_ptr<void> const & lifetime ); template <class T> boost::function<T> lock_function( boost::shared_ptr<function_directory const> const &, name const & ); The passed lifetime weak_ptr is initialized from a shared_ptr that keeps the loaded module afloat. Locking the function throws if the module was unloaded, otherwise the returned boost::function<T> keeps the module afloat until discarded. Something similar could be implemented as a pair of types, boost::shared_function and boost::weak_function, the semantics being analogous to boost::shared_ptr and boost::weak_ptr. Emil Dotchevski Reverge Studios, Inc. http://www.revergestudios.com/reblog/index.php?n=ReCode

2012/1/10 Vladimír Třebický <vladimir.trebicky@gmail.com>
On Tue, Jan 10, 2012 at 10:34 PM, Roman Perepelitsa <roman.perepelitsa@gmail.com> wrote:
2012/1/10 Vladimír Třebický <vladimir.trebicky@gmail.com>
boost::function<void(int)> f = boost::bind(boost::weak_fn(&Foo::bar, some_weak_ptr), _1); f(1);
It's it better to bind &Foo::bar directly?
boost::function<void(int)> f = boost::bind(&Foo::bar, some_weak_ptr, _1);
Much simpler in my opinion.
That cannot be done, boost::bind() indeed accepts references, pointers and shared_ptrs for member functions but doesn't accept weak pointers, because a weak pointer cannot be trivially converted to shared_ptr (or anything that leads to the corresponding class instance).
It can be done. In fact, it has already been done. See http://lists.boost.org/boost-users/2011/09/70647.php. Roman Perepelitsa.

On Wed, Jan 11, 2012 at 9:19 AM, Roman Perepelitsa <roman.perepelitsa@gmail.com> wrote:
2012/1/10 Vladimír Třebický <vladimir.trebicky@gmail.com>
On Tue, Jan 10, 2012 at 10:34 PM, Roman Perepelitsa <roman.perepelitsa@gmail.com> wrote:
2012/1/10 Vladimír Třebický <vladimir.trebicky@gmail.com>
boost::function<void(int)> f = boost::bind(boost::weak_fn(&Foo::bar, some_weak_ptr), _1); f(1);
It's it better to bind &Foo::bar directly?
boost::function<void(int)> f = boost::bind(&Foo::bar, some_weak_ptr, _1);
Much simpler in my opinion.
That cannot be done, boost::bind() indeed accepts references, pointers and shared_ptrs for member functions but doesn't accept weak pointers, because a weak pointer cannot be trivially converted to shared_ptr (or anything that leads to the corresponding class instance).
It can be done. In fact, it has already been done. See http://lists.boost.org/boost-users/2011/09/70647.php.
Nice. I like this. However, once you declare the get_pointer (plus ptr_adater and invoker) there's no way to specify what should happen if lock() returns empty shared_ptr. In the example "invoker" returns default value: R operator()() const { return ci_.first ? (ci_.first->*ci_.second)() : R(); } which may (a) not be possible (if R is not default-constructible) or (b) not desired (eg. if user wants an exception to be thrown). Do you think there would be a way to control the behavior? V.

2012/1/11 Vladimír Třebický <vladimir.trebicky@gmail.com>
On Wed, Jan 11, 2012 at 9:19 AM, Roman Perepelitsa <roman.perepelitsa@gmail.com> wrote:
It can be done. In fact, it has already been done. See http://lists.boost.org/boost-users/2011/09/70647.php.
Nice. I like this. However, once you declare the get_pointer (plus ptr_adater and invoker) there's no way to specify what should happen if lock() returns empty shared_ptr. In the example "invoker" returns default value:
R operator()() const { return ci_.first ? (ci_.first->*ci_.second)() : R(); }
which may (a) not be possible (if R is not default-constructible) or (b) not desired (eg. if user wants an exception to be thrown).
Do you think there would be a way to control the behavior?
In this case one will have to use something like boost::weak_fn or write the trampoline function manually. void do_something(const weak_ptr<foo>& p) { // Complete freedom here. if (shared_ptr<foo> sp = p.lock()) sp->do_something(); else throw "expired weak_ptr"; } bind(do_something, my_weak_ptr)(); In practice, however, I only ever used void-returning functors with weak_ptr. Roman Perepelitsa.

On Wed, Jan 11, 2012 at 10:38 AM, Roman Perepelitsa <roman.perepelitsa@gmail.com> wrote:
2012/1/11 Vladimír Třebický <vladimir.trebicky@gmail.com>
On Wed, Jan 11, 2012 at 9:19 AM, Roman Perepelitsa <roman.perepelitsa@gmail.com> wrote:
It can be done. In fact, it has already been done. See http://lists.boost.org/boost-users/2011/09/70647.php.
Nice. I like this. However, once you declare the get_pointer (plus ptr_adater and invoker) there's no way to specify what should happen if lock() returns empty shared_ptr. In the example "invoker" returns default value:
R operator()() const { return ci_.first ? (ci_.first->*ci_.second)() : R(); }
which may (a) not be possible (if R is not default-constructible) or (b) not desired (eg. if user wants an exception to be thrown).
Do you think there would be a way to control the behavior?
In this case one will have to use something like boost::weak_fn or write the trampoline function manually.
void do_something(const weak_ptr<foo>& p) { // Complete freedom here. if (shared_ptr<foo> sp = p.lock()) sp->do_something(); else throw "expired weak_ptr"; }
bind(do_something, my_weak_ptr)();
Sure, that's always a way. Once you start having a lot of callbacks it may become messy.
In practice, however, I only ever used void-returning functors with weak_ptr.
Agreed, me to. But typically I need the callbacks to either silently do nothing or throw. Anyway, the question is whether such a tool may find place in Boost for people looking for this kind of behavior, wdyt? V.

2012/1/11 Vladimír Třebický <vladimir.trebicky@gmail.com>
On Wed, Jan 11, 2012 at 10:38 AM, Roman Perepelitsa <roman.perepelitsa@gmail.com> wrote:
In practice, however, I only ever used void-returning functors with weak_ptr.
Agreed, me to. But typically I need the callbacks to either silently do nothing or throw.
Anyway, the question is whether such a tool may find place in Boost for people looking for this kind of behavior, wdyt?
I'd support this. I've been using this code for about 6 years and found it very useful. There were several requests for this functionality on boost mailing lists, so I'm not along. As far as I know, this change has never been proposed. Roman Perepelitsa.

On Wed, Jan 11, 2012 at 10:54 AM, Roman Perepelitsa <roman.perepelitsa@gmail.com> wrote:
2012/1/11 Vladimír Třebický <vladimir.trebicky@gmail.com>
On Wed, Jan 11, 2012 at 10:38 AM, Roman Perepelitsa <roman.perepelitsa@gmail.com> wrote:
In practice, however, I only ever used void-returning functors with weak_ptr.
Agreed, me to. But typically I need the callbacks to either silently do nothing or throw.
Anyway, the question is whether such a tool may find place in Boost for people looking for this kind of behavior, wdyt?
I'd support this. I've been using this code for about 6 years and found it very useful. There were several requests for this functionality on boost mailing lists, so I'm not along. As far as I know, this change has never been proposed.
I see, even though I more asked about my weak_fn ;-) V.

Roman Perepelitsa wrote:
It can be done. In fact, it has already been done. See http://lists.boost.org/boost-users/2011/09/70647.php.
PMFJI, but that code looks suspect. get_pointer() locks the weak_ptr to get a raw pointer from the related shared_ptr, but then the shared_ptr returned by lock() is allowed to go out of scope. Thus, the ptr_adapter returned by get_pointer() holds a transiently valid raw pointer. By the time the pointer is used to call the member function, the last shared_ptr owning the associated memory could release the memory, so the ptr_adapter will have a dangling pointer. If ptr_adapter were to hold a shared_ptr instead of a raw pointer, then the memory could not be released out from under the ptr_adapter. That, of course, doesn't work with get_pointer(). _____ Rob Stewart robert.stewart@sig.com Software Engineer using std::disclaimer; Dev Tools & Components Susquehanna International Group, LLP http://www.sig.com ________________________________ IMPORTANT: The information contained in this email and/or its attachments is confidential. If you are not the intended recipient, please notify the sender immediately by reply and immediately delete this message and all its attachments. Any review, use, reproduction, disclosure or dissemination of this message or any attachment by an unintended recipient is strictly prohibited. Neither this message nor any attachment is intended as or should be construed as an offer, solicitation or recommendation to buy or sell any security or other financial instrument. Neither the sender, his or her employer nor any of their respective affiliates makes any warranties as to the completeness or accuracy of any of the information contained herein or that this message or any of its attachments is free of viruses.

2012/1/11 Stewart, Robert <Robert.Stewart@sig.com>
Roman Perepelitsa wrote:
It can be done. In fact, it has already been done. See http://lists.boost.org/boost-users/2011/09/70647.php.
PMFJI, but that code looks suspect. get_pointer() locks the weak_ptr to get a raw pointer from the related shared_ptr, but then the shared_ptr returned by lock() is allowed to go out of scope. Thus, the ptr_adapter returned by get_pointer() holds a transiently valid raw pointer. By the time the pointer is used to call the member function, the last shared_ptr owning the associated memory could release the memory, so the ptr_adapter will have a dangling pointer.
Yes, it's a bug. If ptr_adapter were to hold a shared_ptr instead of a raw pointer, then the
memory could not be released out from under the ptr_adapter. That, of course, doesn't work with get_pointer().
Of course it works with get_pointer. Roman Perepelitsa.

Roman Perepelitsa wrote:
2012/1/11 Stewart, Robert <Robert.Stewart@sig.com>
Roman Perepelitsa wrote:
It can be done. In fact, it has already been done. See http://lists.boost.org/boost-users/2011/09/70647.php.
PMFJI, but that code looks suspect. get_pointer() locks the weak_ptr to get a raw pointer from the related shared_ptr, but then the shared_ptr returned by lock() is allowed to go out of scope. Thus, the ptr_adapter returned by get_pointer() holds a transiently valid raw pointer. By the time the pointer is used to call the member function, the last shared_ptr owning the associated memory could release the memory, so the ptr_adapter will have a dangling pointer.
Yes, it's a bug.
If ptr_adapter were to hold a shared_ptr instead of a raw pointer, then the memory could not be released out from under the ptr_adapter. That, of course, doesn't work with get_pointer().
Of course it works with get_pointer.
shared_ptr doesn't overload ->* which, I thought, was the purpose of get_pointer(). _____ Rob Stewart robert.stewart@sig.com Software Engineer using std::disclaimer; Dev Tools & Components Susquehanna International Group, LLP http://www.sig.com ________________________________ IMPORTANT: The information contained in this email and/or its attachments is confidential. If you are not the intended recipient, please notify the sender immediately by reply and immediately delete this message and all its attachments. Any review, use, reproduction, disclosure or dissemination of this message or any attachment by an unintended recipient is strictly prohibited. Neither this message nor any attachment is intended as or should be construed as an offer, solicitation or recommendation to buy or sell any security or other financial instrument. Neither the sender, his or her employer nor any of their respective affiliates makes any warranties as to the completeness or accuracy of any of the information contained herein or that this message or any of its attachments is free of viruses.

2012/1/11 Stewart, Robert <Robert.Stewart@sig.com>
shared_ptr doesn't overload ->* which, I thought, was the purpose of get_pointer().
ptr_adapter overloads ->*. The fix of the code at http://lists.boost.org/boost-users/2011/09/70647.phpis quite simple. 1. Replace raw pointers with weak_ptr in invoker and ptr_adaptor. 2. get_pointer becomes: template<class T> ptr_adapter<T> get_pointer(const boost::weak_ptr<T> &p) { return ptr_adapter<T>(p); } 3. invoker::operator() becomes: R operator()() const { if (shared_ptr<C> p = ci_.first.lock()) return (p.get()->*ci_.second)(); else return R(); } Roman Perepelitsa.

Roman Perepelitsa wrote:
It can be done. In fact, it has already been done. See http://lists.boost.org/boost-users/2011/09/70647.php.
There is also https://svn.boost.org/trac/boost/ticket/810 but you'll note that it chooses to throw an exception instead. I'm still undecided which of these two responses I prefer as default, or how to choose the other. There is also the use case of void f( shared_ptr<X> p1, shared_ptr<X> p2 ); weak_ptr<X> wp1, wp2; bind( f, wp1, wp2 ); for which the choice is between passing shared_ptr<X>(wp1) or wp1.lock() to f.
participants (6)
-
Emil Dotchevski
-
Peter Dimov
-
Roman Perepelitsa
-
Stewart, Robert
-
Vladimír Třebický
-
yuri kilochek