Транспонировать контейнер структур - PullRequest
2 голосов
/ 19 сентября 2019

Пусть у нас есть struct Record{uint8_t x, y;};, контейнер Container<Record> структур и struct Transposed{Container<uint8_t> x,y};.Контейнер c - это шаблон, первый аргумент которого является типом значения, все остальные аргументы имеют значения по умолчанию.Например, это может быть std::vector (остальные аргументы являются типами) или std::span (остальные аргументы являются значениями).Шаблон должен работать со всеми из них.Также нам может потребоваться передать остальные аргументы шаблона в базовый шаблон.

Как мы можем получить Transposed из контейнера, используя шаблоны?

Я пробовал шаблоны с переменным числом аргументов,

#include <iostream>
#include <vector>

template <typename value_type=uint8_t> struct Record{
    value_type x, y;
};

template<typename value_type, typename value_type2=uint8_t> class ContainerA: public std::vector<value_type>{
    value_type2 b=1u;
};

template<typename value_type, uint8_t int_value=1u> class ContainerB: public std::vector<value_type>{};

template<typename value_type, template <typename ...> typename container_type> class Transposed{
    container_type<value_type> x, y;

    public:
        Transposed(container_type<Record<value_type>> & recs){
            x.reserve(recs.size());
            y.reserve(recs.size());
            x.resize(recs.size());
            y.resize(recs.size());
            size_t i=0;
            for(auto &rec :recs){
                x[i] = rec.x;
                y[i] = rec.y;
                ++i;
            }
        }
};

int main(){
    std::vector<Record<uint8_t>> recsV{
        {1, 2},
        {3, 4}
    };
    Transposed trV{recsV};
    std::cout<<"vec"<<std::endl;

    ContainerA<Record<uint8_t>> recsA{
        {1, 2},
        {3, 4}
    };
    Transposed trA{recsA};
    std::cout<<"A"<<std::endl;

    /*ContainerB<Record<uint8_t>> recsB{
        {1, 2},
        {3, 4}
    };
    Transposed trB{recsB};
    std::cout<<"B"<<std::endl;*/
    return 0;
}

но, похоже, они не могут совпадать как по типам, так и по значениям.Использование более 1 аргумента шаблона переменной не допускается.Является ли это недостатком языка C ++ или преднамеренным выбором дизайна, и нам нужно что-то вроде any_template_arg ключевого слова или просто разрешить указание 2 переменных аргументов различных типов, чтобы разрешить этот вариант использования?

Ответы [ 2 ]

1 голос
/ 19 сентября 2019

Насколько я знаю, не существует способа сопоставления типов и значений (и шаблон-шаблон) аргументов шаблона вместе.И я долго искал его.

Поэтому я не вижу способа сделать то, что вы хотите, простым и элегантным способом.

Пытаясь ответить на ваш вопрос

Как мы можем получить Transposed из контейнера с использованием шаблонов?

лучшее, что я могу себе представить, это объявить (не обязательно определять их, принимая во вниманиеиспользуется только внутри decltype()) пары тривиальных функций следующим образом

template <typename VT,
          template <typename...> typename CT>
CT<VT> extract_func (CT<Record<VT>>);

template <typename VT,
          template <typename, auto...> typename CT>
CT<VT> extract_func (CT<Record<VT>>);

Первая - удалить часть Record, когда контейнер CT принимает список типов с переменным числом (std::vector и ContainerA дел);второй предназначен для CT контейнеров, принимающих (после параметра типа) одно или несколько значений (ContainerB case).

Очевидно, что это не охватывает все возможные случаи, но тривиально объявить другие extract_func() функции для покрытия других случаев.

Теперь вы можете объявить Tranposed следующим образом

template <typename T,
          typename CT = decltype(extract_func(std::declval<T>()))>
class Transposed

Обратите внимание, что теперь Transposed принимает универсальный тип T, но SFINAE активируется только тогда, когда тип Tсоответствует (в качестве аргумента) объявлению extract_func().

В теле Transposed вы можете использовать CT для объявления x и y и T для аргумента конструктора.

Ниже приведен полный пример компиляции

#include <iostream>
#include <vector>

template <typename value_type=std::uint8_t>
struct Record
 { value_type x, y; };

template <typename value_type, typename value_type2=std::uint8_t>
class ContainerA : public std::vector<value_type>
 { value_type2 b=1u; };

template <typename value_type, std::uint8_t int_value=1u>
class ContainerB : public std::vector<value_type>
 { };

template <typename VT,
          template <typename...> typename CT>
CT<VT> extract_func (CT<Record<VT>>);

template <typename VT,
          template <typename, auto...> typename CT>
CT<VT> extract_func (CT<Record<VT>>);

template <typename T,
          typename CT = decltype(extract_func(std::declval<T>()))>
class Transposed
 {
   private:
      CT x, y;

   public:
      Transposed (T & recs)
       {
         x.reserve(recs.size());
         y.reserve(recs.size());
         x.resize(recs.size());
         y.resize(recs.size());
         std::size_t i=0u;
         for(auto &rec :recs){
            x[i] = rec.x;
            y[i] = rec.y;
            ++i;
         }
       }
 };

int main ()
 {
   std::vector<Record<std::uint8_t>> recsV { {1, 2}, {3, 4} };

   Transposed trV{recsV};

   std::cout<<"vec"<<std::endl;

   ContainerA<Record<std::uint8_t>> recsA { };

   Transposed trA{recsA};

   std::cout<<"A"<<std::endl;

   ContainerB<Record<std::uint8_t>> recsB { };

   Transposed trB{recsB};

   std::cout<<"B"<<std::endl;
 }
0 голосов
/ 19 сентября 2019

Один рабочий пример для вас:

template<typename value_type=uint8_t>
struct Record{
    value_type x, y;
};

template<class T>
std::vector<T> rebind_container(std::vector<Record<T>> const&);

// Span transposes into vector.
template<class T>
std::vector<T> rebind_container(std::span<Record<T>> const&); 

template<class T>
std::list<T> rebind_container(std::list<Record<T>> const&);

inline void reserve(...) {}

template<class... Args>
inline void reserve(std::vector<Args...>* v, size_t n) {
    v->reserve(n);
}

template<class container_type>
struct Transposed {
    using container_type2 = decltype(rebind_container(std::declval<container_type>()));

    container_type2 x, y;

    Transposed(container_type const& recs) {
        auto const n = recs.size();
        reserve(&x, n);
        reserve(&y, n);
        for(auto& rec : recs) {
            x.push_back(rec.x);
            y.push_back(rec.y);
        }
    }
};

int main(){
    std::vector<Record<uint8_t>> recsV{
        {1, 2},
        {3, 4}
    };
    Transposed<decltype(recsV)> trV{recsV};
    std::cout << trV.x.size() << std::endl;

    std::list<Record<uint8_t>> recsV2{
        {1, 2},
        {3, 4}
    };
    Transposed<decltype(recsV2)> trV2{recsV2};
    std::cout << trV2.x.size() << std::endl;
}
...