Boost logo

Boost :

From: gregod_at_[hidden]
Date: 2001-05-07 12:55:34


--- In boost_at_y..., Jesse Jones <jesjones_at_h...> wrote:
> > > It'd be great if we could address these two domains with a
single
> > > class, but it seems like we're going to have to make a
trade-off
> >> somewhere. Personally I think that callbacks are used a lot more
> >> often than functors. And being able to compare two callbacks
*is*
> >> useful functionality. If this requires that clients have to
define an
> >> operator== for function objects that would be unpleasant, but
worth
> >> the ugliness from my POV.
> >>
> >> -- Jesse
> >
> >I'm not entirely convinced that comparing two boost::function
objects is
> >necessary for the callback domain. I don't currently deal with
callbacks
> >much, but in my prior life as a GUI person I don't recall ever
needing to
> >compare callbacks.
>
> Well, there's very little that you really have to have. I have
found,
> however, that being able to compare callbacks is helpful in creating
> simpler, less coupled designs.
>
> >In the timer example you gave it is convenient to have function
pointers, but
> >I'm not sure that's the "right way" to do things because it places
extra
> >requirements on the caller that may be tough to handle: what if my
timer
> >could point to one of several different callback targets? Then I
have to keep
> >track of which one was assigned anyway - is this better than
keeping track of
> >a "Timer" object (or a "TimerHandle" object) that would be used to
remove the
> >timer?
>
> I'm not quite sure what you're saying here.

Let me try again :) By building the timer interface so that it
requires callback comparisons, you are effectively restricting the use
of the timer so that only callbacks can be used with it. I don't have
any wonderful examples at the moment, but perhaps you need to ping a
certain address every 100ms. Personally, I would create a 'ping'
function object that takes the address and send an instance of 'ping'
to the timer. Why should I implement operator== just so that I can use
a timer? I think such an interface is overly restrictive.

> >What if two timers end up pointing to the same type of function
> >object?
>
> I consider registering a callback twice a precondition violation.
> Here's the entire public API:
>
> class ITimer : public XUnknown {
>
> public:
> // ----- Adding -----
> virtual void AddTimer(XCallback0<void> f, MilliSecond interval)
= 0;
> /**< Interval is the amount of time you want to
wait
> before calls
> to the callback. Note that the actual interval may
> occasionally be
> larger than the specified interval (but it won't
be
> smaller). */
>
> virtual void AddTimer(XCallback0<void> f, MilliSecond interval,
> MilliSecond delay) = 0;
> /**< Allows you to specify the amount of time to
> wait before the
> timer is called the very first time. Defaults to
the
> interval time. */
>
> virtual void AddOneShotTimer(XCallback0<void> f, MilliSecond
delay) = 0;
> /**< Installs a timer that will only be called
once
> (you can use
> SetInterval to switch over to periodic time). */
>
> // ----- Mutators -----
> virtual void SetInterval(XCallback0<void> f, MilliSecond
interval) = 0;
> /**< Updates the interval of a previously added
callback. */
>
> virtual void SetDelay(XCallback0<void> f, MilliSecond delay) =
0;
> /**< The timer won't fire until delay msecs elapse
> at which time
> it will fire every interval msecs (this is just
like calling
> RemoveTimer and then AddTimer with a new delay).
*/
>
> // ----- Removing -----
> virtual void RemoveTimer(XCallback0<void> f) = 0;
> /**< Note that it's ok to call this if the
callback
> hasn't been added. */
> };
>
> I think this is quite nice: it's dirt simple to use, very cohesive,
> and has low coupling. I think you were arguing for having a timer
> object embedded in each object that wants time. This is certainly
> workable, but it's a bit more cumbersome since you have to, at the
> very least, add the timer to your class declaration. And your
class's
> cohesion is reduced because you have this icky helper object
embedded
> inside your class.

I'm looking at an interface more like:
class ITimer {
  TimerHandle AddTimer(const function<void>& f, int ms, ...);
  void RemoveTimer(TimerHandle handle);
  void SetDelay(TimerHandle handle, int ms);
};
 
This interface doesn't require comparisons between function objects,
so if I've constructed a function object to give to the AddTimer
routine I don't have to reconstruct it (or keep it around) to give to
the constructor.

> >I'm almost wondering if there really is a fundamental difference
between the
> >two domains or if the lack of tools to enable the use of the
"functor" domain
> >in C++ is the cause of the difference. If the binder and lambda
libraries
> >were generally available and understood, would we be so reliant on
the
> >standard callback types, to functions and member functions?
>
> Here are the bulk of the places I use callbacks: to respond to
> clicking on controls, to handle Undo/Redo (using the Command
> pattern), to handle document state notifications (instead of the
> Observer pattern), to respond to key events, to respond to menu
> commands, to enable menu items, to get time, to allow users to
> install custom error handlers , to specify the entry point and error
> handlers for a thread, to support thread IOUs (aka futures), and to
> call a callback for each node in an IHierarchy with an optional
> predicate callback (this is normally the app, documents, windows,
and
> controls).

I wonder if many of these GUI-centric uses of callbacks would better
be suited for a signals/slots system, and if this would reduce the
need for operator==.
 
> Of all of these only the last seems the sort of thing the lambda
> library is good for. Timers, document notification, key handlers,
and
> the menu handlers all rely on comparing callbacks.
>
> >I think that requiring operator== of every function object is too
> >restrictive: standard library function objects don't support it,
and likely
> >neither will functors created by boost::bind or the lambda library,
which
> >will create a big hassle for users.
>
> I certainly don't like it, but when you're in the functor domain you
> can often get by with using an anonymous functor. And clients can
add
> operator== functions easily enough...
>
> -- Jesse

Easily, yes, but what a nuisance if you never intend to compare
function objects. We shouldn't have users paying for what they don't
use, especially when it requires extra work on their part NOT to use
something.

        Doug


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