Electively serialize to/from XML or binary in C++

The lexical_cast template

We’ll do a little excursion away from serialization to another part of my core library. Basically, lexical_cast is meant as a template to solve the ever recurring problem of typecasting from numeric types to strings and vice versa.

template< typename O, typename I >
inline const O lexical_cast( const I& source )
{
	std::stringstream s;
	s << source;
	O destination;
	s >> destination;
	return destination;
}

This is the basic implementation. As you see, it makes use of the stringstream‘s wide range of streaming operands for conversion.
I also added some specializations which string streams could not handle, e.g. converting a bool to a string should result in “true” or “false”.

template<>
inline const std::string lexical_cast( const bool& source )
{
	return source ? "true" : "false";
}
template<>
inline const bool lexical_cast( const std::string& source )
{
	return source == "true" ? true : false;
}
// rest omitted for simplicity

This gets more interesting when we look at the enumerations I mentioned before. For example, I have an enum which defines the nature of an Entity.

enum EEntityType
{
	ET_ROOT,		// identifier for the artificial root entity
	ET_SCENE,		// scenes
	ET_STATIC,		// static entities, e.g. maps, with physics-interaction
	ET_DYNAMIC,		// dynamic entities, e.g. characters, with physics-interaction
	ET_CAMERA,		// cameras
	ET_LIGHT,		// lights
	ET_SKYBOX		// skybox
};

Have a look back at the serialization code I presented to you. In line 13, the type of the Entity is written using writeParameter and the enum (which is casted to s32 by the compiler implicitly) as the second parameter (value) and the lexical_cast of the enum to string as the third parameter (readable). Since there is no specialization of the template available for EEntityType yet, this would lead to a conversion from the numeric value to its string representation, e.g. ET_ROOT becomes “0”.
I would prefer to have a “root” then, and other nice descriptive strings for the other possible values, and that’s what the following lexical_cast specialization does:

namespace core
{
template<>
inline const std::string lexical_cast( const vte::world::EEntityType& type )
{
	std::stringstream s;
	switch( type )
	{
	case vte::world::ET_ROOT:
		s << "root";
		break;
	case vte::world::ET_SCENE:
		s << "scene";
		break;
	case vte::world::ET_STATIC:
		s << "static";
		break;
	case vte::world::ET_DYNAMIC:
		s << "dynamic";
		break;
	case vte::world::ET_CAMERA:
		s << "camera";
		break;
	case vte::world::ET_LIGHT:
		s << "light";
		break;
	case vte::world::ET_SKYBOX:
		s << "skybox";
		break;
	}
	return s.str();
}
} // namespace core

That way, a nice speaking value is available for the enum and the XML serializer will gratefully prefer this one over the numeric value. The binary serializer instead doesn’t care and writes the s32 as binary.
The other way round is a bit more complicated. Since s32 could match for any enum we have, the deserializer will either fill the s32 or the string parameter which are passed by reference to the readParameter method. It’s then upon you to deal with the result.

std::string typeString;
s32 typeS32;
source->readParameter( "type", typeS32, typeString );
vte::world::EEntityType actualType = ET_ROOT;
// Did the deserializer return a descriptive type string?
if( typeString.length() > 0 )
{
	// Use the lexical_cast template to convert from string to EEntityType
	entityType = vte::core::lexical_cast< EEntityType >( typeString );
}
else
{
	// Simply cast the returned s32
	entityType = (EEntityType) typeS32;
}

I’ll leave it to you implement the required template<> inline const vte::world::EEntityType lexical_cast( const std::string& type ) method.

4 Comments

Leave a Reply

Your email address will not be published.