Boost logo

Boost :

From: brianjparker_at_[hidden]
Date: 2001-10-19 02:53:28


--- In boost_at_y..., "Peter Dimov" <pdimov_at_m...> wrote:
> From: <brianjparker_at_h...>
> > I have been experimenting with the boost bind library, and in
> > particular have modified it so that it passes arguments by value
or
> > reference as defined by the function's signature, so that ref()
need
> > not be used.
>
> The fact that bind always takes its parameters by value and keeps an
> internal copy (unless overriden by ref()) is a deliberate design
decision.
>
> The reasons are that:
>
> * This behavior is more consistent and easier to define, teach,
standardize
> (hopefully!);
>
> * You can tell from a glance at the bind expression which arguments
are
> by-value and which are by-ref.

I think that this is back-to-front. The writer of the function and
its signature defines the semantics of the function call- if he/she
uses pass by non-const reference then it because the function is
modifying and returning a result. The caller will need this result in
the vast majority of cases. This also seems (to me) to be a simpler,
more obvious behaviour to explain i.e. just the usual function call
semantics.
 
> * If you make bind autodetect, you need a mechanism to force pass-
by-value
> when the autodetection is wrong.

The autodetection is never wrong (for functions and function pointer
anyway), though the user may wish to subvert the function author's
intentions. In this less common case where a caller wants to discard
a pass by reference result, then they can just pass in a copy of the
variable; using static_cast will do- there is an example in the
sample code of this (one could of course write a simple T copy_val(T)
function to do the same thing).

> I agree that the autodetection can be convenient, especially for
non-const
> reference arguments.

Yes, in fact I would have thought that the ideal to aim for would be
to have the bind library do pure binding with no change to the
function signature, and, as demonstrated in the code I posted, for
functions and function pointers this is possible but it is less clear
that this is the case for general function objects, and it is here
that I appreciate your arguments that for consistency it may be
better to require ref() for all cases.

In the implementation I posted I have left function objects with the
original behaviour, but I think that it is possible to handle them
correctly as well. The problem is that the definition of "function
object" is not well-specified. It seems to me that a basic
requirement for a function object is that it has a *single* operator
() (or possible const and non-const versions of the one operator()).
Without this basic requirement being met then the object can not have
a well-defined signature, and if there are overloaded versions of
operator() then the object is no longer equivalent to a single global
function but rather to a set of overloaded global functions. I think
that this is the definition implied by the standard.
 
Given such a function object, there are two ways to extract the
signature- (1) do type deduction on a member pointer to the single
operator(), (2) use member typedefs (first_argument_type etc) (or,
equivalently, a member typedef of type pointer to member function of
the correct type).
In case (1), without a typeof facility one can't return the deduced
info from bind() (though one could manually use the member function
version of bind to get the same effect). Case (2) is used by the
standard function objects (though they get the types wrong,
specifying T for a const T& argument), but I think one would also
need the arity available as a member.

Finally, I should say that for function objects this whole issue is
less important- being objects they carry their own state and so have
less need for pass by reference.

> > Also, one can now pass temporaries as arguments.
>
> This is an interesting side effect; I haven't thought of this
solution,
> although it's a bit dangerous since you can pass a const lvalue to a
> non-const reference this way AFAICS.

Yes, it weakens type safety slightly, in two cases- (1) temporaries
can be bound to non-const references and (2) non-const references can
bind to const variables. In fact, for the bind function (1) is an
advantage and is used as described above to avoid a pass-by-
reference. Case (2) is unavoidable without additional language
support (e.g. a safe_const_cast that only allows the casting away of
const if the original variable wasn't really const). (Though note
that modifing a const lvalue through a non-const reference has
unspecified behaviour and so a good compiler could give an error or
warning in most cases, in theory, anyway).

> I haven't encountered a need to pass a temporary to bind so far, by
the way.
> I mostly use it with standard algorithms and boost::function that
don't pass
> temps.
>
> > // todo: it would be nice to have 10 separate versions of bind_t
depending
> on arity
> > i.e. each with a single operator()
> > // -arity should be calculable at compile-time based on the
largest value
> of arg<N>
> > found
>
> This is deliberate, too. bind silently accepts and ignores extra
arguments.
> Consider:
>
> bind(f, _9); // ignores 8 arguments at the front
> bind(f, _1); // should be able to ignore 8 arguments at the end, too
 
Oh OK, I didn't realise you could do that. Though even in this case
should it not in theory be possible to deduce the final arity of the
bound function object at compile time using some sophisticated
metaprogramming? The main advantage to having a single operator() is
that the result is then a function object as I have defined it above.

On a related note, one feature that might be useful to add to the
library is a _free dummy argument for the common case where the order
of the unbound arguments isn't changed or duplicated e.g.

func(a, _free, b, _free, _free) (c, d, e)

would be equivalent to

func(a, _1, b, _2, _3)(c, d, e)

I don't know if that is possible, I couldn't think of an easy way to
do it.

> [Your helper functions at the end of bind.hpp are less useful on
compilers
> that don't allow &plus_f<int> due to core issue #115. This issue is
evil.]

Thanks for that info. My Intel compiler wouldn't let me use them
without assigning them to a function pointer first, but I thought
that was just a compiler bug; I hope this issue gets fixed so they
can be used directly.

Thanks again for taking the time to examine my code changes- they
don't really change the functionality of your library in any case, I
really just posted them for interest's sake.

,Brian Parker


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