Boost logo

Boost Users :

Subject: [Boost-users] [signals2] Usage (lifespan) question
From: Nat Goodspeed (nat_at_[hidden])
Date: 2009-03-05 11:44:08


I'd like suggestions for an API designed around Boost.Signals2. This
isn't performance-critical.

Assume a publish class containing a boost::signals2::signal. This
class's listen() method is a thin wrapper around signal::connect().
Using boost::bind, I can listen() with any method (with appropriate
signature) on any class.

I'm trying to introduce the concept of filter objects. Each filter is
itself a publish object. Client logic composes a chain from some publish
object through zero or more filters to the ultimate listener. A filter
passes along to its own listeners some subset of the input notifications.

The central issue is the lifespan of a filter object. I want the filter
objects in such a chain implicitly freed when the ultimate listener
disconnects.

Approach 1
==========
I could achieve that by managing each filter object with shared_ptr,
storing a copy in each listener. When the last listener is destroyed,
the filter goes away. (This assumes passing weak_ptr to boost::bind() so
the connection itself doesn't keep the listener alive. Further assume
I've used connection::track() so that the signal to which the filter is
connected implicitly disconnects.) Naturally this effect cascades
"upstream" through the filter chain.

But it bothers me that such an implementation imposes new requirements
on each listener. Beyond declaring a method with the right signature,
the listener must now store a shared_ptr to the publish object to which
it's listening. That implies a method on the listener to capture the
shared_ptr and establish the connection. Therefore, every listener must
be derived from some particular base class, and explicit connection and
disconnection must be done through the listener rather than directly
through the publish object.

Either this new inverted API must apply to all publishers/listeners, or
client logic must distinguish between the case of connecting to a filter
and connecting to any other type of publish object.

It feels to me as though this approach has gone into the weeds.

Approach 2
==========
Instead of passing a filter's weak_ptr to boost::bind() to the publish
object's listen() method, I could instead pass the filter's shared_ptr.
Now the publish object upstream from the filter is responsible for the
filter's lifespan: when the filter explicitly disconnects, it will
(eventually) go away. It will not implicitly disconnect because it won't
go away as long as the connection still holds a copy of its shared_ptr.

(Frank says[1] he might tweak the library to forget a shared_ptr
immediately on disconnect, but currently it survives for a while.)

Consider a chain:

A is the original publish object
B is a filter listening to A
C is the ultimate listener, listening to B

Again, the goal is to disconnect B (thus deleting it) when C disconnects
or is destroyed.

We could make B store its connection object to A. The question is, how
would B know to disconnect it? How could B become aware of C
disconnecting from B's signal object?

Approach 3-n
============
Suggestions? Thanks!

[1]
http://www.nabble.com/Re%3A--signals2--review--The-review-of-the-signals2-library-(formerly-thread_safe_signals)-begins-today%2C-Nov-1st-p22102367.html


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