Just as a function can be overloaded on the number of arguments, in C++, it would be nice to do something similar with a class template, i.e. specialize on the number of template arguments. Would be? That's what variadic class templates (VCT) do. However, one may need instead a class template with exactly one template parameter that is supposed to be instantiated with an MPL sequence (containing the arguments). Let's denote it SCT.
VCT and SCT can be bridged using preprocessing macros, but this post looks at how to avoid this as much as as possible. To make an SCT from VCT, see this blog, which got me started on this. For the other way around, a solution is proposed below, but it only works for "true" variadic templates i.e. those that are supported in C++x0:
template<typename ...Args>as opposed to the old way, for example, with arity equals two:
struct my_class{};
struct use_default{};
template<typename T0 = use_default,
typename T1 = use_default>
struct my_class{};
Realizing that it's easy to get mixed up between class template and template class, template parameter and template arguments etc., to absolve myself from any mistake I defer to this very reliable source on all matters pertaining to terminology about templates.
Let's invent some specializations:
template<> struct my_class<> {};
template<typename T> struct my_class<T>{};
template<typename T,typename U> struct my_class<T,U>{};
What are the requirements of our algorithm? As an input we have an MPL sequence, equivalently a pair of MPL iterators (the first and the last). The returned value will have to be
my_class<> instantiated with all the elements from the sequence.How do we implement it? It will involve a recursion incrementing the first iterator until it reaches the last. At each iteration, the first of the remaining elements from the sequence is appended to the arguments already collected. Wait! You can only prepend
Args... (by the way, notice that the ellipsis is on the rhs of Args because here it's an argument, not a parameter), therefore we will need reversed iterators. This translates into:namespace impl{Let's test it:
template<typename F,typename L>
struct exit : boost::mpl::equal_to<
typename boost::mpl::distance<F,L>::type,
boost::mpl::int_<0>
>{};
template<typename F,typename L, bool exit,
typename ...Args>
struct to_variadic{
typedef typename boost::mpl::deref<F>::type front_;
typedef typename boost::mpl::next<F>::type next_;
typedef typename impl::exit<next_,L>::type exit_;
typedef typename to_variadic<next_,L,
exit_::value,front_,Args...>::type type;
};
template<typename F,typename L,typename ...Args>
struct to_variadic<F,L,true,Args...>{
typedef my_class<Args...> type;
};
template<typename Seq>
struct seq_traits{
typedef typename boost::mpl::begin<Seq>::type first_;
typedef typename boost::mpl::end<Seq>::type last_;
typedef typename impl::exit<first_,last_>::type exit_;
};
}//impl
template<typename Seq>
struct to_variadic{
typedef typename boost::mpl::reverse<
Seq>::type reversed_;
typedef typename impl::to_variadic<
typename impl::seq_traits<reversed_>::first_,
typename impl::seq_traits<reversed_>::last_,
impl::seq_traits<Seq>::exit_::value
>::type type;
};
int main (int argc, char * const argv[]) {
typedef boost::mpl::int_<0> _0;
typedef boost::mpl::int_<1> _1;
{
typedef to_variadic<
boost::mpl::vector<> >::type found_;
typedef my_class<> wanted_;
BOOST_MPL_ASSERT((boost::is_same<found_,wanted_>));
}
{
typedef to_variadic<
boost::mpl::vector<_0> >::type found_;
typedef my_class<_0> wanted_;
BOOST_MPL_ASSERT((boost::is_same<found_,wanted_>));
}
{
typedef to_variadic<
boost::mpl::vector<_1,_0> >::type found_;
typedef my_class<_1,_0> wanted_;
BOOST_MPL_ASSERT((boost::is_same<found_,wanted_>));
}
return 0;
}
Only a subset of the possible combinations are shown. That's not a trivial comment : my first test was partial and I had missed the reversed iterators part. In fact, I'm not claiming any more than the test shows as this stuff is still fresh.
I used Ubuntu 9.10/GCC 4.4 with the additional gcc command
I used Ubuntu 9.10/GCC 4.4 with the additional gcc command
-std=c++0x. For completeness, here are the headers that have to be included:#include <boost/type_traits/is_same.hpp>
#include <boost/mpl/assert.hpp>
#include <boost/mpl/vector.hpp>
#include <boost/mpl/equal_to.hpp>
#include <boost/mpl/distance.hpp>
#include <boost/mpl/int.hpp>
#include <boost/mpl/deref.hpp>
#include <boost/mpl/begin.hpp>
#include <boost/mpl/end.hpp>
#include <boost/mpl/reverse.hpp>
Thanks for reading till the bitter end and see you, hopefully soon.
This comment has been removed by the author.
ReplyDeleteSorry. I tried to post some code but the inane blog engine trashes the angle brackets, making template code into garbagge :(
ReplyDeleteFwIW, you may now find the code I tried to paste @
ReplyDeletehttps://github.com/scientific-coder/Computer-Languages/blob/master/interpreting/apply.hxx
HTH.
Best Regards,
Bernard