/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2010 Robert Osfield * * This library is open source and may be redistributed and/or modified under * the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or * (at your option) any later version. The full license is in LICENSE file * included with this distribution, and on the openscenegraph.org website. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * OpenSceneGraph Public License for more details. */ // Written by Wang Rui, (C) 2010 #ifndef OSGDB__SERIALIZER #define OSGDB__SERIALIZER #include #include #include #include #include #include #include namespace osgDB { #ifndef OBJECT_CAST #define OBJECT_CAST static_cast #endif class IntLookup { public: typedef int Value; typedef std::map StringToValue; typedef std::map ValueToString; IntLookup() {} unsigned int size() const { return _stringToValue.size(); } void add( const char* str, Value value ) { if ( _valueToString.find(value)!=_valueToString.end() ) { osg::notify(osg::WARN) << "Duplicate enum value " << value << " with old string: " << _valueToString[value] << " and new string: " << str << std::endl; } _valueToString[value] = str; _stringToValue[str] = value; } Value getValue( const char* str ) { StringToValue::iterator itr = _stringToValue.find(str); if ( itr==_stringToValue.end() ) { Value value; std::stringstream stream; stream << str; stream >> value; _stringToValue[str] = value; return value; } return itr->second; } const std::string& getString( Value value ) { ValueToString::iterator itr = _valueToString.find(value); if ( itr==_valueToString.end() ) { std::string str; std::stringstream stream; stream << value; stream >> str; _valueToString[value] = str; return _valueToString[value]; } return itr->second; } protected: StringToValue _stringToValue; ValueToString _valueToString; }; class UserLookupTableProxy { public: typedef void (*AddValueFunc)( IntLookup* lookup ); UserLookupTableProxy( AddValueFunc func ) { if ( func ) (*func)(&_lookup); } IntLookup _lookup; }; #define BEGIN_USER_TABLE(NAME, CLASS) \ static void add_user_value_func_##NAME(osgDB::IntLookup*); \ static osgDB::UserLookupTableProxy s_user_lookup_table_##NAME(&add_user_value_func_##NAME); \ static void add_user_value_func_##NAME(osgDB::IntLookup* lookup) { typedef CLASS MyClass #define ADD_USER_VALUE(VALUE) lookup->add(#VALUE, MyClass::VALUE) #define END_USER_TABLE() } #define USER_READ_FUNC(NAME, FUNCNAME) \ static int FUNCNAME(osgDB::InputStream& is) { \ int value; if (is.isBinary()) is >> value; \ else { std::string str; is >> str; \ value = (s_user_lookup_table_##NAME)._lookup.getValue(str.c_str()); } \ return value; } #define USER_WRITE_FUNC(NAME, FUNCNAME) \ static void FUNCNAME(osgDB::OutputStream& os, int value) { \ if (os.isBinary()) os << value; \ else os << (s_user_lookup_table_##NAME)._lookup.getString(value); } \ class BaseSerializer : public osg::Referenced { public: BaseSerializer() {} virtual bool read( InputStream&, osg::Object& ) = 0; virtual bool write( OutputStream&, const osg::Object& ) = 0; virtual const std::string& getName() const = 0; }; template class UserSerializer : public BaseSerializer { public: typedef bool (*Checker)( const C& ); typedef bool (*Reader)( InputStream&, C& ); typedef bool (*Writer)( OutputStream&, const C& ); UserSerializer( const char* name, Checker cf, Reader rf, Writer wf ) : _name(name), _checker(cf), _reader(rf), _writer(wf) {} virtual bool read( InputStream& is, osg::Object& obj ) { C& object = OBJECT_CAST(obj); if ( is.isBinary() ) { bool ok = false; is >> ok; if ( !ok ) return true; } else { if ( !is.matchString(_name) ) return true; } return (*_reader)(is, object); } virtual bool write( OutputStream& os, const osg::Object& obj ) { const C& object = OBJECT_CAST(obj); bool ok = (*_checker)(object); if ( os.isBinary() ) { os << ok; if ( !ok ) return true; } else { if ( !ok ) return true; os << PROPERTY(_name.c_str()); } return (*_writer)(os, object); } virtual const std::string& getName() const { return _name; } protected: std::string _name; Checker _checker; public: Reader _reader; Writer _writer; }; template class TemplateSerializer : public BaseSerializer { public: TemplateSerializer( const char* name ) : _name(name) {} virtual bool read( InputStream& is, osg::Object& obj ) = 0; virtual bool write( OutputStream& os, const osg::Object& obj ) = 0; virtual const std::string& getName() const { return _name; } protected: std::string _name; P _defaultValue; }; template class PropByValSerializer : public TemplateSerializer

