Несмотря на то, что этому вопросу уже более года, я чувствую, что заслуживает правильного ответа.
Следуя идее Xeo, вы можете сделать именно то, что вы хотите, намного проще.Здесь я предполагаю, что вам нужен чистый c ++ 03.
Прежде всего, объявите тип, который будет использоваться просто как заполнитель
struct _;
Объявите также тип, который будет интерпретироваться как статическая константа.
template<typename T, T>
struct constant;
Обратите внимание, что ни для одного из вышеперечисленных определений не требуется.
Следующим шагом является определение типа, который будет представлять элемент кортежа, то есть типкоторый на самом деле содержит данные.
template<typename T>
struct element
{
typedef T type;
type value;
};
template<>
struct element<_>;
Ключевым моментом является специализация element
для constant
и любых других типов, которые вы хотите, например, int_
и bool_
, которые выцитируется.
template<typename T, T val>
struct element<constant<T, val> >
{
typedef T const type;
static type value = val;
};
template<typename T, T val>
typename element<constant<T, val> >::type element<constant<T, val> >::value;
С этого момента определение ntuple
является простым.
template<typename T0_ = _, typename T1_ = _, typename T2_ = _, typename T3_ = _>
struct ntuple : element<T0_>, ntuple<T1_, T2_, T3_, _>
{
typedef element<T0_> head;
typedef ntuple<T1_, T2_, T3_, _> tail;
};
//nil
template<>
struct ntuple<_, _, _, _>
{};
Для доступа к метаданным, хранящимся в ntuple
, используется метафункция get_type
template<std::size_t n, typename T>
struct get_type;
template<std::size_t n, typename T0, typename T1, typename T2, typename T3>
struct get_type<n, ntuple<T0, T1, T2, T3> > : get_type<n-1, typename ntuple<T0, T1, T2, T3>::tail>
{};
template<typename T0, typename T1, typename T2, typename T3>
struct get_type<0, ntuple<T0, T1, T2, T3> >
{
typedef typename ntuple<T0, T1, T2, T3>::head::type type;
};
Аналогично, для доступа к данным времени выполнения используется функция get_value
template<bool cond, typename T>
struct enable_if
{
};
template<typename T>
struct enable_if<true, T>
{
typedef T type;
};
template<std::size_t n, typename T0, typename T1, typename T2, typename T3>
typename enable_if<n, typename get_type<n, ntuple<T0, T1, T2, T3> >::type&>::type
get_value(ntuple<T0, T1, T2, T3>& tuple)
{
return get_value<n-1>(static_cast<typename ntuple<T0, T1, T2, T3>::tail&>(tuple));
}
template<std::size_t n, typename T0, typename T1, typename T2, typename T3>
typename enable_if<!n, typename ntuple<T0, T1, T2, T3>::head::type&>::type
get_value(ntuple<T0, T1, T2, T3>& tuple)
{
return static_cast<typename ntuple<T0, T1, T2, T3>::head&>(tuple).value;
}
template<std::size_t n, typename T0, typename T1, typename T2, typename T3>
typename enable_if<n, typename get_type<n, ntuple<T0, T1, T2, T3> >::type const&>::type
get_value(ntuple<T0, T1, T2, T3> const& tuple)
{
return get_value<n-1>(static_cast<typename ntuple<T0, T1, T2, T3>::tail const&>(tuple));
}
template<std::size_t n, typename T0, typename T1, typename T2, typename T3>
typename enable_if<!n, typename ntuple<T0, T1, T2, T3>::head::type const&>::type
get_value(ntuple<T0, T1, T2, T3> const& tuple)
{
return static_cast<typename ntuple<T0, T1, T2, T3>::head const&>(tuple).value;
}
Для подсчета количества элементов, хранящихся в кортеже, существует функция под названием get_size
template<std::size_t n, typename T>
struct get_size_impl;
template<std::size_t n, typename T0, typename T1, typename T2, typename T3>
struct get_size_impl<n, ntuple<T0, T1, T2, T3> > :
get_size_impl<n+1, typename ntuple<T0, T1, T2, T3>::tail>
{
};
template<std::size_t n>
struct get_size_impl<n, ntuple<_, _, _, _> >
{
static std::size_t const value = n;
};
template<std::size_t n>
std::size_t const get_size_impl<n, ntuple<_, _, _, _> >::value;
template<typename T0, typename T1, typename T2, typename T3>
std::size_t get_size(ntuple<T0, T1, T2, T3> const&)
{
return get_size_impl<0, ntuple<T0, T1, T2, T3> >::value;
}
Наконец перегрузка operator <<
для печати
template<typename Char, typename CharTraits, typename T0, typename T1, typename T2, typename T3>
void print_element(std::basic_ostream<Char, CharTraits>& ostream, ntuple<T0, T1, T2, T3> const& tuple)
{
ostream << static_cast<typename ntuple<T0, T1, T2, T3>::head const&>(tuple).value;
if(get_size(tuple) > 1)
ostream << std::basic_string<Char, CharTraits>(", ");
print_element(ostream, static_cast<typename ntuple<T0, T1, T2, T3>::tail const&>(tuple));
}
template<typename Char, typename CharTraits>
void print_element(std::basic_ostream<Char, CharTraits>& ostream, ntuple<_, _, _, _> const&)
{
}
template<typename Char, typename CharTraits, typename T0, typename T1, typename T2, typename T3>
std::basic_ostream<Char, CharTraits>& operator <<(std::basic_ostream<Char, CharTraits>& ostream, ntuple<T0, T1, T2, T3> const& tuple)
{
ostream << Char('<');
print_element(ostream, tuple);
ostream << Char('>');
}
Следующий пример использования иллюстрирует использование ntuples
и дает понять, что целевая оптимизация достигнута.
int main()
{
ntuple<char, int, long> a;
ntuple<constant<char, '8'>, int, constant<long, 888> > b;
ntuple<constant<char, '9'>, constant<int, 99>, constant<long, 999> > c;
assert(sizeof(a) > sizeof(b));
assert(sizeof(b) > sizeof(c));
get_value<0>(a) = '1';
get_value<1>(a) = 10;
get_value<2>(a) = 100;
// get_value<0>(b) = '2'; //assignment of read-only location
get_value<1>(b) = 20;
// get_value<2>(b) = 200; //assignment of read-only location
// get_value<0>(c) = '3'; //assignment of read-only location
// get_value<1>(c) = 30; //assignment of read-only location
// get_value<2>(c) = 300; //assignment of read-only location
std::cout << std::endl;
std::cout << "a = " << a << ", length: " << get_size(a) << ", size in bytes: " << sizeof(a) << std::endl;
std::cout << "b = " << b << ", length: " << get_size(b) << ", size in bytes: " << sizeof(b) << std::endl;
std::cout << "c = " << c << ", length: " << get_size(c) << ", size in bytes: " << sizeof(c) << std::endl;
}
Этот простой случай охватывает ntuples, которые имеют максимум 4элементы.Должно быть просто расширить его на столько элементов, сколько нужно (и поддерживается используемым компилятором).Фактически, для генерации кода с использованием этого подхода не требуется никакого сценария.