
Boost : 
From: Douglas Gregor (gregod_at_[hidden])
Date: 20010722 17:01:27
On Sunday 22 July 2001 03:17, you wrote:
> Douglas Gregor writes:
> > > If we take
> > > the analogy of a "pointer to functor", you're fighting with the
> > > difference between:
> > >
> > > 1) functor * p;
> > > 2) functor const * p;
> > > 3) functor * const p;
> > > 4) functor const * const p;
> > >
> > > your current semantics seem to have (=> meaning "is analogous to"):
> > > functor => 1)
> > > const functor => 4)
> > >
> > > while the proposed semantics seem to be:
> > > functor => 1)
> > > const functor => 3)
>
> I made a few typos later on in what I wrote. To clarify:
> 1) functor can be reseated, calls 'operator()' on its referent.
> 2) functor can be reseated, calls 'operator() const' on its referent.
> 3) functor cannot be reseated, calls 'operator()' on its referent.
> 4) functor cannot be reseated, calls 'operator() const' on its referent.
>
> > I was proposing just:
> > functor => 1
> >
> > and an auxiliary function object could (at 'function'
> > assignment/initialization time) decide if for a particular target
> > function object instance, it should be
> > functor => 3
>
> I'm not quite sure what you mean by "auxiliary function object." Could
> you perhaps suggest some syntax?
See below...
> given the following:
>
> struct ftor {
> void operator()();
> void operator()() const;
> };
>
> void my_func(const function<void>& f) { f(); }
>
> what do you propose happens if I call my_func with a boost function
> pointing to 'ftor? I'm under the impression that your proposal would
> call 'operator()()', which was what I meant above in my statement of
> your proposal.
Yes, the new semantics would call the nonconst operator()() of ftor.
> I'm not sure I understand what you're saying here. Could you write
> out a possible syntax for this?
So, if one wanted to call ftor::operator()() const, the 'auxiliary function
object' would come into play. It would be a simple set of function objects
like this:
template<typename F, typename R, typename T1, typename T2, ..., typename TN>
struct invoke_as_constN {
invoke_as_constN(const F& f) : m_f(f) {}
R operator()(T1 a1, T2 a2, ..., TN aN) const
{ return m_f(a1, a2, ..., aN); }
F m_f;
};
These classes would be used like this:
function<void> f;
function<void> fc;
f = ftor();
fc = invoke_as_const0<ftor, void>(ftor());
f(); // calls ftor::operator()()
fc(); // calls ftor::operator()() const via invoke_as_const0
> > > One pertinent question is, is there a sensible use for a functor with
> > > semantics 2) or 4). That is, does it make sense to have a functor
>
> argh! I meant 3) or 4) here, but I think you got the idea. It makes
> sense to have a boost.functon that cannot be reseated regardless of the
> constness of the referent.
Yes.
> > changed. However, see the reason in my prior post why having parallel
> > function/const_function won't (and probably can't) work.
>
> quoted here:
> > Perhaps. The one major difference between iterator/const_iterator and a
> > function/const_function is that iterators have definitive "pointedto"
> > types, e.g., iterator to int and iterator to const int. The same does
> > not quite apply to function, because it "points to" some
> > dynamicallychosen type that always has an operator() const and may have
> > an operator() as well. I think that because of this discrepancy the
> > decision to call const or nonconst shouldn't be up to the 'function'
> > itself (i.e., function or const_function),
>
> Just some thought...suppose you had an iterator it over a container of
> functor base class objects with virtual 'operator()()'. If you call:
> (*it)();
> it would call the derived object's operator()(). if you have a
> "const iterator" (NOT const_iterator), the same thing would happen.
> The difference is that the iterator cannot be moved. This const
> iterator would be analogous to a "const boost.function" which could
> not be reseated.
>
> Now if you have a "const_iterator" cit into this container and call:
> (*cit)();
> it would call the derived's 'operator()() const'. Of course an iterator
> of type "const const_iterator" would do the same, but not be movable.
> Supposing there were a boost.const_function, this would be the
> analogous type (if const, can't be reseated).
>
> now, on to your discussion below:
> > The conversions may even be impossible :). Thinking about this, for
> > instance, with the above my_function_object class:
> >
> > boost::function<int, int, int> f = my_function_object();
> > boost::const_function<int, int, int> fc = f;
> >
> > So "f" should call my_function_object::operator() and fc should call
> > my_function_object::operator() const? Can't happen: f doesn't even know
> > my_function_object::operator() const exists, because it only references
> > the nonconst version, so what actually happens is that fc targets a
> > copy of f, which targets the nonconst my_function_object::operator()!
>
> I don't think that the above line should cause fc to target f as it
> would a function object of any other type.
> const_function would not target function, but cause a conversion from
> type function to type const_function. This is an altogether different
> beast. Now, the big question is as follows: Is it possible to detect
> during a conversion from function to const_function (either at compile
> or runtime) whether the referent of an already created boost.function
> has an 'operator() const'.
I believe the answer to this is 'no'. Once the assignment of a target to a
boost::function object has been completed, the target's type information is
lost. Anything we didn't lookup during the assignment we won't have access
to. So if boost::function only looks up a nonconst operator(), we don't know
how to access operator() const when we copy the boost::function object into a
boost::const_function object. Granted, we could lookup both operator() and
operator() const, but then we're back to stub routines :(.
> take the following code:
> <snip>
> struct my_functor {
> void operator()() { } // no const
> };
>
> int main() {
> // ...
> my_functor* f = new my_functor();
> my_functor const* fc = f;
>
> (*f)();
> (*fc)();
> return 0;
> }
> <snip>
>
> A compiler should not accept it. I like to think that the same sort
> of detection can be made in a conversion from boost.function to
> boost.const_function, but due to casting stuffs, perhaps it cannot be
> determined until runtime (I only vaguely understand the implementation
> of boost.function)? At which point a runtime fault of some sort
> sort could occur.
Having the ability to convert from boost.function to a boost.const_function
requires a const version to always exist, even if it is invalid. Such a
restriction can be a great annoyance. I think that using the wrapper
(invoke_as_constN above, though perhaps will some sugar trickery to make it
easier on the fingers) is the best alternative. Whether the const or
nonconst operator() is called should be dependent on the target ("pointee")
of the boost.function, and not on whether a boost.function or a
boost.const_function is used.
> > > As far as making the semantic change, I vote for doing so. While
> > > Boost.Function is already "out there", I think that the current
> > > semantics confusing. If/When these changes are released, A notice
> > > should go in the Boost Release notes stressing such a semantic
> > > change.
> > >
> > >
> > > ron
> >
> > Does Boost have Release Notes?
> >
> > Doug
>
> I was thinking of what appears on the main web page after a release.
Doug
Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk