Конвертировать последние символы из std :: arrayк инт - PullRequest
1 голос
/ 20 апреля 2019

Учитывая следующий массив: std::array<char, 10> stuff Я хотел бы преобразовать последние 4 символа в соответствующее значение int32.

Я пытался связать операции ИЛИ с последними элементами, но, похоже, это неверный путь:

int a = int(stuff[6] | stuff[7] | stuff[8] | stuff[9])

Есть ли элегантный способ решить эту проблему?

Ответы [ 2 ]

5 голосов
/ 20 апреля 2019

То, что вы пытались сделать, обладает элегантностью, которая не требует проверки порядка байтов для правильной работы. То, что вы пропустили, было некоторое смещение, чтобы указать значение в конечном значении:

int a = stuff[6] << 24 | stuff[7] << 16 | stuff[8] << 8 | stuff[9];

Одно это не заботится о порядке байтов, потому что с точки зрения языка оно основано на значениях, а не на байтах. Вы определяете, какие значения являются наиболее значимыми.

Тем не менее, это также предполагает 8-битный байт и как минимум 4-байтовый int. Если вы хотите элегантности использования, вы можете получить его с безопасной и общей абстракцией :

#include <array>
#include <climits>
#include <cstddef>

namespace detail {
    // Could be replaced by an inline lambda-template in C++20.
    template<typename T, std::size_t N, std::size_t... Is>
    constexpr T pack_into_impl(const std::array<std::byte, N>& bytes, std::index_sequence<Is...>) {
        // Build final value from right to left to make the math more clear 
        // and to use the least significant bytes available when N < sizeof(T).
        // e.g., bytes[3] << 0 | bytes[2] << 8 | bytes[1] << 16 | bytes[0] << 24
        return ((static_cast<int>(bytes[N-Is-1]) << (CHAR_BIT * Is)) | ...);
    }
}

// Takes bytes to pack from most significant to least significant.
// N.B. this is not a production-ready doc comment for this function.
template<typename T, std::size_t N>
constexpr T pack_into(std::array<std::byte, N> bytes) {
    static_assert(sizeof(T) >= N, "Destination type is too small for this many bytes");
    return detail::pack_into_impl<T>(bytes, std::make_index_sequence<N>{});
}

// Convenience overload.
template<typename T, typename... Bytes>
constexpr T pack_into(Bytes... bytes) {
    // Check that each Bytes type can be static_cast to std::byte.
    // Maybe check that values fit within a byte.
    return pack_into<T>(std::array{static_cast<std::byte>(bytes)...});
}

int main() {
    static_assert(pack_into<int>(0x12, 0x34, 0x56, 0x78) == 0x12345678);
    static_assert(pack_into<int>(0x01, 0x02) == 0x0102);
    // pack_into<int>(0x01, 0x02, 0x03, 0x04, 0x05); // static_assert
}

Некоторое из этого можно очистить в C ++ 20 с помощью концепций и []<std::size_t... Is> лямбда, но вы поймете эту идею. Естественно, вы также можете свободно трансформировать API, чтобы сделать размер неизвестным во время компиляции для удобства и использовать возможность проверки во время выполнения, когда передается слишком много байтов. Это зависит от вашего варианта использования.

2 голосов
/ 20 апреля 2019

Верьте или нет, даже несмотря на то, что это C ++, memcpy() - рекомендуемый способ сделать такую ​​вещь:

int32_t a;
memcpy(&a, stuff.data() + 6, 4);

Это позволяет избежать строгих нарушений псевдонимов, и компиляторы оптимизируют memcpy перезвоните.

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

...