использование std :: transform для 2d C-массива в 1d C-массив - PullRequest
0 голосов
/ 25 июня 2018

Я пытаюсь использовать std::transform для преобразования простого 2d массива (из Foo структур) в 1d (преобразованный) массив из Bar структур.

// convert 2d array of Foo structs to 1d array of Bar structs
// suitable for constructing a Baz struct.
Foo array2d[3][2] = 
{ 
    { 
        {1,2}, {3,4} 
    }, 
    { 
        {5,6}, {7,8} 
    }, 
    { 
        {9,10}, {11,12} 
    } 
};

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

using Foo = struct {
    uint32_t a;
    uint32_t b;
};

using Bar = struct {
    uint32_t c;
    uint32_t d;
};

Идея состоит в том, что этот 1d массив структур Bar можно использовать для построения Baz структуры.

У меня проблемы с лямбда-преобразователем (ями). Я полагаю, что внешний занимает строку в то время как внутренний занимает столбец в то время, когда происходит фактическое преобразование Foo-> Bar. В живом демо мне пришлось закомментировать std :: transform, взяв массив 2d, и заменить его плоской версией, где я преобразовал массив 2d в массив 1d (размер строки умножается на col). Это работало безупречно - но я пытался придерживаться типов параметров, не прибегая к reinterpret_cast <>.

    std::vector<Bar> array1d;
    array1d.reserve(Baz::gArraySize);
#if 0
    // I don't know how to use the transform on the 2d array
    std::transform(std::cbegin(array2d), std::cend(array2d),
        std::back_inserter(array1d),
        [](const Foo(&rRow)[Baz::gNumCols]) {
        std::transform(std::cbegin(rRow), std::cend(rRow),
            [](const Foo& rNext) -> Bar {
                // reverse the order of the fields
                return Bar{ rNext.b, rNext.a };
            });
        });
#else
    // Only workaround is to cast the 2d array to a 1d array using reinterpret cast<>
    const auto& special = reinterpret_cast<const Foo(&)[Baz::gArraySize]>(array2d);
    // I don't know how to use the transform on the 2d array
    std::transform(std::cbegin(special), std::cend(special),
        std::back_inserter(array1d),
        [](const Foo& rNext) -> Bar {
            // reverse the order of the fields
            return Bar{ rNext.b, rNext.a };
        });
#endif
    // construct from transformed 2d array
    Baz myBaz(reinterpret_cast<const Bar(&)[Baz::gArraySize]>(array1d[0]));
    std::cout << myBaz;

производит ожидаемый результат следующим образом:

g++ -std=c++17 -O2 -Wall -pedantic -pthread main.cpp && ./a.out
Bar{c=2, d=1},
Bar{c=4, d=3},
Bar{c=6, d=5},
Bar{c=8, d=7},
Bar{c=10, d=9},
Bar{c=12, d=11},

Структуры имеют форму массива, подобную C, поскольку они поступают из внешнего источника. Я не уверен, что то, что я пытаюсь сделать, возможно с помощью std :: transform, но я бы хотел использовать алгоритмы STL, а не разгадывать циклы вручную.

Я создал следующее live coliru demo , чтобы показать, чего я пытался достичь - но в нем было много ошибок с преобразованием на месте. Обратите внимание, что массив, передаваемый в Baz, зависит от того факта, что std :: vector распределяет структуры данных непрерывно в памяти (это гарантируется STL).

struct Baz {
    constexpr static int gNumRows = 3;
    constexpr static int gNumCols = 2;
    constexpr static int gArraySize = gNumRows * gNumCols;
    Bar arrayField[gArraySize];
    // explicit constructor from C style fixed size array.
    explicit Baz(const Bar(&rParam)[gArraySize])
        : arrayField{}
    {
        std::memcpy(arrayField, rParam, gArraySize * sizeof(Bar));
    }

    friend std::ostream& operator<<(
        std::ostream& os, const Baz& rhs) {
        for (auto next : rhs.arrayField) {
            os << "Bar{c=" << next.c << ", d=" << next.d << "},\n";
        }
        return os;
    }

};

1 Ответ

0 голосов
/ 25 июня 2018

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

Учитывая это, было бы намного проще и намного более читабельно использовать здесь простые циклы:

for (auto &&row : array2d)
    for (auto &&foo : row)
        oneDimArray.push_back(Bar{ foo.b, foo.a });

и оставить алгоритмы STL для случаев, когда они действительно облегчают вашу жизнь:).

...