From: Douglas Gregor (gregod_at_[hidden])
Date: 2001-06-14 07:02:30


On Thursday 14 June 2001 03:43, you wrote:
> > The problem is that a bind library tends to grow uncontrollably and
>
> since
>
> > the 'Bind' part of Lambda is scheduled right after the 'Tuple'
>
> part... you
>
> > get the idea. :-)
> >
> > Identifying a 'simple' bind subset isn't easy, since different
>
> people have
>
> > different needs.
>
> I agree with the uncontrollable growth. A code that performs the
> placeholder substitutions with function composition is already over
> 'simple'. And once that part is done, why not add binding of
> operators, and why not control structures ...
>
> We're cutting LL into smaller pieces. tuples became a separate library
> as it is really quite different concept of binding. It is just a tool
> that LL uses.
> bind wont't become another library, but rather the
> user include features like bind functions, operators, control
> structures separately, and each of these include the core machinery
> to make argument substitution and return type deduction work.
>
> Anyway, I like the idea of making LL and function work together.
> But if we take that step, why confine it to just bind invocations:
>
> If something like this would work:
>
> ostream& print(ostream& o, int i) { return o << i; }
> function<ostream&, int> outputter = bind(print, ref(cout), free1)
> // ref is to make bind store cout as reference
> outputter(1);
>
> why not:
>
> function<ostream&, int> outputter = (cout << free1)
> outputter(1);

It's all the same to Boost.Function.

> One detail that requires thinking is that LL stores some of the bound
> arguments as references.
> E.g. the function object resulting from a lambda expression
> a += free1 holds a reference to a.
> In current LL this is ok, as the function objects cannot really be
> stored for future use, but are constructed and evaluated at the same
> site.
> Now, if we store the function object into a variable,
> there's a fear for dangling references.
>
> int *a = new int();
> function<int&, int> f = (*a += free1);
> f(7); // ok
> delete a;
> f(7); // dangling reference
>
> Cheers, Jaakko

Boost.Function won't be dealing with this. However, I was going to bring up
the issue soon because a signals & slots library is on the horizon, and the
issue is pertinent there.

Signals & slots deals with the problem of dangling references, pointers, etc.
by removing connections when objects die. All of the signals & slots
libraries I've seen thus far include limited binder libraries (usually just
something that binds the "this" argument for a pointer-to-member-function)
and when "this" dies, the connection dies.

We don't want to do the same for Boost. Binding and deferred calls are
orthogonal concepts, even for signals & slots. So I need a way to
(statically) traverse all bound objects in a function object (and all
contained function objects, etc). Then I can find all objects that are able
to signal end-of-lifetime so that they can destroy the connection when they
are destroyed.

Here's a possible interface: the signals & slots library defines a visitor
class as such:

struct BindLifetimes {
  void visit(slot_base& slot);
  void visit(slot_base* slot);
  template<typename T> visit(T); // some hook for user-defined pointer types
};

The lambda library (or binding library) defines a function that traverses all
bound objects:

template<typename LambdaFunction, typename Visitor>
inline void traverse_bound_objects(const LambdaFunction&, Visitor);

I need this sort of thing to make signals & slots work well, and of course
I'm willing to help out on the lambda end to get it done.

        Doug