Boost logo

Boost :

From: Douglas Gregor (gregod_at_[hidden])
Date: 2000-11-17 16:29:45


On Fri, 17 Nov 2000 20:54:33 -0000
"William Kempf" <sirwillard_at_[hidden]> wrote:

> --- In boost_at_[hidden], Douglas Gregor <gregod_at_r...> wrote:
> > On Fri, 17 Nov 2000 19:40:05 -0000
> > 1) I use one "Callback" class that takes any number of parameters
> > instead of having many Callback0, Callback1, ..., CallbackN
> > classes.
>
> In theory I really like this. Unfortunately, it requires partial
> template specialization and so may hinder or prevent implementation
> on VC++, which I think is a major problem. It's definately an idea
> worth looking into, however.

I believe that we need partial specialization in any case. Even if we use Callback0, Callback1, etc. classes, we still (might) need a void (partial) specialization. e.g.,

template<typename Arg1, typename Arg2>
struct Callback2<void, Arg1, Arg2> { ... };

I haven't toyed with the idea (since I tend to ignore VC++ as much as I'm allowed), but we may be able to come up with an implementation which correctly omits the "return" when the return type is void without using partial specialization.

> > 2) I provide a conversion to bool so that we can check if a given
> > callback has a target. This allows us to more easily create
> > functions where an outside user can (optionally) change some
> > behavior of the function at runtime. For instance, we may want
> > a system where the user can change (at runtime) the computation
> > of the norm of two vectors within some algorithm. Then we can
> > define a callback:
> > Callback< float, Vector<float>, Vector<float> >
> > calculateNorm;
> >
> > // Then, when we need the norm
> > float norm; Vector<float> u, v;
> > if (calculateNorm)
> > norm = calculateNorm(u,v); // user calculates the norm
> > else
> > norm = innerprod(u,v); // Default to the inner product
>
> Yes, this is a good idea and is trivial to add to the implementation
> I posted.
>
> > 3) I have explicit classes (each derived from EventBase) which
> > allow callbacks to member functions, free functions or
> > functors. It appears that the William Kempf and Jesse Jones'
> > solution would use only functors (relying on another library
> > (the lambda library?) to build functors out of free/member
> > functions). This makes it tougher to include the bool
> > conversion (how do we know if a functor has a real target or
> > not?), which I consider essential.
>
> The bool conversion can be handled by inclusion of a default
> constructor that sets the "impl" pointer to null. The actual functor
> is an internal detail that does not effect this in any way. I find
> it useful to leave things as they are in our implementation in order
> to not duplicate the effort of creating functors yet again. There
> are already numerous libraries available for doing this, including
> the standard library that comes with every compiler. Libraries such
> as Lambda do a much better job of this than we could possibly do with
> out duplicating a tremendous amount of effort, and with no clear
> benefit.

The reason I originally decided to use separate classes (FunctionEvent, MemberFunctionEvent) instead of only using a functor is that I wanted to account for cases where the function/object pointed to are null. In truth, this is probably not sufficient reason to duplicate Lambda's functionality. Additionally, I wanted to leave the door open for other types of events (derived from EventBase) which may have different criteria for the bool conversion. Here's a strange example that I don't believe can be covered with your version:

class KeyboardInputEvent : public EventBase<char>
{
  virtual operator bool() { return is_key_pressed(); }
  virtual char operator()() { return get_key_pressed(); }

  // implement other virtual methods as needed
};

Event<char> getUserInput = KeyboardInputEvent(...);

if (getUserInput) {
  char input = getUserInput();
  // Act on the user's input...
}

I do believe this example has merit, and that existance of a functor is not the only criteria by which the bool conversion should be performed. Perhaps I can conjure a more convincing example.

> > 4) I provide an "event" helper function which builds an
> > appropriate event object given the operands. Granted, this is
> > not very useful if #3 isn't considered useful.
>
> Right. The constructor for the callback is enough here so long as
> we're in agreement about #3. Hopefully I made enough of a case in my
> response to #3 to at least spark detailed debate if not enough to win
> you over completely.

Not yet :). Also, I don't believe the callback constructor is enough because it requires too much (unnecessary) typing. For instance, define an event as such: Event<float, int, int> myEvent;

Now, if we want to create a new event/callback to stick in myEvent, we do it this way:
myEvent = Event<float, int, int>(put_a_functor_here);

The usefulness of the "event" function is that, from a free function pointer or a member function pointer passed to event, we already know the argument types, so we can construct the appropriate (Member)FunctionEvent object.

The assignment operation could, of course, be templated for any functor, so we just write:
myEvent = put_a_functor_here;

In this case I worry about type safety, though there a probably a type-safe solution to this that I'm not seeing.

        Doug Gregor
        gregod_at_[hidden]


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