Better usage of type traits

Don't need register types for serializationanymore
Don't need casting helpers for serialization
This commit is contained in:
Gioacchino Mazzurco 2018-01-27 17:57:33 +01:00
parent ba6f2d7e81
commit 8b774595d7
12 changed files with 145 additions and 279 deletions

View File

@ -205,9 +205,6 @@ void RsPrivateChatMsgConfigItem::get(RsChatMsgItem *ci)
ci->recvTime = recvTime;
}
/* Necessary to serialize `store` that is an STL container with RsChatMsgItem
* inside which is a subtype of RsItem */
RS_REGISTER_SERIALIZABLE_TYPE_DEF(RsChatMsgItem)
void PrivateOugoingMapItem::serial_process(
RsGenericSerializer::SerializeJob j,

View File

@ -38,12 +38,6 @@ OutgoingRecord::OutgoingRecord( RsGxsId rec, GxsTransSubServices cs,
memcpy(&mailData[0], data, size);
}
// for mailItem
RS_REGISTER_SERIALIZABLE_TYPE_DEF(RsGxsTransMailItem)
// for presignedReceipt
RS_REGISTER_SERIALIZABLE_TYPE_DEF(RsNxsTransPresignedReceipt)
void OutgoingRecord_deprecated::serial_process(
RsGenericSerializer::SerializeJob j,
RsGenericSerializer::SerializeContext& ctx )

View File

@ -294,8 +294,6 @@ private:
RsIdentityUsage();
};
RS_REGISTER_SERIALIZABLE_TYPE_DECL(RsIdentityUsage)
struct RsIdentityDetails : RsSerializable
{

View File

@ -364,7 +364,6 @@ class CompressedChunkMap : public RsSerializable
{ RS_SERIAL_PROCESS(_map); }
};
RS_REGISTER_SERIALIZABLE_TYPE_DECL(CompressedChunkMap)
template<class CRCTYPE> class t_CRCMap
{

View File

@ -193,7 +193,6 @@ void RsPeerStunItem::serial_process(RsGenericSerializer::SerializeJob j,RsGeneri
RsTypeSerializer::serial_process<RsTlvItem>(j,ctx,stunList,"stunList") ;
}
RS_REGISTER_SERIALIZABLE_TYPE_DEF(PeerBandwidthLimits)
RsNodeGroupItem::RsNodeGroupItem(const RsGroupInfo& g)
:RsItem(RS_PKT_VERSION1, RS_PKT_CLASS_CONFIG, RS_PKT_TYPE_PEER_CONFIG, RS_PKT_SUBTYPE_NODE_GROUP)

View File

@ -97,10 +97,6 @@ void RsGxsMsgUpdateItem::serial_process(RsGenericSerializer::SerializeJob j,RsGe
RsTypeSerializer::serial_process(j,ctx,msgUpdateInfos,"msgUpdateInfos");
}
RS_REGISTER_SERIALIZABLE_TYPE_DEF(RsGxsMsgUpdate::MsgUpdateInfo)
void RsGxsServerMsgUpdateItem::serial_process(RsGenericSerializer::SerializeJob j,RsGenericSerializer::SerializeContext& ctx)
{
RsTypeSerializer::serial_process (j,ctx,grpId,"grpId");
@ -113,5 +109,3 @@ void RsGxsGrpConfigItem::serial_process(RsGenericSerializer::SerializeJob j,RsGe
RsTypeSerializer::serial_process<uint32_t>(j,ctx,msg_send_delay,"msg_send_delay") ;
RsTypeSerializer::serial_process<uint32_t>(j,ctx,msg_req_delay,"msg_req_delay") ;
}

View File

@ -152,7 +152,6 @@ public:
std::map<RsGxsGroupId, MsgUpdateInfo> msgUpdateInfos;
};
RS_REGISTER_SERIALIZABLE_TYPE_DECL(RsGxsMsgUpdate::MsgUpdateInfo)
class RsGxsMsgUpdateItem : public RsGxsNetServiceItem, public RsGxsMsgUpdate
{

View File

@ -75,5 +75,3 @@ std::ostream &operator<<(std::ostream &out, const FileInfo &info)
out << "Hash: " << info.hash;
return out;
}
RS_REGISTER_SERIALIZABLE_TYPE_DEF(CompressedChunkMap)

View File

@ -17,8 +17,6 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <type_traits>
#include "serialiser/rsserializer.h"
@ -38,7 +36,6 @@ struct RsSerializable
RsGenericSerializer::SerializeContext& ctx) = 0;
};
/** @def RS_SERIAL_PROCESS(I)
* Use this macro to register the members of `YourSerializable` for serial
* processing inside `YourSerializable::serial_process(j, ctx)`
@ -47,110 +44,8 @@ struct RsSerializable
* 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_SERIAL_PROCESS(I) do { \
RsTypeSerializer::serial_process(j, ctx, __priv_to_RS_S_TYPE(I), #I ); \
RsTypeSerializer::serial_process(j, ctx, I, #I ); \
} while(0)
/** @def RS_REGISTER_SERIALIZABLE_TYPE_DEF(T)
* Use this macro into `youritem.cc` only if you need to process members of
* subtypes of RsSerializable.
*
* The usage of this macro is strictly needed only in some cases, for example if
* you are registering for serialization a member of a container that contains
* items of subclasses of RsSerializable like
* `std::map<uint64_t, RsChatMsgItem>`
*
* @code{.cpp}
struct PrivateOugoingMapItem : RsChatItem
{
PrivateOugoingMapItem() : RsChatItem(RS_PKT_SUBTYPE_OUTGOING_MAP) {}
void serial_process( RsGenericSerializer::SerializeJob j,
RsGenericSerializer::SerializeContext& ctx );
std::map<uint64_t, RsChatMsgItem> store;
};
RS_REGISTER_SERIALIZABLE_TYPE_DEF(RsChatMsgItem)
void PrivateOugoingMapItem::serial_process(
RsGenericSerializer::SerializeJob j,
RsGenericSerializer::SerializeContext& ctx )
{
RS_SERIAL_PROCESS(store);
}
* @endcode
*/
#define RS_REGISTER_SERIALIZABLE_TYPE_DEF(T) template<> /*static*/\
void RsTypeSerializer::serial_process<T>( \
RsGenericSerializer::SerializeJob j,\
RsGenericSerializer::SerializeContext& ctx, T& item,\
const std::string& objName ) \
{ \
RsTypeSerializer::serial_process<RsSerializable>( j, \
ctx, static_cast<RsSerializable&>(item), objName ); \
}
/** @def RS_REGISTER_SERIALIZABLE_TYPE_DECL(T)
* 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 derived type.
*
* One example of such case is RsIdentityUsage that is declared in
* retroshare/rsidentity.h but defined in services/p3idservice.cc, also if
* RS_REGISTER_SERIALIZABLE_TYPE_DEF(RsIdentityUsage) was used in p3idservice.cc
* for some reason it was not enough for the compiler to see it so
* RS_REGISTER_SERIALIZABLE_TYPE_DECL(RsIdentityUsage) has been added in
* rsidentity.h too and now the compiler is happy.
*/
#define RS_REGISTER_SERIALIZABLE_TYPE_DECL(T) template<> /*static*/\
void RsTypeSerializer::serial_process<T>( \
RsGenericSerializer::SerializeJob j,\
RsGenericSerializer::SerializeContext& ctx, T& item,\
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;
}

