Boost logo

Boost Users :

Subject: [Boost-users] [OT] Can I count on order of template matching of array dimensions
From: Patrick Horgan (phorgan1_at_[hidden])
Date: 2010-10-24 16:00:34


Off topic. Feel free to ignore. About baby template
meta-programming.

Working my way through "C++ Template Metaprogramming"
by Abrahams and Gurtovoy, and am doing exercise 2-3/4.
This:

template<typename T,size_t D>
struct
type_descriptor<T[D]>
{
     static std::ostream& print(std::ostream& os)
     {
         os << type_descriptor<T>() << '[' << D << ']';
         return os;
     }
};

When asked to deal with a type, for example,
char[7][8][9] results in the reversal of the indices
and prints char[9][8][7]. I can deal with that, (see
the p.s. at the end), but it made me curious. That
implies that the compiler is seeing it like
((char[9])[8])[7] which makes sense. It's an array of
length 7 of arrays of length 8 of arrays of length 9
characters. That's C/C++ row-major order. It has 7
tables each of which has 8 rows of 9 characters. My
question is this. Can I count on this? I don't mean
row-major order, I mean that the compiler will always
match in this order. Is this matching BECAUSE of
row-major order, or is it compiler dependent? Wait,
never mind. I just thought it through. The
implication, if it had done it textually as I expected,
is that T would have had type char[7][8], and the
second time, T would have had type char[7] which would
have been a completely different beast. Well. I'm not
really asking the question now after all, just throwing
this out in case it helps anyone else's understanding.
Please feel free to ignore. lol!

I'm also implementing all of the is_char, is_array,
remove_reference etc. metafunctions instead of using
the boost or c++0x ones. I figure that way by the time
I get through the book I'll have more understanding.
That has given me questions, because after I implement
each one, I look at the boost implementation, and I
look at the the gcc libstdc++ implementation of the
c++0x ones and generally they are, in order of reducing
complexity, boost, gcc, me. Some of the difference in
complexity is that boost needs to work with different
compilers on different architectures, and gcc has to
work with different architectures, and I just have to
get it to compile on my machine (ubuntu maverick
meercat on a dell inspiron 1420 laptop) with the gcc
compiler (last night's pull from trunk) that I'm
using. Other times, though, it LOOKS to me like
there's extra complexity for nothing (I think brought
about by the desire for maximal code reuse, i.e. don't
include something in your template when you can invoke
another cool metafunction to do it for you, but at the
expense of higher compile-time complexity). I'll hold
off on questions like that, though, until I finish the
book in case the answers are in there. ;p

Patrick

p.s. if anyone cares, here's what I came up with (with
the char base type type_descriptor so you can do
std::cout << type_descriptor[7][8][9]). Any comments,
improvements, critiques, and alternate implementations
welcome:

template<typename T>
struct
print_dimensions
{
     // handles exit condition for recursive
print_dimensions
     static std::ostream& print(std::ostream&os)
     {
         return os;
     }
};

template<typename T,size_t D>
struct
print_dimensions<T[D]>
{
     static std::ostream& print(std::ostream&os)
     {
         // print our dimension and invoke ourselve
recursively to do the next
         os << '[' << D << ']';
         print_dimensions<T>::print(os);
         return os;
     }
};

template<typename T>
struct
remove_all_dimensions
{
     // when no more dimensions this is the stop
condition for the recursion
     typedef T type;
};

template<typename T, size_t D>
struct
remove_all_dimensions<T [ D ]>
{
     // recurse to ask our type with one less dimension
to define the type for us
     typedef typename remove_all_dimensions<T>::type type;
};

template<typename T>
struct
type_descriptor<char>
{
     static std::ostream& print(std::ostream& os)
     {
         os << " char";
         return os;
     }
};

template<typename T,size_t D>
struct
type_descriptor<T[D]>
{
     static std::ostream& print(std::ostream& os)
     {
         // pull off all the dimensions, print the base
type, then ask print_dimensions to print the dims.
         os << type_descriptor<typename
remove_all_dimensions<T>::type>();
         print_dimensions<T[D]>::print(os);
         return os;
     }
};

p.p.s This is really fun!
p.p.p.s Does anyone know of a mailing list to discuss
beginning TMP?

Patrick again ;p


Boost-users list run by williamkempf at hotmail.com, kalb at libertysoft.com, bjorn.karlsson at readsoft.com, gregod at cs.rpi.edu, wekempf at cox.net