{ public: typedef TemplateSerializer

ParentType; typedef P (C::*Getter)() const; typedef void (C::*Setter)( P ); PropByValSerializer( const char* name, P def, Getter gf, Setter sf, bool useHex=false ) : ParentType(name), _getter(gf), _setter(sf), _useHex(useHex) { ParentType::_defaultValue = def; } virtual bool read( InputStream& is, osg::Object& obj ) { C& object = OBJECT_CAST(obj); P value; if ( is.isBinary() ) { is >> value; if ( ParentType::_defaultValue!=value ) (object.*_setter)( value ); } else if ( is.matchString(ParentType::_name) ) { if ( _useHex ) is >> std::hex; is >> value; if ( _useHex ) is >> std::dec; (object.*_setter)( value ); } return true; } virtual bool write( OutputStream& os, const osg::Object& obj ) { const C& object = OBJECT_CAST(obj); if ( os.isBinary() ) { os << (object.*_getter)(); } else if ( ParentType::_defaultValue!=(object.*_getter)() ) { os << PROPERTY((ParentType::_name).c_str()); if ( _useHex ) os << std::hex; os << (object.*_getter)(); if ( _useHex ) os << std::dec; os << std::endl; } return true; } public: Getter _getter; Setter _setter; protected: bool _useHex; }; template class PropByRefSerializer : public TemplateSerializer

{ public: typedef TemplateSerializer

ParentType; typedef const P& CP; typedef CP (C::*Getter)() const; typedef void (C::*Setter)( CP ); PropByRefSerializer( const char* name, CP def, Getter gf, Setter sf ) : ParentType(name), _getter(gf), _setter(sf) { ParentType::_defaultValue = def; } virtual bool read( InputStream& is, osg::Object& obj ) { C& object = OBJECT_CAST(obj); P value; if ( is.isBinary() ) { is >> value; if ( ParentType::_defaultValue!=value ) (object.*_setter)( value ); } else if ( is.matchString(ParentType::_name) ) { is >> value; (object.*_setter)( value ); } return true; } virtual bool write( OutputStream& os, const osg::Object& obj ) { const C& object = OBJECT_CAST(obj); if ( os.isBinary() ) { os << (object.*_getter)(); } else if ( ParentType::_defaultValue!=(object.*_getter)() ) { os << PROPERTY((ParentType::_name).c_str()) << (object.*_getter)() << std::endl; } return true; } public: Getter _getter; Setter _setter; }; template class MatrixSerializer : public TemplateSerializer { public: typedef TemplateSerializer ParentType; typedef const osg::Matrix& (C::*Getter)() const; typedef void (C::*Setter)( const osg::Matrix& ); MatrixSerializer( const char* name, const osg::Matrix& def, Getter gf, Setter sf ) : ParentType(name), _getter(gf), _setter(sf) { ParentType::_defaultValue = def; } virtual bool read( InputStream& is, osg::Object& obj ) { C& object = OBJECT_CAST(obj); osg::Matrix value; if ( is.isBinary() ) { readMatrixImplementation( is, value ); if ( ParentType::_defaultValue!=value ) (object.*_setter)( value ); } else if ( is.matchString(ParentType::_name) ) { readMatrixImplementation( is, value ); (object.*_setter)( value ); } return true; } virtual bool write( OutputStream& os, const osg::Object& obj ) { const C& object = OBJECT_CAST(obj); if ( os.isBinary() ) { os << (object.*_getter)(); } else if ( ParentType::_defaultValue!=(object.*_getter)() ) { os << PROPERTY((ParentType::_name).c_str()) << (object.*_getter)() << std::endl; } return true; } protected: void readMatrixImplementation( InputStream& is, osg::Matrix& matrix ) { if ( is.getUseFloatMatrix() ) { osg::Matrixf realValue; is >> realValue; matrix = realValue; } else { osg::Matrixd realValue; is >> realValue; matrix = realValue; } } public: Getter _getter; Setter _setter; }; template class GLenumSerializer : public TemplateSerializer

