распаковка экстентов массива во время компиляции (C ++ 11/14) - PullRequest
0 голосов
/ 04 сентября 2018

Мне нужно "упаковать" и "распаковать" размеры многомерного массива в стиле C во время компиляции. Под «паком» я подразумеваю заданный тип, который представляет размеры массива, такие как

template<typename T, size_t N2, size_t N3> struct SomeType<T,0,0,N2,N3> { ... };

, что я могу извлечь тип T**[N2][N3] и выполнить обратную операцию так что, учитывая тип T**[N2][N3], я могу заполнить std::index_sequence параметрами <0,0,N2,N3>

Мне удалось выполнить операцию "pack" с кодом:

/*   pack_array_type<T,N0>     is  T[N0]
 *   pack_array_type<T,0>      is  T*
 *   pack_array_type<T,N0,N1>  is  T[N0][N1]
 *   pack_array_type<T,0,N1>   is  T*[N1]
 *   pack_array_type<T,0,0>    is  T**
 *   etc                                  */

using namespace std;

template<typename,typename> struct pack_array_type_impl;

template<typename T, size_t N0, size_t...Ns>   
struct pack_array_type_impl<T,index_sequence<N0,Ns...>> {
  using type = typename 
  pack_array_type_impl<T[N0],index_sequence<Ns...>>::type;
};

template<typename T, size_t...Ns>
struct pack_array_type_impl<T,index_sequence<0,Ns...>> {
  using type = typename 
  pack_array_type_impl<T*,index_sequence<Ns...>>::type;
};

template<typename T, size_t N>
struct pack_array_type_impl<T,index_sequence<N>> { using type = T[N];};

template<typename T>
struct pack_array_type_impl<T,index_sequence<0>> { using type = T*; };

template<typename T, size_t...N>
using pack_array_type = typename 
pack_array_type_impl<T,index_sequence<N...>>::type;

Моя попытка обратной операции «распаковать» размеры -

template<typename, typename> struct unpacked_array_type_impl;

template<typename T, size_t...I>
struct unpacked_array_type_impl<T,index_sequence<I...>> {
  using index_type = index_sequence<extent<T[I]>::value...>;
};

template<typename T>
struct unpacked_array_type {
  using value_type = decay_t<T>;

  // EDIT: Need condition here to prevent infinite recursion
  using index_type =
  typename unpacked_array_type_impl<T,make_index_sequence<rank<T>::value>>::index_type;
}

// Print indices for testing
template<size_t...N>
void print_sequence( const index_sequence<N...>& seq, ostream& os ) {
  using expand_type = int[];
  os << "[";
  (void) expand_type
  { 0, ((void)(os << integral_constant<size_t,N>() << " ") , 0) ... };
  os << "]\n";
}

int main( int argc, char* argv[] ) {

  typename unpacked_array_type<double**[3][4][5]>::index_type x;
  // print_sequence(x,cout); // Desired output = [0 0 3 4 5 ]

  return 0;
}

` Компиляция с clang 5.0.1, выдает ошибку

main.cpp:54:12: error: implicit instantiation of undefined template
  'unpacked_array_type_impl<double **[3][4][5], details::make_index_sequence<3> >'
  typename unpacked_array_type_impl<T,make_index_sequence<rank<T>::value>>::type;
           ^
main.cpp:68:12: note: in instantiation of template class 'unpacked_array_type<double**[3][4][5]>' requested here
  typename unpacked_array_type<double**[3][4][5]>::type x;
           ^
main.cpp:43:37: note: template is declared here
template<typename, typename> struct unpacked_array_type_impl;
                                    ^`

Можно ли извлечь эти размеры?

Примечание. Я использую собственную реализацию некоторых функций C ++ 14, таких как std::index_sequence в C ++ 11.

РЕДАКТИРОВАТЬ: добавлен код https://coliru.stacked -crooked.com / / 6776152e348c2c57 Это строит, но выводит [3 4 5] вместо [0 0 3 4 5]

1 Ответ

0 голосов
/ 04 сентября 2018

Некоторые ошибки в вашем коде.

(1) использовать постоянно type или index_type, но не запрашивать type

// .......................................................................vvvv
typename unpacked_array_type_impl<T,make_index_sequence<rank<T>::value>>::type;

когда класс определяют index_type

template<typename T, size_t...I>
struct unpacked_array_type_impl<T,index_sequence<I...>> {
  using index_type = index_sequence<extent<T[I]>::value...>;
}; // ..^^^^^^^^^^

Та же проблема для unpacked_array_sequence, которая определяет index_type, когда в main() вы запрашиваете type

(2) используйте std::extent<T, I>::value..., а не extent<T[I]>::value....

Также обратите внимание, что с помощью extent<T[I]>::value ... вы определяете T[0] (когда I равен нулю), что не приемлемо в стандарте C ++.

(3) добавить template <std::size_t ... N> до

 void print_sequence( const index_sequence<N...>& seq, ostream& os ) {

Исправляя эти ошибки, вы должны получить вывод

[3 4 5 ]

это не совсем то, что вы просили, а улучшение.

Чтобы получить

 [0 0 3 4 5 ]

Предлагаю переписать unpacked_array_type_impl следующим образом

template <typename, typename IS = std::index_sequence<>>
struct unpacked_array_type_impl
 { using type = IS; };

template <typename T, std::size_t ... I>
struct unpacked_array_type_impl<T*, std::index_sequence<I...>>
   : public unpacked_array_type_impl<T, std::index_sequence<0u, I...>>
 { };

template <typename T, std::size_t N, std::size_t ... I>
struct unpacked_array_type_impl<T[N], std::index_sequence<I...>>
   : public unpacked_array_type_impl<T, std::index_sequence<I..., N>>
 { };

и используйте его следующим образом

template<typename T>
struct unpacked_array_type
 { using type = typename unpacked_array_type_impl<T>::type; };

Ниже приведен полный рабочий пример

#include <utility>
#include <iostream>
#include <type_traits>

template <typename, typename IS = std::index_sequence<>>
struct unpacked_array_type_impl
 { using type = IS; };

template <typename T, std::size_t ... I>
struct unpacked_array_type_impl<T*, std::index_sequence<I...>>
   : public unpacked_array_type_impl<T, std::index_sequence<0u, I...>>
 { };

template <typename T, std::size_t N, std::size_t ... I>
struct unpacked_array_type_impl<T[N], std::index_sequence<I...>>
   : public unpacked_array_type_impl<T, std::index_sequence<I..., N>>
 { };

template<typename T>
struct unpacked_array_type
 { using type = typename unpacked_array_type_impl<T>::type; };

// Print indices for testing
template <std::size_t ... N>
void print_sequence (std::index_sequence<N...> const & seq,
                     std::ostream & os)
 {
   using expand_type = int[];
   os << "[";
   (void) expand_type { 0, ((void)(os << N << " ") , 0) ... };
   os << "]\n";
 }

int main ()
 {
   typename unpacked_array_type<double**[3][4][5]>::type x;
   print_sequence(x, std::cout); // Desired output = [0 0 3 4 5 ]
 }
...