1 /* This file is part of the Spring engine (GPL v2 or later), see LICENSE.html */
2 
3 // creg - Code compoment registration system
4 
5 #ifndef _CREG_H
6 #define _CREG_H
7 
8 #include <vector>
9 #include <string>
10 #include <boost/shared_ptr.hpp>
11 
12 #include "ISerializer.h"
13 #include "System/Sync/SyncedPrimitive.h"
14 
15 
16 namespace creg {
17 
18 	class IType;
19 	class Class;
20 	class ClassBinder;
21 
22 	typedef unsigned int uint;
23 
24 	// Fundamental/basic types
25 	enum BasicTypeID
26 	{
27 		crInt,		crUInt,
28 		crShort,	crUShort,
29 		crChar,		crUChar,
30 		crInt64,	crUInt64,
31 		crFloat,
32 		crDouble,
33 		crBool,
34 #if defined(SYNCDEBUG) || defined(SYNCCHECK)
35 		crSyncedSint,   crSyncedUint,
36 		crSyncedSshort, crSyncedUshort,
37 		crSyncedSchar,  crSyncedUchar,
38 		crSyncedFloat,
39 		crSyncedDouble,
40 		crSyncedBool,
41 #endif
42 	};
43 
44 	class IType
45 	{
46 	public:
47 		// Type interface can go here...
48 		virtual ~IType();
49 
50 		virtual void Serialize(ISerializer* s, void* instance) = 0;
51 		virtual std::string GetName() = 0;
52 		virtual size_t GetSize() = 0;
53 
54 		static boost::shared_ptr<IType> CreateBasicType(BasicTypeID t);
55 		static boost::shared_ptr<IType> CreateStringType();
56 		static boost::shared_ptr<IType> CreateObjInstanceType(Class* objectType);
57 		static boost::shared_ptr<IType> CreateEnumeratedType(size_t size);
58 	};
59 
60 	class IMemberRegistrator
61 	{
62 	public:
63 		virtual ~IMemberRegistrator();
64 		virtual void RegisterMembers(Class* cls) = 0;
65 	};
66 
67 	enum ClassFlags {
68 		CF_None = 0,
69 		CF_Abstract = 4,
70 		CF_Synced = 8,
71 	};
72 
73 	struct _DummyStruct {};
74 
75 	/** Class member flags to use with CR_MEMBER_SETFLAG */
76 	enum ClassMemberFlag {
77 		CM_NoSerialize = 1, /// Make the serializers skip the member
78 		CM_Config = 2,  // Exposed in config
79 	};
80 
81 /**
82  * Stores class bindings such as constructor/destructor
83  */
84 	class ClassBinder
85 	{
86 	public:
87 		ClassBinder(const char* className, unsigned int cf, ClassBinder* base,
88 				IMemberRegistrator** mreg, int instanceSize, int instanceAlignment, bool hasVTable,
89 				void (*constructorProc)(void* instance),
90 				void (*destructorProc)(void* instance));
91 
92 		Class* class_;
93 		ClassBinder* base;
94 		ClassFlags flags;
95 		IMemberRegistrator** memberRegistrator;
96 		const char* name;
97 		int size; // size of an instance in bytes
98 		int alignment;
99 		bool hasVTable;
100 
101 		void (*constructor)(void* instance);
102 		/**
103 		 * Needed for classes without virtual destructor.
104 		 * (classes/structs declared with CR_DECLARE_STRUCT)
105 		 */
106 		void (*destructor)(void* instance);
107 
108 		ClassBinder* nextBinder;
109 	};
110 
111 	class System
112 	{
113 	public:
114 		/// Return the global list of classes
GetClasses()115 		static const std::vector<Class*>& GetClasses() { return classes; }
116 
117 		/**
118 		 * Initialization of creg, collects all the classes and initializes
119 		 * metadata.
120 		 */
121 		static void InitializeClasses();
122 
123 		/// Shutdown of creg
124 		static void FreeClasses();
125 
126 		/// Find a class by name
127 		static Class* GetClass(const std::string& name);
128 
129 		static void AddClassBinder(ClassBinder* cb);
130 
131 	protected:
132 		static ClassBinder* binderList;
133 		static std::vector<Class*> classes;
134 	};
135 
136 	/**
137 	 * Represents a C++ class or struct, declared with
138 	 * CR_DECLARE or CR_DECLARE_STRUCT.
139 	 */
140 	class Class
141 	{
142 	public:
143 		struct Member
144 		{
145 			const char* name;
146 			boost::shared_ptr<IType> type;
147 			unsigned int offset;
148 			int alignment;
149 			int flags; // combination of ClassMemberFlag's
150 		};
151 
152 		Class();
153 		~Class();
154 
155 		/// Returns true if this class is equal to or derived from other
156 		bool IsSubclassOf(Class* other) const;
157 		void DeleteInstance(void* inst);
158 		/// Allocate an instance of the class
159 		void* CreateInstance();
160 		/// Calculate a checksum from the class metadata
161 		void CalculateChecksum(unsigned int& checksum);
162 		bool AddMember(const char* name, boost::shared_ptr<IType> type, unsigned int offset, int alignment);
163 		bool AddMember(const char* name, IType* type, unsigned int offset, int alignment);
164 		void SetMemberFlag(const char* name, ClassMemberFlag f);
165 		Member* FindMember(const char* name);
166 
167 		void BeginFlag(ClassMemberFlag flag);
168 		void EndFlag(ClassMemberFlag flag);
169 
170 		void SetFlag(ClassFlags flag);
171 
IsAbstract()172 		bool IsAbstract() const { return (binder->flags & CF_Abstract) != 0; }
173 
174 		/// Returns all concrete classes that implement this class
175 		std::vector<Class*> GetImplementations();
176 
177 		std::vector <Member*> members;
178 		/// all classes that derive from this class
179 		std::vector <Class*> derivedClasses;
180 		ClassBinder* binder;
181 		std::string name;
182 		int size;
183 		int alignment;
184 		Class* base;
185 		void (_DummyStruct::*serializeProc)(ISerializer& s);
186 		void (_DummyStruct::*postLoadProc)();
187 
188 		friend class ClassBinder;
189 	};
190 
191 // -------------------------------------------------------------------
192 // Container Type templates
193 // -------------------------------------------------------------------
194 
195 	// vector,deque container
196 	template<typename T>
197 	class DynamicArrayType : public IType
198 	{
199 	public:
200 		typedef typename T::iterator iterator;
201 		typedef typename T::value_type ElemT;
202 
203 		boost::shared_ptr<IType> elemType;
204 
DynamicArrayType(boost::shared_ptr<IType> et)205 		DynamicArrayType(boost::shared_ptr<IType> et)
206 			: elemType(et) {}
~DynamicArrayType()207 		~DynamicArrayType() {}
208 
Serialize(ISerializer * s,void * inst)209 		void Serialize(ISerializer* s, void* inst) {
210 			T& ct = *(T*)inst;
211 			if (s->IsWriting()) {
212 				int size = (int)ct.size();
213 				s->SerializeInt(&size, sizeof(int));
214 				for (int a = 0; a < size; a++) {
215 					elemType->Serialize(s, &ct[a]);
216 				}
217 			} else {
218 				int size;
219 				s->SerializeInt(&size, sizeof(int));
220 				ct.resize(size);
221 				for (int a = 0; a < size; a++) {
222 					elemType->Serialize(s, &ct[a]);
223 				}
224 			}
225 		}
GetName()226 		std::string GetName() { return elemType->GetName() + "[]"; }
GetSize()227 		size_t GetSize() { return sizeof(T); }
228 	};
229 
230 	class StaticArrayBaseType : public IType
231 	{
232 	public:
233 		boost::shared_ptr<IType> elemType;
234 		int size, elemSize;
235 
StaticArrayBaseType(boost::shared_ptr<IType> et,int Size,int ElemSize)236 		StaticArrayBaseType(boost::shared_ptr<IType> et, int Size, int ElemSize)
237 			: elemType(et), size(Size), elemSize(ElemSize) {}
~StaticArrayBaseType()238 		~StaticArrayBaseType() {}
239 
240 		std::string GetName();
GetSize()241 		size_t GetSize() { return size * elemSize; }
242 	};
243 
244 	template<typename T, int Size>
245 	class StaticArrayType : public StaticArrayBaseType
246 	{
247 	public:
248 		typedef T ArrayType[Size];
StaticArrayType(boost::shared_ptr<IType> et)249 		StaticArrayType(boost::shared_ptr<IType> et)
250 			: StaticArrayBaseType(et, Size, sizeof(ArrayType)/Size) {}
Serialize(ISerializer * s,void * instance)251 		void Serialize(ISerializer* s, void* instance)
252 		{
253 			T* array = (T*)instance;
254 			for (int a = 0; a < Size; a++)
255 				elemType->Serialize(s, &array[a]);
256 		}
257 	};
258 
259 	template<typename T>
260 	class BitArrayType : public IType
261 	{
262 	public:
263 		boost::shared_ptr<IType> elemType;
264 
BitArrayType(boost::shared_ptr<IType> et)265 		BitArrayType(boost::shared_ptr<IType> et)
266 			: elemType(et) {}
~BitArrayType()267 		~BitArrayType() {}
268 
Serialize(ISerializer * s,void * inst)269 		void Serialize(ISerializer* s, void* inst) {
270 			T* ct = (T*)inst;
271 			if (s->IsWriting()) {
272 				int size = (int)ct->size();
273 				s->SerializeInt(&size, sizeof(int));
274 				for (int a = 0; a < size; a++) {
275 					bool b = (*ct)[a];
276 					elemType->Serialize(s, &b);
277 				}
278 			} else {
279 				int size;
280 				s->SerializeInt(&size, sizeof(int));
281 				ct->resize(size);
282 				for (int a = 0; a < size; a++) {
283 					bool b;
284 					elemType->Serialize(s, &b);
285 					(*ct)[a] = b;
286 				}
287 			}
288 		}
GetName()289 		std::string GetName() { return elemType->GetName() + "[]"; }
GetSize()290 		size_t GetSize() { return sizeof(T); }
291 	};
292 
293 	class EmptyType : public IType
294 	{
295 	public:
296 		int size;
EmptyType(int Size)297 		EmptyType(int Size) {size=Size;}
~EmptyType()298 		~EmptyType() {}
299 
Serialize(ISerializer * s,void * instance)300 		void Serialize(ISerializer* s, void* instance)
301 		{
302 			for (int a=0;a<size;a++) {
303 				char c=0;
304 				s->Serialize(&c,1);
305 			}
306 		}
GetName()307 		std::string GetName()
308 		{
309 			return "void";
310 		}
GetSize()311 		size_t GetSize() { return 0; /* size*/ } //FIXME used by CR_RESERVED(), ignored by now
312 	};
313 
314 	class IgnoredType : public IType
315 	{
316 	public:
317 		int size;
IgnoredType(int Size)318 		IgnoredType(int Size) {size=Size;}
~IgnoredType()319 		~IgnoredType() {}
320 
Serialize(ISerializer * s,void * instance)321 		void Serialize(ISerializer* s, void* instance)
322 		{
323 			for (int a=0;a<size;a++) {
324 				char c=0;
325 				s->Serialize(&c,1);
326 			}
327 		}
GetName()328 		std::string GetName()
329 		{
330 			return "ignored";
331 		}
GetSize()332 		size_t GetSize() { return size; }
333 	};
334 }
335 
336 #include "TypeDeduction.h"
337 
338 
339 //FIXME: defined cause gcc4.8 still doesn't support c++11's offsetof for non-static members
340 #define offsetof_creg(type, member) (std::size_t)(((char*)&null->member) - ((char*)null))
341 
342 
343 namespace creg {
344 
345 /** @def CR_DECLARE
346  * Add the definitions for creg binding to the class
347  * this should be put within the class definition
348  */
349 #define CR_DECLARE(TCls)	public:					\
350 	static creg::ClassBinder binder;				\
351 	typedef TCls MyType;							\
352 	static creg::IMemberRegistrator* memberRegistrator;	 \
353 	static void _ConstructInstance(void* d);			\
354 	static void _DestructInstance(void* d);			\
355 	friend struct TCls##MemberRegistrator;			\
356 	inline static creg::Class* StaticClass() { return binder.class_; } \
357 	virtual creg::Class* GetClass() const; \
358 	static const bool hasVTable = true;
359 
360 /** @def CR_DECLARE_STRUCT
361  * Use this to declare a structure
362  * this should be put in the class definition, instead of CR_DECLARE
363  * For creg, the only difference between a class and a structure is having a
364  * vtable or not.
365  */
366 #define CR_DECLARE_STRUCT(TStr)		public:			\
367 	static creg::ClassBinder binder;				\
368 	typedef TStr MyType;							\
369 	static creg::IMemberRegistrator* memberRegistrator;	\
370 	static void _ConstructInstance(void* d);			\
371 	static void _DestructInstance(void* d);			\
372 	friend struct TStr##MemberRegistrator;			\
373 	inline static creg::Class* StaticClass() { return binder.class_; } \
374 	creg::Class* GetClass() const; \
375 	static const bool hasVTable = false;
376 
377 /** @def CR_DECLARE_SUB
378  * Use this to declare a sub class. This should be put in the class definition
379  * of the superclass, alongside CR_DECLARE(CSuperClass) (or CR_DECLARE_STRUCT).
380  */
381 #define CR_DECLARE_SUB(cl) \
382 	struct cl##MemberRegistrator;
383 
384 /** @def CR_BIND_DERIVED
385  * Bind a derived class declared with CR_DECLARE to creg
386  * Should be used in the source file
387  * @param TCls class to bind
388  * @param TBase base class of TCls
389  * @param ctor_args constructor arguments
390  */
391 #define CR_BIND_DERIVED(TCls, TBase, ctor_args) \
392 	creg::IMemberRegistrator* TCls::memberRegistrator=0;	\
393 	creg::Class* TCls::GetClass() const { return binder.class_; } \
394 	void TCls::_ConstructInstance(void* d) { new(d) MyType ctor_args; } \
395 	void TCls::_DestructInstance(void* d) { ((MyType*)d)->~MyType(); } \
396 	creg::ClassBinder TCls::binder(#TCls, 0, &TBase::binder, &TCls::memberRegistrator, sizeof(TCls), alignof(TCls), TCls::hasVTable, TCls::_ConstructInstance, TCls::_DestructInstance);
397 
398 /** @def CR_BIND_DERIVED_SUB
399  * Bind a derived class inside another class to creg
400  * Should be used in the source file
401  * @param TSuper class that contains the class to register
402  * @param TCls subclass to bind
403  * @param TBase base class of TCls
404  * @param ctor_args constructor arguments
405  */
406 #define CR_BIND_DERIVED_SUB(TSuper, TCls, TBase, ctor_args) \
407 	creg::IMemberRegistrator* TSuper::TCls::memberRegistrator=0;	 \
408 	creg::Class* TSuper::TCls::GetClass() const { return binder.class_; }  \
409 	void TSuper::TCls::_ConstructInstance(void* d) { new(d) TCls ctor_args; }  \
410 	void TSuper::TCls::_DestructInstance(void* d) { ((TCls*)d)->~TCls(); }  \
411 	creg::ClassBinder TSuper::TCls::binder(#TSuper "::" #TCls, 0, &TBase::binder, &TSuper::TCls::memberRegistrator, sizeof(TSuper::TCls), alignof(TCls), TCls::hasVTable, TSuper::TCls::_ConstructInstance, TSuper::TCls::_DestructInstance);
412 
413 /** @def CR_BIND
414  * Bind a class not derived from CObject
415  * should be used in the source file
416  * @param TCls class to bind
417  * @param ctor_args constructor arguments
418  */
419 #define CR_BIND(TCls, ctor_args) \
420 	creg::IMemberRegistrator* TCls::memberRegistrator=0;	\
421 	creg::Class* TCls::GetClass() const { return binder.class_; } \
422 	void TCls::_ConstructInstance(void* d) { new(d) MyType ctor_args; } \
423 	void TCls::_DestructInstance(void* d) { ((MyType*)d)->~MyType(); } \
424 	creg::ClassBinder TCls::binder(#TCls, 0, 0, &TCls::memberRegistrator, sizeof(TCls), alignof(TCls), TCls::hasVTable, TCls::_ConstructInstance, TCls::_DestructInstance);
425 
426 #ifdef __clang__
427 	// LLVM/Clang expects a different order
428 	#define CR_BIND_TEMPLATE(TCls, ctor_args) \
429 		template<> creg::IMemberRegistrator* TCls::memberRegistrator=0; \
430 		template<> void TCls::_ConstructInstance(void* d) { new(d) MyType ctor_args; } \
431 		template<> void TCls::_DestructInstance(void* d) { ((MyType*)d)->~MyType(); } \
432 		template<> creg::ClassBinder TCls::binder(#TCls, 0, 0, &TCls::memberRegistrator, sizeof(TCls), alignof(TCls), TCls::hasVTable, TCls::_ConstructInstance, TCls::_DestructInstance); \
433 		template<> creg::Class* TCls::GetClass() const { return binder.class_; }
434 #else
435 	#define CR_BIND_TEMPLATE(TCls, ctor_args) \
436 		template<> creg::IMemberRegistrator* TCls::memberRegistrator=0; \
437 		template<> creg::Class* TCls::GetClass() const { return binder.class_; } \
438 		template<> void TCls::_ConstructInstance(void* d) { new(d) MyType ctor_args; } \
439 		template<> void TCls::_DestructInstance(void* d) { ((MyType*)d)->~MyType(); } \
440 		template<> creg::ClassBinder TCls::binder(#TCls, 0, 0, &TCls::memberRegistrator, sizeof(TCls), alignof(TCls), TCls::hasVTable, TCls::_ConstructInstance, TCls::_DestructInstance);
441 #endif
442 
443 /** @def CR_BIND_DERIVED_INTERFACE
444  * Bind an abstract derived class
445  * should be used in the source file
446  * @param TCls abstract class to bind
447  * @param TBase base class of TCls
448  */
449 #define CR_BIND_DERIVED_INTERFACE(TCls, TBase)	\
450 	creg::IMemberRegistrator* TCls::memberRegistrator=0;	\
451 	creg::Class* TCls::GetClass() const { return binder.class_; } \
452 	creg::ClassBinder TCls::binder(#TCls, (unsigned int)creg::CF_Abstract, &TBase::binder, &TCls::memberRegistrator, sizeof(TCls), alignof(TCls), TCls::hasVTable, 0, 0);
453 
454 /** @def CR_BIND_INTERFACE
455  * Bind an abstract class
456  * should be used in the source file
457  * This simply does not register a constructor to creg, so you can bind
458  * non-abstract class as abstract classes as well.
459  * @param TCls abstract class to bind
460  */
461 #define CR_BIND_INTERFACE(TCls)	\
462 	creg::IMemberRegistrator* TCls::memberRegistrator=0;	\
463 	creg::Class* TCls::GetClass() const { return binder.class_; } \
464 	creg::ClassBinder TCls::binder(#TCls, (unsigned int)creg::CF_Abstract, 0, &TCls::memberRegistrator, sizeof(TCls), alignof(TCls), TCls::hasVTable, 0, 0);
465 
466 /** @def CR_REG_METADATA
467  * Binds the class metadata to the class itself
468  * should be used in the source file
469  * @param TClass class to register the info to
470  * @param Members the metadata of the class\n
471  * should consist of a series of single expression of metadata macros\n
472  * for example: (CR_MEMBER(a),CR_POSTLOAD(PostLoadCallback))
473  * @see CR_MEMBER
474  * @see CR_ENUM_MEMBER
475  * @see CR_SERIALIZER
476  * @see CR_POSTLOAD
477  * @see CR_MEMBER_SETFLAG
478  */
479 #define CR_REG_METADATA(TClass, Members)				\
480 	struct TClass##MemberRegistrator : creg::IMemberRegistrator {\
481 	typedef TClass Type;						\
482 	TClass##MemberRegistrator() {				\
483 		Type::memberRegistrator=this;				\
484 	}												\
485 	void RegisterMembers(creg::Class* class_) {		\
486 		Type* null=nullptr;						\
487 		(void)null; /*suppress compiler warning if this isn't used*/	\
488 		Members; }									\
489 	} static TClass##mreg;
490 
491 /** @def CR_REG_METADATA_SUB
492  * Just like CR_REG_METADATA, but for a subclass.
493  *  @see CR_REG_METADATA
494  */
495 #define CR_REG_METADATA_SUB(TSuperClass, TSubClass, Members)				\
496 	struct TSuperClass::TSubClass##MemberRegistrator : creg::IMemberRegistrator {\
497 	typedef TSuperClass::TSubClass Type;						\
498 	TSubClass##MemberRegistrator() {				\
499 		Type::memberRegistrator=this;				\
500 	}												\
501 	void RegisterMembers(creg::Class* class_) { \
502 		Type* null=nullptr; \
503 		(void)null; \
504 		Members; } \
505 	} static TSuperClass##TSubClass##mreg;
506 
507 /** @def CR_MEMBER
508  * Registers a class/struct member variable, of a type that is:
509  * - a struct registered with CR_DECLARE_STRUCT/CR_BIND_STRUCT
510  * - a class registered with CR_DECLARE/CR_BIND*
511  * - an int,short,char,long,double,float or bool, or any of the unsigned
512  *   variants of those
513  * - a std::set/multiset included with STL_Set.h
514  * - a std::list included with STL_List.h
515  * - a std::deque included with STL_Deque.h
516  * - a std::map/multimap included with STL_Map.h
517  * - a std::vector
518  * - a std::string
519  * - an array
520  * - a pointer/reference to a creg registered struct or class instance
521  * For enumerated type members, @see CR_ENUM_MEMBER
522  */
523 #define CR_MEMBER(Member) \
524 	class_->AddMember( #Member, creg::GetType(null->Member), offsetof_creg(Type, Member), alignof(decltype(Type::Member)))
525 
526 /** @def CR_ENUM_MEMBER
527  * Registers a class/struct member variable with an enumerated type
528  */
529 #define CR_ENUM_MEMBER(Member) \
530 	class_->AddMember( #Member, creg::IType::CreateEnumeratedType(sizeof(Type::Member)), offsetof_creg(Type, Member), alignof(decltype(Type::Member)))
531 
532 /** @def CR_IGNORED
533  * Registers a member variable that isn't saved/loaded
534  */
535 #define CR_IGNORED(Member) \
536 	class_->AddMember( #Member, new creg::IgnoredType(sizeof(Type::Member)), offsetof_creg(Type, Member), alignof(decltype(Type::Member)))
537 
538 
539 /** @def CR_MEMBER_UN
540  * Registers a member variable that is unsynced.
541  * It may be saved depending on the purpose.
542  * Currently works as CR_IGNORED.
543  */
544 #define CR_MEMBER_UN(Member) \
545     CR_IGNORED( Member )
546 
547 
548 /** @def CR_RESERVED
549  *  @author Victor Muraviev
550  * Registers a unused space for compatibility
551  * Size = 1:
552  * - char, synced char, bool, synced bool
553  * - pointer
554  * - enum
555  * Size = 2:
556  * - short, synced short
557  * - enum
558  * Size = 4:
559  * - int, synced int, long, synced long, float, synced float
560  * - std::set/multiset
561  * - std::list
562  * - std::deque
563  * - std::map/multimap
564  * - std::vector
565  * - std::string
566  * - enum
567  * Size = 8:
568  * - double, synced double
569  */
570 #define CR_RESERVED(Size) \
571 	class_->AddMember("Reserved", new creg::EmptyType(Size), 0, 0)
572 
573 /** @def CR_SETFLAG
574  * Set a flag for a class/struct.
575  * @param Flag the class flag @see ClassFlag
576  */
577 #define CR_SETFLAG(Flag) \
578 	class_->SetFlag(creg::Flag)
579 
580 /** @def CR_MEMBER_SETFLAG
581  * Set a flag for a class/struct member
582  * This should come after the CR_MEMBER or CR_ENUM_MEMBER for the member
583  * @param Member the class member variable
584  * @param Flag the class member flag @see ClassMemberFlag
585  * @see ClassMemberFlag
586  */
587 #define CR_MEMBER_SETFLAG(Member, Flag) \
588 	class_->SetMemberFlag(#Member, creg::Flag)
589 
590 #define CR_MEMBER_BEGINFLAG(Flag) \
591 	class_->BeginFlag(creg::Flag)
592 
593 #define CR_MEMBER_ENDFLAG(Flag) \
594 	class_->EndFlag(creg::Flag)
595 
596 /** @def CR_SERIALIZER
597  * Registers a custom serialization method for the class/struct
598  * this function will be called when an instance is serialized.
599  * There can only be one serialize method per class/struct.
600  * On serialization, the registered members will be serialized first,
601  * and then this function will be called if specified
602  *
603  * @param SerializeFunc the serialize method, should be a member function of the
604  *   class
605  */
606 #define CR_SERIALIZER(SerializeFunc) \
607 	(class_->serializeProc = (void(creg::_DummyStruct::*)(creg::ISerializer&))&Type::SerializeFunc)
608 
609 /** @def CR_POSTLOAD
610  * Registers a custom post-loading method for the class/struct
611  * this function will be called during package loading when all serialization is
612  * finished.
613  * There can only be one postload method per class/struct
614  */
615 #define CR_POSTLOAD(PostLoadFunc) \
616 	(class_->postLoadProc = (void(creg::_DummyStruct::*)())&Type::PostLoadFunc)
617 }
618 
619 #endif // _CREG_H
620