From 000ca1c6d95a2d316d2f071ba4aa73599362e86b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9o=20Lam?= Date: Wed, 5 Jan 2022 23:55:54 +0100 Subject: [PATCH] Havok: Add hkClass and other reflection types --- lib/hkStubs/CMakeLists.txt | 5 + .../Havok/Common/Base/Reflection/hkClass.h | 114 ++++++++++ .../Common/Base/Reflection/hkClassEnum.h | 79 +++++++ .../Common/Base/Reflection/hkClassMember.h | 195 ++++++++++++++++++ .../Havok/Common/Base/Reflection/hkTypeInfo.h | 91 ++++++++ .../Havok/Common/Base/Types/hkBaseDefs.h | 2 + .../Havok/Common/Base/Types/hkBaseTypes.h | 61 +++--- 7 files changed, 523 insertions(+), 24 deletions(-) create mode 100644 lib/hkStubs/Havok/Common/Base/Reflection/hkClass.h create mode 100644 lib/hkStubs/Havok/Common/Base/Reflection/hkClassEnum.h create mode 100644 lib/hkStubs/Havok/Common/Base/Reflection/hkClassMember.h create mode 100644 lib/hkStubs/Havok/Common/Base/Reflection/hkTypeInfo.h diff --git a/lib/hkStubs/CMakeLists.txt b/lib/hkStubs/CMakeLists.txt index efa96f49..06f6380f 100644 --- a/lib/hkStubs/CMakeLists.txt +++ b/lib/hkStubs/CMakeLists.txt @@ -21,6 +21,11 @@ add_library(hkStubs OBJECT Havok/Common/Base/Object/hkReferencedObject.cpp Havok/Common/Base/Object/hkReferencedObject.h + Havok/Common/Base/Reflection/hkClass.h + Havok/Common/Base/Reflection/hkClassEnum.h + Havok/Common/Base/Reflection/hkClassMember.h + Havok/Common/Base/Reflection/hkTypeInfo.h + Havok/Common/Base/Thread/Atomic/hkAtomicPrimitives.h Havok/Common/Base/Thread/Thread/hkThreadLocalData.h diff --git a/lib/hkStubs/Havok/Common/Base/Reflection/hkClass.h b/lib/hkStubs/Havok/Common/Base/Reflection/hkClass.h new file mode 100644 index 00000000..be1632d0 --- /dev/null +++ b/lib/hkStubs/Havok/Common/Base/Reflection/hkClass.h @@ -0,0 +1,114 @@ +#pragma once + +#include + +class hkClassEnum; +class hkClassMember; +class hkCustomAttributes; +class hkStreamWriter; +class hkTypedUnion; + +template +class hkPointerMap; + +class hkClass { +public: + HK_DECLARE_CLASS_ALLOCATOR(hkClass) + HK_DECLARE_REFLECTION() + + enum SignatureFlags { + SIGNATURE_LOCAL = 1, + }; + + enum FlagValues { + FLAGS_NONE = 0, + FLAGS_NOT_SERIALIZABLE = 1, + }; + using Flags = hkFlags; + + // Note: this is not constexpr and this is the reason Havok code is littered with + // static constructor functions that call this hkClass constructor at runtime + // even though everything could have been initialised at compile time. + hkClass(const char* className, const hkClass* parentClass, int objectSizeInBytes, + const hkClass** implementedInterfaces, int numImplementedInterfaces, + const hkClassEnum* declaredEnums, int numDeclaredEnums, + const hkClassMember* declaredMembers, int numDeclaredMembers, + const void* defaults = nullptr, const hkCustomAttributes* attributes = nullptr, + hkUint32 flags = 0, hkUint32 version = 0); + + const char* getName() const; + bool equals(const hkClass* other) const; + + const hkClass* getParent() const; + hkClass* getParent(); + int getInheritanceDepth() const; + hkBool isSuperClass(const hkClass& k) const; + const hkClass* getInterface(int i) const; + int getNumInterfaces() const; + const hkClass* getDeclaredInterface(int i) const; + int getNumDeclaredInterfaces() const; + + const hkClassEnum& getEnum(int i) const; + const hkClassEnum* getEnumByName(const char* name) const; + int getNumEnums() const; + const hkClassEnum& getDeclaredEnum(int i) const; + const hkClassEnum* getDeclaredEnumByName(const char* name) const; + int getNumDeclaredEnums() const; + + const hkClassMember& getMember(int i) const; + hkClassMember& getMember(int i); + const hkClassMember* getMemberByName(const char* name) const; + int getMemberIndexByName(const char* name) const; + int getMemberIndexByNameCaseInsensitive(const char* name) const; + int getNumMembers() const; + const hkClassMember& getDeclaredMember(int i) const; + const hkClassMember* getDeclaredMemberByName(const char* name) const; + int getDeclaredMemberIndexByName(const char* name) const; + int getNumDeclaredMembers() const; + + int getObjectSize() const; + void setObjectSize(int size); + + hkBool hasVtable() const; + + hkBool32 hasDefault(int memberIndex) const; + hkBool32 hasDeclaredDefault(int declaredIndex) const; + hkResult getDefault(int memberIndex, hkStreamWriter* w) const; + const void* getDefault(int memberIndex) const; + hkResult getDeclaredDefault(int declaredIndex, hkStreamWriter* w) const; + hkResult getDefault(int memberIndex, hkTypedUnion& value) const; + const void* getDeclaredDefault(int memberIndex) const; + hkResult getDeclaredDefault(int declaredIndex, hkTypedUnion& value) const; + + hkUint32 getSignature(int signatureFlags = 0) const; + int getDescribedVersion() const; + void writeSignature(hkStreamWriter* w) const; + + const hkVariant* getAttribute(const char* id) const; + + const Flags& getFlags() const; + Flags& getFlags(); + + using UpdateFlagFromClassMap = hkPointerMap; + static void updateMetadataInplace(hkClass** c, int sourceVersion); + static void updateMetadataInplace(hkClass* c, UpdateFlagFromClassMap& updatedAlready, + int sourceVersion); + +private: + hkResult retrieveMember(int memberIndex, const void*& defaultOut, + const hkClassMember*& memberOut) const; + +protected: + const char* m_name; + const hkClass* m_parent; + int m_objectSize; + int m_numImplementedInterfaces; + const class hkClassEnum* m_declaredEnums; + int m_numDeclaredEnums; + const class hkClassMember* m_declaredMembers; + int m_numDeclaredMembers; + const void* m_defaults; + const hkCustomAttributes* m_attributes; + Flags m_flags; + int m_describedVersion; +}; diff --git a/lib/hkStubs/Havok/Common/Base/Reflection/hkClassEnum.h b/lib/hkStubs/Havok/Common/Base/Reflection/hkClassEnum.h new file mode 100644 index 00000000..a0836be3 --- /dev/null +++ b/lib/hkStubs/Havok/Common/Base/Reflection/hkClassEnum.h @@ -0,0 +1,79 @@ +#pragma once + +#include + +class hkCustomAttributes; +class hkStreamWriter; + +class hkClassEnum { +public: + HK_DECLARE_CLASS_ALLOCATOR(hkClassEnum) + HK_DECLARE_REFLECTION() + + class Item { + public: + HK_DECLARE_CLASS_ALLOCATOR(Item) + HK_DECLARE_REFLECTION() + + constexpr Item(int v, const char* n) : m_value(v), m_name(n) {} + + const char* getName() const { return m_name; } + int getValue() const { return m_value; } + + private: + int m_value; + const char* m_name; + }; + + enum FlagValues { FLAGS_NONE = 0 }; + using Flags = hkFlags; + + hkClassEnum(const char* name, const Item* items, int numItems); + + constexpr hkClassEnum(const char* name, const Item* items, int numItems, + hkCustomAttributes* attributes, hkUint32 flags) + : m_name(name), m_items(items), m_numItems(numItems), m_attributes(attributes), + m_flags(flags) {} + + const char* getName() const; + int getNumItems() const; + const hkClassEnum::Item& getItem(int i) const; + hkResult getNameOfValue(int val, const char** name) const; + hkResult getValueOfName(const char* name, int* val) const; + hkResult decomposeFlags(int flagValue, hkArray& bitsOut, int& bitsOver) const; + hkUint32 getSignature() const; + void writeSignature(hkStreamWriter* w) const; + const hkVariant* getAttribute(const char* name) const; + inline const Flags& getFlags() const; + inline Flags& getFlags(); + +private: + const char* m_name; + const class Item* m_items; + int m_numItems; + hkCustomAttributes* m_attributes; + Flags m_flags; +}; + +inline hkClassEnum::hkClassEnum(const char* name, const hkClassEnum::Item* items, int numItems) + : m_name(name), m_items(items), m_numItems(numItems), m_attributes(nullptr), m_flags(0) {} + +inline const char* hkClassEnum::getName() const { + return m_name; +} + +inline int hkClassEnum::getNumItems() const { + return m_numItems; +} + +inline const hkClassEnum::Item& hkClassEnum::getItem(int i) const { + return m_items[i]; +} + +inline const hkClassEnum::Flags& hkClassEnum::getFlags() const { + return m_flags; +} + +inline hkClassEnum::Flags& hkClassEnum::getFlags() { + return m_flags; +} diff --git a/lib/hkStubs/Havok/Common/Base/Reflection/hkClassMember.h b/lib/hkStubs/Havok/Common/Base/Reflection/hkClassMember.h new file mode 100644 index 00000000..95834fe3 --- /dev/null +++ b/lib/hkStubs/Havok/Common/Base/Reflection/hkClassMember.h @@ -0,0 +1,195 @@ +#pragma once + +#include + +class hkClassEnum; +class hkCustomAttributes; + +class hkClassMember { +public: + HK_DECLARE_CLASS_ALLOCATOR(hkClassMember) + HK_DECLARE_REFLECTION() + + enum Type { + TYPE_VOID = 0, + TYPE_BOOL, + TYPE_CHAR, + TYPE_INT8, + TYPE_UINT8, + TYPE_INT16, + TYPE_UINT16, + TYPE_INT32, + TYPE_UINT32, + TYPE_INT64, + TYPE_UINT64, + TYPE_REAL, + TYPE_VECTOR4, + TYPE_QUATERNION, + TYPE_MATRIX3, + TYPE_ROTATION, + TYPE_QSTRANSFORM, + TYPE_MATRIX4, + TYPE_TRANSFORM, + TYPE_ZERO, + TYPE_POINTER, + TYPE_FUNCTIONPOINTER, + /// hkArray + TYPE_ARRAY, + /// hkInplaceArray or hkInplaceArrayAligned16 + TYPE_INPLACEARRAY, + /// hkEnum + TYPE_ENUM, + TYPE_STRUCT, + TYPE_SIMPLEARRAY, + TYPE_HOMOGENEOUSARRAY, + TYPE_VARIANT, + TYPE_CSTRING, + TYPE_ULONG, + TYPE_FLAGS, + TYPE_HALF, + /// hkStringPtr + TYPE_STRINGPTR, + /// hkRelArray + TYPE_RELARRAY, + TYPE_MAX + }; + + enum FlagValues { + FLAGS_NONE = 0, + ALIGN_8 = 1 << 7, + ALIGN_16 = 1 << 8, + NOT_OWNED = 1 << 9, + SERIALIZE_IGNORED = 1 << 10, + ALIGN_32 = 1 << 11, +#ifdef HK_REAL_IS_DOUBLE + ALIGN_REAL = ALIGN_32, +#else + ALIGN_REAL = ALIGN_16, +#endif + }; + using Flags = hkFlags; + + enum DeprecatedFlagValues { + DEPRECATED_SIZE_8 = 8, + DEPRECATED_ENUM_8 = 8, + DEPRECATED_SIZE_16 = 16, + DEPRECATED_ENUM_16 = 16, + DEPRECATED_SIZE_32 = 32, + DEPRECATED_ENUM_32 = 32, + }; + + enum { + HK_CLASS_ZERO_DEFAULT = -2, + }; + + constexpr hkClassMember(const char* name, const hkClass* class_, const hkClassEnum* enum_, + const hkEnum& type, const hkEnum& subtype, + hkInt16 cArraySize, hkUint16 flags, hkUint16 offset, + const hkCustomAttributes* attributes) + : m_name(name), m_class(class_), m_enum(enum_), m_type(type), m_subtype(subtype), + m_cArraySize(cArraySize), m_flags(flags), m_offset(offset), m_attributes(attributes) {} + + inline const char* getName() const; + + inline hkClassMember::Type getType() const; + inline void setType(hkClassMember::Type type); + + inline hkClassMember::Type getSubType() const; + inline void setSubType(hkClassMember::Type subtype); + + hkClassMember::Type getArrayType() const; + + int getSizeInBytes() const; + int getAlignment() const; + hkBool isNotOwner() const; + int getTypeName(char* buf, int bufLen) const; + int getArrayMemberSize() const; + + inline hkBool hasClass() const; + const hkClass& getStructClass() const; + const hkClass* getClass() const; + inline hkBool hasEnumClass() const; + const hkClassEnum& getEnumClass() const; + + int getCstyleArraySize() const; + + const hkClassEnum& getEnumType() const; + int getEnumValue(const void* memberAddress) const; + void setEnumValue(void* memberAddress, int value) const; + + inline int getOffset() const; + inline void setOffset(int offset); + + inline const Flags& getFlags() const; + inline Flags& getFlags(); + + const hkVariant* getAttribute(const char* id) const; + + static hkClassMember::Type getTypeOf(const char* name); + static hkClassMember::Type getSubtypeOf(const char* name); + + struct TypeProperties { + HK_DECLARE_CLASS_ALLOCATOR(hkClassMember::TypeProperties) + + hkEnum m_type; + const char* m_name; + short m_size; + short m_align; + }; + static const TypeProperties& getClassMemberTypeProperties(Type type); + +private: + const char* m_name; + const hkClass* m_class; + const hkClassEnum* m_enum; + hkEnum m_type; + hkEnum m_subtype; + hkInt16 m_cArraySize; + Flags m_flags; + hkUint16 m_offset; + const hkCustomAttributes* m_attributes; +}; + +inline const char* hkClassMember::getName() const { + return m_name; +} + +inline hkClassMember::Type hkClassMember::getType() const { + return static_cast(m_type); +} + +inline void hkClassMember::setType(hkClassMember::Type type) { + m_type = type; +} + +inline hkClassMember::Type hkClassMember::getSubType() const { + return static_cast(m_subtype); +} + +inline void hkClassMember::setSubType(hkClassMember::Type subtype) { + m_subtype = subtype; +} + +inline int hkClassMember::getOffset() const { + return m_offset; +} + +inline void hkClassMember::setOffset(int offset) { + m_offset = static_cast(offset); +} + +inline const hkClassMember::Flags& hkClassMember::getFlags() const { + return m_flags; +} + +inline hkClassMember::Flags& hkClassMember::getFlags() { + return m_flags; +} + +inline hkBool hkClassMember::hasClass() const { + return m_class != nullptr; +} + +inline hkBool hkClassMember::hasEnumClass() const { + return m_enum != nullptr; +} diff --git a/lib/hkStubs/Havok/Common/Base/Reflection/hkTypeInfo.h b/lib/hkStubs/Havok/Common/Base/Reflection/hkTypeInfo.h new file mode 100644 index 00000000..c8637a44 --- /dev/null +++ b/lib/hkStubs/Havok/Common/Base/Reflection/hkTypeInfo.h @@ -0,0 +1,91 @@ +#pragma once + +#include +#include +#include + +class hkClass; + +class hkTypeInfo { +public: + HK_DECLARE_CLASS_ALLOCATOR(hkTypeInfo) + + using FinishLoadedObjectFunction = void (*)(void*, int); + using CleanupLoadedObjectFunction = void (*)(void*); + + template + HK_ALWAYS_INLINE static hkTypeInfo make(const char* name, const char* scopedName) { + FinishLoadedObjectFunction finish = nullptr; + constexpr bool needsFinish = std::is_constructible_v; + if constexpr (needsFinish) + finish = finishFunctionImpl; + + CleanupLoadedObjectFunction cleanup = cleanupFunctionImpl; + + const void* vtable = nullptr; + if constexpr (std::is_polymorphic_v) { + static_assert(needsFinish, "polymorphic types must have a finish constructor"); + vtable = getVtableFunctionImpl(); + } + + return hkTypeInfo{name, scopedName, finish, cleanup, vtable, sizeof(T)}; + } + + hkTypeInfo(const char* name, const char* scopedName, FinishLoadedObjectFunction finish, + CleanupLoadedObjectFunction cleanup, const void* vtable, hk_size_t size) + : m_typeName(name), m_scopedName(scopedName), m_finishLoadedObjectFunction(finish), + m_cleanupLoadedObjectFunction(cleanup), m_vtable(vtable), m_size(size) {} + + const char* getTypeName() const { return m_typeName; } + const char* getScopedName() const { return m_scopedName; } + const void* getVtable() const { return m_vtable; } + void cleanupLoadedObject(void* ptr) const; + void finishLoadedObject(void* ptr, int finishFlag) const; + void finishLoadedObjectWithoutTracker(void* ptr, int finishFlag) const; + + hkBool hasFinishFunction() const { return m_finishLoadedObjectFunction != nullptr; } + hkBool hasCleanupFunction() const { return m_cleanupLoadedObjectFunction != nullptr; } + hk_size_t getSize() const { return m_size; } + hkBool isVirtual() const { return m_vtable != nullptr; } + +private: + template + HK_VISIBILITY_HIDDEN static void finishFunctionImpl(void* p, int finishing) { + hkFinishLoadedObjectFlag flag{finishing}; + new (p) T{flag}; + } + + template + HK_VISIBILITY_HIDDEN static void cleanupFunctionImpl(void* p) { + static_cast(p)->~T(); + } + + template + HK_VISIBILITY_HIDDEN static const void* getVtableFunctionImpl() { + // The following trick only works because all Havok polymorphic types are supposed + // to have hkBaseObject as the most-base class -- forcing the vtable to be at offset 0. + // + // Unfortunately we can't check if hkBaseObject is at offset 0, but let's at least + // verify that hkBaseObject is a base class of T. + static_assert(std::is_base_of_v, + "polymorphic types must have hkBaseObject as a base"); + + union { + alignas(16) void* dummy; + std::aligned_storage_t obj; + }; + hkFinishLoadedObjectFlag flag; + new (static_cast(std::addressof(obj))) T{flag}; + + const void* vtable_ptr; + std::memcpy(&vtable_ptr, &obj, sizeof(vtable_ptr)); + return vtable_ptr; + } + + const char* m_typeName; + const char* m_scopedName; + FinishLoadedObjectFunction m_finishLoadedObjectFunction; + CleanupLoadedObjectFunction m_cleanupLoadedObjectFunction; + const void* m_vtable; + hk_size_t m_size; +}; diff --git a/lib/hkStubs/Havok/Common/Base/Types/hkBaseDefs.h b/lib/hkStubs/Havok/Common/Base/Types/hkBaseDefs.h index c8e55687..de378d7a 100644 --- a/lib/hkStubs/Havok/Common/Base/Types/hkBaseDefs.h +++ b/lib/hkStubs/Havok/Common/Base/Types/hkBaseDefs.h @@ -3,3 +3,5 @@ #define HK_FORCE_INLINE inline #define HK_ALWAYS_INLINE HK_FORCE_INLINE #define HK_NEVER_INLINE __attribute__((noinline)) + +#define HK_VISIBILITY_HIDDEN __attribute__((visibility("hidden"))) diff --git a/lib/hkStubs/Havok/Common/Base/Types/hkBaseTypes.h b/lib/hkStubs/Havok/Common/Base/Types/hkBaseTypes.h index 8d20c094..a3e31a26 100644 --- a/lib/hkStubs/Havok/Common/Base/Types/hkBaseTypes.h +++ b/lib/hkStubs/Havok/Common/Base/Types/hkBaseTypes.h @@ -7,6 +7,7 @@ using hkFloat32 = float; using hkDouble64 = double; using hkReal = hkFloat32; +#define HK_REAL_IS_FLOAT using hkChar = char; using hkInt8 = std::int8_t; @@ -75,17 +76,17 @@ class hkBool { public: HK_ALWAYS_INLINE hkBool() = default; // NOLINTNEXTLINE(google-explicit-constructor) - HK_FORCE_INLINE hkBool(bool b) : m_bool(static_cast(b)) {} + HK_FORCE_INLINE constexpr hkBool(bool b) : m_bool(static_cast(b)) {} - HK_FORCE_INLINE explicit operator bool() const { return m_bool != 0; } + HK_FORCE_INLINE constexpr explicit operator bool() const { return m_bool != 0; } - HK_FORCE_INLINE hkBool& operator=(bool e) { + HK_FORCE_INLINE constexpr hkBool& operator=(bool e) { m_bool = static_cast(e); return *this; } - HK_FORCE_INLINE hkBool operator==(bool e) const { return (m_bool != 0) == e; } - HK_FORCE_INLINE hkBool operator!=(bool e) const { return (m_bool != 0) != e; } + HK_FORCE_INLINE constexpr hkBool operator==(bool e) const { return (m_bool != 0) == e; } + HK_FORCE_INLINE constexpr hkBool operator!=(bool e) const { return (m_bool != 0) != e; } private: char m_bool; @@ -100,18 +101,19 @@ template struct hkEnum { HK_ALWAYS_INLINE hkEnum() {} - hkEnum(Enum value) { *this = value; } // NOLINT(google-explicit-constructor) + constexpr hkEnum(Enum value) // NOLINT(google-explicit-constructor) + : m_storage(static_cast(value)) {} // NOLINTNEXTLINE(google-explicit-constructor) - operator Enum() const { return static_cast(m_storage); } + constexpr operator Enum() const { return static_cast(m_storage); } - hkEnum& operator=(Enum value) { + constexpr hkEnum& operator=(Enum value) { m_storage = static_cast(value); return *this; } - bool operator==(Enum e) const { return m_storage == static_cast(e); } - bool operator!=(Enum e) const { return m_storage != static_cast(e); } + constexpr bool operator==(Enum e) const { return m_storage == static_cast(e); } + constexpr bool operator!=(Enum e) const { return m_storage != static_cast(e); } Storage m_storage; }; @@ -120,29 +122,31 @@ template class hkFlags { public: HK_FORCE_INLINE hkFlags() {} - HK_FORCE_INLINE explicit hkFlags(Storage s) : m_storage(s) {} + HK_FORCE_INLINE constexpr explicit hkFlags(Storage s) : m_storage(s) {} - HK_FORCE_INLINE void clear() { m_storage = 0; } - HK_FORCE_INLINE void clear(Storage mask) { m_storage &= ~mask; } - HK_FORCE_INLINE void setAll(Storage s) { m_storage = s; } + HK_FORCE_INLINE constexpr void clear() { m_storage = 0; } + HK_FORCE_INLINE constexpr void clear(Storage mask) { m_storage &= ~mask; } + HK_FORCE_INLINE constexpr void setAll(Storage s) { m_storage = s; } - HK_FORCE_INLINE void operator|=(Storage s) { m_storage |= s; } - HK_FORCE_INLINE void operator^=(Storage s) { m_storage ^= s; } - HK_FORCE_INLINE void operator&=(Storage s) { m_storage &= s; } + HK_FORCE_INLINE constexpr void operator|=(Storage s) { m_storage |= s; } + HK_FORCE_INLINE constexpr void operator^=(Storage s) { m_storage ^= s; } + HK_FORCE_INLINE constexpr void operator&=(Storage s) { m_storage &= s; } - HK_FORCE_INLINE void setWithMask(Storage s, Storage mask) { + HK_FORCE_INLINE constexpr void setWithMask(Storage s, Storage mask) { m_storage = (m_storage & ~mask) | (s & mask); } - HK_FORCE_INLINE Storage get() const { return m_storage; } - HK_FORCE_INLINE bool anyIsSet(Storage mask) const { return (m_storage & mask) != 0; } - HK_FORCE_INLINE bool noneIsSet(Storage mask) const { return (m_storage & mask) == 0; } - HK_FORCE_INLINE bool allAreSet(Storage mask) const { return (m_storage & mask) == mask; } + HK_FORCE_INLINE constexpr Storage get() const { return m_storage; } + HK_FORCE_INLINE constexpr bool anyIsSet(Storage mask) const { return (m_storage & mask) != 0; } + HK_FORCE_INLINE constexpr bool noneIsSet(Storage mask) const { return (m_storage & mask) == 0; } + HK_FORCE_INLINE constexpr bool allAreSet(Storage mask) const { + return (m_storage & mask) == mask; + } - HK_FORCE_INLINE bool operator==(const hkFlags& other) const { + HK_FORCE_INLINE constexpr bool operator==(const hkFlags& other) const { return other.m_storage == m_storage; } - HK_FORCE_INLINE bool operator!=(const hkFlags& other) const { + HK_FORCE_INLINE constexpr bool operator!=(const hkFlags& other) const { return other.m_storage != m_storage; } @@ -187,3 +191,12 @@ public: HK_FORCE_INLINE hkLong hkGetByteOffset(const void* base, const void* pntr) { return hkLong(pntr) - hkLong(base); } + +class hkClass; + +struct hkVariant { + void* m_object; + const hkClass* m_class; +}; + +#define HK_DECLARE_REFLECTION() static const hkClass& staticClass();