Safer and elgant serial helper macros

This commit is contained in:
Gioacchino Mazzurco 2018-01-25 11:37:16 +01:00
parent 7409de5170
commit 13d4a2c916
8 changed files with 133 additions and 115 deletions

View file

@ -17,60 +17,48 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <type_traits>
#include "serialiser/rsserializer.h"
/** @brief Minimal ancestor for all serializable structs in RetroShare
/** @brief Minimal ancestor for all serializable structs in RetroShare.
* If you want your struct to be easly serializable you should inherit from this
* struct.
* If you want your struct to be serializable as part of a container like an
* `std::vector<T>` @see RS_REGISTER_SERIALIZABLE_TYPE(T)
* `std::vector<T>` @see RS_REGISTER_SERIALIZABLE_TYPE_DEF(T) and
* @see RS_REGISTER_SERIALIZABLE_TYPE_DECL(T)
*/
struct RsSerializable
{
/** Register struct members to serialize in this method taking advantage of
* the helper macros
* @see RS_PROCESS_SERIAL_MEMBER(I)
* @see RS_PROCESS_SERIAL_MEMBER_TYPED(I, T)
* the helper macro @see RS_SERIAL_PROCESS(I)
*/
virtual void serial_process(RsGenericSerializer::SerializeJob j,
RsGenericSerializer::SerializeContext& ctx) = 0;
};
/** @def RS_PROCESS_SERIAL_MEMBER(I)
/** @def RS_SERIAL_PROCESS(I)
* Use this macro to register the members of `YourSerializable` for serial
* processing inside `YourSerializable::serial_process(j, ctx)`
*
* Pay special attention for member of enum type which must be declared
* specifying the underlying type otherwise the serialization format may differ
* in an uncompatible way depending on the compiler/platform.
*
* If your member is a derivative of RsSerializable in some cases it may be
* convenient also to register your item type with
* @see RS_REGISTER_SERIALIZABLE_TYPE_DEF(T).
*
* Inspired by http://stackoverflow.com/a/39345864
*/
#define RS_PROCESS_SERIAL_MEMBER(I) \
do { RsTypeSerializer::serial_process(j, ctx, I, #I); } while(0)
/** @def RS_PROCESS_SERIAL_MEMBER_TYPED(I, T)
* This macro usage is similar to @see RS_PROCESS_SERIAL_MEMBER(I) but it
* permit to force serialization/deserialization type, it is expecially useful
* with enum class members or RsTlvItem derivative members, be very careful with
* the type you pass, as reinterpret_cast on a reference is used that is
* expecially permissive so you can shot your feet if not carefull enough.
*
* If you are using this with an RsSerializable derivative (so passing
* RsSerializable as T) consider to register your item type with
* @see RS_REGISTER_SERIALIZABLE_TYPE(T) in
* association with @see RS_PROCESS_SERIAL_MEMBER(I) that rely on template
* function generation, as in this particular case
* RS_PROCESS_SERIAL_MEMBER_TYPED(I, T) would cause the serial code rely on
* C++ dynamic dispatching that may have a noticeable impact on runtime
* performances.
*/
#pragma GCC diagnostic ignored "-Wstrict-aliasing"
#define RS_PROCESS_SERIAL_MEMBER_TYPED(I, T) do {\
RsTypeSerializer::serial_process<T>(j, ctx, reinterpret_cast<T&>(I), #I);\
#define RS_SERIAL_PROCESS(I) do { \
RsTypeSerializer::serial_process(j, ctx, __priv_to_RS_S_TYPE(I), #I ); \
} while(0)
#pragma GCC diagnostic pop
/** @def RS_REGISTER_SERIALIZABLE_TYPE(T)
/** @def RS_REGISTER_SERIALIZABLE_TYPE_DEF(T)
* Use this macro into `youritem.cc` only if you need to process members of
* subtypes of RsSerializable.
*
@ -90,21 +78,15 @@ struct PrivateOugoingMapItem : RsChatItem
std::map<uint64_t, RsChatMsgItem> store;
};
RS_REGISTER_SERIALIZABLE_TYPE(RsChatMsgItem)
RS_REGISTER_SERIALIZABLE_TYPE_DEF(RsChatMsgItem)
void PrivateOugoingMapItem::serial_process(
RsGenericSerializer::SerializeJob j,
RsGenericSerializer::SerializeContext& ctx )
{
// store is of type
RS_PROCESS_SERIAL_MEMBER(store);
RS_SERIAL_PROCESS(store);
}
* @endcode
*
* If you use this macro with a lot of different item types this can cause the
* generated binary grow in size, consider the usage of
* @see RS_PROCESS_SERIAL_MEMBER_TYPED(I, T) passing RsSerializable as type in
* that case.
*/
#define RS_REGISTER_SERIALIZABLE_TYPE_DEF(T) template<> /*static*/\
void RsTypeSerializer::serial_process<T>( \
@ -118,11 +100,11 @@ void PrivateOugoingMapItem::serial_process(
/** @def RS_REGISTER_SERIALIZABLE_TYPE_DECL(T)
* The usage of this macro into your header file is needed only in case you
* The usage of this macro into your header file may be needed only in case you
* needed @see RS_REGISTER_SERIALIZABLE_TYPE_DEF(T) in your definitions file,
* but it was not enough and the compiler kept complanining about undefined
* references to serialize, deserialize, serial_size, print_data, to_JSON,
* from_JSON for your RsSerializable derrived type.
* from_JSON for your RsSerializable derived type.
*
* One example of such case is RsIdentityUsage that is declared in
* retroshare/rsidentity.h but defined in services/p3idservice.cc, also if
@ -135,4 +117,40 @@ void PrivateOugoingMapItem::serial_process(
void RsTypeSerializer::serial_process<T>( \
RsGenericSerializer::SerializeJob j,\
RsGenericSerializer::SerializeContext& ctx, T& item,\
const std::string& /*objName*/ );
const std::string& objName );
//============================================================================//
// Private type conversion helpers //
//============================================================================//
/**
* @brief Privates type conversion helpers for RS_SERIAL_PROCESS.
* @private DO NOT use explicitely outside this file.
* Used to cast enums and derived type to matching types supported by
* RsTypeSerializer::serial_process(...) templated methods.
*/
template<typename T, typename = typename std::enable_if<std::is_enum<T>::value>::type>
inline typename std::underlying_type<T>::type& __priv_to_RS_S_TYPE(T& i)
{
return reinterpret_cast<typename std::underlying_type<T>::type&>(i);
}
template<typename T, typename = typename std::enable_if<std::is_base_of<RsSerializable, T>::value>::type>
inline RsSerializable& __priv_to_RS_S_TYPE(T& i)
{
return static_cast<RsSerializable&>(i);
}
struct RsTlvItem;
template<typename T, typename = typename std::enable_if<std::is_base_of<RsTlvItem, T>::value>::type>
inline RsTlvItem& __priv_to_RS_S_TYPE(T& i)
{
return static_cast<RsTlvItem&>(i);
}
template<typename T, typename = typename std::enable_if<!(std::is_enum<T>::value||std::is_base_of<RsTlvItem, T>::value||std::is_base_of<RsSerializable, T>::value)>::type>
inline T& __priv_to_RS_S_TYPE(T& i)
{
return i;
}