Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Python's struct.pack/unpack equivalence in C++

I used struct.pack in Python to transform a data into serialized byte stream.

>>> import struct
>>> struct.pack('i', 1234)
'\xd2\x04\x00\x00'

What is the equivalence in C++?

like image 797
prosseek Avatar asked Jun 15 '13 16:06

prosseek


1 Answers

You'll probably be better off in the long run using a third party library (e.g. Google Protocol Buffers), but if you insist on rolling your own, the C++ version of your example might be something like this:

#include <stdint.h>
#include <string.h>

int32_t myValueToPack = 1234;  // or whatever
uint8_t myByteArray[sizeof(myValueToPack)];
int32_t bigEndianValue = htonl(myValueToPack);  // convert the value to big-endian for cross-platform compatibility
memcpy(&myByteArray[0], &bigEndianValue, sizeof(bigEndianValue));
// At this point, myByteArray contains the "packed" data in network-endian (aka big-endian) format

The corresponding 'unpack' code would look like this:

// Assume at this point we have the packed array myByteArray, from before
int32_t bigEndianValue;
memcpy(&bigEndianValue, &myByteArray[0], sizeof(bigEndianValue));
int32_t theUnpackedValue = ntohl(bigEndianValue);

In real life you'd probably be packing more than one value, which is easy enough to do (by making the array size larger and calling htonl() and memcpy() in a loop -- don't forget to increase memcpy()'s first argument as you go, so that your second value doesn't overwrite the first value's location in the array, and so on).

You'd also probably want to pack (aka serialize) different data types as well. uint8_t's (aka chars) and booleans are simple enough as no endian-handling is necesary for them -- you can just copy each of them into the array verbatim as a single byte. uint16_t's you can convert to big-endian via htons(), and convert back to native-endian via ntohs(). Floating point values are a bit tricky, since there is no built-in htonf(), but you can roll your own that will work on IEEE754-compliant machines:

uint32_t htonf(float f)
{
   uint32_t x;
   memcpy(&x, &f, sizeof(float));
   return htonl(x);
}

.... and the corresponding ntohf() to unpack them:

float ntohf(uint32_t nf)
{
   float x;
   nf = ntohl(nf);
   memcpy(&x, &nf, sizeof(float));
   return x;
}

Lastly for strings you can just add the bytes of the string to the buffer (including the NUL terminator) via memcpy:

const char * s = "hello";
int slen = strlen(s);
memcpy(myByteArray, s, slen+1);  // +1 for the NUL byte
like image 178
Jeremy Friesner Avatar answered Sep 23 '22 02:09

Jeremy Friesner