{ public: typedef TemplateSerializer

ParentType; typedef P (C::*Getter)() const; typedef void (C::*Setter)( P ); GLenumSerializer( const char* name, P def, Getter gf, Setter sf ) : ParentType(name), _getter(gf), _setter(sf) { ParentType::_defaultValue = def; } virtual bool read( InputStream& is, osg::Object& obj ) { C& object = OBJECT_CAST(obj); if ( is.isBinary() ) { GLenum value; is >> value; if ( ParentType::_defaultValue!=static_cast

(value) ) (object.*_setter)( static_cast

(value) ); } else if ( is.matchString(ParentType::_name) ) { DEF_GLENUM(value); is >> value; (object.*_setter)( static_cast

(value.get()) ); } return true; } virtual bool write( OutputStream& os, const osg::Object& obj ) { const C& object = OBJECT_CAST(obj); if ( os.isBinary() ) { os << static_cast((object.*_getter)()); } else if ( ParentType::_defaultValue!=(object.*_getter)() ) { os << PROPERTY((ParentType::_name).c_str()) << GLENUM((object.*_getter)()) << std::endl; } return true; } public: Getter _getter; Setter _setter; }; template class StringSerializer : public TemplateSerializer { public: typedef TemplateSerializer ParentType; typedef const std::string& (C::*Getter)() const; typedef void (C::*Setter)( const std::string& ); StringSerializer( const char* name, const std::string& def, Getter gf, Setter sf ) : ParentType(name), _getter(gf), _setter(sf) { ParentType::_defaultValue = def; } virtual bool read( InputStream& is, osg::Object& obj ) { C& object = OBJECT_CAST(obj); std::string value; if ( is.isBinary() ) { is >> value; if ( ParentType::_defaultValue!=value ) (object.*_setter)( value ); } else if ( is.matchString(ParentType::_name) ) { is.readWrappedString( value ); if ( !value.empty() ) (object.*_setter)( value ); } return true; } virtual bool write( OutputStream& os, const osg::Object& obj ) { const C& object = OBJECT_CAST(obj); if ( os.isBinary() ) { os << (object.*_getter)(); } else if ( ParentType::_defaultValue!=(object.*_getter)() ) { os << PROPERTY((ParentType::_name).c_str()); os.writeWrappedString( (object.*_getter)() ); os << std::endl; } return true; } public: Getter _getter; Setter _setter; }; template class ObjectSerializer : public TemplateSerializer { public: typedef TemplateSerializer ParentType; typedef const P* (C::*Getter)() const; typedef void (C::*Setter)( P* ); ObjectSerializer( const char* name, P* def, Getter gf, Setter sf ) : ParentType(name), _getter(gf), _setter(sf) { ParentType::_defaultValue = def; } virtual bool read( InputStream& is, osg::Object& obj ) { C& object = OBJECT_CAST(obj); bool hasObject = false; if ( is.isBinary() ) { is >> hasObject; if ( hasObject ) { P* value = dynamic_cast( is.readObject() ); if ( ParentType::_defaultValue!=value ) (object.*_setter)( value ); } } else if ( is.matchString(ParentType::_name) ) { is >> hasObject; if ( hasObject ) { is >> BEGIN_BRACKET; P* value = dynamic_cast( is.readObject() ); if ( ParentType::_defaultValue!=value ) (object.*_setter)( value ); is >> END_BRACKET; } } return true; } virtual bool write( OutputStream& os, const osg::Object& obj ) { const C& object = OBJECT_CAST(obj); bool hasObject = ((object.*_getter)()!=NULL); if ( os.isBinary() ) { os << hasObject; os.writeObject( (object.*_getter)() ); } else if ( ParentType::_defaultValue!=(object.*_getter)() ) { os << PROPERTY((ParentType::_name).c_str()) << hasObject; if ( hasObject ) { os << BEGIN_BRACKET << std::endl; os.writeObject( (object.*_getter)() ); os << END_BRACKET; } os << std::endl; } return true; } public: Getter _getter; Setter _setter; }; template class ImageSerializer : public TemplateSerializer { public: typedef TemplateSerializer ParentType; typedef const P* (C::*Getter)() const; typedef void (C::*Setter)( P* ); ImageSerializer( const char* name, P* def, Getter gf, Setter sf ) : ParentType(name), _getter(gf), _setter(sf) { ParentType::_defaultValue = def; } virtual bool read( InputStream& is, osg::Object& obj ) { C& object = OBJECT_CAST(obj); bool hasObject = false; if ( is.isBinary() ) { is >> hasObject; if ( hasObject ) { P* value = dynamic_cast( is.readImage() ); if ( ParentType::_defaultValue!=value ) (object.*_setter)( value ); } } else if ( is.matchString(ParentType::_name) ) { is >> hasObject; if ( hasObject ) { is >> BEGIN_BRACKET; P* value = dynamic_cast( is.readImage() ); if ( ParentType::_defaultValue!=value ) (object.*_setter)( value ); is >> END_BRACKET; } } return true; } virtual bool write( OutputStream& os, const osg::Object& obj ) { const C& object = OBJECT_CAST(obj); bool hasObject = ((object.*_getter)()!=NULL); if ( os.isBinary() ) { os << hasObject; os.writeImage( (object.*_getter)() ); } else if ( ParentType::_defaultValue!=(object.*_getter)() ) { os << PROPERTY((ParentType::_name).c_str()) << hasObject; if ( hasObject ) { os << BEGIN_BRACKET << std::endl; os.writeImage( (object.*_getter)() ); os << END_BRACKET; } os << std::endl; } return true; } public: Getter _getter; Setter _setter; }; template class EnumSerializer : public TemplateSerializer

