简体   繁体   中英

The easiest way to convert an arithmetic type to a std::array<uint8_t> in C++

I have a function that calculates CRC64 and returns it as uint64_t :

static inline uint64_t crc64(uint64_t crc, const uint8_t *s, size_t l)
{
    const uint8_t * end = s + l;

    for (const uint8_t * p = s; p != end; ++p)
    {
        const uint8_t byte = *p;
        crc = crc64_tab[(uint8_t)crc ^ byte] ^ (crc >> 8);
    }

    return crc;
}

and I'd like to convert the result to std::array<sizeof(uint8_t)> and make it independent of the endianness (little/big endian). Is the there a simple and elegant way to do this in C++?

Is it possible to have a simple template function working for all the arithmetic types like uintN_t ?

Is it possible to have a simple template function working for all the arithmetic types like uintN_t?

#include <cstddef>
#include <cstdint>
#include <array>

template< typename T >
std::array<std::uint8_t, sizeof(T)> to_array(T value)
{
    std::array<std::uint8_t, sizeof(T)> result;

    for (std::size_t i{ sizeof(T) }; i; --i)
        result[i - 1] = value >> ((sizeof(T) - i) * 8);

    return result;
}

I think I'd be inclined to invert the dependencies and create the concept of "bytewise_accumulation" of a type.

It seems to generate good code on gcc8:

#include <cstddef>
#include <cstdint>
#include <type_traits>

// general case
template<class T>
struct bytewise_accumulator
{
    template<class Arg, class F, class Acc>
    constexpr decltype(auto) operator()(Arg value, F&& f, Acc acc) const
    {
        for(std::size_t i = 0 ; i < sizeof(T) ; ++i)
        {
            acc = f(acc, std::uint8_t(value));
            value >>= 8;
        }
        return acc;
    }    
};

template<class T>
auto make_bytewise_accumulator(T&&)
{
    return bytewise_accumulator<std::remove_reference_t<std::remove_const_t<T>>>();
}

// overload for c-arrays but others could be provided for std::array, vector, etc
template<class T, std::size_t N>
struct bytewise_accumulator<T[N]>
{
    template<class F, class Acc>
    constexpr decltype(auto) operator()(T(&value)[N], F&& f, Acc acc) const
    {
        for(auto&& x : value)
        {
            auto op = make_bytewise_accumulator(x);
            acc = op(x, f, acc);
        }
        return acc;
    }    
};

template< 
    typename Acc,
    typename T, 
    class F
>
constexpr decltype(auto) bytewise_accumulate(T&& value, F&& f, Acc acc)
{
    auto op = make_bytewise_accumulator(value);
    return op(value, f, acc);
}


// the actual crc function
auto crc64(std::uint64_t crc, std::uint8_t byte) -> std::uint64_t
{
    extern std::uint64_t crc64_tab[256];
    return crc64_tab[(uint8_t)crc ^ byte] ^ (crc >> 8);
}

// compute a crc of a series of objects    
template<class...Ts> 
auto compute_crc64(Ts&&...ts) -> std::uint64_t
{
    auto crc = std::uint64_t(0);
    ((crc = bytewise_accumulate(ts, crc64, crc)), ...);
    return crc;
}

// some tests... 

extern int get_int();
extern void emit(std::uint64_t);

struct Data
{
    int x;
    int y;
    unsigned long z;
    char data[128];
};

// overload bytewise_accumulate for my custom type    
template< 
    typename Acc,
    class F
>
constexpr decltype(auto) bytewise_accumulate(Data const& d, F&& f, Acc acc)
{
    acc = bytewise_accumulate(d.x, f, acc);
    acc = bytewise_accumulate(d.y, f, acc);
    acc = bytewise_accumulate(d.z, f, acc);
    acc = bytewise_accumulate(d.data, f, acc);
    return acc;
}

extern Data const& get_data();

int main()
{
    auto i = get_int();
    auto crc = compute_crc64(i);
    emit(crc);

    emit(compute_crc64(get_data()));
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM