Boost logo

Boost :

From: Douglas Gregor (gregod_at_[hidden])
Date: 2001-07-22 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 non-const 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 "pointed-to"
> > types, e.g., iterator to int and iterator to const int. The same does
> > not quite apply to function, because it "points to" some
> > dynamically-chosen 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 non-const 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 non-const version, so what actually happens is that fc targets a
> > copy of f, which targets the non-const 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 non-const 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
non-const 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