Introduction
I recently needed to be able to convert any instance of an object in C++ to a file so it could be serialised, and then restored later; I did most of this by writing out each member variable of the object individually at the most basic level. This works fine and is easy enough to implement; but then came the time to do the “generic” version, the version that could write out any object (such as a struct) as a whole.
The I/O classes can write out/read in using either binary or text, the binary version of the generic writer is simply a case of writing out the length of the data, and then each byte of the data. The text version however required me to convert the bytes of data making up the object into something that was sensible as text, I decided to do this by converting each byte into a hex value and adding it to a string. As I was working on this I found that the information about how to do this without using some of the old C-style string manipulation functions is quite sparse on the net, so after much searching, trying, and failing, I now have a solution that works both ways using good old C++ string streams.
I should probably point out that I don’t think this solution is endian neutral, so you would likely want to add a method of endian detection and switching to ensure consistency across different architectures.
I should also point out that using a string stream isn’t the most optimal solution to this, so I would advise using C-style code if performance is critical.
The Code
When a byte is encoded to hex string, each byte becomes two characters in the range 0 to F. For example, the value “10” would be written out as “0A”.
The code works by using a std::stringstream with hex mode set (std::hex), along with using zero’s for padding (std::setfill(‘0’)) and a fixed width of two for each byte added as hex (std::setw(2)).
The toHex function works by adding each byte of the passed data into the string stream as an integer with a width of two; because zero’s have been set as padding, any numbers which are added to the stream that are only one character long will be automatically prefixed with a zero resulting in the correct two character hex value being added to the string stream.
The fromHex function works by reading out two characters from the hex string into a string stream, these characters are then converted back into the byte value when read out from the string stream into an integer. Finally, the value of the integer is stored as the value of the byte, and the string is moved on to the next set of two characters.
// ------------------------------------------------------------------
/*!
Required headers
*/
#include <string>
#include <sstream>
#include <iomanip>
// ------------------------------------------------------------------
/*!
Convert a block of data to a hex string
*/
void toHex(
void *const data, //!< Data to convert
const size_t dataLength, //!< Length of the data to convert
std::string &dest //!< Destination string
)
{
unsigned char *byteData = reinterpret_cast<unsigned char*>(data);
std::stringstream hexStringStream;
hexStringStream << std::hex << std::setfill('0');
for(size_t index = 0; index < dataLength; ++index)
hexStringStream << std::setw(2) << static_cast<int>(byteData[index]);
dest = hexStringStream.str();
}
// ------------------------------------------------------------------
/*!
Convert a hex string to a block of data
*/
void fromHex(
const std::string &in, //!< Input hex string
void *const data //!< Data store
)
{
size_t length = in.length();
unsigned char *byteData = reinterpret_cast<unsigned char*>(data);
std::stringstream hexStringStream; hexStringStream >> std::hex;
for(size_t strIndex = 0, dataIndex = 0; strIndex < length; ++dataIndex)
{
// Read out and convert the string two characters at a time
const char tmpStr[3] = { in[strIndex++], in[strIndex++], 0 };
// Reset and fill the string stream
hexStringStream.clear();
hexStringStream.str(tmpStr);
// Do the conversion
int tmpValue = 0;
hexStringStream >> tmpValue;
byteData[dataIndex] = static_cast<unsigned char>(tmpValue);
}
}
comments powered by Disqus