{ public: typedef TemplateSerializer

ParentType; typedef P (C::*Getter)() const; typedef B (C::*Setter)( P ); EnumSerializer( const char* name, P def, Getter gf, Setter sf ) : ParentType(name), _getter(gf), _setter(sf) { ParentType::_defaultValue = def; } void add( const char* str, P value ) { _lookup.add(str, static_cast(value)); } P getValue( const char* str ) { return static_cast

(_lookup.getValue(str)); } const std::string& getString( P value ) { return _lookup.getString(static_cast(value)); } virtual bool read( InputStream& is, osg::Object& obj ) { C& object = OBJECT_CAST(obj); IntLookup::Value value; if ( is.isBinary() ) { is >> value; if ( ParentType::_defaultValue!=static_cast

(value) ) (object.*_setter)( static_cast

(value) ); } else if ( is.matchString(ParentType::_name) ) { std::string str; is >> str; (object.*_setter)( getValue(str.c_str()) ); } return true; } virtual bool write( osgDB::OutputStream& os, const osg::Object& obj ) { const C& object = OBJECT_CAST(obj); if ( os.isBinary() ) { os << (osgDB::IntLookup::Value)(object.*_getter)(); } else if ( ParentType::_defaultValue!=(object.*_getter)() ) { os << PROPERTY((ParentType::_name).c_str()) << getString((object.*_getter)()) << std::endl; } return true; } public: Getter _getter; Setter _setter; protected: IntLookup _lookup; }; template class ListSerializer : public TemplateSerializer

