Boost logo

Boost Users :

From: Roman Perepelitsa (roman.perepelitsa_at_[hidden])
Date: 2008-05-15 03:43:34


Alejandro Martínez <elpeque2 <at> gmail.com> writes:
>
> I have this problem.
>
> Im using boost::bind for programming the call-back mechanism for a gui
> system and to pass messages (possibly, delayed messages).
>
> My problem is, that when i bind member functions, if i specify the
> instance of the class as a shared_ptr, then i get memory leaks because
> the binding's shared_ptr to the instance keeps it alive, and it potentially
> produces cyclic references that in the current model, would be complicated
> to break.
>
> And of course, if i use raw pointers to the binding instances, then i'll
> get a crash if the instance has been destroyed and i try to execute the
> binding function.
>
> So what i would need, is to use bind with weak pointers, and just do
> nothing if locking it fails.
>
> I'm not sure how to do that, or how to assemble a good walkaround. I'd
> like it to be as automatic as possible.
>
> Thanks for your time, i appreciate any help :)

If you want boost::bind to work with weak_ptr then just add the following
code before using bind.

#include <boost/bind.hpp>
#include <boost/function.hpp>
#include <boost/weak_ptr.hpp>
#include <boost/shared_ptr.hpp>

#include <boost/preprocessor/inc.hpp>
#include <boost/preprocessor/iteration/local.hpp>
#include <boost/preprocessor/control/expr_if.hpp>
#include <boost/preprocessor/punctuation/comma_if.hpp>
#include <boost/preprocessor/repetition/enum_params.hpp>
#include <boost/preprocessor/repetition/enum_shifted_params.hpp>

#ifndef MAX_WEAK_FUNCTOR_ARGS
  #define MAX_WEAK_FUNCTOR_ARGS 10
#endif

namespace detail
{
  struct do_nothing {
    typedef void result_type;
    void operator()() const {}
  };

  template <class T>
  struct weak_ptr_wrapper
  {
    explicit weak_ptr_wrapper(const boost::weak_ptr<T>& p) : p_(p) {}

#define BOOST_PP_LOCAL_MACRO(N) \
  BOOST_PP_EXPR_IF(N, template <) \
  BOOST_PP_ENUM_PARAMS(N, class Q) \
  BOOST_PP_EXPR_IF(N, >) \
  boost::function<void( BOOST_PP_ENUM_PARAMS(N, Q) )> \
  operator ->* (void (T::*fun)( BOOST_PP_ENUM_PARAMS(N, Q))) \
  { \
    if(boost::shared_ptr<T> p = p_.lock()) \
      return boost::bind(fun, p BOOST_PP_COMMA_IF(N) \
                         BOOST_PP_ENUM_SHIFTED_PARAMS \
                           (BOOST_PP_INC(N), _)); \
    else \
      return boost::bind(do_nothing()); \
  }

#define BOOST_PP_LOCAL_LIMITS (0, MAX_WEAK_FUNCTOR_ARGS)
#include BOOST_PP_LOCAL_ITERATE()

  private:
    boost::weak_ptr<T> p_;
  };
}

namespace boost
{
  template <class T>
  ::detail::weak_ptr_wrapper<T> get_pointer(const weak_ptr<T>& ptr)
  {
    return ::detail::weak_ptr_wrapper<T>(ptr);
  }
}

And here is the test:

#include <iostream>

struct foo {
  void f() { std::cout << "Hello" << std::endl; }
};

int main() {
  boost::shared_ptr<foo> p(new foo);
  boost::weak_ptr<foo> w(p);
  boost::function<void()> f(bind(&foo::f, w));
  f(); // prints "Hello"
  p.reset();
  f(); // does nothing
}

It does not have the best performance because boost::function is created
each time you call functor, but it can be easily improved by writing your
own function wrapper and using it instead of boost::function as a return
value from weak_ptr_wrapper::operator->.

HTH,
Roman Perepelitsa.


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