Boost logo

Boost :

From: Douglas Gregor (gregod_at_[hidden])
Date: 2002-10-28 11:33:19


Dave,
  Thanks for the detailed comments. I'm updating the documentation to fix the
(many!) errors you've found, and I'm responding here to any unanswered
questions.

On Sunday 20 October 2002 12:01 am, David Abrahams wrote:
> Just reading through the Signals docs here, and came across a strange
> use of the term "named template parameter":
[l'il snip]
> This is supposed to be a use of named template parameters to pass
> maximim<float> to the signal class. Where's the "named parameter?"
> This doesn't look anything like NTP as we've seen, e.g., in the
> iterator adaptors library.

The docs are out-of-date. Named template parameters are no longer used in
Signals. Will fix.

> It also says
>
> "At this time there is a significant limitation to the use of
> trackable objects in making slot connections: function objects
> built using Boost.Bind are understood, so that any place a
> trackable object appears in a bind expression."
>
>Isn't there a conclusion missing from this sentence?

"... it will be tracked upon connection to a signal." The wording needs more
thought, but it's better than that grammatical mess I wrote above :)

>And I think you should be careful about exactly what you write. I
>think there are lots of ways a trackable object can "appear in a bind
>expression" yet still be effectively hidden from bind, e.g.:
>
> bind(bind1st(f, x), y)
>
>And what about references to trackable objects? If I build a bind
>expression around ref(x), will it still be tracked?

Arguments bound by boost::bind and boost::ref will be tracked. Any other
binding method (Lambda, bind1st, user-defined function objects) cannot safely
be used with trackable objects. The docs should state this.

> In the section on "Passing Slots" it says "any valid function object
> can be passed to a slot_type parameter". Do you mean "any _compatible_
> function object", using the definition of compatible from the function
> library?

The really picky answer is "any function object that can be implicitly
converted to the signal's slot_function_type". For the default case (using
boost::function), this means any compatible function object. I'll find a
better wording for the docs.

> The formal docs for disconnect read:
>
> "Complexity: O(lg n) + k where n is the number of slots known to
> the signal and k is the number of ."
> ^
>I think something is missing here---+

Indeed. k is the number of slots within the group being disconnected.

> The "Signal Invocation" docs say:
>
> "Effects: invokes the combiner with a slot_call_iterator range
> [first, last) (i.e., combiner(first, last)) that iterates over the
> results of calling each slot with the given set of parameters a1,
> a2, ..., aN."
>
> An iterator range doesn't iterate. Maybe you should say "...whose
> values are the results of calling..." You should also mention that
> if the iterator stops early, only a subset of the slots will be
> called. Also you should specify whether it's incrementing or
> dereferencing of the iterator which causes a slot call.

Perhaps this would be better:

Effects: invokes the combiner with a slot_call_iterator range [first, last)
(i.e., combiner(first, last)). Dereferencing an iterator in this range causes
a slot call with the given set of parameters (a1, a2, ..., aN), the result of
which is returned from the iterator dereference operation.

Notes: Only the slots associated with iterators that are actually dereferenced
will be invoked. Multiple dereferences of the same iterator will not result
in multiple slot invocations, because the return value of the slot will be
cached.

> The docs don't make it clear that visit_each is being called without
> qualification, nor do they say which boost functions call it. Both of
> those features are important for any point-of-customization that may
> use Koenig lookup. Also, I realize that with existing compilers, we
> have to rely on overloading in the user's namespace for this function,
> but some consideration ought to be given to whether a
> partially-specializable static member function wrapper wouldn't be the
> better long-term solution for this case.

Hmmm, yes. We could use a hybrid approach, with a partially-specializable
static member function wrapper whose default falls back to calling visit_each
(or some mangled name thereof) unqualified. Is this the best solution within
the currently language (and assuming that not everyone can use partial
specialization)?

> We also refer to "slot names", which confused me. I think we really
> mean "slot group names" here, since individual slots aren't named. I'd
> actually prefer if "name" was replaced with "identifier" or "id"
> throughout the docs, since "name" really suggests a string.

"Slot group identifiers" it is, then.

> "uses a set of well-defined interfaces to discover information
> about the composition of arbitrary function objects."
>
>Well, let's not oversell it. We can't really do that for arbitrary
>function objects unless visit_each has been suitably defined, can we?

Right. I'll reword & expand this to say that we don't provide the primitives
within the signals, but instead provide hooks (via visit_each) so that
primitives from other libraries can be adapted to make it easy for Signals to
track the lifetimes of function object elements.

> The last sentence in the "pass slot to disconnect" section is a major
> run-on. I'm having trouble figuring out what it's supposed to mean.

Woah! Do I get an award for that sentence? It's astoundingly horrible.

> "This type of interface is supported in Boost.Signals via the
> named connections mechanism"
>
>As they say in the movies, "What the...?" Is this mechanism documented
>anywhere?

It's the slot group identifier interface, described very poorly. The slot
(group) identifier interface changed due to discussions during the review.
Prior to the review, names were given on a per-slot basis; reusing a slot
name would disconnect the pre-existing slot with that name, e.g.,

  sig.connect("foo", foo_slot);
  sig.connect("foo", bar_slot); // foo_slot disconnected

Now the slots are grouped by their names. The new semantics are:

  sig.connect("foo", foo_slot);
  sig.connect("foo", bar_slot);
  // both foo_slot and bar_slot are connected

  sig.disconnect("foo");
  // both foo_slot and bar_slot are disconnected

> The docs apparently haven't all been updated to reflect this change :(.
>
> "The "push" model, on the other hand, relies on an interface
> specific to the caller and is generally reusable."
> "not"?--------------------------^

Right. The "push" model relies on interfaces that are NOT generally reusable.

> "the connection syntax supplied by Boost.Signals is no less
> powerful that than supplied by the signal."
>
> What signal?

Oh, dear. Should read "the connection syntax supplied by Boost.Signals is no
less powerful that than supplied by this syntax." I'm just trying to say that
sig += slot would have the same semantics as sig.connect(slot), so we don't
need both.

> Must trackable be a public base, or is a private base OK? Can we have
> an example of its use? OK, there's one in CVS. How about some links
> from the docs to these examples?

Must be a public base. And yes, I'll add the example to the tutorial.

> I'm still wondering, after reading the docs, whether it's OK to send a
> signal which ultimately destroys the signal itself.

Yes, that's fine. Signals can be used recursively (signal A calls slot B calls
signal A) and can be deleted from within a slot they call (signal A calls
slot B, slot B deletes signal A).

> This line in libs/signal/example/first_positive.hpp seems to rely on
> unspecified behavior to match its comment, since none of the slots are
> members of a named group.
>
> assert(sig_positive(3, -5) == 8); // returns 8, but prints nothing

Yes, you're right. I'll throw in some slot group identifiers to make it
correct.

Thanks again for the detailed comments, and sorry for the delay.

        Doug


Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk