Контейнер для boost::multi_array того же типа, но разной размерности

Мне нужно создать класс, который может содержать boost::multi_array того же типа, но с разными размерами.

предположим, что есть один или несколько таких массивов Double

boost::multi_array<double, 2> array_2d; //2D array
boost::multi_array<double, 3> array_3d; //4D array
boost::multi_array<double, 4> array_4d; //5D array
etc...

мне нужен класс контейнера, который может содержать все вышеперечисленные типы, способный позже, когда это необходимо, уважать правильный тип

что-то вроде

GenericArray<double> arr;
arr.IsEmpty() // check if it has valid ref
arr.assign(array_2d); //set reference to

затем верните ссылку на массив, используя какую-то логику шаблона

try{
    boost::multi_array<double, 2>& array_2d_ref = arr.get<2>
    //OR
    boost::multi_array<double, 2>& array_2d_ref = arr.get<multi_array<double, 2>>
    // First one look cleaner tho
}catch (ArrayDimentinalityMismatch e){

}

В том же блоке кода

arr.assign(array_3d);

try{
    boost::multi_array<double, 2>& array_3d_ref=arr.get<multi_array<double, 3>>
}catch (ArrayDimentinalityMismatch e){

}

Возможно ли это с помощью шаблонов?


person Indika Herath    schedule 31.10.2014    source источник


Ответы (1)


Во-первых, я думаю, вы можете оценить свой дизайн. Как и в случае с функторами, редко требуется кодировать полужесткие оболочки типов вокруг ваших аргументов универсального типа.

Однако, если вы обнаружите, что вам это нужно, вот решение, использующее boost::variant:

template <typename T>
struct GenericArray
{
    template <size_t N> using array_t = boost::multi_array<T, N>;

    template <typename Rhs> GenericArray& operator=(Rhs&& rhs) {
        _storage = std::forward<Rhs>(rhs);
        return *this;
    }

    template <size_t N> array_t<N>      & get()       { return boost::get<array_t<N> >(_storage); }
    template <size_t N> array_t<N> const& get() const { return boost::get<array_t<N> >(_storage); }

  private:
    typename detail::make_generic_array_storage<T>::type _storage;
};

Функция-член get<> выдает исключение boost::bad_get, если вы ошиблись с измерением во время выполнения.

Теперь хитрость, конечно же, в том, как реализован _storage. Я генерирую вариант по списку измерений массива, используя немного магии Boost MPL:

namespace detail {
    namespace mpl = boost::mpl;

    template <typename T, size_t Mindim = 1, size_t Maxdim = 5>
    struct make_generic_array_storage
    {
        template <size_t N> using array_t = boost::multi_array<T, N>;
        template<typename N> struct to_array_f { typedef array_t<N::value> type; };

        using list = typename mpl::transform<
            mpl::range_c<size_t, Mindim, Maxdim>, 
            to_array_f<mpl::_1>,
            mpl::back_inserter<mpl::vector<> > 
        >::type;

        using type = typename boost::make_variant_over<list>::type;
    };
}

Ничего сверхсложного, если посмотреть на это с высокого уровня :)

Далее: демо! Смотрите Прямой эфир на Coliru

GenericArray<double> arr;
arr = array_3d;

try { auto& ref3 = arr.get<3>(); }
catch (boost::bad_get const& e) { std::cout << "L:" << __LINE__ << " " << e.what() << "\n"; }

try { auto& ref2 = arr.get<2>(); } // this fails
catch (boost::bad_get const& e) { std::cout << "L:" << __LINE__ << " " << e.what() << "\n"; }

arr = array_2d;

try { auto& ref2 = arr.get<2>(); } // this succeeds
catch (boost::bad_get const& e) { std::cout << "L:" << __LINE__ << " " << e.what() << "\n"; }

std::cout << "Done";

Отпечатки:

L:58 boost::bad_get: failed value get using boost::get
Done

как и ожидалось.


Дополнительно. Чтобы реализовать больше операций, подобных массивам, над хранилищем вариантов, посмотрите здесь:

которые затрагивают эту тему

person sehe    schedule 31.10.2014
comment
Привет, возможно ли преобразовать это в версию, которая не использует c++11 . я использую вс2010 - person Indika Herath; 31.10.2014
comment
Конечно: coliru.stacked-crooked.com/a/ee16d93b1cb5764d (только что заменил using с эквивалентными typedefs и удаленной семантикой перемещения) - person sehe; 31.10.2014