$include_dir="/home/hyper-archives/boost/include"; include("$include_dir/msg-header.inc") ?>
From: Aleksey Gurtovoy (alexy_at_[hidden])
Date: 2001-11-27 15:40:01
Emily Winch wrote:
> So, there are functions, which expose a return value called 
> "type", there are  objects which expose a "type" because 
> they can be used as functions, and there are objects which 
> are definitely not functions and should not expose a 
> return value.
Exactly. 
> People who write _normal_ functions manage to cope somehow 
> without naming their return value :) 
Yeah, I wondered how I do it too :). IMO "normal" functions can be
classified into the same two categories as well: ones which return value's
type is obvious from the name of the function (i.e. which are in the same
category as add_const<>; "person.name()" is one example, although may be a
controversial one :), and the ones for which you infer the information about
their return value's type _mainly_ from the function's signature or its
usage context (or from reading the docs :); for example, if it was possible
to write:
    std::pair<my_iterator,bool> res(map.insert(777));
as
    auto typename T res(map.insert(777));
when one would have no chances to guess what type "res" is, without looking
at std::map<>::insert signature, or reading the docs (supposing that it's
the first time she came across this function, of course :). 
So, the issue is pretty much the same in the "run-time world" as well, it's
only shaded a little bit by the language strong static type system that
forces you to write clues about function return type all over the place :).
Hmm, just realized that I haven't thought about dynamic-typed languages :). 
Anyway, I guess you are right in sense that it's mainly a matter of
familiarity with the library; after you've used 'find<seq, T>' for a while,
'find<>::type' would be as clear to you as 'find<>::iterator', and the
former is shorter :).
> So long as it is clearly documented that "type" means "return  
> value" and not "member variable named type", then I think the
> intention is not  confusing. (other people's MMV...)
Hmm.. may be the ability to write both 'begin<seq>::iterator' as well as
'begin<seq>::type' is indeed a misfeature, and we should stick to simple and
consistent "::type" everywhere.. what do you think?
> Also useful would be a convention for denoting the return 
> type of a runtime function, where that type depends on the 
> template parameters of the function, like this
> 
> struct X{
>   template<class T> struct get_return_type{
>     typedef typename long_calculation_with_T<T>::type type;
>   };
>   template<class T>
>   typename get_return_type<T>::type
>   operator()( /* whatever */ ){
>     /* runtime stuff */
>   }
> };
Yes, IMO handling both runtime and compile-time computations
simultaneously/in the same place makes a perfect sense, and often it's the
only way to do things effectively. In fact, chances are that I would write
the above as 
    template<class T>
    struct X {
      typedef typename calculation_with_T<T>::type type;
      type operator()( /* whatever */ ){
        /* runtime stuff */
      }
    };
FWIW, a while ago MPL had the following:
    template<typename SequenceTag>
    struct back_algorithm_traits
    {
        template<typename Sequence> struct algorithm
        {
            typedef /**/ type;
            static type& result(Sequence& seq) { return /**/; }
            static type const& result(Sequence const& seq) { return /**/; }
        };
    };
    template<typename Sequence>
    struct back
        : back_algorithm_traits< 
              typename mpl::sequence_traits<Sequence>::sequence_category
              >::template algorithm<Sequence>
    {
    };
    template<typename Sequence>
    typename mpl::back<Sequence>::type&
    back(Sequence& seq)
    {
        return mpl::back<Sequence>::result(seq);
    }
    template<typename Sequence>
    typename mpl::back<Sequence>::type const&
    back(Sequence const& seq)
    {
        return mpl::back<Sequence>::result(seq);
    }
but at some point I got rid of run-time 'result()' stuff in sequence
algorithms under the flag of simplification :). Needless to say that now I
think it was a mistake :).
 
> > One particular commonality that I was talking about is 
> > (IMO) in tuple algorithms and iterators. For example, as 
> > far as I can see, your 'for_each' and my 'for_each' do 
> > pretty much the same thing, except that mine allows
> > function object to change its type on each step of 
> > iteration (and ignoring the fact that my compile-time 
> > check for iterators' equality is kind of strange :).
> 
> Strange compared to which other compile-time iterator 
> equality check? ;) I don't see any other way to do that: 
> your way is exactly the same way as I came up with. 
Well, at least yours is hided inside of the iterator type itself :).
> Unless anyone can think of a better way to compare 
> equality of iterators at compile time (than comparing the 
> type of the list they point at) I think we can enshrine that 
> as the Normal Way To Do It.
It probably is.
> 
> The thing where the function object itself can change type is 
> actually a neater way to do what I've been trying to do all 
> along. I had the function object calling a Thingy that the 
> user could specialise. 
I like your names :). I am afraid I don't have enough information to analyze
how different our _applications_ of the tuple algorithms are (but from what
you've said, it seems that they are be different), but my main motivation to
give a function object a possibility to change its type was to allow one to
accumulate internal state on each step of iteration; I guess I need to see
some examples to be able to grasp a need for specialization here :).
> _Some_ way of doing that kind of thing seems fundamental 
> to tuple algorithms, since the bigger the tuple the more likely 
> it is to contain a type that requires special treatment. Not to 
> mention pointers and references which both also often seem
> to require special treatment.
> 
> I shall have to do some thinking about that. Hmm.
Sure. 
-- Aleksey