├── .gitignore ├── CMakeLists.txt ├── GenerateClangMakefile.sh ├── GenerateGCCMakefile.bat ├── GenerateGCCMakefile.sh ├── GenerateVisualStudioProject.bat ├── LICENSE.txt ├── README.md └── Source ├── .clang-format ├── KCL ├── KCL_Platform.h ├── KCL_RTTI.h └── KCL_Utils_Preprocessor.h └── KCL_Test ├── KCL_RTTI_Test.cpp ├── KCL_RTTI_Test.h └── main.cpp /.gitignore: -------------------------------------------------------------------------------- 1 | #Ignore cmake generated files 2 | Build/ 3 | CMakeFiles/ 4 | CMakeCache.txt 5 | Makefile 6 | cmake_install.cmake 7 | 8 | #Ignore build artifacts 9 | Binaries/ 10 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.10) 2 | project(KCL) 3 | 4 | file(GLOB_RECURSE SOURCES "Source/*") 5 | 6 | set_property(GLOBAL PROPERTY USE_FOLDERS ON) 7 | 8 | file(GLOB_RECURSE SOURCE_CODE RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "Source/*") 9 | 10 | foreach(SOURCE IN LISTS SOURCE_CODE) 11 | get_filename_component(SOURCE_PATH "${SOURCE}" PATH) 12 | string(REPLACE "/" "\\" SOURCE_PATH_MSVC "${SOURCE_PATH}") 13 | source_group("${SOURCE_PATH_MSVC}" FILES "${SOURCE}") 14 | endforeach() 15 | 16 | add_executable(KCL WIN32 ${SOURCES}) 17 | 18 | target_include_directories(KCL PRIVATE Source/) 19 | 20 | set_property(TARGET KCL PROPERTY CXX_STANDARD 17) 21 | set_target_properties(KCL PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}/Binaries) 22 | set_target_properties(KCL PROPERTIES LINKER_LANGUAGE CXX) 23 | 24 | if(CMAKE_CXX_COMPILER_ID MATCHES "MSVC") 25 | 26 | set_target_properties(KCL PROPERTIES LINK_FLAGS /SUBSYSTEM:CONSOLE) 27 | 28 | endif(CMAKE_CXX_COMPILER_ID MATCHES "MSVC") 29 | 30 | -------------------------------------------------------------------------------- /GenerateClangMakefile.sh: -------------------------------------------------------------------------------- 1 | mkdir -p Build 2 | cd Build 3 | cmake -D CMAKE_C_COMPILER=clang-9 -D CMAKE_CXX_COMPILER=clang++-9 -D CMAKE_CXX_FLAGS="-O3" .. 4 | -------------------------------------------------------------------------------- /GenerateGCCMakefile.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | setlocal 3 | 4 | set BUILD_DIR=%~dp0Build 5 | 6 | if not exist %BUILD_DIR% ( 7 | mkdir %BUILD_DIR% 8 | cd %BUILD_DIR% 9 | cmake -G "MinGW Makefiles" -D CMAKE_C_COMPILER=mingw32-gcc -D CMAKE_CXX_COMPILER=mingw32-g++ -D CMAKE_CXX_FLAGS="-mconsole -O3" .. 10 | ) else ( 11 | cd %BUILD_DIR% 12 | cmake --debug-outputs .. 13 | ) 14 | 15 | pause -------------------------------------------------------------------------------- /GenerateGCCMakefile.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | mkdir -p Build 4 | cd Build 5 | cmake -D CMAKE_C_COMPILER=gcc -D CMAKE_CXX_COMPILER=g++ -D CMAKE_CXX_FLAGS="-O3" .. 6 | -------------------------------------------------------------------------------- /GenerateVisualStudioProject.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | setlocal 3 | 4 | set BUILD_DIR=%~dp0Build 5 | 6 | if not exist %BUILD_DIR% ( 7 | mkdir %BUILD_DIR% 8 | cd %BUILD_DIR% 9 | cmake -G "Visual Studio 15 2017 Win64" .. 10 | ) else ( 11 | cd %BUILD_DIR% 12 | cmake --debug-outputs .. 13 | ) 14 | 15 | pause -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Samuel Kahn 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # KCL - Kahncode Core Library 2 | 3 | A suite of C++ core constructs and idioms that may or may not be useful to you. 4 | 5 | Every part of KCL will be released following an article on http://kahncode.com/ 6 | 7 | KCL is open-source and released under the license found in the LICENSE file at the root of the repository. 8 | Contributions and comments are welcome. Please use GitHub (https://github.com/Kahncode/kcl) or contact me directly (samuel@kahncode.com). 9 | 10 | KCL is concieved from the point of view of a game developer, and therefore the solutions offered may be more opinionated than the standard. 11 | KCL focuses on speed, performance, modernity and expressivity of the code. 12 | Best practices of game development apply. In order to allow a consistent behavior across all platforms and compilers, it avoids usage of the standard library as much as possible. Some features of C++ are intentionally not supported, such as RTTI or exceptions, as they are generally considered bad practice in game development. 13 | KCL stays within what the c++ language can do without external tooling. Third-Party dependencies, code generation or external processing are therefore excluded. 14 | KCL strives to support the lowest common denominator for game development. Currently the target is set to Visual Studio 2017 (MSVC Toolset v141) as all other compilers for relevant game development platforms are more standard compliant. 15 | 16 | KCL is not intended to be production code, or to be included as-is into a project. This is merely a showcase. In order to use it properly, you should integrate, customize and extend it to fit your specific needs. 17 | 18 | I hope that it is helpful to the game development and the c++ community at large. 19 | 20 | ## How to use 21 | 22 | - Install CMake: https://cmake.org/ 23 | - Make sure to add CMake to the PATH during installation 24 | - (Windows) Run GenerateVisualStudioProject.bat 25 | - (Bash) Run GenerateMakeFile.sh 26 | - (Other) Use CMake and your preffered build system. Please contribute your changes to the CMake files are I am certain it can be improved. 27 | -------------------------------------------------------------------------------- /Source/.clang-format: -------------------------------------------------------------------------------- 1 | #clang-format file for KCL, currently supporting clang-format version 6 2 | --- 3 | Language: Cpp 4 | BasedOnStyle: LLVM 5 | AccessModifierOffset: -4 6 | AlignAfterOpenBracket: DontAlign 7 | AlignConsecutiveAssignments: false 8 | AlignConsecutiveDeclarations: false 9 | AlignEscapedNewlines: Right 10 | AlignOperands: false 11 | AlignTrailingComments: true 12 | AllowAllParametersOfDeclarationOnNextLine: true 13 | AllowShortBlocksOnASingleLine: false 14 | AllowShortCaseLabelsOnASingleLine: true 15 | AllowShortFunctionsOnASingleLine: Inline 16 | AllowShortIfStatementsOnASingleLine: false 17 | AllowShortLoopsOnASingleLine: false 18 | AlwaysBreakAfterReturnType: None 19 | AlwaysBreakBeforeMultilineStrings: true 20 | AlwaysBreakTemplateDeclarations: true # clang 7 Use Multiline in newer clang-format 21 | BinPackArguments: true 22 | BinPackParameters: true 23 | BreakBeforeBinaryOperators: All 24 | BreakBeforeBraces: Allman # Could use a custom style to avoid breaking for empty classes 25 | BreakBeforeInheritanceComma: false 26 | BreakBeforeTernaryOperators: true 27 | BreakConstructorInitializers: BeforeComma 28 | BreakStringLiterals: false 29 | ColumnLimit: 140 30 | CompactNamespaces: false 31 | ConstructorInitializerAllOnOneLineOrOnePerLine: true 32 | ConstructorInitializerIndentWidth: 4 33 | ContinuationIndentWidth: 4 34 | Cpp11BracedListStyle: true 35 | DerivePointerAlignment: false 36 | DisableFormat: false 37 | ExperimentalAutoDetectBinPacking: false 38 | FixNamespaceComments: true 39 | ForEachMacros: [ foreach, Q_FOREACH, BOOST_FOREACH ] 40 | IncludeBlocks: Regroup 41 | IncludeCategories: 42 | - Regex: '.*(PCH).*' 43 | Priority: -2 44 | - Regex: 'CoreMinimal.h' 45 | Priority: -2 46 | - Regex: '.*\.generated\.h' # Unreal requires ".generated.h" files to be the last include in any header file 47 | Priority: 5 48 | - Regex: '^<.*\.(h)>' 49 | Priority: 2 50 | - Regex: '^<.*>' 51 | Priority: 3 52 | - Regex: '".*"' 53 | Priority: 4 54 | IndentCaseLabels: false 55 | IndentPPDirectives: AfterHash 56 | IndentWidth: 4 57 | IndentWrappedFunctionNames: false 58 | KeepEmptyLinesAtTheStartOfBlocks: false 59 | MacroBlockBegin: '' 60 | MacroBlockEnd: '' 61 | MaxEmptyLinesToKeep: 1 62 | NamespaceIndentation: None 63 | PenaltyBreakAssignment: 20 64 | PenaltyBreakBeforeFirstCallParameter: 100 65 | PenaltyBreakComment: 200 66 | PenaltyBreakFirstLessLess: 100 67 | PenaltyBreakString: 100 68 | # PenaltyBreakTemplateDeclaration: 10 # clang 7 69 | PenaltyExcessCharacter: 10 70 | PenaltyReturnTypeOnItsOwnLine: 200 71 | PointerAlignment: Left 72 | # ReflowComments: Important to keep line width consistent and to enable comments to break insteand of the code line. 73 | # However can create issues with existing comments and comments after code lines, 74 | # adjust PenaltyBreakComment and PenaltyExcessCharacter until desired effect is reached 75 | ReflowComments: true 76 | SortIncludes: true 77 | SortUsingDeclarations: true 78 | SpaceAfterCStyleCast: false 79 | SpaceAfterTemplateKeyword: false 80 | SpaceBeforeAssignmentOperators: true 81 | # SpaceBeforeCpp11BracedList: false # clang 7 82 | # SpaceBeforeCtorInitializerColon: true # clang 7 83 | # SpaceBeforeInheritanceColon: true # clang 7 84 | SpaceBeforeParens: ControlStatements 85 | # SpaceBeforeRangeBasedForLoopColon: true # clang 7 86 | SpaceInEmptyParentheses: false 87 | SpacesBeforeTrailingComments: 1 88 | SpacesInAngles: false 89 | SpacesInContainerLiterals: true 90 | SpacesInCStyleCastParentheses: false 91 | SpacesInParentheses: false 92 | SpacesInSquareBrackets: false 93 | Standard: Cpp11 94 | TabWidth: 4 95 | UseTab: Always -------------------------------------------------------------------------------- /Source/KCL/KCL_Platform.h: -------------------------------------------------------------------------------- 1 | // MIT License 2 | // 3 | // Copyright(c) 2019 Samuel Kahn 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files(the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions : 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | #pragma once 24 | 25 | ////////////////////////////////////////////////////////////////////////// 26 | // Compiler and language detection 27 | ////////////////////////////////////////////////////////////////////////// 28 | 29 | // KCL Compiler defines 30 | // Useful link: http://sourceforge.net/p/predef/wiki/Compilers 31 | #if defined(_MSC_VER) 32 | # define KCL_COMPILER_MSVC 1 33 | # define KCL_COMPILER_MSVC_2013 (_MSC_VER >= 1800) 34 | # define KCL_COMPILER_MSVC_2015 (_MSC_VER >= 1900) 35 | # define KCL_COMPILER_MSVC_2017 (_MSC_VER >= 1910) 36 | #elif defined(__clang__) 37 | # define KCL_COMPILER_CLANG 1 38 | #elif (defined(__GNUC__) || defined(__GNUG__)) && !defined(__clang__) // clang also defines these 39 | # define KCL_COMPILER_GCC 1 40 | #else 41 | # error Compiler not recognized 42 | //#elif defined(__ICC) || defined(__INTEL_COMPILER) 43 | /* Intel ICC/ICPC. ------------------------------------------ */ 44 | //#elif defined(__HP_cc) || defined(__HP_aCC) 45 | /* Hewlett-Packard C/aC++. ---------------------------------- */ 46 | //#elif defined(__IBMC__) || defined(__IBMCPP__) 47 | /* IBM XL C/C++. -------------------------------------------- */ 48 | //#elif defined(__PGI) 49 | /* Portland Group PGCC/PGCPP. ------------------------------- */ 50 | //#elif defined(__SUNPRO_C) || defined(__SUNPRO_CC) 51 | /* Oracle Solaris Studio. ----------------------------------- */ 52 | #endif 53 | 54 | // C++ language support 55 | #if !defined(__cplusplus) 56 | # error This library must be compiled by a C++ compiler 57 | #endif 58 | 59 | #if (__cplusplus >= 201703L) 60 | # define KCL_CPPLANG 17 61 | #elif (__cplusplus >= 201402L) 62 | # define KCL_CPPLANG 14 63 | #elif (__cplusplus >= 201103L) 64 | # define KCL_CPPLANG 11 65 | #else 66 | # define KCL_CPPLANG 03 67 | #endif 68 | 69 | // MSVC still defines __cplusplus to be 199711L (valid for MSVC 2017), better use _MSVC_LANG 70 | #if defined(KCL_COMPILER_MSVC) 71 | # undef KCL_CPPLANG 72 | 73 | # if (_MSVC_LANG >= 201703L) 74 | # define KCL_CPPLANG 17 75 | # elif (_MSVC_LANG >= 201402L) 76 | # define KCL_CPPLANG 14 77 | # else 78 | # define KCL_CPPLANG 11 // _MSVC_LANG did not even exist prior to MSVC 2015 which is fully C++11 79 | # endif 80 | 81 | #endif 82 | 83 | // Check minimum required language version 84 | #if !defined(KCL_CPPLANG) 85 | # error No C++ language support detected 86 | #elif (KCL_CPPLANG < 14) 87 | # error C++14 compatible compiler required 88 | #endif 89 | 90 | ////////////////////////////////////////////////////////////////////////// 91 | // Platform specific configuration and utils 92 | ////////////////////////////////////////////////////////////////////////// 93 | 94 | // Compiler behavior 95 | #if defined(KCL_COMPILER_MSVC) 96 | // Improve inlining behaviour of compiler 97 | # pragma inline_depth(255) 98 | # pragma inline_recursion(on) 99 | # pragma auto_inline(on) 100 | #elif defined(KCL_COMPILER_CLANG) 101 | // TODO : inlining behavior 102 | # pragma clang diagnostic ignored "-Wcomment" // disable nested comments warning 103 | #elif defined(KCL_COMPILER_GCC) 104 | #else 105 | # error Not implemented for this compiler 106 | #endif 107 | 108 | // UE : check corresponding Platform.h for implementation details 109 | #if defined(KCL_COMPILER_MSVC) 110 | # define KCL_FORCEINLINE __forceinline 111 | # define KCL_NOINLINE __declspec(noinline) 112 | # define KCL_ALIGN(X) MS_ALIGN(X) 113 | # define KCL_API_IMPORT_IMPL __declspec(dllimport) 114 | # define KCL_API_EXPORT_IMPL __declspec(dllexport) 115 | #elif defined(KCL_COMPILER_CLANG) 116 | # define KCL_FORCEINLINE inline __attribute__((always_inline)) 117 | # define KCL_NOINLINE __attribute__((noinline)) 118 | # define KCL_ALIGN(X) GCC_ALIGN(X) 119 | #elif defined(KCL_COMPILER_GCC) 120 | # define KCL_FORCEINLINE inline __attribute__((always_inline)) 121 | # define KCL_NOINLINE __attribute__((noinline)) 122 | # define KCL_ALIGN(X) GCC_ALIGN(X) 123 | #else 124 | # error Not implemented for this compiler 125 | #endif 126 | 127 | ////////////////////////////////////////////////////////////////////////// 128 | // C++ Language Support 129 | ////////////////////////////////////////////////////////////////////////// 130 | 131 | // Polyfill for upcoming language features 132 | #if (KCL_CPPLANG >= 17) 133 | # define KCL_NODISCARD [[nodiscard]] 134 | # define KCL_FALLTHROUGH [[fallthrough]] 135 | # define KCL_UNUSED [[maybe_unused]] 136 | #else 137 | # if defined(KCL_COMPILER_MSVC) 138 | # define KCL_NODISCARD 139 | # define KCL_FALLTHROUGH 140 | # define KCL_UNUSED 141 | # elif defined(KCL_COMPILER_CLANG) 142 | # define KCL_NODISCARD __attribute__((warn_unused_result)) 143 | # define KCL_UNUSED __attribute__((unused)) 144 | # define KCL_FALLTHROUGH 145 | # endif 146 | #endif 147 | -------------------------------------------------------------------------------- /Source/KCL/KCL_RTTI.h: -------------------------------------------------------------------------------- 1 | // MIT License 2 | // 3 | // Copyright(c) 2019 Samuel Kahn 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files(the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions : 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | #pragma once 24 | 25 | #include 26 | #include 27 | #include 28 | #include 29 | 30 | #include "KCL_Platform.h" 31 | #include "KCL_Utils_Preprocessor.h" 32 | 33 | // Minimalistic and efficient RTTI Implementation. 34 | // Adds virtual methods to registered types, as polymorphism is necessary 35 | // Dynamic casts cost in the worst case one virtual call and walking through a data buffer 36 | 37 | // Note: 38 | // * This is not safe to pass across boundaries. 39 | // * This is not thread safe. The type info is created the first time it is accessed, race conditions may occur. 40 | 41 | /*Usage : 42 | 43 | //For all polymorphic types including base classes 44 | class Type : public Base1, public Base2 45 | { 46 | public: 47 | KCL_RTTI_IMPL() 48 | //... 49 | }; 50 | 51 | //For all types except types using inheritance 52 | KCL_RTTI_REGISTER(Type) 53 | //For types using inheritance 54 | KCL_RTTI_REGISTER(Type, Base1, Base2 ...) 55 | 56 | */ 57 | 58 | namespace KCL 59 | { 60 | // Details, this is not meant to be used outside of this file 61 | namespace RTTI_Private 62 | { 63 | // You can reduce the size at will here 64 | typedef uint32_t typeId_t; 65 | 66 | // Member ::Get() will return const TypeInfo* 67 | template 68 | struct GetTypeInfo 69 | { 70 | }; 71 | 72 | } // namespace RTTI_Private 73 | 74 | // Public RTTI API 75 | namespace RTTI 76 | { 77 | typedef KCL::RTTI_Private::typeId_t typeId_t; 78 | 79 | // Interface of TypeInfo 80 | struct TypeInfo 81 | { 82 | KCL_FORCEINLINE const char* GetName() const { return myName; } 83 | KCL_FORCEINLINE const char* GetTypeData() const { return (char*)(this + 1); } 84 | KCL_FORCEINLINE typeId_t GetTypeId() const { return *(typeId_t*)(GetTypeData() + sizeof(typeId_t)); } 85 | inline intptr_t CastTo(intptr_t aPtr, typeId_t aTypeId) const 86 | { 87 | const char* data = GetTypeData(); 88 | size_t byteIndex = 0; 89 | ptrdiff_t offset = 0; 90 | 91 | while (true) 92 | { 93 | typeId_t size = *reinterpret_cast(data + byteIndex); 94 | byteIndex += sizeof(typeId_t); 95 | 96 | for (typeId_t i = 0; i < size; i++, byteIndex += sizeof(typeId_t)) 97 | { 98 | if (*reinterpret_cast(data + byteIndex) == aTypeId) 99 | return aPtr + offset; 100 | } 101 | 102 | offset = *reinterpret_cast(data + byteIndex); 103 | if (offset == 0) 104 | return 0; 105 | 106 | byteIndex += sizeof(ptrdiff_t); 107 | } 108 | } 109 | 110 | KCL_FORCEINLINE bool operator==(const TypeInfo& anOther) const { return GetTypeId() == anOther.GetTypeId(); } 111 | KCL_FORCEINLINE bool operator!=(const TypeInfo& anOther) const { return GetTypeId() != anOther.GetTypeId(); } 112 | 113 | const char* myName; 114 | }; 115 | 116 | // Public interface to access type information 117 | // Always access through this or risk wrong behavior 118 | template 119 | KCL_FORCEINLINE const TypeInfo* GetTypeInfo() 120 | { 121 | typedef typename std::decay::type>::type Type; 122 | return KCL::RTTI_Private::GetTypeInfo::Get(); 123 | } 124 | 125 | template 126 | KCL_FORCEINLINE typeId_t GetTypeId() 127 | { 128 | return GetTypeInfo()->GetTypeId(); 129 | } 130 | 131 | template 132 | KCL_FORCEINLINE Derived DynamicCast(Base* aBasePtr) 133 | { 134 | static_assert(std::is_pointer::value, "Return type must be a pointer"); 135 | typedef typename std::remove_pointer::type DerivedObjectType; 136 | 137 | if constexpr (std::is_base_of::value) 138 | return static_cast(aBasePtr); 139 | else if (aBasePtr) 140 | return reinterpret_cast(aBasePtr->KCL_RTTI_DynamicCast(GetTypeId())); 141 | else 142 | return nullptr; 143 | } 144 | 145 | } // namespace RTTI 146 | 147 | namespace RTTI_Private 148 | { 149 | static typeId_t GenerateId() 150 | { 151 | // magic number that increases every time it is called 152 | static typeId_t theTypeIdCounter = 0; 153 | return ++theTypeIdCounter; 154 | } 155 | 156 | template 157 | static ptrdiff_t ComputePointerOffset() 158 | { 159 | Derived* derivedPtr = (Derived*)1; 160 | Base* basePtr = static_cast(derivedPtr); 161 | return (intptr_t)basePtr - (intptr_t)derivedPtr; 162 | } 163 | 164 | #pragma pack(push, 1) 165 | 166 | // Specialization will contain the magic data. 167 | template 168 | struct TypeData 169 | { 170 | }; 171 | 172 | // Recursively populates the typeData 173 | // Layout of typeData: 174 | // [ typeId_t size, typeId_t firstTypeId ... typeId_t lastTypeId, ptrdiff_t offset/endMarker if = 0, 175 | // typeId_t size, typeId_t firstTypeId ... typeId_t lastTypeId, ptrdiff_t offset/endMarker if = 0... ] 176 | // Each block represents inherited types from a base, the first block doesn't need offset as it is implicitly 0 177 | // Therefore we can use the offset as an end marker, all other bases will have a positive offset 178 | template 179 | struct BaseTypeData 180 | { 181 | }; 182 | 183 | template 184 | struct BaseTypeData> { 185 | template 186 | void FillBaseTypeData(std::ptrdiff_t, typeId_t&) {} 187 | }; 188 | 189 | template 190 | struct BaseTypeData 191 | { 192 | template 193 | void FillBaseTypeData(ptrdiff_t aOffset, typeId_t& outHeadSize) 194 | { 195 | myFirst.template FillBaseTypeData(ComputePointerOffset(), outHeadSize); 196 | 197 | myOffset = ComputePointerOffset(); 198 | myNext.template FillBaseTypeData(myOffset, mySize); 199 | } 200 | 201 | BaseTypeData myFirst; 202 | ptrdiff_t myOffset; 203 | typeId_t mySize; 204 | BaseTypeData myNext; 205 | }; 206 | 207 | template 208 | struct BaseTypeData 209 | { 210 | template 211 | void FillBaseTypeData(ptrdiff_t aOffset, typeId_t& outHeadSize) 212 | { 213 | const TypeData* baseTypeId = (TypeData*)(GetTypeInfo::Get()->GetTypeData()); 214 | 215 | // return size of head list 216 | outHeadSize = baseTypeId->mySize; 217 | 218 | const char* data = baseTypeId->GetData(); 219 | size_t byteSize = baseTypeId->mySize * sizeof(typeId_t); 220 | 221 | // copy type list 222 | memcpy(myData, data, byteSize); 223 | 224 | size_t byteIndex = byteSize; 225 | ptrdiff_t offset = *reinterpret_cast(data + byteIndex); 226 | while (offset != 0) 227 | { 228 | // fill next offset and add pointer offset 229 | *reinterpret_cast(myData + byteIndex) = offset + aOffset; 230 | byteIndex += sizeof(ptrdiff_t); 231 | 232 | // fill next size 233 | const typeId_t size = *reinterpret_cast(data + byteIndex); 234 | *reinterpret_cast(myData + byteIndex) = size; 235 | byteSize = size * sizeof(typeId_t); 236 | byteIndex += sizeof(typeId_t); 237 | 238 | // copy types 239 | memcpy(myData + byteIndex, data + byteIndex, byteSize); 240 | byteIndex += byteSize; 241 | 242 | offset = *reinterpret_cast(data + byteIndex); 243 | } 244 | } 245 | 246 | // We only need the previous type data array, but not its size or end marker 247 | char myData[sizeof(TypeData) - sizeof(ptrdiff_t) - sizeof(typeId_t)]; 248 | }; 249 | 250 | // Actual implementation of TypeData 251 | template 252 | struct TypeDataImpl 253 | { 254 | TypeDataImpl() 255 | { 256 | myTypeId = GenerateId(); 257 | myBaseTypeData.template FillBaseTypeData(0 /* No offset with first base */, mySize); 258 | mySize++; // Size is the base's size + 1 to account for current type id 259 | myEndMarker = 0; 260 | } 261 | 262 | const char* GetData() const { return (char*)&myTypeId; } 263 | 264 | typeId_t mySize; 265 | typeId_t myTypeId; 266 | BaseTypeData myBaseTypeData; 267 | ptrdiff_t myEndMarker; 268 | }; 269 | 270 | template 271 | struct TypeDataImpl 272 | { 273 | TypeDataImpl() : mySize(1), myTypeId(GenerateId()), myEndMarker(0) {} 274 | 275 | const char* GetData() const { return (char*)&myTypeId; } 276 | 277 | typeId_t mySize; 278 | typeId_t myTypeId; 279 | ptrdiff_t myEndMarker; 280 | }; 281 | 282 | template 283 | struct TypeInfoImpl 284 | { 285 | const RTTI::TypeInfo myInfo; 286 | const TypeData myData; 287 | }; 288 | 289 | #pragma pack(pop) 290 | 291 | } // namespace RTTI_Private 292 | } // namespace KCL 293 | 294 | template 295 | KCL_FORCEINLINE Derived kcl_dynamic_cast(Base* aBasePtr) 296 | { 297 | return KCL::RTTI::DynamicCast(aBasePtr); 298 | } 299 | 300 | // Common declaration 301 | #define KCL_RTTI_TYPEINFO(TYPE) \ 302 | template<> \ 303 | struct GetTypeInfo \ 304 | { \ 305 | static const KCL::RTTI::TypeInfo* Get() \ 306 | { \ 307 | static TypeInfoImpl ourInstance = {{#TYPE}, TypeData()}; \ 308 | return &ourInstance.myInfo; \ 309 | } \ 310 | }; 311 | 312 | // Use for all types, must include all directly inherited types in the macro 313 | // Note: Ideally we could remove the Register macro by making it possible to register it all from withing the class, 314 | // but this approach allows registering non-class types as well. 315 | #define KCL_RTTI_REGISTER(...) \ 316 | namespace KCL \ 317 | { \ 318 | namespace RTTI_Private \ 319 | { \ 320 | template<> \ 321 | struct TypeData : public TypeDataImpl<__VA_ARGS__> \ 322 | { \ 323 | }; \ 324 | KCL_RTTI_TYPEINFO(KCL_FIRST_ARG(__VA_ARGS__)) \ 325 | } \ 326 | } 327 | 328 | // Use in the body of all polymorphic types 329 | #define KCL_RTTI_IMPL() \ 330 | \ 331 | virtual intptr_t KCL_RTTI_DynamicCast(KCL::RTTI::typeId_t aOtherTypeId) const \ 332 | { \ 333 | typedef std::remove_pointer::type ObjectType; \ 334 | return KCL::RTTI::template GetTypeInfo()->CastTo((intptr_t)this, aOtherTypeId); \ 335 | } \ 336 | virtual const KCL::RTTI::TypeInfo* KCL_RTTI_GetTypeInfo() const \ 337 | { \ 338 | typedef std::remove_pointer::type ObjectType; \ 339 | return KCL::RTTI::template GetTypeInfo(); \ 340 | } \ 341 | virtual const char* KCL_RTTI_GetTypeName() const { return KCL_RTTI_GetTypeInfo()->GetName(); } \ 342 | virtual KCL::RTTI::typeId_t KCL_RTTI_GetTypeId() const \ 343 | { \ 344 | typedef std::remove_pointer::type ObjectType; \ 345 | return KCL::RTTI::template GetTypeId(); \ 346 | } 347 | -------------------------------------------------------------------------------- /Source/KCL/KCL_Utils_Preprocessor.h: -------------------------------------------------------------------------------- 1 | // MIT License 2 | // 3 | // Copyright(c) 2019 Samuel Kahn 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files(the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions : 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | #pragma once 24 | 25 | // clang-format off 26 | 27 | // Preprocessor utilities 28 | // Macros with _ prefix are considered implementation details and should not be used or relied upon 29 | 30 | // Converts parameter to string, guarantees macro expansion 2 levels 31 | // Ex: KCL_TOSTRING(arg) <=> "arg" 32 | #define KCL_TOSTRING(arg) _KCL_TOSTRING1(arg) 33 | #define _KCL_TOSTRING1(arg) _KCL_TOSTRING2(arg) 34 | #define _KCL_TOSTRING2(arg) #arg 35 | 36 | // Concatenates macro parameters text to string, guarantees macro expansion 2 levels 37 | #define KCL_CONCATENATE(arg1, arg2) _KCL_CONCATENATE1(arg1, arg2) 38 | #define _KCL_CONCATENATE1(arg1, arg2) _KCL_CONCATENATE2(arg1, arg2) 39 | #define _KCL_CONCATENATE2(arg1, arg2) arg1 ## arg2 40 | 41 | //Used most notably for MSVC's different behavior in handling __VA_ARGS__. 42 | //In MSVC __VA_ARGS__ is expanded as a single token which means the next macro will only have one parameter 43 | //To work around this remplace MACRO(__VA_ARGS__) by KCL_EXPAND(MACRO(__VA_ARGS)) which will perform correct substitution 44 | //More background info in https://stackoverflow.com/questions/5134523/msvc-doesnt-expand-va-args-correctly 45 | #define KCL_EXPAND(va_args) va_args 46 | 47 | // Returns the first argument of a variadic macro pack 48 | #define _KCL_FIRST_ARG(FIRST, ...) FIRST 49 | #define KCL_FIRST_ARG(...) KCL_EXPAND(_KCL_FIRST_ARG(__VA_ARGS__)) 50 | 51 | // Counts the number of variadic arguments 52 | // Used with no arguments it will in fact return 1, this seems to be unavoidable as even an empty __VA_ARGS__ count as an argument to _KCL_VA_COUNT_IMPL 53 | #define KCL_VA_COUNT(...) \ 54 | KCL_EXPAND(_KCL_VA_COUNT_EXPAND(__VA_ARGS__, _KCL_RSEQ_N())) 55 | 56 | #define _KCL_VA_COUNT_EXPAND(...) \ 57 | KCL_EXPAND(_KCL_VA_COUNT_IMPL(__VA_ARGS__)) 58 | 59 | #define _KCL_VA_COUNT_IMPL( \ 60 | _1, _2, _3, _4, _5, _6, _7, _8, _9,_10, \ 61 | _11,_12,_13,_14,_15,_16,_17,_18,_19,_20, \ 62 | _21,_22,_23,_24,_25,_26,_27,_28,_29,_30, \ 63 | _31,_32,_33,_34,_35,_36,_37,_38,_39,_40, \ 64 | _41,_42,_43,_44,_45,_46,_47,_48,_49,_50, \ 65 | _51,_52,_53,_54,_55,_56,_57,_58,_59,_60, \ 66 | _61,_62,_63,N,...) N 67 | 68 | #define _KCL_RSEQ_N() \ 69 | 63,62,61,60, \ 70 | 59,58,57,56,55,54,53,52,51,50, \ 71 | 49,48,47,46,45,44,43,42,41,40, \ 72 | 39,38,37,36,35,34,33,32,31,30, \ 73 | 29,28,27,26,25,24,23,22,21,20, \ 74 | 19,18,17,16,15,14,13,12,11,10, \ 75 | 9,8,7,6,5,4,3,2,1,0 76 | 77 | // Foreach macro applies a macro to all of the following arguments 78 | #define KCL_FOREACH(MACRO, ...) KCL_EXPAND(_KCL_FOREACH_IMPL(KCL_VA_COUNT(__VA_ARGS__), MACRO, __VA_ARGS__)) 79 | 80 | #define _KCL_FOREACH_1(MACRO, FIRST, ...) MACRO(FIRST) 81 | #define _KCL_FOREACH_2(MACRO, FIRST, ...) MACRO(FIRST) KCL_EXPAND(_KCL_FOREACH_1(MACRO, __VA_ARGS__)) 82 | #define _KCL_FOREACH_3(MACRO, FIRST, ...) MACRO(FIRST) KCL_EXPAND(_KCL_FOREACH_2(MACRO, __VA_ARGS__)) 83 | #define _KCL_FOREACH_4(MACRO, FIRST, ...) MACRO(FIRST) KCL_EXPAND(_KCL_FOREACH_3(MACRO, __VA_ARGS__)) 84 | #define _KCL_FOREACH_5(MACRO, FIRST, ...) MACRO(FIRST) KCL_EXPAND(_KCL_FOREACH_4(MACRO, __VA_ARGS__)) 85 | #define _KCL_FOREACH_6(MACRO, FIRST, ...) MACRO(FIRST) KCL_EXPAND(_KCL_FOREACH_5(MACRO, __VA_ARGS__)) 86 | #define _KCL_FOREACH_7(MACRO, FIRST, ...) MACRO(FIRST) KCL_EXPAND(_KCL_FOREACH_6(MACRO, __VA_ARGS__)) 87 | #define _KCL_FOREACH_8(MACRO, FIRST, ...) MACRO(FIRST) KCL_EXPAND(_KCL_FOREACH_7(MACRO, __VA_ARGS__)) 88 | 89 | #define _KCL_FOREACH_IMPL(N, MACRO, ...) KCL_EXPAND(KCL_CONCATENATE(_KCL_FOREACH_, N)(MACRO, __VA_ARGS__)) 90 | 91 | 92 | // Foreach with a macro of two parameters 93 | // Will not compile with odd number of parameters 94 | #define KCL_FOREACH_2ARGS(MACRO, ...) KCL_EXPAND(_KCL_FOREACH_IMPL_2ARGS(KCL_VA_COUNT(__VA_ARGS__), MACRO, __VA_ARGS__)) 95 | 96 | #define _KCL_FOREACH_2ARGS_2(MACRO, FIRST, SECOND, ...) MACRO(FIRST, SECOND) 97 | #define _KCL_FOREACH_2ARGS_4(MACRO, FIRST, SECOND, ...) MACRO(FIRST, SECOND) KCL_EXPAND(_KCL_FOREACH_2ARGS_2 (MACRO, __VA_ARGS__)) 98 | #define _KCL_FOREACH_2ARGS_6(MACRO, FIRST, SECOND, ...) MACRO(FIRST, SECOND) KCL_EXPAND(_KCL_FOREACH_2ARGS_4 (MACRO, __VA_ARGS__)) 99 | #define _KCL_FOREACH_2ARGS_8(MACRO, FIRST, SECOND, ...) MACRO(FIRST, SECOND) KCL_EXPAND(_KCL_FOREACH_2ARGS_6 (MACRO, __VA_ARGS__)) 100 | #define _KCL_FOREACH_2ARGS_10(MACRO, FIRST, SECOND, ...) MACRO(FIRST, SECOND) KCL_EXPAND(_KCL_FOREACH_2ARGS_8 (MACRO, __VA_ARGS__)) 101 | #define _KCL_FOREACH_2ARGS_12(MACRO, FIRST, SECOND, ...) MACRO(FIRST, SECOND) KCL_EXPAND(_KCL_FOREACH_2ARGS_10(MACRO, __VA_ARGS__)) 102 | #define _KCL_FOREACH_2ARGS_14(MACRO, FIRST, SECOND, ...) MACRO(FIRST, SECOND) KCL_EXPAND(_KCL_FOREACH_2ARGS_12(MACRO, __VA_ARGS__)) 103 | #define _KCL_FOREACH_2ARGS_16(MACRO, FIRST, SECOND, ...) MACRO(FIRST, SECOND) KCL_EXPAND(_KCL_FOREACH_2ARGS_14(MACRO, __VA_ARGS__)) 104 | 105 | #define _KCL_FOREACH_IMPL_2ARGS(N, MACRO, ...) KCL_EXPAND(KCL_CONCATENATE(_KCL_FOREACH_2ARGS_, N)(MACRO, __VA_ARGS__)) 106 | -------------------------------------------------------------------------------- /Source/KCL_Test/KCL_RTTI_Test.cpp: -------------------------------------------------------------------------------- 1 | // MIT License 2 | // 3 | // Copyright(c) 2019 Samuel Kahn 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files(the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions : 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | #include "KCL_RTTI_Test.h" 24 | 25 | #include 26 | #include 27 | #include 28 | #include 29 | 30 | #include "KCL/KCL_RTTI.h" 31 | 32 | ////////////////////////////////////////////////////////////////////////// 33 | 34 | #define BASE_CLASS(CLASS) \ 35 | struct CLASS \ 36 | { \ 37 | KCL_RTTI_IMPL() virtual ~CLASS() {} \ 38 | int myInt##CLASS; \ 39 | }; \ 40 | KCL_RTTI_REGISTER(CLASS) 41 | 42 | #define DERIVED_CLASS(CLASS, ...) \ 43 | struct CLASS : __VA_ARGS__ \ 44 | { \ 45 | KCL_RTTI_IMPL() virtual ~CLASS() {} \ 46 | int myInt##CLASS; \ 47 | }; \ 48 | KCL_EXPAND(KCL_RTTI_REGISTER(CLASS, __VA_ARGS__)) 49 | 50 | // Single inheritance hierarchies 51 | 52 | BASE_CLASS(Base1) 53 | DERIVED_CLASS(Derived1A, Base1) 54 | 55 | DERIVED_CLASS(Derived2A, Derived1A) 56 | DERIVED_CLASS(Derived3A, Derived2A) 57 | DERIVED_CLASS(Derived4A, Derived3A) 58 | DERIVED_CLASS(Derived5A, Derived4A) 59 | DERIVED_CLASS(Derived6A, Derived5A) 60 | DERIVED_CLASS(Derived7A, Derived6A) 61 | 62 | DERIVED_CLASS(Derived1B, Base1) 63 | DERIVED_CLASS(Derived2B, Derived1B) 64 | DERIVED_CLASS(Derived3B, Derived2B) 65 | DERIVED_CLASS(Derived4B, Derived3B) 66 | DERIVED_CLASS(Derived5B, Derived4B) 67 | DERIVED_CLASS(Derived6B, Derived5B) 68 | DERIVED_CLASS(Derived7B, Derived6B) 69 | 70 | DERIVED_CLASS(Derived1C, Base1) 71 | DERIVED_CLASS(Derived2C, Derived1C) 72 | DERIVED_CLASS(Derived3C, Derived2C) 73 | DERIVED_CLASS(Derived4C, Derived3C) 74 | DERIVED_CLASS(Derived5C, Derived4C) 75 | DERIVED_CLASS(Derived6C, Derived5C) 76 | DERIVED_CLASS(Derived7C, Derived6C) 77 | 78 | BASE_CLASS(Base2) 79 | 80 | DERIVED_CLASS(Derived1D, Base2) 81 | DERIVED_CLASS(Derived2D, Derived1D) 82 | DERIVED_CLASS(Derived3D, Derived2D) 83 | DERIVED_CLASS(Derived4D, Derived3D) 84 | DERIVED_CLASS(Derived5D, Derived4D) 85 | DERIVED_CLASS(Derived6D, Derived5D) 86 | DERIVED_CLASS(Derived7D, Derived6D) 87 | 88 | DERIVED_CLASS(Derived1E, Base2) 89 | DERIVED_CLASS(Derived2E, Derived1E) 90 | DERIVED_CLASS(Derived3E, Derived2E) 91 | DERIVED_CLASS(Derived4E, Derived3E) 92 | DERIVED_CLASS(Derived5E, Derived4E) 93 | DERIVED_CLASS(Derived6E, Derived5E) 94 | DERIVED_CLASS(Derived7E, Derived6E) 95 | 96 | DERIVED_CLASS(Derived1F, Base2) 97 | DERIVED_CLASS(Derived2F, Derived1F) 98 | DERIVED_CLASS(Derived3F, Derived2F) 99 | DERIVED_CLASS(Derived4F, Derived3F) 100 | DERIVED_CLASS(Derived5F, Derived4F) 101 | DERIVED_CLASS(Derived6F, Derived5F) 102 | DERIVED_CLASS(Derived7F, Derived6F) 103 | 104 | // Multiple inheritance hierarchies 105 | 106 | // Inherits from two long inheritance chains 107 | DERIVED_CLASS(Multi1A, Base1, Base2) 108 | DERIVED_CLASS(Multi2A, Derived1A, Derived1D) 109 | DERIVED_CLASS(Multi3A, Derived2A, Derived2D) 110 | DERIVED_CLASS(Multi4A, Derived3A, Derived3D) 111 | DERIVED_CLASS(Multi5A, Derived4A, Derived4D) 112 | DERIVED_CLASS(Multi6A, Derived5A, Derived5D) 113 | DERIVED_CLASS(Multi7A, Derived6A, Derived6D) 114 | 115 | // Inherits from all the previously defined hierarchies 116 | // This diamond pattern is not adviced but KCL_RTTI should handle it as well as virtual inheritance 117 | DERIVED_CLASS(Multi1B, Derived1A, Derived1B, Derived1C, Derived1D, Derived1E, Derived1F) 118 | DERIVED_CLASS(Multi3B, Derived3A, Derived3B, Derived3C, Derived3D, Derived3E, Derived3F) 119 | DERIVED_CLASS(Multi7B, Derived7A, Derived7B, Derived7C, Derived7D, Derived7E, Derived7F) 120 | 121 | // Nested multi inheritance scenario 122 | 123 | BASE_CLASS(Base3) 124 | BASE_CLASS(Base4) 125 | BASE_CLASS(Base5) 126 | BASE_CLASS(Base6) 127 | 128 | DERIVED_CLASS(Multi1C, Base1, Base2) 129 | DERIVED_CLASS(Multi2C, Base3, Base4) 130 | DERIVED_CLASS(Multi3C, Base5, Base6) 131 | DERIVED_CLASS(Multi4C, Multi1C, Multi2C, Multi3C) 132 | DERIVED_CLASS(Multi5C, Multi2C, Multi3C, Multi1C) 133 | DERIVED_CLASS(Multi6C, Multi3C, Multi1C, Multi2C) 134 | 135 | ////////////////////////////////////////////////////////////////////////// 136 | 137 | namespace KCL_Test 138 | { 139 | class Forward; 140 | } 141 | 142 | // Ensuring we can use RTTI with a type that is not yet declared 143 | KCL_RTTI_REGISTER(KCL_Test::Forward) 144 | 145 | namespace KCL_Test 146 | { 147 | class Forward 148 | { 149 | KCL_RTTI_IMPL(); 150 | int i; 151 | }; 152 | 153 | void RTTI_Test() 154 | { 155 | using namespace KCL::RTTI; 156 | 157 | assert(GetTypeId() != GetTypeId()); 158 | assert(GetTypeInfo() != GetTypeInfo()); 159 | 160 | // Testing type info validity with modifiers 161 | assert(GetTypeInfo() == GetTypeInfo()); 162 | assert(GetTypeInfo() == GetTypeInfo()); 163 | assert(GetTypeInfo() == GetTypeInfo()); 164 | 165 | // Testing basic dynamic cast 166 | Base1 b; 167 | Derived1A d; 168 | Base1* basePtrToDerived = &d; 169 | Derived1A* dPtr = kcl_dynamic_cast(basePtrToDerived); 170 | assert(dPtr); 171 | 172 | dPtr = kcl_dynamic_cast(&b); 173 | assert(!dPtr); 174 | 175 | { 176 | // confirming that deep inheritance chains work and the logic of recursive typeId building is sound 177 | Derived7A der7; 178 | Base1* base = kcl_dynamic_cast(&der7); 179 | Derived1A* der = kcl_dynamic_cast(&der7); 180 | Derived2A* der2 = kcl_dynamic_cast(&der7); 181 | Derived6A* der6 = kcl_dynamic_cast(&der7); 182 | assert(&der7 == base && base == der && base == der2 && base == der6); 183 | } 184 | 185 | { 186 | // confirm dynamic cast is working in multiple inheritance scenario 187 | Multi5A m; 188 | 189 | Base1* base1dyn = kcl_dynamic_cast(&m); 190 | Base2* base2dyn = kcl_dynamic_cast(&m); 191 | assert(base1dyn && base2dyn && (intptr_t)base1dyn != (intptr_t)base2dyn); 192 | 193 | Derived4A* der4A = kcl_dynamic_cast(base1dyn); 194 | Derived2D* der2D = kcl_dynamic_cast(base2dyn); 195 | Derived7A* der7A = kcl_dynamic_cast(base1dyn); 196 | assert(der4A && der2D && !der7A); 197 | } 198 | 199 | { 200 | // multiple inheritance, verify we are correctly using static_cast to cast to base 201 | 202 | Multi1A m; 203 | Base1* base1dyn = kcl_dynamic_cast(&m); 204 | Base1* base1static = static_cast(&m); 205 | Base2* base2dyn = kcl_dynamic_cast(&m); 206 | Base2* base2static = static_cast(&m); 207 | 208 | assert(base1dyn && base1static && base1dyn == base1static && (intptr_t)base1dyn == (intptr_t)&m); 209 | assert(base2dyn && base2static && base2dyn == base2static && (intptr_t)base2dyn != (intptr_t)&m); 210 | } 211 | 212 | { 213 | Multi4C m; 214 | 215 | // Test casting to all inherited bases from m ptr and Base1 ptr 216 | 217 | Base1* base1dyn = kcl_dynamic_cast(&m); 218 | Base1* base1static = static_cast(&m); 219 | assert(base1dyn == base1static && base1dyn == &m); 220 | 221 | Base2* base2dyn = kcl_dynamic_cast(base1dyn); 222 | Base2* base2dyn2 = kcl_dynamic_cast(&m); 223 | Base2* base2static = static_cast(&m); 224 | 225 | assert(base2dyn == base2static && base2dyn == base2static && base2dyn != nullptr); 226 | 227 | Base3* base3dyn = kcl_dynamic_cast(base1dyn); 228 | Base3* base3dyn3 = kcl_dynamic_cast(&m); 229 | Base3* base3static = static_cast(&m); 230 | 231 | assert(base3dyn == base3static && base3dyn == base3static && base3dyn != nullptr); 232 | 233 | Base4* base4dyn = kcl_dynamic_cast(base1dyn); 234 | Base4* base4dyn4 = kcl_dynamic_cast(&m); 235 | Base4* base4static = static_cast(&m); 236 | 237 | assert(base4dyn == base4static && base4dyn == base4static && base4dyn != nullptr); 238 | 239 | Base5* base5dyn = kcl_dynamic_cast(base1dyn); 240 | Base5* base5dyn5 = kcl_dynamic_cast(&m); 241 | Base5* base5static = static_cast(&m); 242 | 243 | assert(base5dyn == base5static && base5dyn == base5static && base5dyn != nullptr); 244 | 245 | Base6* base6dyn = kcl_dynamic_cast(base1dyn); 246 | Base6* base6dyn6 = kcl_dynamic_cast(&m); 247 | Base6* base6static = static_cast(&m); 248 | 249 | assert(base6dyn == base6static && base6dyn == base6static && base6dyn != nullptr); 250 | 251 | // Test casting to base1 ptr from all the other pointers should work so the back offset calculation is good 252 | 253 | Base1* base1dynfrom2 = kcl_dynamic_cast(base2dyn); 254 | assert(base1dyn == base1dynfrom2); 255 | Base1* base1dynfrom3 = kcl_dynamic_cast(base3dyn); 256 | assert(base1dyn == base1dynfrom3); 257 | Base1* base1dynfrom4 = kcl_dynamic_cast(base4dyn); 258 | assert(base1dyn == base1dynfrom4); 259 | Base1* base1dynfrom5 = kcl_dynamic_cast(base5dyn); 260 | assert(base1dyn == base1dynfrom5); 261 | Base1* base1dynfrom6 = kcl_dynamic_cast(base6dyn); 262 | assert(base1dyn == base1dynfrom6); 263 | 264 | // Same test with Base3 265 | 266 | Base3* base3dynfrom1 = kcl_dynamic_cast(base1dyn); 267 | assert(base3dyn == base3dynfrom1); 268 | Base3* base3dynfrom2 = kcl_dynamic_cast(base2dyn); 269 | assert(base3dyn == base3dynfrom2); 270 | Base3* base3dynfrom4 = kcl_dynamic_cast(base4dyn); 271 | assert(base3dyn == base3dynfrom4); 272 | Base3* base3dynfrom5 = kcl_dynamic_cast(base5dyn); 273 | assert(base3dyn == base3dynfrom5); 274 | Base3* base3dynfrom6 = kcl_dynamic_cast(base6dyn); 275 | assert(base3dyn == base3dynfrom6); 276 | 277 | // Same test with Base4 278 | 279 | Base4* base4dynfrom1 = kcl_dynamic_cast(base1dyn); 280 | assert(base4dyn == base4dynfrom1); 281 | Base4* base4dynfrom2 = kcl_dynamic_cast(base2dyn); 282 | assert(base4dyn == base4dynfrom2); 283 | Base4* base4dynfrom3 = kcl_dynamic_cast(base3dyn); 284 | assert(base4dyn == base4dynfrom3); 285 | Base4* base4dynfrom5 = kcl_dynamic_cast(base5dyn); 286 | assert(base4dyn == base4dynfrom5); 287 | Base4* base4dynfrom6 = kcl_dynamic_cast(base6dyn); 288 | assert(base4dyn == base4dynfrom6); 289 | 290 | // Same test with Base6 291 | 292 | Base6* base6dynfrom1 = kcl_dynamic_cast(base1dyn); 293 | assert(base6dyn == base6dynfrom1); 294 | Base6* base6dynfrom2 = kcl_dynamic_cast(base2dyn); 295 | assert(base6dyn == base6dynfrom2); 296 | Base6* base6dynfrom3 = kcl_dynamic_cast(base3dyn); 297 | assert(base6dyn == base6dynfrom3); 298 | Base6* base6dynfrom6 = kcl_dynamic_cast(base6dyn); 299 | assert(base6dyn == base6dynfrom6); 300 | Base6* base6dynfrom5 = kcl_dynamic_cast(base5dyn); 301 | assert(base6dyn == base6dynfrom5); 302 | 303 | // Check we allow unrelated types to be attempted (and possibly failed) 304 | Forward* forwardDyn = kcl_dynamic_cast(base1dyn); 305 | assert(!forwardDyn); 306 | } 307 | 308 | // Note: this will result in ambiguous conversion which is expected 309 | // Multi7B m; 310 | // Base1* base1dyn = kcl_dynamic_cast(&m); 311 | } 312 | 313 | static int validCastCounter = 0; 314 | 315 | template 316 | KCL_NOINLINE void RunDynamicCastTest(const std::vector>& testVector, int loopCount) 317 | { 318 | for (int i = 0; i < loopCount; i++) 319 | { 320 | for (const auto& it : testVector) 321 | { 322 | Derived* result = dynamic_cast(it.get()); 323 | if (result) 324 | validCastCounter++; 325 | } 326 | } 327 | } 328 | 329 | template 330 | KCL_NOINLINE void RunKCLCastTest(const std::vector>& testVector, int loopCount) 331 | { 332 | for (int i = 0; i < loopCount; i++) 333 | { 334 | for (const auto& it : testVector) 335 | { 336 | Derived* result = kcl_dynamic_cast(it.get()); 337 | if (result) 338 | validCastCounter++; 339 | } 340 | } 341 | } 342 | 343 | void RTTI_Benchmark() 344 | { 345 | using namespace std; 346 | using namespace chrono; 347 | 348 | static const int iterations = 1000000; 349 | static const int loopCount = 10; 350 | 351 | validCastCounter = 0; 352 | 353 | // One level deep single inheritance hierarchies 354 | { 355 | // Prepare test vector 356 | vector> testObjects; 357 | testObjects.reserve(iterations * 3); 358 | 359 | for (int i = 0; i < iterations; i++) 360 | { 361 | testObjects.emplace_back(make_shared()); 362 | testObjects.emplace_back(make_shared()); 363 | testObjects.emplace_back(make_shared()); 364 | } 365 | 366 | // Upcast std 367 | { 368 | auto before = steady_clock::now(); 369 | 370 | RunDynamicCastTest(testObjects, loopCount); 371 | 372 | auto after = steady_clock::now(); 373 | duration deltaTime = after - before; 374 | 375 | printf("Single inheritance, 1 level deep. STD Upcast i: %zu, time (ms): %f\n", testObjects.size(), 376 | deltaTime.count() / (float)loopCount); 377 | } 378 | 379 | // Upcast kcl 380 | { 381 | auto before = steady_clock::now(); 382 | 383 | RunKCLCastTest(testObjects, loopCount); 384 | 385 | auto after = steady_clock::now(); 386 | duration deltaTime = after - before; 387 | 388 | printf("Single inheritance, 1 level deep. KCL Upcast i: %zu, time (ms): %f\n", testObjects.size(), 389 | deltaTime.count() / (float)loopCount); 390 | } 391 | 392 | // Downcast std 393 | { 394 | auto before = steady_clock::now(); 395 | 396 | RunDynamicCastTest(testObjects, loopCount); 397 | 398 | auto after = steady_clock::now(); 399 | duration deltaTime = after - before; 400 | 401 | printf("Single inheritance, 1 level deep. STD Downcast i: %zu, time (ms): %f\n", testObjects.size(), 402 | deltaTime.count() / (float)loopCount); 403 | } 404 | 405 | // Downcast kcl 406 | { 407 | auto before = steady_clock::now(); 408 | 409 | RunKCLCastTest(testObjects, loopCount); 410 | 411 | auto after = steady_clock::now(); 412 | duration deltaTime = after - before; 413 | 414 | printf("Single inheritance, 1 level deep. KCL Downcast i: %zu, time (ms): %f\n", testObjects.size(), 415 | deltaTime.count() / (float)loopCount); 416 | } 417 | } 418 | 419 | // Three level deep single inheritance hierarchies 420 | { 421 | // Prepare test vector 422 | vector> testObjects; 423 | testObjects.reserve(iterations * 3); 424 | 425 | for (int i = 0; i < iterations; i++) 426 | { 427 | testObjects.emplace_back(make_shared()); 428 | testObjects.emplace_back(make_shared()); 429 | testObjects.emplace_back(make_shared()); 430 | } 431 | 432 | // Upcast std 433 | { 434 | auto before = steady_clock::now(); 435 | 436 | RunDynamicCastTest(testObjects, loopCount); 437 | 438 | auto after = steady_clock::now(); 439 | duration deltaTime = after - before; 440 | 441 | printf("Single inheritance, 3 level deep. STD Upcast i: %zu, time (ms): %f\n", testObjects.size(), 442 | deltaTime.count() / (float)loopCount); 443 | } 444 | 445 | // Upcast kcl 446 | { 447 | auto before = steady_clock::now(); 448 | 449 | RunKCLCastTest(testObjects, loopCount); 450 | 451 | auto after = steady_clock::now(); 452 | duration deltaTime = after - before; 453 | 454 | printf("Single inheritance, 3 level deep. KCL Upcast i: %zu, time (ms): %f\n", testObjects.size(), 455 | deltaTime.count() / (float)loopCount); 456 | } 457 | 458 | // Downcast std 459 | { 460 | auto before = steady_clock::now(); 461 | 462 | RunDynamicCastTest(testObjects, loopCount); 463 | 464 | auto after = steady_clock::now(); 465 | duration deltaTime = after - before; 466 | 467 | printf("Single inheritance, 3 level deep. STD Downcast i: %zu, time (ms): %f\n", testObjects.size(), 468 | deltaTime.count() / (float)loopCount); 469 | } 470 | 471 | // Downcast kcl 472 | { 473 | auto before = steady_clock::now(); 474 | 475 | RunKCLCastTest(testObjects, loopCount); 476 | 477 | auto after = steady_clock::now(); 478 | duration deltaTime = after - before; 479 | 480 | printf("Single inheritance, 3 level deep. KCL Downcast i: %zu, time (ms): %f\n", testObjects.size(), 481 | deltaTime.count() / (float)loopCount); 482 | } 483 | } 484 | 485 | // Seven level deep single inheritance hierarchies 486 | { 487 | // Prepare test vector 488 | vector> testObjects; 489 | testObjects.reserve(iterations * 3); 490 | 491 | for (int i = 0; i < iterations; i++) 492 | { 493 | testObjects.emplace_back(make_shared()); 494 | testObjects.emplace_back(make_shared()); 495 | testObjects.emplace_back(make_shared()); 496 | } 497 | 498 | // Upcast std 499 | { 500 | auto before = steady_clock::now(); 501 | 502 | RunDynamicCastTest(testObjects, loopCount); 503 | 504 | auto after = steady_clock::now(); 505 | duration deltaTime = after - before; 506 | 507 | printf("Single inheritance, 7 level deep. STD Upcast i: %zu, time (ms): %f\n", testObjects.size(), 508 | deltaTime.count() / (float)loopCount); 509 | } 510 | 511 | // Upcast kcl 512 | { 513 | auto before = steady_clock::now(); 514 | 515 | RunKCLCastTest(testObjects, loopCount); 516 | 517 | auto after = steady_clock::now(); 518 | duration deltaTime = after - before; 519 | 520 | printf("Single inheritance, 7 level deep. KCL Upcast i: %zu, time (ms): %f\n", testObjects.size(), 521 | deltaTime.count() / (float)loopCount); 522 | } 523 | 524 | // Downcast std 525 | { 526 | auto before = steady_clock::now(); 527 | 528 | RunDynamicCastTest(testObjects, loopCount); 529 | 530 | auto after = steady_clock::now(); 531 | duration deltaTime = after - before; 532 | 533 | printf("Single inheritance, 7 level deep. STD Downcast i: %zu, time (ms): %f\n", testObjects.size(), 534 | deltaTime.count() / (float)loopCount); 535 | } 536 | 537 | // Downcast kcl 538 | { 539 | auto before = steady_clock::now(); 540 | 541 | RunKCLCastTest(testObjects, loopCount); 542 | 543 | auto after = steady_clock::now(); 544 | duration deltaTime = after - before; 545 | 546 | printf("Single inheritance, 7 level deep. KCL Downcast i: %zu, time (ms): %f\n", testObjects.size(), 547 | deltaTime.count() / (float)loopCount); 548 | } 549 | } 550 | 551 | // Wrong cast, one level deep 552 | { 553 | // Prepare test vector 554 | vector> testObjects; 555 | testObjects.reserve(iterations * 3); 556 | 557 | for (int i = 0; i < iterations; i++) 558 | { 559 | testObjects.emplace_back(make_shared()); 560 | testObjects.emplace_back(make_shared()); 561 | testObjects.emplace_back(make_shared()); 562 | } 563 | 564 | // Upcast std 565 | { 566 | auto before = steady_clock::now(); 567 | 568 | RunDynamicCastTest(testObjects, loopCount); 569 | 570 | auto after = steady_clock::now(); 571 | duration deltaTime = after - before; 572 | 573 | printf("Wrong cast, 1 level deep. STD i: %zu, time (ms): %f\n", testObjects.size(), deltaTime.count() / (float)loopCount); 574 | } 575 | 576 | // Upcast kcl 577 | { 578 | auto before = steady_clock::now(); 579 | 580 | RunKCLCastTest(testObjects, loopCount); 581 | 582 | auto after = steady_clock::now(); 583 | duration deltaTime = after - before; 584 | 585 | printf("Wrong cast, 1 level deep. KCL i: %zu, time (ms): %f\n", testObjects.size(), deltaTime.count() / (float)loopCount); 586 | } 587 | } 588 | 589 | // Wrong cast, 3 level deep 590 | { 591 | // Prepare test vector 592 | vector> testObjects; 593 | testObjects.reserve(iterations * 3); 594 | 595 | for (int i = 0; i < iterations; i++) 596 | { 597 | testObjects.emplace_back(make_shared()); 598 | testObjects.emplace_back(make_shared()); 599 | testObjects.emplace_back(make_shared()); 600 | } 601 | 602 | // Upcast std 603 | { 604 | auto before = steady_clock::now(); 605 | 606 | RunDynamicCastTest(testObjects, loopCount); 607 | 608 | auto after = steady_clock::now(); 609 | duration deltaTime = after - before; 610 | 611 | printf("Wrong cast, 3 level deep. STD i: %zu, time (ms): %f\n", testObjects.size(), deltaTime.count() / (float)loopCount); 612 | } 613 | 614 | // Upcast kcl 615 | { 616 | auto before = steady_clock::now(); 617 | 618 | RunKCLCastTest(testObjects, loopCount); 619 | 620 | auto after = steady_clock::now(); 621 | duration deltaTime = after - before; 622 | 623 | printf("Wrong cast, 3 level deep. KCL i: %zu, time (ms): %f\n", testObjects.size(), deltaTime.count() / (float)loopCount); 624 | } 625 | } 626 | 627 | // Wrong cast, 7 level deep 628 | { 629 | // Prepare test vector 630 | vector> testObjects; 631 | testObjects.reserve(iterations * 3); 632 | 633 | for (int i = 0; i < iterations; i++) 634 | { 635 | testObjects.emplace_back(make_shared()); 636 | testObjects.emplace_back(make_shared()); 637 | testObjects.emplace_back(make_shared()); 638 | } 639 | 640 | // Upcast std 641 | { 642 | auto before = steady_clock::now(); 643 | 644 | RunDynamicCastTest(testObjects, loopCount); 645 | 646 | auto after = steady_clock::now(); 647 | duration deltaTime = after - before; 648 | 649 | printf("Wrong cast, 7 level deep. STD i: %zu, time (ms): %f\n", testObjects.size(), deltaTime.count() / (float)loopCount); 650 | } 651 | 652 | // Upcast kcl 653 | { 654 | auto before = steady_clock::now(); 655 | 656 | RunKCLCastTest(testObjects, loopCount); 657 | 658 | auto after = steady_clock::now(); 659 | duration deltaTime = after - before; 660 | 661 | printf("Wrong cast, 7 level deep. KCL i: %zu, time (ms): %f\n", testObjects.size(), deltaTime.count() / (float)loopCount); 662 | } 663 | } 664 | 665 | // nullptr cast 666 | { 667 | // Prepare test vector 668 | vector> testObjects; 669 | testObjects.reserve(iterations * 3); 670 | 671 | for (int i = 0; i < iterations; i++) 672 | { 673 | testObjects.emplace_back(nullptr); 674 | testObjects.emplace_back(nullptr); 675 | testObjects.emplace_back(nullptr); 676 | } 677 | 678 | // Upcast std 679 | { 680 | auto before = steady_clock::now(); 681 | 682 | RunDynamicCastTest(testObjects, loopCount); 683 | 684 | auto after = steady_clock::now(); 685 | duration deltaTime = after - before; 686 | 687 | printf("Nullptr cast STD i: %zu, time (ms): %f\n", testObjects.size(), deltaTime.count() / (float)loopCount); 688 | } 689 | 690 | // Upcast kcl 691 | { 692 | auto before = steady_clock::now(); 693 | 694 | RunKCLCastTest(testObjects, loopCount); 695 | 696 | auto after = steady_clock::now(); 697 | duration deltaTime = after - before; 698 | 699 | printf("Nullptr cast KCL i: %zu, time (ms): %f\n", testObjects.size(), deltaTime.count() / (float)loopCount); 700 | } 701 | } 702 | 703 | // Multiple inheritance, base1, 1 level deep 704 | { 705 | // Prepare test vector 706 | vector> testObjects; 707 | testObjects.reserve(iterations * 3); 708 | 709 | for (int i = 0; i < iterations; i++) 710 | { 711 | testObjects.emplace_back(make_shared()); 712 | testObjects.emplace_back(make_shared()); 713 | testObjects.emplace_back(make_shared()); 714 | } 715 | 716 | // Upcast std 717 | { 718 | auto before = steady_clock::now(); 719 | 720 | RunDynamicCastTest(testObjects, loopCount); 721 | 722 | auto after = steady_clock::now(); 723 | duration deltaTime = after - before; 724 | 725 | printf("Multiple inheritance, base1, 1 level deep. STD Upcast i: %zu, time (ms): %f\n", testObjects.size(), 726 | deltaTime.count() / (float)loopCount); 727 | } 728 | 729 | // Upcast kcl 730 | { 731 | auto before = steady_clock::now(); 732 | 733 | RunKCLCastTest(testObjects, loopCount); 734 | 735 | auto after = steady_clock::now(); 736 | duration deltaTime = after - before; 737 | 738 | printf("Multiple inheritance, base1, 1 level deep. KCL Upcast i: %zu, time (ms): %f\n", testObjects.size(), 739 | deltaTime.count() / (float)loopCount); 740 | } 741 | 742 | // Downcast std 743 | { 744 | auto before = steady_clock::now(); 745 | 746 | RunDynamicCastTest(testObjects, loopCount); 747 | 748 | auto after = steady_clock::now(); 749 | duration deltaTime = after - before; 750 | 751 | printf("Multiple inheritance, base1, 1 level deep. STD Downcast i: %zu, time (ms): %f\n", testObjects.size(), 752 | deltaTime.count() / (float)loopCount); 753 | } 754 | 755 | // Downcast kcl 756 | { 757 | auto before = steady_clock::now(); 758 | 759 | RunKCLCastTest(testObjects, loopCount); 760 | 761 | auto after = steady_clock::now(); 762 | duration deltaTime = after - before; 763 | 764 | printf("Multiple inheritance, base1, 1 level deep. KCL Downcast i: %zu, time (ms): %f\n", testObjects.size(), 765 | deltaTime.count() / (float)loopCount); 766 | } 767 | } 768 | 769 | // Multiple inheritance, base2, 1 level deep 770 | { 771 | // Prepare test vector 772 | vector> testObjects; 773 | testObjects.reserve(iterations * 3); 774 | 775 | for (int i = 0; i < iterations; i++) 776 | { 777 | testObjects.emplace_back(make_shared()); 778 | testObjects.emplace_back(make_shared()); 779 | testObjects.emplace_back(make_shared()); 780 | } 781 | 782 | // Upcast std 783 | { 784 | auto before = steady_clock::now(); 785 | 786 | RunDynamicCastTest(testObjects, loopCount); 787 | 788 | auto after = steady_clock::now(); 789 | duration deltaTime = after - before; 790 | 791 | printf("Multiple inheritance, base2, 1 level deep. STD Upcast i: %zu, time (ms): %f\n", testObjects.size(), 792 | deltaTime.count() / (float)loopCount); 793 | } 794 | 795 | // Upcast kcl 796 | { 797 | auto before = steady_clock::now(); 798 | 799 | RunKCLCastTest(testObjects, loopCount); 800 | 801 | auto after = steady_clock::now(); 802 | duration deltaTime = after - before; 803 | 804 | printf("Multiple inheritance, base2, 1 level deep. KCL Upcast i: %zu, time (ms): %f\n", testObjects.size(), 805 | deltaTime.count() / (float)loopCount); 806 | } 807 | 808 | // Downcast std 809 | { 810 | auto before = steady_clock::now(); 811 | 812 | RunDynamicCastTest(testObjects, loopCount); 813 | 814 | auto after = steady_clock::now(); 815 | duration deltaTime = after - before; 816 | 817 | printf("Multiple inheritance, base2, 1 level deep. STD Downcast i: %zu, time (ms): %f\n", testObjects.size(), 818 | deltaTime.count() / (float)loopCount); 819 | } 820 | 821 | // Downcast kcl 822 | { 823 | auto before = steady_clock::now(); 824 | 825 | RunKCLCastTest(testObjects, loopCount); 826 | 827 | auto after = steady_clock::now(); 828 | duration deltaTime = after - before; 829 | 830 | printf("Multiple inheritance, base2, 1 level deep. KCL Downcast i: %zu, time (ms): %f\n", testObjects.size(), 831 | deltaTime.count() / (float)loopCount); 832 | } 833 | } 834 | 835 | // Multiple inheritance, base1, 3 level deep 836 | { 837 | // Prepare test vector 838 | vector> testObjects; 839 | testObjects.reserve(iterations * 3); 840 | 841 | for (int i = 0; i < iterations; i++) 842 | { 843 | testObjects.emplace_back(make_shared()); 844 | testObjects.emplace_back(make_shared()); 845 | testObjects.emplace_back(make_shared()); 846 | } 847 | 848 | // Upcast std 849 | { 850 | auto before = steady_clock::now(); 851 | 852 | RunDynamicCastTest(testObjects, loopCount); 853 | 854 | auto after = steady_clock::now(); 855 | duration deltaTime = after - before; 856 | 857 | printf("Multiple inheritance, base1, 3 level deep. STD Upcast i: %zu, time (ms): %f\n", testObjects.size(), 858 | deltaTime.count() / (float)loopCount); 859 | } 860 | 861 | // Upcast kcl 862 | { 863 | auto before = steady_clock::now(); 864 | 865 | RunKCLCastTest(testObjects, loopCount); 866 | 867 | auto after = steady_clock::now(); 868 | duration deltaTime = after - before; 869 | 870 | printf("Multiple inheritance, base1, 3 level deep. KCL Upcast i: %zu, time (ms): %f\n", testObjects.size(), 871 | deltaTime.count() / (float)loopCount); 872 | } 873 | 874 | // Downcast std 875 | { 876 | auto before = steady_clock::now(); 877 | 878 | RunDynamicCastTest(testObjects, loopCount); 879 | 880 | auto after = steady_clock::now(); 881 | duration deltaTime = after - before; 882 | 883 | printf("Multiple inheritance, base1, 3 level deep. STD Downcast i: %zu, time (ms): %f\n", testObjects.size(), 884 | deltaTime.count() / (float)loopCount); 885 | } 886 | 887 | // Downcast kcl 888 | { 889 | auto before = steady_clock::now(); 890 | 891 | RunKCLCastTest(testObjects, loopCount); 892 | 893 | auto after = steady_clock::now(); 894 | duration deltaTime = after - before; 895 | 896 | printf("Multiple inheritance, base1, 3 level deep. KCL Downcast i: %zu, time (ms): %f\n", testObjects.size(), 897 | deltaTime.count() / (float)loopCount); 898 | } 899 | } 900 | 901 | // Multiple inheritance, base2, 3 level deep 902 | { 903 | // Prepare test vector 904 | vector> testObjects; 905 | testObjects.reserve(iterations * 3); 906 | 907 | for (int i = 0; i < iterations; i++) 908 | { 909 | testObjects.emplace_back(make_shared()); 910 | testObjects.emplace_back(make_shared()); 911 | testObjects.emplace_back(make_shared()); 912 | } 913 | 914 | // Upcast std 915 | { 916 | auto before = steady_clock::now(); 917 | 918 | RunDynamicCastTest(testObjects, loopCount); 919 | 920 | auto after = steady_clock::now(); 921 | duration deltaTime = after - before; 922 | 923 | printf("Multiple inheritance, base2, 3 level deep. STD Upcast i: %zu, time (ms): %f\n", testObjects.size(), 924 | deltaTime.count() / (float)loopCount); 925 | } 926 | 927 | // Upcast kcl 928 | { 929 | auto before = steady_clock::now(); 930 | 931 | RunKCLCastTest(testObjects, loopCount); 932 | 933 | auto after = steady_clock::now(); 934 | duration deltaTime = after - before; 935 | 936 | printf("Multiple inheritance, base2, 3 level deep. KCL Upcast i: %zu, time (ms): %f\n", testObjects.size(), 937 | deltaTime.count() / (float)loopCount); 938 | } 939 | 940 | // Downcast std 941 | { 942 | auto before = steady_clock::now(); 943 | 944 | RunDynamicCastTest(testObjects, loopCount); 945 | 946 | auto after = steady_clock::now(); 947 | duration deltaTime = after - before; 948 | 949 | printf("Multiple inheritance, base2, 3 level deep. STD Downcast i: %zu, time (ms): %f\n", testObjects.size(), 950 | deltaTime.count() / (float)loopCount); 951 | } 952 | 953 | // Downcast kcl 954 | { 955 | auto before = steady_clock::now(); 956 | 957 | RunKCLCastTest(testObjects, loopCount); 958 | 959 | auto after = steady_clock::now(); 960 | duration deltaTime = after - before; 961 | 962 | printf("Multiple inheritance, base2, 3 level deep. KCL Downcast i: %zu, time (ms): %f\n", testObjects.size(), 963 | deltaTime.count() / (float)loopCount); 964 | } 965 | } 966 | 967 | // Multiple inheritance, base1, 7 levels deep for greater effect 968 | { 969 | // Prepare test vector 970 | vector> testObjects; 971 | testObjects.reserve(iterations * 3); 972 | 973 | for (int i = 0; i < iterations; i++) 974 | { 975 | testObjects.emplace_back(make_shared()); 976 | testObjects.emplace_back(make_shared()); 977 | testObjects.emplace_back(make_shared()); 978 | } 979 | 980 | // Upcast std 981 | { 982 | auto before = steady_clock::now(); 983 | 984 | RunDynamicCastTest(testObjects, loopCount); 985 | 986 | auto after = steady_clock::now(); 987 | duration deltaTime = after - before; 988 | 989 | printf("Multiple inheritance, base1, 7 level deep. STD Upcast i: %zu, time (ms): %f\n", testObjects.size(), 990 | deltaTime.count() / (float)loopCount); 991 | } 992 | 993 | // Upcast kcl 994 | { 995 | auto before = steady_clock::now(); 996 | 997 | RunKCLCastTest(testObjects, loopCount); 998 | 999 | auto after = steady_clock::now(); 1000 | duration deltaTime = after - before; 1001 | 1002 | printf("Multiple inheritance, base1, 7 level deep. KCL Upcast i: %zu, time (ms): %f\n", testObjects.size(), 1003 | deltaTime.count() / (float)loopCount); 1004 | } 1005 | 1006 | // Downcast std 1007 | { 1008 | auto before = steady_clock::now(); 1009 | 1010 | RunDynamicCastTest(testObjects, loopCount); 1011 | 1012 | auto after = steady_clock::now(); 1013 | duration deltaTime = after - before; 1014 | 1015 | printf("Multiple inheritance, base1, 7 level deep. STD Downcast i: %zu, time (ms): %f\n", testObjects.size(), 1016 | deltaTime.count() / (float)loopCount); 1017 | } 1018 | 1019 | // Downcast kcl 1020 | { 1021 | auto before = steady_clock::now(); 1022 | 1023 | RunKCLCastTest(testObjects, loopCount); 1024 | 1025 | auto after = steady_clock::now(); 1026 | duration deltaTime = after - before; 1027 | 1028 | printf("Multiple inheritance, base1, 7 level deep. KCL Downcast i: %zu, time (ms): %f\n", testObjects.size(), 1029 | deltaTime.count() / (float)loopCount); 1030 | } 1031 | } 1032 | 1033 | // Multiple inheritance, base2, 7 levels deep for greater effect 1034 | { 1035 | // Prepare test vector 1036 | vector> testObjects; 1037 | testObjects.reserve(iterations * 3); 1038 | 1039 | for (int i = 0; i < iterations; i++) 1040 | { 1041 | testObjects.emplace_back(make_shared()); 1042 | testObjects.emplace_back(make_shared()); 1043 | testObjects.emplace_back(make_shared()); 1044 | } 1045 | 1046 | // Upcast std 1047 | { 1048 | auto before = steady_clock::now(); 1049 | 1050 | RunDynamicCastTest(testObjects, loopCount); 1051 | 1052 | auto after = steady_clock::now(); 1053 | duration deltaTime = after - before; 1054 | 1055 | printf("Multiple inheritance, base2, 7 level deep. STD Upcast i: %zu, time (ms): %f\n", testObjects.size(), 1056 | deltaTime.count() / (float)loopCount); 1057 | } 1058 | 1059 | // Upcast kcl 1060 | { 1061 | auto before = steady_clock::now(); 1062 | 1063 | RunKCLCastTest(testObjects, loopCount); 1064 | 1065 | auto after = steady_clock::now(); 1066 | duration deltaTime = after - before; 1067 | 1068 | printf("Multiple inheritance, base2, 7 level deep. KCL Upcast i: %zu, time (ms): %f\n", testObjects.size(), 1069 | deltaTime.count() / (float)loopCount); 1070 | } 1071 | 1072 | // Downcast std 1073 | { 1074 | auto before = steady_clock::now(); 1075 | 1076 | RunDynamicCastTest(testObjects, loopCount); 1077 | 1078 | auto after = steady_clock::now(); 1079 | duration deltaTime = after - before; 1080 | 1081 | printf("Multiple inheritance, base2, 7 level deep. STD Downcast i: %zu, time (ms): %f\n", testObjects.size(), 1082 | deltaTime.count() / (float)loopCount); 1083 | } 1084 | 1085 | // Downcast kcl 1086 | { 1087 | auto before = steady_clock::now(); 1088 | 1089 | RunKCLCastTest(testObjects, loopCount); 1090 | 1091 | auto after = steady_clock::now(); 1092 | duration deltaTime = after - before; 1093 | 1094 | printf("Multiple inheritance, base2, 7 level deep. KCL Downcast i: %zu, time (ms): %f\n", testObjects.size(), 1095 | deltaTime.count() / (float)loopCount); 1096 | } 1097 | 1098 | // Multiple inheritance 3*2 1099 | { 1100 | // Prepare test vector 1101 | vector> testObjects; 1102 | testObjects.reserve(iterations * 3); 1103 | 1104 | for (int i = 0; i < iterations; i++) 1105 | { 1106 | testObjects.emplace_back(make_shared()); 1107 | testObjects.emplace_back(make_shared()); 1108 | testObjects.emplace_back(make_shared()); 1109 | } 1110 | 1111 | // Upcast std 1112 | { 1113 | auto before = steady_clock::now(); 1114 | 1115 | RunDynamicCastTest(testObjects, loopCount); 1116 | 1117 | auto after = steady_clock::now(); 1118 | duration deltaTime = after - before; 1119 | 1120 | printf("Nested Multiple Inheritance 3*2 STD Upcast i: %zu, time (ms): %f\n", testObjects.size(), 1121 | deltaTime.count() / (float)loopCount); 1122 | } 1123 | 1124 | // Upcast kcl 1125 | { 1126 | auto before = steady_clock::now(); 1127 | 1128 | RunKCLCastTest(testObjects, loopCount); 1129 | 1130 | auto after = steady_clock::now(); 1131 | duration deltaTime = after - before; 1132 | 1133 | printf("Nested Multiple Inheritance 3*2 KCL Upcast i: %zu, time (ms): %f\n", testObjects.size(), 1134 | deltaTime.count() / (float)loopCount); 1135 | } 1136 | 1137 | // Downcast std 1138 | { 1139 | auto before = steady_clock::now(); 1140 | 1141 | RunDynamicCastTest(testObjects, loopCount); 1142 | 1143 | auto after = steady_clock::now(); 1144 | duration deltaTime = after - before; 1145 | 1146 | printf("Nested Multiple Inheritance 3*2 STD Downcast i: %zu, time (ms): %f\n", testObjects.size(), 1147 | deltaTime.count() / (float)loopCount); 1148 | } 1149 | 1150 | // Downcast kcl 1151 | { 1152 | auto before = steady_clock::now(); 1153 | 1154 | RunKCLCastTest(testObjects, loopCount); 1155 | 1156 | auto after = steady_clock::now(); 1157 | duration deltaTime = after - before; 1158 | 1159 | printf("Nested Multiple Inheritance 3*2 KCL Downcast i: %zu, time (ms): %f\n", testObjects.size(), 1160 | deltaTime.count() / (float)loopCount); 1161 | } 1162 | } 1163 | } 1164 | 1165 | printf("Valid cast counter: %d", validCastCounter); 1166 | } 1167 | } // namespace KCL_Test 1168 | -------------------------------------------------------------------------------- /Source/KCL_Test/KCL_RTTI_Test.h: -------------------------------------------------------------------------------- 1 | // MIT License 2 | // 3 | // Copyright(c) 2019 Samuel Kahn 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files(the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions : 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | #pragma once 24 | 25 | namespace KCL_Test 26 | { 27 | void RTTI_Test(); 28 | void RTTI_Benchmark(); 29 | } // namespace KCL_Test 30 | -------------------------------------------------------------------------------- /Source/KCL_Test/main.cpp: -------------------------------------------------------------------------------- 1 | // MIT License 2 | // 3 | // Copyright(c) 2019 Samuel Kahn 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files(the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions : 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | #include "KCL_RTTI_Test.h" 24 | #include 25 | 26 | int main(int argc, char* argv[]) 27 | { 28 | KCL_Test::RTTI_Test(); 29 | KCL_Test::RTTI_Benchmark(); 30 | return 0; 31 | } 32 | --------------------------------------------------------------------------------