Boost logo

Boost :

From: Geurt Vos (G.Vos_at_[hidden])
Date: 2001-04-10 04:00:44


I've been studying the code of any_function a bit, and came to
the conclusion that at least the goals are like, quite different.
For instance:
* it's not really type-strict. My implementation is.
* the implementation is particularly optimized for speed.
Mine for simplicity and extendibility. (I actually never
intended to use it as e.g. predicate to std algorithms)

I think I can came come up with some more, well, design
differences. Still, I'll comment on a view things:

> > >
> > > ------------------------------
> > > Interface summary:
> > >
> > > template<typename Result, typename Arg1, typename Arg2,
> > > ..., typename ArgN>
> >
> > The main problem with this approach is that it isn't extendable.
> > I mean, how many parameters to support? as I view the code, it's
> > implemented for up to 6 parameters, which is in my opinion enough
> > (my 'previous version' had this approach for up to 5), but I
> > guarantee you someone will require 7. The largest implementation
> > I've seen so far is Alexandrescu's Functor class: a massive 15
> > parameters (the one in Modern C++ Design).
>
> This argument has been made on this list in the past. Both sides
> have valid arguments, IMHO, making this a very sticky design decision
> to deal with.
>

Hmm, the main problem with the current approach is that there's
_no_ way to extended it with e.g. policies. For instance, whether
or not to throw when no function attached. I mean, that would mess
up the default Argx declaration. I wouldn't want to declare it like:

function<
        void,
        int,
        detail::unusable,
        detail::unusable,
        detail::unusable,
        detail::unusable,
        nothrow
> instance;

Apart from undesirable, it now is impossible to add a new
(a 7th) parameter.

So I simply think doing it this way is wrong, but hey,
that's just me...

> > > bool empty() const; // true if no target
> > > operator const void*() const; // evaluates true if non-empty
> > >
> >
> > Again, I'm not certain whether both are required.
> > I personally would lose the operator...
>
> The operator allows for seemless replacement with traditional C
> style "callbacks". I'm not at all sure that I'd lose the operator
> here.
>

You're probably right. I think it actually makes more sense
to remove 'empty()', because function pointer don't have
member functions, and empty() doesn't really add anything.
Besides that one could mistakenly assume it can contain more
than one function pointer (as with regular containers).

> I also think it would be more
> appropriate to throw than to return Result(),

It really depends on what 'function' is used for. When it's used
as e.g. predicate to whatever algorithm, it should throw. When
it's used to hold callback functions for an event driven system,
particularly when the return type is 'void', it would make sense
to simply ignore the call. Then again, the first one might be a
stronger argument. I mean, the 'even manager' or whatever could
of course simply perform a check before calling the function.

> and can think of no
> valid reason to ever use validate=false.
>

Efficiency. If it's used as predicate, it's more efficient to
check ones before calling the algorithm, than check on every
call.

For my implementation, I'll probably un-decide this one, because
also provided is a non-checking function. Maybe I'll throw out
the entire auto-checking system as a whole - make it an assertion.

> > Callback will inherit from CxParam<>, and CxParam will
> > inherit from CallbackHolder<StoragexParam>. StoragexParam
> > is a class template, which has two nested classes: one for
> > the interface (with only operator() as virtual func) and
> > one for storing a pointer to the actual function (which is
> > either a function, function object or a function member).
>
> Ahh... this helps to figure out how you eliminated the fixed number
> of parameters, but I'd have to see all of the implementation to fully
> comprehend this. However, you've added in virtual methods here,
> which was specifically avoided in any_function (again, read the
> documentation). It's being used in a slightly different location so
> may not be prone to the same problems being avoided here, but it
> still throws up red flags for me.
>

It's a flexibility & extendibility vs. efficiency trade of. I could
create an implementation similar to how it's solved for any_function,
but I still feel it's close to a hack, and at least harder to
understand. From a user point of view it won't really harm type
safety, though, and of course, there are easy ways to make it as
type strict as my implementation is.

Another reason for choosing the virtual function approach is because
99% of the time the overhead generated by it is negligible. I don't
think designing out this overhead will really have that much effect,
unless you're really pushing the limits. In that case, I recommend
using a faster CPU :), because it's highly doubtful that if it were
designed out you won't run into performance problems elsewhere...
  

> > The two criteria I mentioned at the beginning of this mail
> > I think have been met quite nicely with this implementation.
> > I can make it even easier by using virtual inheritance at
> > two place, but I didn't want to touch that yet :)
>
> It appears that you've also made the interface more complex to use,
> which is a bad thing IMHO.

On the contrary. It's basically the same. The one thing you could
say is that it's scattered over three classes. Other than that, the
only member function is 'IsAttached()'. There's of course one
additional constructor for passing a memfunc/instance pair (not
std::pair!), and for assignment only copy assignment is implemented
(and required).

If you look at instantiation, my implementation adds an
indirection, which might be annoying at first. The good
thing about it is that you don't have to count the number
of parameters used because it's in the name.

One last thing: assigning a function will cause memory allocation.
copy construction/assignment doesn't, because reference counting
is used...

> I'd love to see the actual implementation
> so I could better evaluate it, though.

I'll have it uploaded to the boost file area.

Geurt


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