{ public: typedef TemplateSerializer

ParentType; typedef typename P::value_type ValueType; typedef typename P::const_iterator ConstIterator; typedef const P& (C::*Getter)() const; typedef void (C::*Setter)( const P& ); ListSerializer( const char* name, Getter gf, Setter sf ) : ParentType(name), _getter(gf), _setter(sf) {} virtual bool read( InputStream& is, osg::Object& obj ) { C& object = OBJECT_CAST(obj); unsigned int size = 0; P list; if ( is.isBinary() ) { is >> size; for ( unsigned int i=0; i> value; list.push_back( value ); } if ( size>0 ) (object.*_setter)( list ); } else if ( is.matchString(ParentType::_name) ) { is >> size; if ( size>0 ) is >> BEGIN_BRACKET; for ( unsigned int i=0; i> value; list.push_back( value ); } if ( size>0 ) { is >> END_BRACKET; (object.*_setter)( list ); } } return true; } virtual bool write( OutputStream& os, const osg::Object& obj ) { const C& object = OBJECT_CAST(obj); const P& list = (object.*_getter)(); unsigned int size = (unsigned int)list.size(); if ( os.isBinary() ) { os << size; for ( ConstIterator itr=list.begin(); itr!=list.end(); ++itr ) { os << (*itr); } } else if ( size>0 ) { os << PROPERTY((ParentType::_name).c_str()) << size << BEGIN_BRACKET << std::endl; for ( ConstIterator itr=list.begin(); itr!=list.end(); ++itr ) { os << (*itr); } os << END_BRACKET << std::endl; } return true; } public: Getter _getter; Setter _setter; }; // ADDING MANIPULATORS #define ADD_SERIALIZER(S) \ wrapper->addSerializer(S) #define ADD_USER_SERIALIZER(PROP) \ wrapper->addSerializer( new osgDB::UserSerializer( \ #PROP, &check##PROP, &read##PROP, &write##PROP) ) #define ADD_BOOL_SERIALIZER(PROP, DEF) \ wrapper->addSerializer( new osgDB::PropByValSerializer< MyClass, bool >( \ #PROP, DEF, &MyClass::get##PROP, &MyClass::set##PROP) ) #define ADD_SHORT_SERIALIZER(PROP, DEF) \ wrapper->addSerializer( new osgDB::PropByValSerializer< MyClass, short >( \ #PROP, DEF, &MyClass::get##PROP, &MyClass::set##PROP) ) #define ADD_USHORT_SERIALIZER(PROP, DEF) \ wrapper->addSerializer( new osgDB::PropByValSerializer< MyClass, unsigned short >( \ #PROP, DEF, &MyClass::get##PROP, &MyClass::set##PROP) ) #define ADD_HEXSHORT_SERIALIZER(PROP, DEF) \ wrapper->addSerializer( new osgDB::PropByValSerializer< MyClass, unsigned short >( \ #PROP, DEF, &MyClass::get##PROP, &MyClass::set##PROP, true) ) #define ADD_INT_SERIALIZER(PROP, DEF) \ wrapper->addSerializer( new osgDB::PropByValSerializer< MyClass, int >( \ #PROP, DEF, &MyClass::get##PROP, &MyClass::set##PROP) ) #define ADD_UINT_SERIALIZER(PROP, DEF) \ wrapper->addSerializer( new osgDB::PropByValSerializer< MyClass, unsigned int >( \ #PROP, DEF, &MyClass::get##PROP, &MyClass::set##PROP) ) #define ADD_HEXINT_SERIALIZER(PROP, DEF) \ wrapper->addSerializer( new osgDB::PropByValSerializer< MyClass, unsigned int >( \ #PROP, DEF, &MyClass::get##PROP, &MyClass::set##PROP, true) ) #define ADD_FLOAT_SERIALIZER(PROP, DEF) \ wrapper->addSerializer( new osgDB::PropByValSerializer< MyClass, float >( \ #PROP, DEF, &MyClass::get##PROP, &MyClass::set##PROP) ) #define ADD_DOUBLE_SERIALIZER(PROP, DEF) \ wrapper->addSerializer( new osgDB::PropByValSerializer< MyClass, double >( \ #PROP, DEF, &MyClass::get##PROP, &MyClass::set##PROP) ) #define ADD_VEC3F_SERIALIZER(PROP, DEF) \ wrapper->addSerializer( new osgDB::PropByRefSerializer< MyClass, osg::Vec3f >( \ #PROP, DEF, &MyClass::get##PROP, &MyClass::set##PROP) ) #define ADD_VEC3D_SERIALIZER(PROP, DEF) \ wrapper->addSerializer( new osgDB::PropByRefSerializer< MyClass, osg::Vec3d >( \ #PROP, DEF, &MyClass::get##PROP, &MyClass::set##PROP) ) #define ADD_VEC3_SERIALIZER(PROP, DEF) ADD_VEC3F_SERIALIZER(PROP, DEF) #define ADD_VEC4F_SERIALIZER(PROP, DEF) \ wrapper->addSerializer( new osgDB::PropByRefSerializer< MyClass, osg::Vec4f >( \ #PROP, DEF, &MyClass::get##PROP, &MyClass::set##PROP) ) #define ADD_VEC4D_SERIALIZER(PROP, DEF) \ wrapper->addSerializer( new osgDB::PropByRefSerializer< MyClass, osg::Vec4d >( \ #PROP, DEF, &MyClass::get##PROP, &MyClass::set##PROP) ) #define ADD_VEC4_SERIALIZER(PROP, DEF) ADD_VEC4F_SERIALIZER(PROP, DEF) #define ADD_QUAT_SERIALIZER(PROP, DEF) \ wrapper->addSerializer( new osgDB::PropByRefSerializer< MyClass, osg::Quat >( \ #PROP, DEF, &MyClass::get##PROP, &MyClass::set##PROP) ) #define ADD_PLANE_SERIALIZER(PROP, DEF) \ wrapper->addSerializer( new osgDB::PropByRefSerializer< MyClass, osg::Plane >( \ #PROP, DEF, &MyClass::get##PROP, &MyClass::set##PROP) ) #define ADD_MATRIXF_SERIALIZER(PROP, DEF) \ wrapper->addSerializer( new osgDB::PropByRefSerializer< MyClass, osg::Matrixf >( \ #PROP, DEF, &MyClass::get##PROP, &MyClass::set##PROP) ) #define ADD_MATRIXD_SERIALIZER(PROP, DEF) \ wrapper->addSerializer( new osgDB::PropByRefSerializer< MyClass, osg::Matrixd >( \ #PROP, DEF, &MyClass::get##PROP, &MyClass::set##PROP) ) #define ADD_MATRIX_SERIALIZER(PROP, DEF) \ wrapper->addSerializer( new osgDB::MatrixSerializer< MyClass >( \ #PROP, DEF, &MyClass::get##PROP, &MyClass::set##PROP) ) #define ADD_GLENUM_SERIALIZER(PROP, TYPE, DEF) \ wrapper->addSerializer( new osgDB::GLenumSerializer< MyClass, TYPE >( \ #PROP, DEF, &MyClass::get##PROP, &MyClass::set##PROP) ) #define ADD_STRING_SERIALIZER(PROP, DEF) \ wrapper->addSerializer( new osgDB::StringSerializer< MyClass >( \ #PROP, DEF, &MyClass::get##PROP, &MyClass::set##PROP) ) #define ADD_OBJECT_SERIALIZER(PROP, TYPE, DEF) \ wrapper->addSerializer( new osgDB::ObjectSerializer< MyClass, TYPE >( \ #PROP, DEF, &MyClass::get##PROP, &MyClass::set##PROP) ) #define ADD_IMAGE_SERIALIZER(PROP, TYPE, DEF) \ wrapper->addSerializer( new osgDB::ImageSerializer< MyClass, TYPE >( \ #PROP, DEF, &MyClass::get##PROP, &MyClass::set##PROP) ) #define ADD_LIST_SERIALIZER(PROP, TYPE) \ wrapper->addSerializer( new osgDB::ListSerializer< MyClass, TYPE >( \ #PROP, &MyClass::get##PROP, &MyClass::set##PROP) ) #define BEGIN_ENUM_SERIALIZER(PROP, DEF) \ { typedef osgDB::EnumSerializer MySerializer; \ osg::ref_ptr serializer = new MySerializer( \ #PROP, MyClass::DEF, &MyClass::get##PROP, &MyClass::set##PROP) #define BEGIN_ENUM_SERIALIZER2(PROP, TYPE, DEF) \ { typedef osgDB::EnumSerializer MySerializer; \ osg::ref_ptr serializer = new MySerializer( \ #PROP, MyClass::DEF, &MyClass::get##PROP, &MyClass::set##PROP) #define BEGIN_ENUM_SERIALIZER3(PROP, DEF) \ { typedef osgDB::EnumSerializer MySerializer; \ osg::ref_ptr serializer = new MySerializer( \ #PROP, MyClass::DEF, &MyClass::get##PROP, &MyClass::set##PROP) #define BEGIN_ENUM_SERIALIZER4(PROPERTIES_CLASS, PROP, DEF) \ { typedef osgDB::EnumSerializer MySerializer; \ osg::ref_ptr serializer = new MySerializer( \ #PROP, PROPERTIES_CLASS::DEF, &MyClass::get##PROP, &MyClass::set##PROP) #define ADD_ENUM_VALUE(VALUE) \ serializer->add(#VALUE, MyClass::VALUE) #define ADD_ENUM_CLASS_VALUE(CLASS, VALUE) \ serializer->add(#VALUE, CLASS::VALUE) #define END_ENUM_SERIALIZER() \ wrapper->addSerializer(serializer.get()); } } #endif