Альтернативы std :: array для объектов разных типов в C ++ 11 - PullRequest
1 голос
/ 17 февраля 2020

Я ищу лучшие решения о том, как организовать и получить доступ к моим данным. Мои данные - это набор структур (_array_10 и _array_20 в приведенном ниже примере), которые содержат std::array разных размеров (см. my_data ниже). В идеале я хотел бы получить к нему доступ, поскольку это был массив структур разной длины, но это недопустимо, поскольку разные длины имеют разные типы. Решение, которое я имею ниже, работает, но я нахожу его крайне уродливым (особенно массив void *).

Q1. Любые идеи о том, как получить более безопасное, более эффективное / портативное или хотя бы менее уродливое решение?
Q2. Является ли предлагаемое решение без шаблонов переносимым? Он основывается на том факте, что длина хранится до остальной части данных, поскольку приведение указателя на объект с неправильной длиной приведет к нарушению доступа ко всем полям, которые идут после первого поля переменной длины.

Мои ограничения включают в себя:

  • C ++ 11
  • стандартные библиотеки
  • no std::vector
  • использование памяти предотвращает я не могу просто выделить массив my_data с максимально возможной длиной
  • , большая часть данных (_array_10, _array_20 и т. д. c) будет помещена в область памяти зарезервировано специально для него

Использование data_view и шаблона требует знания длины массивов во время сборки. Было бы здорово, если бы мы могли избежать этого.

  • Вопрос отредактирован, чтобы включить решение, предложенное Гийомом Рашико
#include <iostream>
#include <array>

std::array<void *, 2> _ptrs;

template <int length>
struct my_data
{
    int                     array_length;
    std::array<int, length> something;
    std::array<int, length> data;
    my_data()
    {
        array_length = length;
    }
};


struct my_data_view
{
    int         array_length;
    const int * something;
    const int * data;

    template <int length>
    my_data_view(my_data<length> const & data_in) :
        array_length(length),
        something(data_in.something.data()),
        data(data_in.data.data())
    {}
};

template <int length>
void
print_element(int array_idx, int element)
{
    my_data<length> * ptr = reinterpret_cast<my_data<length> *>(_ptrs[array_idx]);

    std::cout << "array " << length << ", data[" << element << "] = " << ptr->data[element] << ".\n";
}

void
print_element(int array_idx, int element)
{
    my_data<1> * ptr    = reinterpret_cast<my_data<1> *>(_ptrs[array_idx]);
    int          length = ptr->array_length;

    int data_to_print = 0;
    switch (length)
    {
        case 10:
        {
            data_to_print = reinterpret_cast<my_data<10> *>(_ptrs[array_idx])->data[element];
            break;
        }
        case 20:
        {
            data_to_print = reinterpret_cast<my_data<20> *>(_ptrs[array_idx])->data[element];
            break;
        }
    }
    std::cout << "array " << length << ", data[" << element << "] = " << data_to_print << ".\n";
}

void
print_element(my_data_view view, int element)
{
    int length        = view.array_length;
    int data_to_print = view.data[element];

    std::cout << "array " << length << ", data[" << element << "] = " << data_to_print << ".\n";
}

int
main()
{
    my_data<10> _array_10;
    my_data<20> _array_20;

    _ptrs[0] = static_cast<void *>(&_array_10);
    _ptrs[1] = static_cast<void *>(&_array_20);

    _array_10.data[5] = 11;
    _array_20.data[5] = 22;

    std::cout << "using template\n";
    print_element<10>(0, 5);
    print_element<20>(1, 5);

    std::cout << "\nwithout template\n";
    print_element(0, 5);
    print_element(1, 5);

    std::cout << "\nusing data_view\n";
    print_element(my_data_view(_array_10), 5);
    print_element(my_data_view(_array_20), 5);
}

1 Ответ

2 голосов
/ 17 февраля 2020

Вы можете создать динамический c класс представления, который не выделяет:

struct my_data_view
{
    int array_length;
    std::span<int> something;
    std::span<int> data;

    template<int length>
    my_data_view(my_data<length> const& data) : 
        array_length{length}, something{data.something}, data{data.data}
    {}
};

Пролеты - это просто указатель и размер. Если у вас нет доступа к std::span (из C ++ 20), вы можете просто заменить этот элемент на int* и использовать array_length для размера.

Этот тип my_data_view используется следующим образом:

void
print_element(my_data_view view, int element)
{
    int length = view.array_length;
    int data_to_print = view.data[element];

    std::cout << "array " << length << ", data[" << element << "] = " << data_to_print << ".\n";
}

Этот код будет работать как с std::span, так и с простым int*.

...