2
\$\begingroup\$

I'm working on caching library which is intended to work with all types of values.

Values are represented as instances of value_type: value_type holds a std::vector<char> for the raw data, and a uint32_t flags integer value which is used to indicate the format of the stored data.

I would like to make it easy for the users of the library and provided a serialize() method that would convert a C++ type to a value_type so that instead of writing:

// In the line below, serialization::string is an integer constant
value_type value(serialization::string, "My string");

People could just write:

value_type value = serialize("My string");

And somehow the serialize() method would "guess" that the associated integer flag is serialization::string.

I would also make it possible for an user to register his own types and conversions into the serialization mechanism.

I came up with the following code:

/**
 * \brief A default serializer type.
 * \tparam Type The type to serialize.
 */
template <typename Type>
struct serializer {};

/**
 * \brief The base serializer type.
 * \tparam Type The serialized type.
 * \tparam Flags The flags.
 *
 * Derive from this type to enable a new serialization.
 */
template <typename Type, value_type::flags_type Flags>
struct base_serializer
{
    /**
     * \brief The type supported by the serializer.
     */
    typedef Type type;

    /**
     * \brief The flags.
     */
    static const value_type::flags_type flags = Flags;

    static value_type serialize(const type& value)
    {
        return value_type(flags, serializer<type>::serialize_data(value));
    }

    /**
     * \brief Deserialize a value.
     * \param value The value.
     * \return The deserialized value.
     */
    static Type deserialize(const value_type& value)
    {
        if (value.flags() != flags)
        {
            throw boost::system::system_error(serialization::error::invalid_types, serialization::serialization_category(), "deserialization failed");
        }

        return serializer<type>::deserialize_data(value.data());
    }
};

/**
 * \brief Serialize a value.
 * \tparam Type The type.
 * \param value The value.
 * \return The serialized value.
 */
template <typename Type>
value_type serialize(const Type& value) { return serializer<Type>::serialize(value); }

/**
 * \brief Deserialize a value.
 * \tparam Type The type.
 * \param value The value.
 * \return The deserialized value.
 */
template <typename Type>
Type deserialize(const value_type& value) { return serializer<Type>::deserialize(value); }

/**
 * \brief A const char* serializer.
 */
template <>
struct serializer<const char*> : public base_serializer<const char*, serialization::string>
{
    static std::vector<char> serialize_data(type value)
    {
        return std::vector<char>(value, value + strlen(value));
    }
};

/**
 * \brief An overload.
 * \param value A C-string.
 * \return The serialized value.
 */
value_type serialize(const char* value) { return serializer<const char*>::serialize(value); }

/**
 * \brief A std::string serializer.
 */
template <>
struct serializer<std::string> : public base_serializer<std::string, serialization::string>
{
    static std::vector<char> serialize_data(type value)
    {
        return std::vector<char>(value.begin(), value.end());
    }

    static type deserialize_data(const std::vector<char>& value)
    {
        return type(value.begin(), value.end());
    }
};

Do you guys see any improvement ?

Thank you.

\$\endgroup\$
2
\$\begingroup\$

You can use an internal traits type to reduce redundancy and complexity.

template< typename serialized >
struct serialize_traits; // no implementation of general case

enum class serialize_style {
    string,
    integral
};

typedef std::vector< char > serial_type; // consider std::string instead

template<>
struct serialize_traits< char * > {
    static const serialize_style style = serialize_style::string;
};

template<>
struct serialize_traits< std::string > {
    static const serialize_style style = serialize_style::string;
};

// Since serializer has no state, I broke this into a free function.
/* It works with both char * and std::string, but uses std::enable_if
   to hide itself unless you specify in serialize_traits that it will work. */

template < typename serialized >
typename std::enable_if< serialize_traits< serialized >::style == serialize_style::string,
serial_type >::type
serialize( serialized const &in ) {
    using std::begin;
    using std::end;
    return serial_type( begin( in ), end( in ) );
}

I need to go so I'll post this now… not tested.

\$\endgroup\$
  • \$\begingroup\$ Thank you very much for taking the time of posting this. I will try it and keep you posted :) \$\endgroup\$ – ereOn Mar 29 '12 at 11:56
  • \$\begingroup\$ Ah, it will fail because begin and end are not defined for char* strings. But you get the idea… hope this ultimately helps. \$\endgroup\$ – Potatoswatter Mar 29 '12 at 13:28

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.