|
Boost : |
From: Douglas Gregor (gregod_at_[hidden])
Date: 2000-11-17 19:59:05
On Fri, 17 Nov 2000 23:59:10 -0000
"William Kempf" <sirwillard_at_[hidden]> wrote:
> No, it wouldn't die. But you're wrong about only one function being
> instantiated. The number instantiated is totally dependent on how
> the programmer uses the class. You may be able to get it down so
> that only one can legally be instantiated, which may be good enough.
> I'll have to work on an implementation.
It should be easy to allow only one to be instantiated:
template<typename T> struct RequireUnused;
template<> struct RequireUnused<Unused> {};
Then, for an operator() with M arguments (with N arguments supported by
Callback), we have:
RequireUnused<ArgM+1>;
RequireUnused(ArgM+2>;
...
RequireUnused<ArgN>;
Giving "Unused" a private default constructor will make sure that the user
doesn't try to pass an "Unused" as an extra parameter.
[snip]
> > > Please do. This particular example at first seems compelling.
> > > However, you're looking at this as an "event" while I'm looking
> at it
> > > as a "callback". Though related they are different concepts.
> > > Realizing that, if the above functionality is not common it may
> make
> > > more sense to have both concept types, building the event type
> out of
> > > the callback type.
> >
> > The obvious question of "which does Boost need?" comes to mind. It
> seems
> > that the only difference between an event and a callback that we
> have
> > listed is that an event can turn itself off, which is to some
> extent a
> > minor difference, since a callback is just an event that is always
> on.
> > Are there any other differences that should be discussed?
>
> The ability for external criteria to turn off an event would make it
> VERY problematic to use for thread creation, which bolstered me to
> push for discussion here. No, what I need is a callback, not an
> event. The nice thing is, if we have a callback that's well defined
> and implemented, it can be the basis for many other related types,
> such as events (or Singals/Slots, which are synonymous with your
> MultiEvent).
Taking a selective on/off mechanism and adapting it to always be on
(as is needed for a callback) is easy to do, I'm just worried that if
we implement the least common denominator we will make it much tougher
to add that selective on/off mechanism (would it require an additional
functor? new member functions in the functor?).
> > > I wasn't really advocating the removal of the creation function,
> only
> > > questioning the need for multiple class types for functions,
> member
> > > functions and functors.
> >
> > The Functor implementation class is the only essential class. The
> > otherwise were included because I believed them to enhance type and
> > runtime safety (e.g., a NULL function pointer will never be called).
> > These concerns can be addressed in other places, but I think it
> would
> > be a mistake to allow a user to pass a NULL function pointer and
> then
> > have the callback code dereference it.
>
> This kind of safety should really be addressed by the code that
> implements the functors.
What should a functor do, then, if it gets a NULL pointer? The options
I currently see are:
1) Immediatly throw an exception.
2) Throw an exception when the function call operation is
invoked.
3) Don't dereference, but don't complain.
The first and second options are fine, though it's hard to see where to
put the exception handling code for #2. The third option isn't
reasonable when the callback returns a non-void value. The only way for
the calling function to know if the event will be valid or not is if
it is guaranteed to be valid (e.g., by #1).
> > > > 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.
> > >
> > > Might be simplest to just include a reassign() method instead of
> an
> > > operator=(). This would be as type safe as the templated
> constructor
> > > in the original implemenation.
> >
> > I think operator=() is most natural.
>
> Not when applied to Lambda expressions. Does the following really
> make sense?
>
> callback<void, int> cb;
> cb = cout << free1 << endl;
>
> In contrast, reassign is understandable and intuitive for either.
Why not allow both and let the user decide what is readable? (even
cb.reassign(cout << free1 << endl); would confuse me, though I can't
say I've familiar with the lambda library).
> > As for the original templated
> > constructor, I dislike where an error will occur if an incorrect
> > functor type is given to the callback, which will be way down in the
> > detail::callback0_impl::operator() function. Using the more rigid
> > approach with the three implementation classes, this error will be
> > picked up when an assignment is attempted.
>
> This can be handled with Concept Checking :). No, I don't have code
> to do this yet, but I don't see any other way to handle arbitrary
> functors.
In this case we would probably need a functor_traits class to dig out
the number and types of arguments. I used a simple FunctorTraits class
(which contains a "nArgs" value given the number of arguments, a Result
typedef, and Arg1, Arg2, ..., ArgN typedefs giving the argument types).
A GetArg<Functor, ArgNum> class gets the appropriate argument type
(either a type, or "Unused" if ArgNum > nArgs).
Doug Gregor
Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk