Boost logo

Boost :

From: Emily Winch (emily_at_[hidden])
Date: 2001-11-27 06:15:27


> > Yes, it was the practical value that I was missing :) But,
> > practical value or not, it makes sense that all functions
> > over types have a consistent interface.
>
> Yes, and we already have a de-facto standard:
>
> template<typename T1, .., typename Tn>
> struct value_func
> {
> static N const value = /* ...*/; // N is some integral or enumerated
> type
> };
>
> template<typename T1, .., typename Tn>
> struct type_func
> {
> typedef /* ...*/ type;
> };
>
> The above "interface rules" are applicable not only to compile-time
> "functions", but to compile-time "objects" too:
>
> template<long N>
> struct int_t
> {
> BOOST_STATIC_CONSTANT(long, value = N);
> };
>
> Difference between "functions" and "objects" here is kind of subtle,
though
> :). Unfortunately, things do not always fit that "value/type" pattern.
> Consider, for example, mpl::fixed_t<> template that represents
compile-time
> fixed-point arithmetic type:
>
> template<long IntegerPart, unsigned long FractionPart>
> struct fixed_t
> {
> BOOST_STATIC_CONSTANT(long, integer_part = IntegerPart);
> BOOST_STATIC_CONSTANT(unsigned long, fraction_part = FractionPart);
> };

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.

> The template just can provide you a meaningful "value" interface. And
there
> are cases where compliance to the above interface rules is possible, but
not
> necessary desirable; 'type' can be too "impersonal" for a function return
> value; after all, almost everything you operate at compile time is a type
> :). Often it's not a problem, for example, rewriting
>
> add_const<T>::type
>
> as
> add_const<T>::const_type
>
> doesn't add much value; some may argue that the latter version is indeed
> _harder_ to read than the first one. But there are cases when choosing a
> more specific name for a function's result typedef _does_ improve
> readability and expressiveness of the code - consider, for example,
> mpl::find<> algorithm:
>
> mpl::find<type_list, T>::type
>
> What do you think it returns? :). Well, even if you guessed right, I think
> it's much easier to answer the same question when the above line is
written
> as
>
> mpl::find<type_list, T>::iterator

People who write _normal_ functions manage to cope somehow without naming
their return value :) 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...)

> (BTW, renaming the function to "find_T_and_return_iterator" is not
something
> that I am very keen of :). Still, even for algorithms, having a "type"
> interface somewhere in a "backyard" is IMO an obligatory option, because
it
> makes combination of those compile-time functions and passing them around
> much easier. For example, one can easily implement 'transform' algorithm
in
> terms of 'mpl::for_each' and 'mpl::push_back' exactly because 'push_back'
> supports "type" interface, so you can pass it to 'compose_*' (which
produces
> a function class with "type" interface too :):

Yep. Being able to use the return value of one function to pass to another
function is a serious major fundamental thing.

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 */
  }
};

It would be really really nice if we could do

struct X : public runtime_function<long_calculation_with_T>{
  /* operator() like before, get_return_type now inherited
     from runtime_function */
};

But you'd need to be able to "overload" runtime_function to deal with
different numbers of parameters to the operator(), and with return types
that don't depend on the template parameter. Template template parameters
would muck it up too. :(

> > Last night I finally managed to spend some time looking at
> > mpl and at the tuple extension stuff. Well, it hurts my head :)
> > Some documentation would be really useful!
>
> I'll check in something in a few days.

Cool.
 
> > There is certainly a lot in common between the functionality
> > of my tuple stuff and your typelist stuff. There seems to be less
> > commonality in the approaches we are using to get there,
> > which is a shame.
>
> 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. 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.

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. _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.

Emily.


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