Я изучаю программирование сокетов и мне нужно преобразовать данные из байтового порядка хоста (в моем случае LITTLE ENDIAN) в сетевой порядок байтов. Поэтому мне нужно поменять местами порядок байтов данных, которые я буду отправлять. Итак, я использовал std::memcpy, чтобы передать целое число в массив временных символов, а затем поменять местами байты этого массива временных символов. Но теперь я пытаюсь добиться того же, используя std::reverse_copy, но не смог достичь (не компилируя сам). Это мой пример кода:

#include <iostream>
#include <cstring>
#include <algorithm>

int main()
{
    const int a = 0x89ABCDEF;
    char arr[sizeof(decltype(a))] {};
    char r_arr[sizeof(decltype(a))] {};
    
    std::memcpy(arr, reinterpret_cast<const char*>(&a), sizeof(decltype(a)));
    char *start = arr;
    char *end = arr + sizeof(decltype(a)) - 1;
    while (start < end) {
        char temp = *start;
        *start = *end;
        *end = temp;
        
        ++start;
        --end;
    }
    for (int32_t i = 0; i < sizeof(decltype(a)); ++i)
        std::cout << std::hex << static_cast<uint16_t>(arr[i]) << std::endl;
    
    // error in below line
    // std::reverse_copy(std::begin(reinterpret_cast<const char*>(&a)), std::begin(reinterpret_cast<const char*>(&a)) + sizeof(decltype(a)), std::begin(r_arr));
    for (int32_t i = 0; i < sizeof(decltype(a)); ++i)
        std::cout << std::hex << static_cast<uint16_t>(r_arr[i]) << std::endl;
        
    return 0;
}
c++
2
Harry 18 Янв 2021 в 18:50

2 ответа

Лучший ответ

У вас слишком много звонков на std::begin. Вы хотите просто:

std::reverse_copy(reinterpret_cast<const char*>(&a), reinterpret_cast<const char*>(&a) + sizeof(decltype(a)), r_arr);
4
Paul Sanders 18 Янв 2021 в 16:15

Это требует представления с приведением типов, которое можно использовать как в коде на основе диапазонов C ++ 20, так и в коде до C ++ 20 на основе итераторов. Таким образом, вам не нужно возиться с временными буферами символов и т. Д.

#include <algorithm>

template <typename Src>
class const_char_view_t {
    const Src &src;
public:
    explicit const_char_view_t(const Src &src) : src(src) {}
    const char * begin() const noexcept {
        return reinterpret_cast<const char*>(&src);
    }
    const char * end() const noexcept { 
        return reinterpret_cast<const char*>((&src)+1);
    }
};

template <typename Src>
auto const_char_view(const Src &val) {
    return const_char_view_t<Src>(val);
}

template <typename Out>
class tie_out_adapter_t {
    Out &out;
public:
    explicit tie_out_adapter_t(Out &out) : out(out) {}
    template <typename Src>
    Out &operator=(Src &&src) {
        return (out = std::move(src.out));
    }
};
template <typename Out>
auto tie_out(Out && out) { return tie_out_adapter_t(out); }

Пример использования ниже демонстрирует использование вышеупомянутого, а также то, как использовать итераторы, возвращаемые std::[ranges::]reverse_copy, для объединения последовательных элементов в выходной вектор.

Чтобы использовать диапазоны, необходимо указать параметр компилятора -std=c++20 (как минимум) для gcc, clang и MSVC.

#include <cassert>
#include <iterator>
#include <vector>
#ifdef __has_include
#   if __has_include(<version>)
#   include <version>
#       if __cpp_lib_ranges >= 201911L
#       define HAS_RANGES 1
#       endif
#   endif
#endif

int main() {
    int32_t val_1 = 0x01234567;
    int16_t val_2 = 0x89AB;
    const char copy_expected[4+2] = {'\x01','\x23','\x45','\x67','\x89','\xAB'};

    #if HAS_RANGES
    {
        std::vector<char> copy;
        auto out = std::back_inserter(copy);
        tie_out(out) = std::ranges::reverse_copy(const_char_view(val_1), out);
        tie_out(out) = std::ranges::reverse_copy(const_char_view(val_2), out);
        assert(std::ranges::equal(copy, copy_expected));
        copy.clear();
    }
    #endif

    std::vector<char> copy;
    auto out = std::back_inserter(copy);
    out = std::reverse_copy(cchar_begin(val_1), cchar_end(val_1), out);
    out = std::reverse_copy(cchar_begin(val_2), cchar_end(val_2), out);
    assert(std::equal(std::begin(copy), std::end(copy), std::begin(copy_expected)));
}
1
Kuba hasn't forgotten Monica 18 Янв 2021 в 18:04