Kilian
Kilian

Reputation: 1939

Swapping endians when using reinterpret cast

When sending data via the network converting byte data can be achieved via several ways:

12345  --> {0 0 48 57}

typedef unsigned char byte;

//1. Bit shift
int32_t shiftedInteger =  (int32_t) (inBytes[0] << 24 | inBytes[1] << 16  | 
inBytes[2] << 8 | inBytes[3]);

//2. Reinterpret cast
int32_t reinterpretedInteger = *reinterpret_cast<int32_t*>(&inBytes);

//3. Using unions
union{
    byte b[4];
    int32_t i;
}unionCast;

memcpy(unionCast.b,inBytes,4);
int_32t unionCasted = unctionCast.i;

Which is the preferable way of converting the data (use on an arduino like microporcessor)?

The union and reinterpretCast method face issues of big vs small endians but will come in handy once working with floating point numbers as simple bitshifts will not be enough to convert the data back. How can I swap endians when using reinterpret_cast?

Upvotes: 2

Views: 622

Answers (2)

Yakk - Adam Nevraumont
Yakk - Adam Nevraumont

Reputation: 275220

Your use of reinterpret and accessing inactive members of union both violate the standard. The rules saying so are known as strict aliasing.

So of your options, shifting is the only standard compliant one.

An additional option -- memcpy directly into the target type -- is also standard compliant.

You can do in place legally by memcpy to a stack array, placement new bew tyoe, then memcpy back. The optimizer will eliminate the memcpys!

You could even do this:

template<class T> 
struct raw_bytes:
  std::array<char, sizeof(T)>
{
  static_assert(std::is_pod<T>::value, "pod only");
  static raw_bytes to_raw( T in ){
    raw_bytes r;
    std::memcpy( r.data(), std::addressof(in), sizeof(T) );
    return r;
  }
  // this object no longer valid after convert, but returned reference is until this object destroyed
  T& convert(){
    char tmp[sizeof(T)];
    memcpy(tmp, data(), sizeof(T));
    T* r= ::new( (void*)data() ) T;
    memcpy(r, tmp, sizeof(T));
    return *r;
  }
};

that may or may not be worth it.

You can stick a raw_bytes in a struct and blit bytes into it. Then you can convert() those bytes in-place to T. The returned reference is the only way to legally access those bytes; methods of raw_bytes are no longer legal under a strict reading of the standard.

Upvotes: 1

Sorin
Sorin

Reputation: 11968

You can't. Reinterprert cast only changes the type the compiler uses, it doesn't touch the data.

As suggested in How do I convert between big-endian and little-endian values in C++? Use

int32_t __builtin_bswap32 (int32_t x)

Upvotes: 0

Related Questions