Transposer and Indexer are implemented in such a way that a partial template specialization is provided for each possible RANK (up to 11), and the corresponding code is automatically generated by the Boost.Preprocessor library. This can be seen in utils/include/details/IndexerImplementations(Specializations).h. To actually see what code is generated by them, they need to be preprocessed. Issue the following command from the root directory of the distribution:
g++ -P -E -Iutils/include/ utils/include/details/IndexerImplementations.h | tail -n286
To store and manipulate the heterogenous collection of slicing indeces like 0,a,2,a,4,5,a,a,8,a,10 above, the boost::fusion::vector class of the Boost.Fusion library is used.
Iterator is implemented in terms of the above two helper classes. Each Iterator involves a transpose() at its construction, and—if RANK is larger than mpl::size<V>::value—an index() is invoked at the point of its dereferencing, when the actual slicing occurs.
A special implementation is needed when the size of the compile-time vector V equals RANK because in this case actually no slicing takes place, only transposition. For this, as at several other places in the framework, we apply conditional inheritance: Iterator inherits from either of two classes (IteratorBase or IteratorBaseSpecial), the decision being made at compile time with the help of mpl::if_c.
The iteration over dummy indeces is implemented with the help of MultiIndexIterator.
In the following we analyse a metaprogramming example typical for the framework: how the compile-time vector 0,3,2,6,4,5,1,9,8,7,10 for the self-transposition above is prepared.
This is done by the following snippet in utils/include/impl/BlitzArraySliceIterator.tcc:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 | namespace blitzplusplus {
namespace basi {
namespace namehider {
using namespace boost::mpl;
namespace mpl=boost::mpl;
using namespace tmptools;
template<int RANK, typename V>
struct Algorithm
: fold<Ordinals<RANK>,
pair<vector_c<int>,typename boost::mpl::begin<V>::type>,
pair<push_back<first<mpl::_1>,
if_<numerical_contains<V,mpl::_2>,
deref<second<mpl::_1> >,
mpl::_2
>
>,
if_<numerical_contains<V,mpl::_2>,
next<second<mpl::_1> >,
second<mpl::_1>
>
>
>
{};
} // namehider
template<int RANK, typename V>
struct TransposerMeta : boost::mpl::first<typename namehider::Algorithm<RANK,V>::type>
{};
} // basi
} // blitzplusplus
|
express the forward operation of the fold algorithm in the form of a compile-time lambda expression. After each step, the new state will again be a pair composed of
(Note that in TMP, “calling” numerical_contains<V,mpl::_2> twice is no waste because the second time no further template instantiations are needed, the compiler will use the ones already instantiated the first time.)
Lines 32-34: the first element of the resulting pair of the above algorithm is picked. As it is easy to verify, TransposerMeta<11,tmptools::Vector<3,6,1,9,7> >::type will be an mpl::vector_c<int,0,3,2,6,4,5,1,9,8,7,10>.