View File

@ -44,16 +44,17 @@ static const uint32_t MAX_SERIALIZED_CHUNK_SIZE = 10*1024*1024 ; // 10 MB.
#define SAFE_GET_JSON_V() \
const char* mName = memberName.c_str(); \
bool ret = jVal.HasMember(mName); \
bool ret = jDoc.HasMember(mName); \
if(!ret) \
{ \
std::cerr << __PRETTY_FUNCTION__ << " \"" << memberName \
<< "\" not found in JSON:" << std::endl \
<< jVal << std::endl << std::endl; \
<< jDoc << std::endl << std::endl; \
print_stacktrace(); \
return false; \
} \
rapidjson::Value& v = jVal[mName]
rapidjson::Value& v = jDoc[mName]
//============================================================================//
// Integer types //
@ -227,7 +228,7 @@ SIMPLE_TO_JSON_DEF(uint64_t)
template<> /*static*/
bool RsTypeSerializer::from_JSON( const std::string& memberName, bool& member,
RsJson& jVal )
RsJson& jDoc )
{
SAFE_GET_JSON_V();
ret = ret && v.IsBool();
@ -237,7 +238,7 @@ bool RsTypeSerializer::from_JSON( const std::string& memberName, bool& member,
template<> /*static*/
bool RsTypeSerializer::from_JSON( const std::string& memberName,
int32_t& member, RsJson& jVal )
int32_t& member, RsJson& jDoc )
{
SAFE_GET_JSON_V();
ret = ret && v.IsInt();
@ -247,7 +248,7 @@ bool RsTypeSerializer::from_JSON( const std::string& memberName,
template<> /*static*/
bool RsTypeSerializer::from_JSON( const std::string& memberName, time_t& member,
RsJson& jVal )
RsJson& jDoc )
{
SAFE_GET_JSON_V();
ret = ret && v.IsUint();
@ -257,7 +258,7 @@ bool RsTypeSerializer::from_JSON( const std::string& memberName, time_t& member,
template<> /*static*/
bool RsTypeSerializer::from_JSON( const std::string& memberName,
uint8_t& member, RsJson& jVal )
uint8_t& member, RsJson& jDoc )
{
SAFE_GET_JSON_V();
ret = ret && v.IsUint();
@ -267,7 +268,7 @@ bool RsTypeSerializer::from_JSON( const std::string& memberName,
template<> /*static*/
bool RsTypeSerializer::from_JSON( const std::string& memberName,
uint16_t& member, RsJson& jVal )
uint16_t& member, RsJson& jDoc )
{
SAFE_GET_JSON_V();
ret = ret && v.IsUint();
@ -277,7 +278,7 @@ bool RsTypeSerializer::from_JSON( const std::string& memberName,
template<> /*static*/
bool RsTypeSerializer::from_JSON( const std::string& memberName,
uint32_t& member, RsJson& jVal )
uint32_t& member, RsJson& jDoc )
{
SAFE_GET_JSON_V();
ret = ret && v.IsUint();
@ -287,7 +288,7 @@ bool RsTypeSerializer::from_JSON( const std::string& memberName,
template<> /*static*/
bool RsTypeSerializer::from_JSON( const std::string& memberName,
uint64_t& member, RsJson& jVal )
uint64_t& member, RsJson& jDoc )
{
SAFE_GET_JSON_V();
ret = ret && v.IsUint();
@ -319,7 +320,7 @@ SIMPLE_TO_JSON_DEF(float)
template<> /*static*/
bool RsTypeSerializer::from_JSON( const std::string& memberName,
float& member, RsJson& jVal )
float& member, RsJson& jDoc )
{
SAFE_GET_JSON_V();
ret = ret && v.IsFloat();
@ -371,7 +372,7 @@ bool RsTypeSerializer::to_JSON( const std::string& membername,
}
template<> /*static*/
bool RsTypeSerializer::from_JSON( const std::string& memberName,
std::string& member, RsJson& jVal )
std::string& member, RsJson& jDoc )
{
SAFE_GET_JSON_V();
ret = ret && v.IsString();
@ -416,9 +417,9 @@ bool RsTypeSerializer::to_JSON( const std::string& memberName,
template<> /*static*/
bool RsTypeSerializer::from_JSON( const std::string& memberName,
uint16_t /*sub_type*/,
std::string& member, RsJson& jVal )
std::string& member, RsJson& jDoc )
{
return from_JSON<std::string>(memberName, member, jVal);
return from_JSON<std::string>(memberName, member, jDoc);
}
//============================================================================//
@ -463,9 +464,9 @@ bool RsTypeSerializer::to_JSON( const std::string& memberName,
template<> /*static*/
bool RsTypeSerializer::from_JSON( const std::string& memberName,
uint16_t /*sub_type*/,
uint32_t& member, RsJson& jVal )
uint32_t& member, RsJson& jDoc )
{
return from_JSON<uint32_t>(memberName, member, jVal);
return from_JSON<uint32_t>(memberName, member, jDoc);
}
@ -517,7 +518,7 @@ bool RsTypeSerializer::to_JSON( const std::string& memberName,
template<> /*static*/
bool RsTypeSerializer::from_JSON( const std::string& /*memberName*/,
RsTlvItem& member, RsJson& /*jVal*/)
RsTlvItem& member, RsJson& /*jDoc*/)
{
member.TlvClear();
return true;
@ -607,134 +608,9 @@ bool RsTypeSerializer::to_JSON(
template<> /*static*/
bool RsTypeSerializer::from_JSON( const std::string& /*memberName*/,
RsTypeSerializer::TlvMemBlock_proxy&,
RsJson& /*jVal*/)
RsJson& /*jDoc*/)
{ return true; }
//============================================================================//
// RsSerializable and derivated //
//============================================================================//
template<> uint32_t RsTypeSerializer::serial_size(const RsSerializable& s)
{
RsGenericSerializer::SerializeContext ctx(
NULL, 0, RsGenericSerializer::FORMAT_BINARY,
RsGenericSerializer::SERIALIZATION_FLAG_NONE );
ctx.mOffset = 8; // header size
const_cast<RsSerializable&>(s).serial_process(
RsGenericSerializer::SIZE_ESTIMATE, ctx );
return ctx.mOffset;
}
template<> bool RsTypeSerializer::serialize( uint8_t data[], uint32_t size,
uint32_t &offset,
const RsSerializable& s )
{
RsGenericSerializer::SerializeContext ctx(
data, size, RsGenericSerializer::FORMAT_BINARY,
RsGenericSerializer::SERIALIZATION_FLAG_NONE );
ctx.mOffset = offset;
const_cast<RsSerializable&>(s).serial_process(
RsGenericSerializer::SERIALIZE, ctx );
return true;
}
template<> bool RsTypeSerializer::deserialize( const uint8_t data[],
uint32_t size, uint32_t& offset,
RsSerializable& s )
{
RsGenericSerializer::SerializeContext ctx(
const_cast<uint8_t*>(data), size,
RsGenericSerializer::FORMAT_BINARY,
RsGenericSerializer::SERIALIZATION_FLAG_NONE );
ctx.mOffset = offset;
const_cast<RsSerializable&>(s).serial_process(
RsGenericSerializer::DESERIALIZE, ctx );
return true;
}
template<> void RsTypeSerializer::print_data( const std::string& /*n*/,
const RsSerializable& s )
{
RsGenericSerializer::SerializeContext ctx(
NULL, 0,
RsGenericSerializer::FORMAT_BINARY,
RsGenericSerializer::SERIALIZATION_FLAG_NONE );
const_cast<RsSerializable&>(s).serial_process( RsGenericSerializer::PRINT,
ctx );
}
template<> /*static*/
bool RsTypeSerializer::to_JSON( const std::string& memberName,
const RsSerializable& member, RsJson& jDoc )
{
rapidjson::Document::AllocatorType& allocator = jDoc.GetAllocator();
// Reuse allocator to avoid deep copy later
RsGenericSerializer::SerializeContext ctx(
NULL, 0, RsGenericSerializer::FORMAT_BINARY,
RsGenericSerializer::SERIALIZATION_FLAG_NONE,
&allocator );
const_cast<RsSerializable&>(member).serial_process(
RsGenericSerializer::TO_JSON, ctx );
rapidjson::Value key;
key.SetString(memberName.c_str(), memberName.length(), allocator);
/* Because the passed allocator is reused it doesn't go out of scope and
* there is no need of deep copy and we can take advantage of the much
* faster rapidjson move semantic */
jDoc.AddMember(key, ctx.mJson, allocator);
return ctx.mOk;
}
template<> /*static*/
bool RsTypeSerializer::from_JSON( const std::string& memberName,
RsSerializable& member, RsJson& jVal )
{
SAFE_GET_JSON_V();
if(ret)
{
RsGenericSerializer::SerializeContext ctx(
NULL, 0, RsGenericSerializer::FORMAT_BINARY,
RsGenericSerializer::SERIALIZATION_FLAG_NONE );
ctx.mJson.SetObject() = v; // Beware of move semantic!!
member.serial_process(RsGenericSerializer::FROM_JSON, ctx);
ret = ret && ctx.mOk;
}
return ret;
}
template<> /*static*/
void RsTypeSerializer::serial_process<RsSerializable>(
RsGenericSerializer::SerializeJob j,
RsGenericSerializer::SerializeContext& ctx, RsSerializable& object,
const std::string& objName )
{
switch (j)
{
case RsGenericSerializer::SIZE_ESTIMATE: // fall-through
case RsGenericSerializer::SERIALIZE: // fall-through
case RsGenericSerializer::DESERIALIZE: // fall-through
case RsGenericSerializer::PRINT: // fall-through
object.serial_process(j, ctx);
break;
case RsGenericSerializer::TO_JSON:
ctx.mOk = ctx.mOk && to_JSON(objName, object, ctx.mJson);
break;
case RsGenericSerializer::FROM_JSON:
ctx.mOk = ctx.mOk && from_JSON(objName, object, ctx.mJson);
break;
default: break;
}
}
//============================================================================//
// RsJson std:ostream support //

View File

@ -37,6 +37,8 @@
#include <rapidjson/document.h>
#include <typeinfo> // for typeid
#include <type_traits>
#include <errno.h>
/** INTERNAL ONLY helper to avoid copy paste code for std::{vector,list,set}<T>
@ -129,7 +131,8 @@ struct RsTypeSerializer
/// Generic types
template<typename T>
static void serial_process( RsGenericSerializer::SerializeJob j,
typename std::enable_if<std::is_same<RsTlvItem,T>::value || !(std::is_base_of<RsSerializable,T>::value || std::is_enum<T>::value || std::is_base_of<RsTlvItem,T>::value)>::type
static /*void*/ serial_process( RsGenericSerializer::SerializeJob j,
RsGenericSerializer::SerializeContext& ctx,
T& member, const std::string& member_name)
{
@ -156,8 +159,10 @@ struct RsTypeSerializer
ctx.mOk = ctx.mOk && from_JSON(member_name, member, ctx.mJson);
break;
default:
ctx.mOk = false;
throw std::runtime_error("Unknown serial job");
std::cerr << __PRETTY_FUNCTION__ << " Unknown serial job: "
<< static_cast<std::underlying_type<decltype(j)>::type>(j)
<< std::endl;
exit(EINVAL);
}
}
@ -193,8 +198,10 @@ struct RsTypeSerializer
from_JSON(member_name, type_id, member, ctx.mJson);
break;
default:
ctx.mOk = false;
throw std::runtime_error("Unknown serial job");
std::cerr << __PRETTY_FUNCTION__ << " Unknown serial job: "
<< static_cast<std::underlying_type<decltype(j)>::type>(j)
<< std::endl;
exit(EINVAL);
}
}
@ -567,6 +574,118 @@ struct RsTypeSerializer
}
}
/**
* @brief serial process enum types
*
* On declaration of your member of enum type you must specify the
* underlying type otherwise the serialization format may differ in an
* uncompatible way depending on the compiler/platform.
*/
template<typename E>
typename std::enable_if<std::is_enum<E>::value>::type
static /*void*/ serial_process( RsGenericSerializer::SerializeJob j,
RsGenericSerializer::SerializeContext& ctx,
E& member,
const std::string& memberName )
{
#ifdef RSSERIAL_DEBUG
std::cerr << __PRETTY_FUNCTION__ << " processing enum: "
<< typeid(E).name() << " as "
<< typeid(typename std::underlying_type<E>::type).name()
<< std::endl;
#endif
serial_process(
j, ctx,
reinterpret_cast<typename std::underlying_type<E>::type&>(member),
memberName );
}
/// RsSerializable and derivatives
template<typename T>
typename std::enable_if<std::is_base_of<RsSerializable,T>::value>::type
static /*void*/ serial_process( RsGenericSerializer::SerializeJob j,
RsGenericSerializer::SerializeContext& ctx,
T& member,
const std::string& memberName )
{
switch(j)
{
case RsGenericSerializer::SIZE_ESTIMATE: // fallthrough
case RsGenericSerializer::DESERIALIZE: // fallthrough
case RsGenericSerializer::SERIALIZE: // fallthrough
case RsGenericSerializer::PRINT:
member.serial_process(j, ctx);
break;
case RsGenericSerializer::TO_JSON:
{
rapidjson::Document& jDoc(ctx.mJson);
rapidjson::Document::AllocatorType& allocator = jDoc.GetAllocator();
// Reuse allocator to avoid deep copy later
RsGenericSerializer::SerializeContext lCtx(
nullptr, 0, RsGenericSerializer::FORMAT_BINARY,
RsGenericSerializer::SERIALIZATION_FLAG_NONE,
&allocator );
member.serial_process(j, lCtx);
rapidjson::Value key;
key.SetString(memberName.c_str(), memberName.length(), allocator);
/* Because the passed allocator is reused it doesn't go out of scope and
* there is no need of deep copy and we can take advantage of the much
* faster rapidjson move semantic */
jDoc.AddMember(key, lCtx.mJson, allocator);
ctx.mOk = ctx.mOk && lCtx.mOk;
break;
}
case RsGenericSerializer::FROM_JSON:
{
rapidjson::Document& jDoc(ctx.mJson);
const char* mName = memberName.c_str();
ctx.mOk = ctx.mOk && jDoc.HasMember(mName);
if(!ctx.mOk)
{
std::cerr << __PRETTY_FUNCTION__ << " \"" << memberName
<< "\" not found in JSON:" << std::endl
<< jDoc << std::endl << std::endl;
print_stacktrace();
break;
}
rapidjson::Value& v = jDoc[mName];
RsGenericSerializer::SerializeContext lCtx(
nullptr, 0, RsGenericSerializer::FORMAT_BINARY,
RsGenericSerializer::SERIALIZATION_FLAG_NONE );
lCtx.mJson.SetObject() = v; // Beware of move semantic!!
member.serial_process(j, lCtx);
ctx.mOk = ctx.mOk && lCtx.mOk;
break;
}
default:
std::cerr << __PRETTY_FUNCTION__ << " Unknown serial job: "
<< static_cast<std::underlying_type<decltype(j)>::type>(j)
<< std::endl;
exit(EINVAL);
break;
}
}
/// RsTlvItem derivatives only
template<typename T>
typename std::enable_if<std::is_base_of<RsTlvItem,T>::value && !std::is_same<RsTlvItem,T>::value>::type
static /*void*/ serial_process( RsGenericSerializer::SerializeJob j,
RsGenericSerializer::SerializeContext& ctx,
T& member,
const std::string& memberName )
{
serial_process(j, ctx, static_cast<RsTlvItem&>(member), memberName);
}
protected:

View File

@ -4560,5 +4560,3 @@ RsIdentityUsage::RsIdentityUsage(
RsIdentityUsage::RsIdentityUsage() :
mServiceId(0), mUsageCode(UNKNOWN_USAGE), mAdditionalId(0) {}
RS_REGISTER_SERIALIZABLE_TYPE_DEF(RsIdentityUsage)