├── .clang-format ├── .github └── workflows │ └── cmake-multi-platform.yml ├── .gitignore ├── .gitmodules ├── CMakeLists.txt ├── ReadMe.md ├── mirrow ├── CMakeLists.txt ├── mirrow │ ├── assert.hpp │ ├── drefl │ │ ├── any.hpp │ │ ├── array.hpp │ │ ├── array_operations.hpp │ │ ├── bool.hpp │ │ ├── cast_any.hpp │ │ ├── class.hpp │ │ ├── class_visitor.hpp │ │ ├── config.hpp │ │ ├── drefl.hpp │ │ ├── enum.hpp │ │ ├── exception.hpp │ │ ├── factory.hpp │ │ ├── make_any.hpp │ │ ├── numeric.hpp │ │ ├── operation_traits.hpp │ │ ├── optional.hpp │ │ ├── pointer.hpp │ │ ├── property.hpp │ │ ├── property_factory.hpp │ │ ├── qualifier.hpp │ │ ├── raw_type.hpp │ │ ├── string.hpp │ │ ├── type.hpp │ │ └── value_kind.hpp │ ├── serd │ │ ├── dynamic │ │ │ └── backends │ │ │ │ └── tomlplusplus.hpp │ │ └── static │ │ │ └── backends │ │ │ └── tomlplusplus.hpp │ ├── srefl │ │ ├── reflect.hpp │ │ ├── srefl.hpp │ │ ├── srefl_begin.hpp │ │ └── srefl_end.hpp │ └── util │ │ ├── const_str.hpp │ │ ├── function_traits.hpp │ │ ├── misc.hpp │ │ ├── type_list.hpp │ │ └── variable_traits.hpp └── src │ ├── drefl │ ├── any.cpp │ ├── array.cpp │ ├── bool.cpp │ ├── class.cpp │ ├── numeric.cpp │ ├── operation_traits.cpp │ ├── property.cpp │ ├── string.cpp │ └── type.cpp │ └── serd │ └── dynamic │ └── backends │ ├── tomlplusplus_serialize.cpp │ └── tomlpulspuls_deserialize.cpp └── tests ├── CMakeLists.txt ├── catch.hpp ├── drefl ├── CMakeLists.txt ├── any.cpp └── factory.cpp ├── serd ├── CMakeLists.txt ├── drefl_serd.cpp ├── srefl_serd.cpp └── srefl_serd_custom.cpp ├── srefl ├── CMakeLists.txt ├── reflect.cpp └── srefl.cpp └── util ├── CMakeLists.txt ├── const_str.cpp ├── function_traits.cpp ├── misc.cpp ├── type_list.cpp └── variable_traits.cpp /.clang-format: -------------------------------------------------------------------------------- 1 | --- 2 | Language: Cpp 3 | # BasedOnStyle: Google 4 | AccessModifierOffset: -4 5 | AlignAfterOpenBracket: Align 6 | AlignArrayOfStructures: Right 7 | AlignConsecutiveMacros: None 8 | AlignConsecutiveAssignments: None 9 | AlignConsecutiveBitFields: None 10 | AlignConsecutiveDeclarations: None 11 | AlignEscapedNewlines: Left 12 | AlignOperands: Align 13 | AlignTrailingComments: true 14 | AllowAllArgumentsOnNextLine: true 15 | AllowAllParametersOfDeclarationOnNextLine: true 16 | AllowShortEnumsOnASingleLine: true 17 | AllowShortBlocksOnASingleLine: Never 18 | AllowShortCaseLabelsOnASingleLine: false 19 | AllowShortFunctionsOnASingleLine: Inline 20 | AllowShortLambdasOnASingleLine: All 21 | AllowShortIfStatementsOnASingleLine: WithoutElse 22 | AllowShortLoopsOnASingleLine: true 23 | AlwaysBreakAfterDefinitionReturnType: None 24 | AlwaysBreakAfterReturnType: None 25 | AlwaysBreakBeforeMultilineStrings: false 26 | AlwaysBreakTemplateDeclarations: Yes 27 | AttributeMacros: 28 | - __capability 29 | BinPackArguments: true 30 | BinPackParameters: true 31 | BraceWrapping: 32 | AfterCaseLabel: false 33 | AfterClass: false 34 | AfterControlStatement: Never 35 | AfterEnum: false 36 | AfterFunction: false 37 | AfterNamespace: false 38 | AfterObjCDeclaration: false 39 | AfterStruct: false 40 | AfterUnion: false 41 | AfterExternBlock: false 42 | BeforeCatch: false 43 | BeforeElse: false 44 | BeforeLambdaBody: false 45 | BeforeWhile: false 46 | IndentBraces: false 47 | SplitEmptyFunction: true 48 | SplitEmptyRecord: true 49 | SplitEmptyNamespace: true 50 | BreakBeforeBinaryOperators: None 51 | BreakBeforeConceptDeclarations: true 52 | BreakBeforeBraces: Attach 53 | BreakBeforeInheritanceComma: false 54 | BreakInheritanceList: BeforeColon 55 | BreakBeforeTernaryOperators: true 56 | BreakConstructorInitializersBeforeComma: false 57 | BreakConstructorInitializers: BeforeColon 58 | BreakAfterJavaFieldAnnotations: false 59 | BreakStringLiterals: true 60 | ColumnLimit: 80 61 | CommentPragmas: '^ IWYU pragma:' 62 | QualifierAlignment: Leave 63 | CompactNamespaces: false 64 | ConstructorInitializerIndentWidth: 4 65 | ContinuationIndentWidth: 4 66 | Cpp11BracedListStyle: true 67 | DeriveLineEnding: true 68 | DerivePointerAlignment: true 69 | DisableFormat: false 70 | EmptyLineAfterAccessModifier: Never 71 | EmptyLineBeforeAccessModifier: LogicalBlock 72 | ExperimentalAutoDetectBinPacking: false 73 | PackConstructorInitializers: NextLine 74 | BasedOnStyle: '' 75 | ConstructorInitializerAllOnOneLineOrOnePerLine: false 76 | AllowAllConstructorInitializersOnNextLine: true 77 | FixNamespaceComments: true 78 | ForEachMacros: 79 | - foreach 80 | - Q_FOREACH 81 | - BOOST_FOREACH 82 | IfMacros: 83 | - KJ_IF_MAYBE 84 | # IncludeBlocks: Regroup 85 | # IncludeCategories: 86 | # - Regex: '^' 87 | # Priority: 2 88 | # SortPriority: 0 89 | # CaseSensitive: false 90 | # - Regex: '^<.*\.h>' 91 | # Priority: 1 92 | # SortPriority: 0 93 | # CaseSensitive: false 94 | # - Regex: '^<.*' 95 | # Priority: 2 96 | # SortPriority: 0 97 | # CaseSensitive: false 98 | # - Regex: '.*' 99 | # Priority: 3 100 | # SortPriority: 0 101 | # CaseSensitive: false 102 | # IncludeIsMainRegex: '([-_](test|unittest))?$' 103 | # IncludeIsMainSourceRegex: '' 104 | IndentAccessModifiers: false 105 | IndentCaseLabels: true 106 | IndentCaseBlocks: false 107 | IndentGotoLabels: true 108 | IndentPPDirectives: None 109 | IndentExternBlock: AfterExternBlock 110 | IndentRequires: false 111 | IndentWidth: 4 112 | IndentWrappedFunctionNames: false 113 | InsertTrailingCommas: None 114 | JavaScriptQuotes: Leave 115 | JavaScriptWrapImports: true 116 | KeepEmptyLinesAtTheStartOfBlocks: false 117 | LambdaBodyIndentation: Signature 118 | MacroBlockBegin: '' 119 | MacroBlockEnd: '' 120 | MaxEmptyLinesToKeep: 1 121 | NamespaceIndentation: None 122 | ObjCBinPackProtocolList: Never 123 | ObjCBlockIndentWidth: 2 124 | ObjCBreakBeforeNestedBlockParam: true 125 | ObjCSpaceAfterProperty: false 126 | ObjCSpaceBeforeProtocolList: true 127 | PenaltyBreakAssignment: 2 128 | PenaltyBreakBeforeFirstCallParameter: 1 129 | PenaltyBreakComment: 300 130 | PenaltyBreakFirstLessLess: 120 131 | PenaltyBreakOpenParenthesis: 0 132 | PenaltyBreakString: 1000 133 | PenaltyBreakTemplateDeclaration: 10 134 | PenaltyExcessCharacter: 1000000 135 | PenaltyReturnTypeOnItsOwnLine: 200 136 | PenaltyIndentedWhitespace: 0 137 | PointerAlignment: Left 138 | PPIndentWidth: -1 139 | RawStringFormats: 140 | - Language: Cpp 141 | Delimiters: 142 | - cc 143 | - CC 144 | - cpp 145 | - Cpp 146 | - CPP 147 | - 'c++' 148 | - 'C++' 149 | CanonicalDelimiter: '' 150 | BasedOnStyle: google 151 | - Language: TextProto 152 | Delimiters: 153 | - pb 154 | - PB 155 | - proto 156 | - PROTO 157 | EnclosingFunctions: 158 | - EqualsProto 159 | - EquivToProto 160 | - PARSE_PARTIAL_TEXT_PROTO 161 | - PARSE_TEST_PROTO 162 | - PARSE_TEXT_PROTO 163 | - ParseTextOrDie 164 | - ParseTextProtoOrDie 165 | - ParseTestProto 166 | - ParsePartialTestProto 167 | CanonicalDelimiter: pb 168 | BasedOnStyle: google 169 | ReferenceAlignment: Pointer 170 | ReflowComments: true 171 | RemoveBracesLLVM: false 172 | SeparateDefinitionBlocks: Always 173 | ShortNamespaceLines: 1 174 | # SortIncludes: CaseSensitive 175 | SortJavaStaticImport: Before 176 | SortUsingDeclarations: true 177 | SpaceAfterCStyleCast: false 178 | SpaceAfterLogicalNot: false 179 | SpaceAfterTemplateKeyword: true 180 | SpaceBeforeAssignmentOperators: true 181 | SpaceBeforeCaseColon: false 182 | SpaceBeforeCpp11BracedList: false 183 | SpaceBeforeCtorInitializerColon: true 184 | SpaceBeforeInheritanceColon: true 185 | SpaceBeforeParens: ControlStatements 186 | SpaceBeforeParensOptions: 187 | AfterControlStatements: true 188 | AfterForeachMacros: true 189 | AfterFunctionDefinitionName: false 190 | AfterFunctionDeclarationName: false 191 | AfterIfMacros: true 192 | AfterOverloadedOperator: false 193 | BeforeNonEmptyParentheses: false 194 | SpaceAroundPointerQualifiers: Default 195 | SpaceBeforeRangeBasedForLoopColon: true 196 | SpaceInEmptyBlock: false 197 | SpaceInEmptyParentheses: false 198 | SpacesBeforeTrailingComments: 2 199 | SpacesInAngles: Never 200 | SpacesInConditionalStatement: false 201 | SpacesInContainerLiterals: true 202 | SpacesInCStyleCastParentheses: false 203 | SpacesInLineCommentPrefix: 204 | Minimum: 1 205 | Maximum: -1 206 | SpacesInParentheses: false 207 | SpacesInSquareBrackets: false 208 | SpaceBeforeSquareBrackets: false 209 | BitFieldColonSpacing: Both 210 | Standard: Auto 211 | StatementAttributeLikeMacros: 212 | - Q_EMIT 213 | StatementMacros: 214 | - Q_UNUSED 215 | - QT_REQUIRE_VERSION 216 | TabWidth: 8 217 | UseCRLF: false 218 | UseTab: Never 219 | WhitespaceSensitiveMacros: 220 | - STRINGIZE 221 | - PP_STRINGIZE 222 | - BOOST_PP_STRINGIZE 223 | - NS_SWIFT_NAME 224 | - CF_SWIFT_NAME 225 | ... 226 | 227 | -------------------------------------------------------------------------------- /.github/workflows/cmake-multi-platform.yml: -------------------------------------------------------------------------------- 1 | # This starter workflow is for a CMake project running on multiple platforms. There is a different starter workflow if you just want a single platform. 2 | # See: https://github.com/actions/starter-workflows/blob/main/ci/cmake-single-platform.yml 3 | name: CMake on multiple platforms 4 | 5 | on: 6 | push: 7 | branches: [ "main" ] 8 | pull_request: 9 | branches: [ "main" ] 10 | 11 | jobs: 12 | build: 13 | runs-on: ${{ matrix.os }} 14 | 15 | strategy: 16 | # Set fail-fast to false to ensure that feedback is delivered for all matrix combinations. Consider changing this to true when your workflow is stable. 17 | fail-fast: false 18 | 19 | # Set up a matrix to run the following 3 configurations: 20 | # 1. 21 | # 2. 22 | # 3. 23 | # 24 | # To add more build types (Release, Debug, RelWithDebInfo, etc.) customize the build_type list. 25 | matrix: 26 | os: [ubuntu-latest, windows-latest] 27 | build_type: [Release] 28 | c_compiler: [gcc, clang, cl] 29 | include: 30 | - os: windows-latest 31 | c_compiler: cl 32 | cpp_compiler: cl 33 | - os: ubuntu-latest 34 | c_compiler: gcc 35 | cpp_compiler: g++ 36 | - os: ubuntu-latest 37 | c_compiler: clang 38 | cpp_compiler: clang++ 39 | exclude: 40 | - os: windows-latest 41 | c_compiler: gcc 42 | - os: windows-latest 43 | c_compiler: clang 44 | - os: ubuntu-latest 45 | c_compiler: cl 46 | 47 | steps: 48 | - uses: actions/checkout@v3 49 | 50 | - name: Set reusable strings 51 | # Turn repeated input strings (such as the build output directory) into step outputs. These step outputs can be used throughout the workflow file. 52 | id: strings 53 | shell: bash 54 | run: | 55 | echo "build-output-dir=${{ github.workspace }}/build" >> "$GITHUB_OUTPUT" 56 | 57 | - name: pull submoules 58 | run: git submodule update --init --recursive 59 | 60 | - name: Configure CMake 61 | # Configure CMake in a 'build' subdirectory. `CMAKE_BUILD_TYPE` is only required if you are using a single-configuration generator such as make. 62 | # See https://cmake.org/cmake/help/latest/variable/CMAKE_BUILD_TYPE.html?highlight=cmake_build_type 63 | run: > 64 | cmake -B ${{ steps.strings.outputs.build-output-dir }} 65 | -DCMAKE_CXX_COMPILER=${{ matrix.cpp_compiler }} 66 | -DCMAKE_C_COMPILER=${{ matrix.c_compiler }} 67 | -DCMAKE_BUILD_TYPE=${{ matrix.build_type }} 68 | -S ${{ github.workspace }} 69 | 70 | - name: Build 71 | # Build your program with the given configuration. Note that --config is needed because the default Windows generator is a multi-config generator (Visual Studio generator). 72 | run: cmake --build ${{ steps.strings.outputs.build-output-dir }} --config ${{ matrix.build_type }} 73 | 74 | - name: Test 75 | working-directory: ${{ steps.strings.outputs.build-output-dir }} 76 | # Execute tests defined by the CMake configuration. Note that --build-config is needed because the default Windows generator is a multi-config generator (Visual Studio generator). 77 | # See https://cmake.org/cmake/help/latest/manual/ctest.1.html for more detail 78 | run: ctest --build-config ${{ matrix.build_type }} 79 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | cmake-build 2 | .vscode 3 | compile_commands.json 4 | .cache 5 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "mirrow/serd_backend/tomlplusplus"] 2 | path = mirrow/serd_backend/tomlplusplus 3 | url = https://github.com/marzer/tomlplusplus.git 4 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.20) 2 | project(mirrow 3 | VERSION 1.0.0 4 | LANGUAGES C CXX) 5 | 6 | option(MIRROW_BUILD_TOMLPLUSPLUS_SERD "build serialization of toml++" OFF) 7 | option(MIRROW_BUILD_TEST "build test" OFF) 8 | 9 | if (PROJECT_IS_TOP_LEVEL) 10 | set(MIRROW_BUILD_TOMLPLUSPLUS_SERD ON) 11 | set(MIRROW_BUILD_TEST ON) 12 | endif() 13 | 14 | set_property(GLOBAL PROPERTY USE_FOLDERS ON) 15 | 16 | add_subdirectory(mirrow) 17 | 18 | if (MIRROW_BUILD_TEST) 19 | enable_testing() 20 | add_subdirectory(tests) 21 | endif() -------------------------------------------------------------------------------- /ReadMe.md: -------------------------------------------------------------------------------- 1 | # Mirrow - A Template Meta Programming utility framework 2 | 3 | `mirrow` is a TMP(template meta programming) utility framework in C++17. It aimed to make some utility to help programmer do TMP easier. Referenced [meta](https://github.com/skypjack/meta) and [ponder](https://github.com/billyquith/ponder). 4 | 5 | Nowadays, `mirrow` has these parts: 6 | 7 | * `util`: some common utilities 8 | * `srefl`: static reflection framework 9 | * `drefl`: dynamic reflection framework 10 | * `serd`: a serialize framework based on reflection(serial with `drefl`&`srefl`) with TOML 11 | 12 | ## :book: docs 13 | 14 | ### util 15 | 16 | `util`(utility) has some convenient utility to do TMP: 17 | 18 | * `type_list`: compile-time type list. [:microscope: unittest](./test/utility/type_list.cpp) 19 | * `function_traits`: compile-time function info trait. [:microscope: unittest](./test/utility/function_traits.cpp) 20 | * `variable_traits`: compile-time variable info trait. [:microscope: unittest](./test/utility/variable_traits.cpp) 21 | * `const_str`: compile-time string. [:microscope: unittest](./test/utility/const_str.cpp) 22 | 23 | ### srefl 24 | 25 | static reflection framework. :microscope: [do reflect unittest](./test/srefl/srefl.cpp), [get reflected info unittest](./test/srefl/reflect.cpp) 26 | 27 | To reflect your class, you must do like this: 28 | 29 | ```cpp 30 | // your class 31 | class Foo final { 32 | public: 33 | void foo() {} 34 | void foo(int) {} 35 | void foo(float) {} 36 | void another() {} 37 | 38 | int value_1 = 1; 39 | int value_2 = 2; 40 | }; 41 | 42 | // include srefl_begin.hpp 43 | #include "mirrow/srefl/srefl_begin.hpp" 44 | // do your reflection 45 | srefl_class(Foo, 46 | ctors() 47 | fields( 48 | field(static_cast(&Foo::foo)), 49 | field(static_cast(&Foo::foo)), 50 | field(static_cast(&Foo::foo)), 51 | field(&Foo::another), 52 | field(&Foo::value_1), 53 | field(&Foo::value_2) 54 | ) 55 | ) 56 | // include srefl_end.hpp 57 | #include "mirrow/srefl/srefl_end.hpp" 58 | ``` 59 | 60 | [srefl_begin.hpp](./include/mirrow/srefl/srefl_begin.hpp) provide a bunch of macros to help you regist your class. And [srefl_end.hpp](./include/mirrow/srefl/srefl_end.hpp) will `#undef` these macros to avoid pollute your codes. 61 | 62 | Then, use `srefl_class(, ...)` to start regist your class. use `ctors()` to regist constructors(optional), use `fields(...)` to start regist member/static variable/functions. 63 | 64 | After reflect, you can use `auto refl = reflect();` to get reflected informations. And visit member variables: 65 | 66 | ```cpp 67 | refl.visit_member_variables([&vars](auto&& value) { 68 | vars.push_back(value.name()); 69 | }); 70 | ``` 71 | 72 | *visit function/static fields is WIP, it is easy to implement but currently I don't need them* 73 | 74 | ### drefl 75 | 76 | dynamic reflection framework. 77 | 78 | #### any 79 | 80 | `any` is similar to `std::any`, but support ownership :microscope:[unittest](./test/drefl/any.cpp) 81 | 82 | `any` has 3 ownership(defined in `any::access_type`): 83 | 84 | * `Null`: don't contain data 85 | * `ConstRef`: const reference to a value 86 | * `Ref`: mutable reference to a value 87 | * `Copy`: the data's ownership is any itself, when any destruct, data will destruct together 88 | 89 | use `any_make_xxx` to create an any from ownership: 90 | 91 | ```cpp 92 | int a = 123; 93 | auto cref = mirrow::drefl::any_make_constref(a); // make a const reference 94 | auto ref = mirrow::drefl::any_make_ref(a); // make a reference 95 | auto new_value = mirrow::drefl::any_make_copy(a); // copy a to any inner data 96 | ``` 97 | 98 | and use member function `constref()`, `ref()`, `copy()`, `steal()` to translate ownership. 99 | 100 | 101 | 102 | use `try_cast()` & `try_cast_const()` to cast any to a determined type. use `try_cast()` on `ConstRef` any will throw a `bad_any_access` exception and return `nullptr`. 103 | 104 | #### factory 105 | 106 | `factory` is where you reflect your type:microscope:[unittest](./test/drefl/factory.cpp) 107 | 108 | register your class by: 109 | 110 | ```cpp 111 | struct Person { 112 | std::string name; 113 | float height; 114 | const bool hasChild; 115 | const Person* couple; 116 | }; 117 | 118 | // in main(): 119 | mirrow::drefl::factory::instance() 120 | .regist("Person") 121 | .property("name", &Person::name) 122 | .property("height", &Person::height) 123 | .property("hasChild", &Person::hasChild) 124 | .property("couple", &Person::couple); 125 | ``` 126 | 127 | or register your enum by: 128 | 129 | ```cpp 130 | enum class MyEnum { 131 | Value1 = 1, 132 | Value2 = 2, 133 | Value3 = 3, 134 | }; 135 | 136 | // in main(): 137 | auto& inst = mirrow::drefl::factory::instance() 138 | .regist("MyEnum") 139 | .add("Value1", MyEnum::Value1) 140 | .add("Value2", MyEnum::Value2) 141 | .add("Value3", MyEnum::Value3); 142 | ``` 143 | 144 | then use 145 | 146 | ```cpp 147 | auto info = mirrow::drefl::typeinfo(); 148 | ``` 149 | 150 | to get registered type information; 151 | 152 | **NOTE: currently we don't support register member function.** 153 | 154 | 155 | 156 | there are may type information you can access(by member function`as_xxx()`): 157 | 158 | * `enum_info`: enumerates 159 | * `numeric`: numerics(`int`,`float`,`char`...) 160 | * `boolean`: boolean 161 | * `string`:`std::string` or `std::string_view` (may add `const char*` support later) 162 | * `pointer`: pointers like `T*`, `T* const`, `T**`... 163 | * `array`: `std::vector`, `std::array`, `std::list`, `T[N]` 164 | * `class`: other classes 165 | 166 | *future support:* 167 | 168 | * `map`: `std::unordered_map`, `std::map` 169 | * `set`: `std::unordered_set`, `std::set` 170 | * `optional`: `std::optional` 171 | * `smart points`: `std::unique_ptr`. `std::shared_ptr` 172 | * `pair`: `std::pair` 173 | 174 | 175 | ### serd 176 | 177 | A serialize/deserialize tools based on dynamic/static reflection. 178 | 179 | `serd` provide two serialize/deserialize method: dynamic and static, which need you use dynamic/static reflection to provide type info first. 180 | 181 | dynamic reflection based serialize [:microscope: unittest](./test/serd/drefl_serd.cpp) 182 | static reflection based serialize [:microscope: unittest](./test/serd/srefl_serd.cpp) 183 | 184 | After reflected type, you can do serialize like: 185 | 186 | ```cpp 187 | type instance; // create an instance 188 | 189 | // use static reflection based serialize 190 | toml::table tbl; 191 | mirrow::serd::srefl::serialize(instance, tbl); 192 | // use static reflection based deserialize 193 | mirrow::serd::srefl::deserialize(tbl, instance); 194 | 195 | // convert instance to any to prepare serialize 196 | mirrow::drefl::reference_any data{instance}; 197 | // use dynamic reflection based serialize 198 | toml::table tbl = mirrow::serd::drefl::serialize(data); 199 | // use dynamic reflection based deserialize 200 | mirrow::serd::drefl::deserialize(tbl, data); 201 | ``` 202 | 203 | If you don't know which toml node would be serialize/deserialize, you can use `mirrow::serd::srefl::serialize_destination_type_t` to get the type. 204 | 205 | #### custom serialize function 206 | 207 | all `serialize` and `deserialize` function will iterate all member fields in your type info and \[de\]serialize them. If field not exists when deserialize, it will log and ignore this field. 208 | 209 | There are some inner-support type: 210 | 211 | * numeric(integer like `int`, `char` ..., and floating point(`float`, `double`)) 212 | * `bool` 213 | * `std::vector` 214 | * `std::array` 215 | * `std::optional` 216 | * `std::unordered_map` 217 | * `std::unordered_set` 218 | 219 | If you want do specific \[de\]serialize method on your own type, here: 220 | 221 | for static \[de\]serialize, need two step: 222 | 223 | ```cpp 224 | namespace mirrow::serd::srefl { 225 | 226 | // 1. tell serd which toml node you want to serialize to 227 | // use SFINEA 228 | namespace impl { 229 | 230 | template <> 231 | struct serialize_destination_type { 232 | // we want [de]serialize to/from toml::value 233 | using type = toml::value; 234 | }; 235 | 236 | } 237 | 238 | 239 | // 2. provide your [de]serialize method 240 | // also use SFINEA, serialize function 241 | template 242 | std::enable_if_t> 243 | serialize(const T& value, serialize_destination_type_t& node) { 244 | // try put value into node 245 | ... 246 | } 247 | 248 | // also use SFINEA, deserialize function 249 | template 250 | std::enable_if_t> 251 | deserialize(const toml::node& node, T& elem) { 252 | // try parse elem from node 253 | ... 254 | } 255 | 256 | } 257 | ``` 258 | 259 | for dynamic \[de\]serialize, you need regist your function into `serialize_method_storage` at runtime: 260 | 261 | ```cpp 262 | mirrow::serd::drefl::serialize_method_storage::instance().regist(type_info, serialize_fn, deserialize_fn); 263 | ``` 264 | 265 | the second and thrid param is your serialize/deserialize function, must be: 266 | 267 | ```cpp 268 | // serialize 269 | void serialize(toml::node&, const any&)>; 270 | // deserialize 271 | void deserialize(const toml::node&, any&)>; 272 | ``` 273 | 274 | after these, you can use serialize/deserialize: 275 | 276 | ```cpp 277 | Person p; 278 | mirrow::drefl::any any = mirrow::drefl::any_make_ref(p); 279 | 280 | // serialize to toml node 281 | auto tbl = ::mirrow::serd::drefl::serialize(any); 282 | 283 | // deserialize from toml node 284 | ::mirrow::serd::drefl::deserialize(p, tbl); 285 | ``` 286 | -------------------------------------------------------------------------------- /mirrow/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | file(GLOB_RECURSE MIRROW_SRC 2 | ./mirrow/drefl/*.hpp ./mirrow/srefl/*.hpp 3 | ./src/drefl/*.cpp) 4 | 5 | file(GLOB_RECURSE MIRROW_UTIL_SRC ./mirrow/util/*.hpp) 6 | file(GLOB_RECURSE MIRROW_DREFL_SRC ./mirrow/drefl/*.hpp ./src/drefl/*.cpp) 7 | file(GLOB_RECURSE MIRROW_SREFL_SRC ./mirrow/srefl/*.hpp ./src/srefl/*.cpp) 8 | 9 | add_library(Mirrow_Util INTERFACE ${MIRROW_UTIL_SRC}) 10 | target_include_directories(Mirrow_Util INTERFACE .) 11 | target_compile_features(Mirrow_Util INTERFACE cxx_std_17) 12 | add_library(Mirrow::Util ALIAS Mirrow_Util) 13 | 14 | add_library(Mirrow_Srefl INTERFACE ${MIRROW_SREFL_SRC}) 15 | target_include_directories(Mirrow_Srefl INTERFACE .) 16 | target_link_libraries(Mirrow_Srefl INTERFACE Mirrow_Util) 17 | target_compile_features(Mirrow_Srefl INTERFACE cxx_std_17) 18 | add_library(Mirrow::Srefl ALIAS Mirrow_Srefl) 19 | 20 | add_library(Mirrow_Drefl STATIC ${MIRROW_DREFL_SRC}) 21 | target_include_directories(Mirrow_Drefl PUBLIC .) 22 | target_link_libraries(Mirrow_Drefl PUBLIC Mirrow_Util) 23 | target_compile_features(Mirrow_Drefl PUBLIC cxx_std_17) 24 | add_library(Mirrow::Drefl ALIAS Mirrow_Drefl) 25 | 26 | add_library(Mirrow_Mirrow INTERFACE) 27 | target_link_libraries(Mirrow_Mirrow INTERFACE Mirrow_Util Mirrow_Drefl Mirrow_Srefl) 28 | add_library(Mirrow::Mirrow ALIAS Mirrow_Mirrow) 29 | 30 | if (MIRROW_BUILD_TOMLPLUSPLUS_SERD) 31 | add_subdirectory(serd_backend/tomlplusplus) 32 | file(GLOB_RECURSE MIRROW_SERD_SRC ./mirrow/serd/*.hpp ./src/serd/*.cpp) 33 | add_library(Mirrow_Serd STATIC ${MIRROW_SERD_SRC}) 34 | target_include_directories(Mirrow_Serd PUBLIC .) 35 | target_link_libraries(Mirrow_Serd PUBLIC Mirrow_Mirrow tomlplusplus::tomlplusplus) 36 | add_library(Mirrow::Serd ALIAS Mirrow_Serd) 37 | endif() -------------------------------------------------------------------------------- /mirrow/mirrow/assert.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #ifndef MIRROW_ASSERT 7 | #define MIRROW_ASSERT(x, expr) assert(((void)(expr), (x))) 8 | #endif 9 | 10 | #ifndef MIRROW_LOG 11 | #define MIRROW_LOG(...) std::cout << __VA_ARGS__ << std::endl; 12 | #endif -------------------------------------------------------------------------------- /mirrow/mirrow/drefl/any.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "mirrow/drefl/exception.hpp" 4 | #include "mirrow/drefl/operation_traits.hpp" 5 | #include "mirrow/util/misc.hpp" 6 | 7 | 8 | namespace mirrow::drefl { 9 | 10 | struct type; 11 | 12 | class any final { 13 | public: 14 | friend class clazz; 15 | 16 | template 17 | friend any any_make_constref(const T&) noexcept; 18 | 19 | template 20 | friend any any_make_ref(T&) noexcept; 21 | 22 | template 23 | friend any any_make_copy(T&& value) noexcept( 24 | std::is_rvalue_reference_v 25 | ? std::is_nothrow_move_constructible_v> 26 | : std::is_nothrow_copy_constructible_v>); 27 | 28 | template 29 | friend T* try_cast(any&); 30 | 31 | template 32 | friend const T* try_cast_const(const any&); 33 | 34 | enum class access_type { 35 | Null, 36 | ConstRef, 37 | Ref, 38 | Copy, 39 | }; 40 | 41 | auto access_type() const noexcept { return access_; } 42 | 43 | any() = default; 44 | 45 | any(enum access_type access, void* payload, const type_operations* operations, 46 | const type* typeinfo) 47 | : access_(access), 48 | payload_(payload), 49 | operations_(operations), 50 | type_(typeinfo) {} 51 | 52 | any(const any&); 53 | any(any&& o); 54 | any& operator=(const any&); 55 | any& operator=(any&& o); 56 | 57 | ~any(); 58 | 59 | any constref() noexcept { 60 | return {access_type::ConstRef, payload_, operations_, type_}; 61 | } 62 | 63 | any ref() { 64 | if (access_type() == access_type::ConstRef) { 65 | throw bad_any_access("can't make_ref from const& any"); 66 | } 67 | return {access_type::Ref, payload_, operations_, type_}; 68 | } 69 | 70 | any copy() { 71 | void* elem = operations_->copy_construct(payload_); 72 | return {access_type::Copy, elem, operations_, type_}; 73 | } 74 | 75 | void copy_assign(any& o) { 76 | if (o.type_info() == type_info()) { 77 | operations_->copy_assignment(payload_, o.payload_); 78 | } else { 79 | MIRROW_LOG("can't copy assign between two different types"); 80 | } 81 | } 82 | 83 | void steal_assign(any&& o) { 84 | if (o.type_info() == type_info()) { 85 | operations_->steal_assignment(payload_, o.payload_); 86 | } else { 87 | MIRROW_LOG("can't copy assign between two different types"); 88 | } 89 | } 90 | 91 | [[nodiscard]] any steal() { 92 | auto access = access_; 93 | auto payload = operations_->steal_construct(payload_); 94 | auto operations = operations_; 95 | auto type = type_; 96 | 97 | access_ = access_type::Null; 98 | if (payload_ && access_ == access_type::Copy) { 99 | operations_->destroy(payload_); 100 | } 101 | payload_ = nullptr; 102 | type_ = nullptr; 103 | 104 | return {access, payload, operations, type}; 105 | } 106 | 107 | const type* type_info() const noexcept { return type_; } 108 | 109 | bool has_value() const noexcept { return access_ != access_type::Null && payload_; } 110 | 111 | bool is_ref() const noexcept { 112 | return access_ == access_type::Ref; 113 | } 114 | 115 | bool is_constref() const noexcept { 116 | return access_ == access_type::ConstRef; 117 | } 118 | 119 | bool is_copy() const noexcept { 120 | return access_ == access_type::Copy; 121 | } 122 | 123 | bool is_null() const noexcept { 124 | return access_ == access_type::Null; 125 | } 126 | 127 | void* payload() noexcept { return payload_; } 128 | const void* payload() const noexcept { return payload_; } 129 | 130 | /** 131 | * @brief release payload 132 | */ 133 | void* release() { 134 | auto payload = payload_; 135 | payload_ = nullptr; 136 | access_ = access_type::Null; 137 | return payload; 138 | } 139 | 140 | private: 141 | enum access_type access_ = access_type::Null; 142 | void* payload_ = nullptr; 143 | const type_operations* operations_ = &type_operations::null; 144 | const type* type_ = nullptr; 145 | }; 146 | 147 | } // namespace mirrow::drefl -------------------------------------------------------------------------------- /mirrow/mirrow/drefl/array.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "mirrow/drefl/any.hpp" 4 | #include "mirrow/drefl/array_operations.hpp" 5 | #include "mirrow/drefl/operation_traits.hpp" 6 | #include "mirrow/drefl/type.hpp" 7 | #include "mirrow/util/misc.hpp" 8 | #include 9 | 10 | namespace mirrow::drefl { 11 | 12 | struct type_operations; 13 | 14 | class array : public type { 15 | public: 16 | template 17 | friend class array_factory; 18 | 19 | enum class array_type { 20 | Static, 21 | Dynamic, 22 | }; 23 | 24 | enum class addressing_type { 25 | Random, // like std::vector, std::array 26 | Forward, // like std::list 27 | }; 28 | 29 | template 30 | static array create(const type* elem_type) { 31 | return { 32 | get_name(), 33 | get_arr_type(), 34 | get_addr_type(), 35 | elem_type, 36 | &array_operation_traits::get_operations(), 37 | &type_operation_traits>::get_operations(), 38 | nullptr}; 39 | } 40 | 41 | enum array_type array_type() const noexcept { return array_type_; } 42 | 43 | enum addressing_type addressing_type() const noexcept { 44 | return addressing_type_; 45 | } 46 | 47 | auto elem_type() const noexcept { 48 | return elem_type_; 49 | } 50 | 51 | any get(size_t idx, any&) const noexcept; 52 | any get_const(size_t idx, const any&) const noexcept; 53 | bool push_back(const any&, any&) const noexcept; 54 | bool pop_back(any&) const noexcept; 55 | any back(any&) const noexcept; 56 | any back_const(const any&) const noexcept; 57 | bool resize(size_t size, any& array) const noexcept; 58 | size_t size(const any&) const noexcept; 59 | size_t capacity(const any&) const noexcept; 60 | bool insert(size_t idx, const any&, any&) const noexcept; 61 | 62 | private: 63 | enum array_type array_type_; 64 | enum addressing_type addressing_type_; 65 | const type* elem_type_; 66 | const array_operations* operations_; 67 | const type_operations* elem_operations_; 68 | 69 | array(const std::string& name, enum array_type arr_type, 70 | enum addressing_type addr_type, const type* elem_type, 71 | const array_operations* operations, 72 | const type_operations* elem_operations, 73 | default_construct_fn fn) 74 | : type{value_kind::Array, name, fn}, 75 | array_type_(arr_type), 76 | addressing_type_(addr_type), 77 | elem_type_(elem_type), 78 | operations_(operations), 79 | elem_operations_(elem_operations) {} 80 | 81 | template 82 | static std::string get_name() { 83 | if constexpr (std::is_array_v) { 84 | return "array"; 85 | } else if constexpr (util::is_std_array_v) { 86 | return "std::array"; 87 | } else if constexpr (util::is_vector_v) { 88 | return "std::vector"; 89 | } else if constexpr (util::is_std_list_v) { 90 | return "std::list"; 91 | } 92 | 93 | return "unknown-array"; 94 | } 95 | 96 | template 97 | static enum array_type get_arr_type() { 98 | if constexpr (std::is_array_v || util::is_std_array_v) { 99 | return array_type::Static; 100 | } else { 101 | return array_type::Dynamic; 102 | } 103 | } 104 | 105 | template 106 | static enum addressing_type get_addr_type() { 107 | if constexpr (std::is_array_v) { 108 | return addressing_type::Random; 109 | } else { 110 | return std::is_same_v::iterator_category, 112 | std::random_access_iterator_tag> 113 | ? addressing_type::Random 114 | : addressing_type::Forward; 115 | } 116 | } 117 | }; 118 | 119 | } // namespace mirrow::drefl -------------------------------------------------------------------------------- /mirrow/mirrow/drefl/array_operations.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | namespace mirrow::drefl { 8 | 9 | struct array_operations final { 10 | using get_fn = void*(size_t, void*, bool); 11 | using push_back_fn = bool(const void*, void*); 12 | using pop_back_fn = bool(void*); 13 | using back_fn = void*(void*, bool); 14 | using resize_fn = bool(size_t, void*); 15 | using size_fn = size_t(const void*); 16 | using capacity_fn = size_t(const void*); 17 | using insert_fn = bool(size_t, const void*, void*); 18 | 19 | static array_operations null; 20 | 21 | get_fn* get = empty_get; 22 | push_back_fn* push_back = empty_push_back; 23 | back_fn* back = empty_back; 24 | pop_back_fn* pop_back = empty_pop_back; 25 | resize_fn* resize = empty_resize; 26 | size_fn* size = empty_size; 27 | capacity_fn* capacity = empty_capacity; 28 | insert_fn* insert = empty_insert; 29 | 30 | private: 31 | static void* empty_get(size_t, void*, bool) { return nullptr; } 32 | 33 | static bool empty_push_back(const void*, void*) { return false; } 34 | 35 | static bool empty_pop_back(void*) { return false; } 36 | 37 | static void* empty_back(void*, bool) { return nullptr; } 38 | 39 | static bool empty_resize(size_t, void*) { return false; } 40 | 41 | static size_t empty_size(const void*) { return 0; } 42 | 43 | static size_t empty_capacity(const void*) { return 0; } 44 | 45 | static bool empty_insert(size_t, const void*, void*) { return false; } 46 | }; 47 | 48 | template 49 | struct array_operation_traits; 50 | 51 | // sperialize for T[N] 52 | template 53 | struct array_operation_traits final { 54 | using type = T[N]; 55 | 56 | static void* get(size_t idx, void* array, bool is_const) { 57 | if (is_const) { 58 | return (void*)((const T*)array + idx); 59 | } else { 60 | return (T*)array + idx; 61 | } 62 | } 63 | 64 | static bool push_back(const void*, void*) { return false; } 65 | 66 | static bool pop_back(void*) { return false; } 67 | 68 | static void* back(void*, bool) { return nullptr; } 69 | 70 | static bool resize(size_t, void*) { return false; } 71 | 72 | static size_t size(const void*) { return N; } 73 | 74 | static size_t capacity(const void*) { return N; } 75 | 76 | static bool insert(size_t, const void*, void*) { return false; } 77 | 78 | static auto& get_operations() { 79 | using traits = array_operation_traits; 80 | 81 | static array_operations operations = { 82 | traits::get, traits::push_back, traits::back, 83 | traits::pop_back, traits::resize, traits::size, 84 | traits::capacity, traits::insert}; 85 | return operations; 86 | } 87 | }; 88 | 89 | // sperialize for std::array 90 | template 91 | struct array_operation_traits> final { 92 | using type = std::array; 93 | 94 | static void* get(size_t idx, void* array, bool is_const) { 95 | if (is_const) { 96 | return (void*)&((const type*)array)->operator[](idx); 97 | } else { 98 | return &((type*)array)->operator[](idx); 99 | } 100 | } 101 | 102 | static bool push_back(const void*, void*) { return false; } 103 | 104 | static bool pop_back(void*) { return false; } 105 | 106 | static void* back(void*, bool) { return nullptr; } 107 | 108 | static bool resize(size_t, void*) { return false; } 109 | 110 | static size_t size(const void*) { return N; } 111 | 112 | static size_t capacity(const void*) { return N; } 113 | 114 | static bool insert(size_t, const void*, void*) { return false; } 115 | 116 | static auto& get_operations() { 117 | using traits = array_operation_traits; 118 | 119 | static array_operations operations = { 120 | traits::get, traits::push_back, traits::back, 121 | traits::pop_back, traits::resize, traits::size, 122 | traits::capacity, traits::insert}; 123 | return operations; 124 | } 125 | }; 126 | 127 | // specialize for std::vector 128 | template 129 | struct array_operation_traits> final { 130 | using type = std::vector; 131 | 132 | static void* get(size_t idx, void* array, bool is_const) { 133 | if (is_const) { 134 | return (void*)&((const type*)array)->operator[](idx); 135 | } else { 136 | return &((type*)array)->operator[](idx); 137 | } 138 | } 139 | 140 | static bool push_back(const void* elem, void* array) { 141 | ((type*)array)->push_back(*((T*)elem)); 142 | return true; 143 | } 144 | 145 | static bool pop_back(void* array) { 146 | ((type*)array)->pop_back(); 147 | return true; 148 | } 149 | 150 | static void* back(void* array, bool is_const) { 151 | if (is_const) { 152 | return (void*)&((const type*)array)->back(); 153 | } else { 154 | return &((type*)array)->back(); 155 | } 156 | } 157 | 158 | static bool resize(size_t idx, void* array) { 159 | ((type*)array)->resize(idx); 160 | return true; 161 | } 162 | 163 | static size_t size(const void* array) { return ((type*)array)->size(); } 164 | 165 | static size_t capacity(const void* array) { 166 | return ((type*)array)->capacity(); 167 | } 168 | 169 | static bool insert(size_t idx, const void* elem, void* array) { 170 | type* arr = (type*)array; 171 | auto it = arr->insert(arr->begin() + idx, *((T*)elem)); 172 | return it != arr->end(); 173 | } 174 | 175 | static auto& get_operations() { 176 | using traits = array_operation_traits; 177 | 178 | static array_operations operations = { 179 | traits::get, traits::push_back, traits::back, 180 | traits::pop_back, traits::resize, traits::size, 181 | traits::capacity, traits::insert}; 182 | return operations; 183 | } 184 | }; 185 | 186 | // specialize for std::list 187 | template 188 | struct array_operation_traits> final { 189 | using type = std::list; 190 | 191 | static void* get(size_t idx, void* array, bool is_const) { 192 | if (is_const) { 193 | auto arr = (const type*)array; 194 | auto it = arr->begin(); 195 | while (idx > 0) { 196 | it++; 197 | idx--; 198 | } 199 | return (void*)&(*it); 200 | } else { 201 | auto arr = (type*)array; 202 | auto it = arr->begin(); 203 | while (idx > 0) { 204 | it++; 205 | idx--; 206 | } 207 | return &(*it); 208 | } 209 | } 210 | 211 | static bool push_back(const void* elem, void* array) { 212 | ((type*)array)->push_back(*((T*)elem)); 213 | return true; 214 | } 215 | 216 | static bool pop_back(void* array) { 217 | ((type*)array)->pop_back(); 218 | return true; 219 | } 220 | 221 | static void* back(void* array, bool is_const) { 222 | if (is_const) { 223 | return (void*)&((const type*)array)->back(); 224 | } else { 225 | return &((type*)array)->back(); 226 | } 227 | } 228 | 229 | static bool resize(size_t idx, void* array) { 230 | ((type*)array)->resize(idx); 231 | return true; 232 | } 233 | 234 | static size_t size(const void* array) { return ((type*)array)->size(); } 235 | 236 | static size_t capacity(const void* array) { return ((type*)array)->size(); } 237 | 238 | static bool insert(size_t idx, const void* elem, void* array) { 239 | type* arr = (type*)array; 240 | auto it = arr->begin(); 241 | while (idx > 0) { 242 | idx--; 243 | it++; 244 | } 245 | auto result = arr->insert(it, *((T*)elem)); 246 | return result != arr->end(); 247 | } 248 | 249 | static auto& get_operations() { 250 | using traits = array_operation_traits; 251 | 252 | static array_operations operations = { 253 | traits::get, traits::push_back, traits::back, 254 | traits::pop_back, traits::resize, traits::size, 255 | traits::capacity, traits::insert}; 256 | return operations; 257 | } 258 | }; 259 | 260 | } // namespace mirrow::drefl -------------------------------------------------------------------------------- /mirrow/mirrow/drefl/bool.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "mirrow/drefl/type.hpp" 4 | 5 | namespace mirrow::drefl { 6 | 7 | class any; 8 | 9 | class boolean final: public type { 10 | public: 11 | boolean(); 12 | 13 | void set_value(any&, bool) const; 14 | bool get_value(const any&) const; 15 | }; 16 | 17 | } -------------------------------------------------------------------------------- /mirrow/mirrow/drefl/cast_any.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "mirrow/drefl/any.hpp" 4 | #include "mirrow/drefl/factory.hpp" 5 | 6 | namespace mirrow::drefl { 7 | 8 | template 9 | T* try_cast(any& a) { 10 | if (typeinfo() == a.type_) { 11 | if (a.access_ == any::access_type::Ref || a.access_ == any::access_type::Copy) { 12 | return static_cast(a.payload_); 13 | } 14 | } 15 | throw bad_any_access{"can't cast mutable type from const reference"}; 16 | return nullptr; 17 | } 18 | 19 | template 20 | const T* try_cast_const(const any& a) { 21 | if (typeinfo() == a.type_) { 22 | return static_cast(a.payload_); 23 | } 24 | return nullptr; 25 | } 26 | 27 | 28 | } -------------------------------------------------------------------------------- /mirrow/mirrow/drefl/class.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "mirrow/drefl/qualifier.hpp" 4 | #include "mirrow/drefl/type.hpp" 5 | #include "mirrow/util/misc.hpp" 6 | #include 7 | #include 8 | #include 9 | 10 | 11 | namespace mirrow::drefl { 12 | 13 | #define SET_QUALIFIER_BIT(qualif, newBit) \ 14 | qualif = static_cast(static_cast(qualif) | \ 15 | static_cast(newBit)) 16 | 17 | template 18 | qualifier get_qualifier() { 19 | qualifier qualif = qualifier::None; 20 | 21 | if constexpr (std::is_lvalue_reference_v) { 22 | SET_QUALIFIER_BIT(qualif, qualifier::Ref); 23 | if constexpr (std::is_const_v>) { 24 | SET_QUALIFIER_BIT(qualif, qualifier::Const); 25 | } 26 | } 27 | if constexpr (std::is_const_v) { 28 | SET_QUALIFIER_BIT(qualif, qualifier::Const); 29 | } 30 | 31 | return qualif; 32 | } 33 | 34 | #undef SET_QUALIFIER_BIT 35 | 36 | class property; 37 | class any; 38 | 39 | class clazz final : public type { 40 | public: 41 | template 42 | friend class class_factory; 43 | 44 | using default_construct_fn = any(void); 45 | 46 | explicit clazz(const std::string& name, default_construct_fn dc) 47 | : type(value_kind::Class, name, dc) {} 48 | 49 | clazz() : type(value_kind::Class, nullptr) {} 50 | 51 | auto& properties() const noexcept { return properties_; } 52 | 53 | void set_value(any& from, any& to); 54 | void steal_value(any& from, any& to); 55 | 56 | private: 57 | std::vector> properties_; 58 | }; 59 | 60 | } // namespace mirrow::drefl 61 | -------------------------------------------------------------------------------- /mirrow/mirrow/drefl/class_visitor.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "mirrow/drefl/factory.hpp" 4 | 5 | namespace mirrow::drefl { 6 | 7 | class class_visitor { 8 | public: 9 | virtual ~class_visitor() = default; 10 | 11 | virtual void operator()(numeric_property&) = 0; 12 | virtual void operator()(enum_property&) = 0; 13 | virtual void operator()(clazz_property&) = 0; 14 | virtual void operator()(string_property&) = 0; 15 | virtual void operator()(boolean_property&) = 0; 16 | virtual void operator()(pointer_property&) = 0; 17 | virtual void operator()(array_property&) = 0; 18 | virtual void operator()(optional_property&) = 0; 19 | }; 20 | 21 | } // namespace mirrow::drefl -------------------------------------------------------------------------------- /mirrow/mirrow/drefl/config.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace mirrow::drefl { 6 | 7 | #ifndef MIRROW_ATTRIBUTE_TYPE 8 | #define MIRROW_ATTRIBUTE_TYPE int 9 | #endif 10 | 11 | using attribute_t = MIRROW_ATTRIBUTE_TYPE; 12 | 13 | } -------------------------------------------------------------------------------- /mirrow/mirrow/drefl/drefl.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "mirrow/assert.hpp" 4 | #include "mirrow/drefl/config.hpp" 5 | #include "mirrow/drefl/any.hpp" 6 | #include "mirrow/drefl/factory.hpp" 7 | #include "mirrow/drefl/cast_any.hpp" 8 | #include "mirrow/drefl/make_any.hpp" -------------------------------------------------------------------------------- /mirrow/mirrow/drefl/enum.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "mirrow/drefl/any.hpp" 4 | #include "mirrow/drefl/type.hpp" 5 | #include 6 | #include 7 | 8 | 9 | namespace mirrow::drefl { 10 | 11 | class enum_info; 12 | 13 | class enum_item final { 14 | public: 15 | using enum_numeric_type = long; 16 | 17 | template 18 | enum_item(const std::string& name, T value, 19 | const class enum_info* enumerate) 20 | : enum_info_(enumerate), 21 | name_(name), 22 | value_(static_cast(value)) {} 23 | 24 | auto& name() const noexcept { return name_; } 25 | 26 | long value() const noexcept { return value_; } 27 | 28 | const class enum_info* enum_info() const noexcept { return enum_info_; } 29 | 30 | private: 31 | const class enum_info* enum_info_; 32 | std::string name_; 33 | enum_numeric_type value_; 34 | }; 35 | 36 | class enum_info : public type { 37 | public: 38 | template 39 | friend class enum_factory; 40 | 41 | using enum_numeric_type = typename enum_item::enum_numeric_type; 42 | 43 | explicit enum_info() : type(value_kind::Enum, nullptr) {} 44 | 45 | explicit enum_info(const std::string& name, default_construct_fn fn) 46 | : type(value_kind::Enum, name, fn) {} 47 | 48 | long get_value(const any& elem) const { return *(long*)(elem.payload()); } 49 | 50 | void set_value(any& elem, enum_numeric_type value) const { 51 | if (!SET_VALUE_CHECK(elem, value_kind::Enum)) { 52 | MIRROW_LOG("can't set enum value to any"); 53 | return; 54 | } 55 | 56 | *(long*)(elem.payload()) = value; 57 | } 58 | 59 | auto& enums() const noexcept { return items_; } 60 | 61 | private: 62 | std::vector items_; 63 | 64 | template 65 | void add(const std::string& name, T value) { 66 | items_.emplace_back(name, value, this); 67 | } 68 | }; 69 | 70 | } // namespace mirrow::drefl -------------------------------------------------------------------------------- /mirrow/mirrow/drefl/exception.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | namespace mirrow::drefl { 7 | 8 | class bad_any_access: public std::logic_error { 9 | public: 10 | using std::logic_error::logic_error; 11 | }; 12 | 13 | class any_no_copyable: public std::logic_error { 14 | public: 15 | using std::logic_error::logic_error; 16 | }; 17 | 18 | } -------------------------------------------------------------------------------- /mirrow/mirrow/drefl/make_any.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "mirrow/drefl/any.hpp" 4 | #include "mirrow/drefl/factory.hpp" 5 | #include "mirrow/assert.hpp" 6 | 7 | namespace mirrow::drefl { 8 | 9 | template 10 | any any_make_constref(const T& value) noexcept { 11 | using type = util::remove_cvref_t; 12 | return {any::access_type::ConstRef, (void*)&value, 13 | &type_operation_traits::get_operations(), 14 | typeinfo>()}; 15 | } 16 | 17 | template 18 | any any_make_ref(T& value) noexcept { 19 | using type = util::remove_cvref_t; 20 | return {any::access_type::Ref, (void*)&value, 21 | &type_operation_traits::get_operations(), 22 | typeinfo>()}; 23 | } 24 | 25 | template 26 | any any_make_copy(T&& value) noexcept( 27 | std::is_rvalue_reference_v 28 | ? std::is_nothrow_move_constructible_v> 29 | : std::is_nothrow_copy_constructible_v>) { 30 | using type = util::remove_cvref_t; 31 | 32 | void* elem = nullptr; 33 | try { 34 | if constexpr (std::is_enum_v) { 35 | elem = new long{static_cast(value)}; 36 | } else { 37 | elem = new type{std::forward(value)}; 38 | } 39 | } catch (const std::bad_alloc&) { 40 | MIRROW_LOG("make copy failed! due to allocate memory failed!"); 41 | elem = nullptr; 42 | } catch (...) { 43 | throw; 44 | } 45 | return {any::access_type::Copy, elem, &type_operation_traits::get_operations(), 46 | typeinfo>()}; 47 | } 48 | 49 | } // namespace mirrow::drefl -------------------------------------------------------------------------------- /mirrow/mirrow/drefl/numeric.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "mirrow/drefl/type.hpp" 4 | #include "mirrow/drefl/value_kind.hpp" 5 | #include "mirrow/drefl/any.hpp" 6 | #include 7 | 8 | namespace mirrow::drefl { 9 | 10 | class numeric: public type { 11 | public: 12 | template 13 | friend class numeric_factory; 14 | 15 | enum numeric_kind { 16 | Unknown, 17 | Char, 18 | Int, 19 | Short, 20 | LongLong, 21 | Uint8, 22 | Uint16, 23 | Uint32, 24 | Uint64, 25 | Float, 26 | Double, 27 | }; 28 | 29 | auto numeric_kind() const noexcept { return kind_; } 30 | 31 | numeric(value_kind value_kind, enum numeric_kind numeric_kind, 32 | const std::string& name); 33 | 34 | void set_value(any&, long value) const; 35 | void set_value(any&, uint64_t value) const; 36 | void set_value(any&, double value) const; 37 | double get_value(const any&) const; 38 | 39 | bool is_integer() const { 40 | return kind_ != Unknown && kind_ != Float && kind_ != Double; 41 | } 42 | 43 | bool is_floating_point() const { 44 | return kind_ != Unknown && (kind_ == Float || kind_ == Double); 45 | } 46 | 47 | private: 48 | enum numeric_kind kind_; 49 | 50 | template 51 | static numeric create() { return {get_kind_from_type(), get_kind(), get_name()};} 52 | 53 | template 54 | static enum numeric_kind get_kind() { 55 | if constexpr (std::is_same_v) { 56 | return numeric_kind::Int; 57 | } 58 | if constexpr (std::is_same_v) { 59 | return numeric_kind::Char; 60 | } 61 | if constexpr (std::is_same_v) { 62 | return numeric_kind::Short; 63 | } 64 | if constexpr (std::is_same_v) { 65 | return numeric_kind::LongLong; 66 | } 67 | if constexpr (std::is_same_v) { 68 | return numeric_kind::Uint8; 69 | } 70 | if constexpr (std::is_same_v) { 71 | return numeric_kind::Uint16; 72 | } 73 | if constexpr (std::is_same_v) { 74 | return numeric_kind::Uint32; 75 | } 76 | if constexpr (std::is_same_v) { 77 | return numeric_kind::Uint64; 78 | } 79 | if constexpr (std::is_same_v) { 80 | return numeric_kind::Float; 81 | } 82 | if constexpr (std::is_same_v) { 83 | return numeric_kind::Double; 84 | } 85 | 86 | return Unknown; 87 | } 88 | 89 | template 90 | static std::string get_name() { 91 | if constexpr (std::is_same_v) { 92 | return "int"; 93 | } 94 | if constexpr (std::is_same_v) { 95 | return "char"; 96 | } 97 | if constexpr (std::is_same_v) { 98 | return "short"; 99 | } 100 | if constexpr (std::is_same_v) { 101 | return "long long"; 102 | } 103 | if constexpr (std::is_same_v) { 104 | return "uint8"; 105 | } 106 | if constexpr (std::is_same_v) { 107 | return "uint16"; 108 | } 109 | if constexpr (std::is_same_v) { 110 | return "uint32"; 111 | } 112 | if constexpr (std::is_same_v) { 113 | return "uint64"; 114 | } 115 | if constexpr (std::is_same_v) { 116 | return "float"; 117 | } 118 | if constexpr (std::is_same_v) { 119 | return "double"; 120 | } 121 | 122 | return "unknown-numeric-type"; 123 | } 124 | }; 125 | 126 | } -------------------------------------------------------------------------------- /mirrow/mirrow/drefl/operation_traits.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "mirrow/util/variable_traits.hpp" 4 | #include "mirrow/drefl/exception.hpp" 5 | #include "mirrow/assert.hpp" 6 | #include 7 | #include 8 | 9 | namespace mirrow::drefl { 10 | 11 | struct type_operations final { 12 | using destroy_fn = void(void*); 13 | using copy_construct_fn = void*(void*); 14 | using steal_construct_fn = void*(void*); 15 | using copy_assignment_fn = void(void*, void*); 16 | using steal_assignment_fn = void(void*, void*); 17 | 18 | destroy_fn* destroy = empty_destroy; 19 | copy_construct_fn* copy_construct = empty_copy; 20 | steal_construct_fn* steal_construct = empty_steal; 21 | copy_assignment_fn* copy_assignment = empty_copy_assign; 22 | steal_assignment_fn* steal_assignment = empty_steal_assign; 23 | 24 | static type_operations null; 25 | 26 | private: 27 | static void empty_destroy(void*) {} 28 | static void* empty_copy(void*) { return nullptr; } 29 | static void* empty_steal(void*) { return nullptr; } 30 | static void empty_copy_assign(void*, void*) {} 31 | static void empty_steal_assign(void*, void*) {} 32 | }; 33 | 34 | template 35 | struct type_operation_traits { 36 | static void destroy(void* elem) { 37 | if constexpr (std::is_destructible_v) { 38 | if constexpr (std::is_array_v){ 39 | delete[] (T*)(elem); 40 | } else { 41 | delete (T*)(elem); 42 | } 43 | } else { 44 | MIRROW_LOG("type don't support destruct"); 45 | } 46 | } 47 | 48 | static void* copy_construct(void* elem) { 49 | if constexpr (std::is_copy_constructible_v) { 50 | return new T{*(const T*)elem}; 51 | } else { 52 | MIRROW_LOG("type don't support copy construct"); 53 | return nullptr; 54 | } 55 | } 56 | 57 | static void* steal_construct(void* elem) { 58 | if constexpr (std::is_move_constructible_v) { 59 | return new T{std::move(*(T*)elem)}; 60 | } else { 61 | MIRROW_LOG("type don't support move construct"); 62 | return nullptr; 63 | } 64 | } 65 | 66 | static void copy_assignment(void* dst, void* src) { 67 | if constexpr (std::is_copy_assignable_v) { 68 | *(T*)(dst) = *(const T*)(src); 69 | } else { 70 | MIRROW_LOG("type don't support copy assignment"); 71 | } 72 | } 73 | 74 | static void steal_assignment(void* dst, void* src) { 75 | if constexpr (std::is_move_assignable_v) { 76 | *(T*)(dst) = std::move(*(T*)(src)); 77 | } else { 78 | MIRROW_LOG("type don't support copy assignment"); 79 | } 80 | } 81 | 82 | static auto& get_operations() { 83 | using traits = type_operation_traits; 84 | 85 | static type_operations operations = { 86 | traits::destroy, traits::copy_construct, traits::steal_construct, 87 | traits::copy_assignment, traits::steal_assignment}; 88 | return operations; 89 | } 90 | }; 91 | 92 | } // namespace mirrow::drefl -------------------------------------------------------------------------------- /mirrow/mirrow/drefl/optional.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "mirrow/drefl/any.hpp" 4 | #include "mirrow/drefl/operation_traits.hpp" 5 | #include "mirrow/drefl/type.hpp" 6 | #include "mirrow/drefl/value_kind.hpp" 7 | 8 | 9 | namespace mirrow::drefl { 10 | 11 | class optional final : public type { 12 | public: 13 | template 14 | friend class optional_factory; 15 | 16 | using set_value_fn = void(const void*, void*); 17 | using set_inner_value_fn = void(const void*, void*); 18 | using get_value_fn = void*(void*); 19 | using has_value_fn = bool(const void*); 20 | 21 | struct operations final { 22 | set_value_fn* set_value_ = nullptr; 23 | get_value_fn* get_value_ = nullptr; 24 | has_value_fn* has_value_ = nullptr; 25 | set_inner_value_fn* set_inner_value_ = nullptr; 26 | }; 27 | 28 | template 29 | struct traits { 30 | using value_type = typename T::value_type; 31 | 32 | static void set_inner_value(const void* src, void* dst) { 33 | *(T*)(dst) = *(value_type*)src; 34 | } 35 | 36 | static void set_value(const void* src, void* dst) { 37 | *(T*)(dst) = *(T*)src; 38 | } 39 | 40 | static void* get_value(void* value) { 41 | return &((T*)value)->value(); 42 | } 43 | 44 | static bool has_value(const void* value) { 45 | return ((const T*)(value))->has_value(); 46 | } 47 | 48 | static const operations& get_operations() { 49 | using type = traits; 50 | static operations ops = {type::set_value, type::get_value, 51 | type::has_value, type::set_inner_value}; 52 | return ops; 53 | } 54 | }; 55 | 56 | optional(const type* elem_type, const std::string& name, 57 | const operations& ops, const type_operations& elem_ops) 58 | : type{value_kind::Optional, name, nullptr}, 59 | elem_type_(elem_type), 60 | ops_{&ops}, 61 | elem_operations_(&elem_ops) {} 62 | 63 | template 64 | static optional create(const type* elem_type) { 65 | return {elem_type, "std::optional<" + elem_type->name() + ">", 66 | traits::get_operations(), 67 | type_operation_traits::get_operations()}; 68 | } 69 | 70 | const type* elem_type() const noexcept { return elem_type_; } 71 | 72 | any get_value(any& value) const { 73 | void* elem = ops_->get_value_(value.payload()); 74 | return {value.is_constref() ? any::access_type::ConstRef 75 | : any::access_type::Ref, 76 | elem, elem_operations_, elem_type_}; 77 | } 78 | 79 | any get_value_const(const any& value) const { 80 | void* elem = ops_->get_value_((void*)value.payload()); 81 | return {any::access_type::ConstRef, 82 | elem, elem_operations_, elem_type_}; 83 | } 84 | 85 | void set_value(const any& src, any& value) const { 86 | if (src.type_info() == this) { 87 | ops_->set_value_(src.payload(), value.payload()); 88 | } else { 89 | MIRROW_LOG("can't set std::optional value due to type incorrect"); 90 | } 91 | } 92 | 93 | void set_inner_value(const any& src, any& value) const { 94 | if (src.type_info() == elem_type_) { 95 | ops_->set_inner_value_(src.payload(), value.payload()); 96 | } else { 97 | MIRROW_LOG("can't set std::optional value due to type incorrect"); 98 | } 99 | } 100 | 101 | bool has_value(const any& value) const noexcept { 102 | return ops_->has_value_(value.payload()); 103 | } 104 | 105 | private: 106 | const type* elem_type_; 107 | const operations* ops_; 108 | const type_operations* elem_operations_; 109 | }; 110 | 111 | } // namespace mirrow::drefl -------------------------------------------------------------------------------- /mirrow/mirrow/drefl/pointer.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "mirrow/drefl/value_kind.hpp" 4 | #include "mirrow/util/misc.hpp" 5 | #include "mirrow/drefl/type.hpp" 6 | 7 | 8 | namespace mirrow::drefl { 9 | 10 | struct type; 11 | 12 | class pointer final: public type { 13 | public: 14 | template 15 | static pointer create(const type* pure_type) { 16 | static_assert(std::is_pointer_v); 17 | std::string pointer_name = pure_type->name(); 18 | int layer = util::pointer_layer_v; 19 | for (int i = 0; i < layer; i++) { 20 | pointer_name += "*"; 21 | } 22 | return {pointer_name, 23 | std::is_const_v, 24 | std::is_const_v>>, 25 | pure_type, 26 | layer}; 27 | } 28 | 29 | bool is_const() const noexcept { return is_const_; } 30 | bool is_point_type_const() const noexcept { return is_point_type_const_; } 31 | 32 | const type* type_info() const noexcept { return typeinfo_; } 33 | 34 | int layers() const noexcept { return layers_; } 35 | 36 | private: 37 | pointer(): type(value_kind::Pointer, "", nullptr) {} 38 | 39 | pointer(const std::string& name, bool is_const, bool is_point_type_const, 40 | const type* typeinfo, int layers) 41 | : type(value_kind::Pointer, name, nullptr), 42 | is_const_(is_const), 43 | is_point_type_const_(is_point_type_const), 44 | typeinfo_(typeinfo), 45 | layers_(layers) {} 46 | 47 | bool is_const_ = false; // means `int* const`(not `const int*`) 48 | bool is_point_type_const_ = false; // means `const int*`(not `int* const`) 49 | const type* typeinfo_; 50 | int layers_ = 0; // pointer layer count: int** == 2, int* == 1 51 | }; 52 | 53 | } // namespace mirrow::drefl 54 | -------------------------------------------------------------------------------- /mirrow/mirrow/drefl/property.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "mirrow/drefl/enum.hpp" 4 | #include "mirrow/drefl/factory.hpp" 5 | #include "mirrow/drefl/type.hpp" 6 | #include "mirrow/drefl/value_kind.hpp" 7 | #include "mirrow/drefl/enum.hpp" 8 | #include "mirrow/drefl/string.hpp" 9 | #include "mirrow/drefl/qualifier.hpp" 10 | #include "mirrow/util/misc.hpp" 11 | #include "mirrow/util/variable_traits.hpp" 12 | #include 13 | 14 | 15 | namespace mirrow::drefl { 16 | 17 | class clazz; 18 | class class_visitor; 19 | 20 | namespace internal { 21 | 22 | template 23 | using property_raw_t = util::remove_all_pointers_t< 24 | util::remove_cvref_t::type>>; 25 | } 26 | 27 | class property : public type { 28 | public: 29 | property(const std::string& name, const clazz* owner, qualifier q) 30 | : type(value_kind::Property, name), owner_(owner), qualifier_(q) {} 31 | 32 | virtual void visit(class_visitor*) = 0; 33 | 34 | virtual ~property() = default; 35 | 36 | auto class_info() const noexcept { return owner_; } 37 | 38 | bool is_const_pointer() const noexcept { 39 | return static_cast(qualifier_) & 40 | static_cast(qualifier::ConstPointer); 41 | } 42 | 43 | bool is_const() const noexcept { 44 | return static_cast(qualifier_) & 45 | static_cast(qualifier::Const); 46 | } 47 | 48 | bool is_ref() const noexcept { 49 | return static_cast(qualifier_) & 50 | static_cast(qualifier::Ref); 51 | } 52 | 53 | bool is_pointer() const noexcept { 54 | return static_cast(qualifier_) & 55 | static_cast(qualifier::Pointer); 56 | } 57 | 58 | virtual const type* type_info() const noexcept = 0; 59 | 60 | private: 61 | const clazz* owner_; 62 | qualifier qualifier_ = qualifier::None; 63 | }; 64 | 65 | class string_property : public property { 66 | public: 67 | using property::property; 68 | 69 | void visit(class_visitor* visitor) override; 70 | }; 71 | 72 | template 73 | class string_property_impl : public string_property { 74 | public: 75 | string_property_impl(const std::string& name, const clazz* owner, 76 | qualifier q, T pointer) 77 | : string_property(name, owner, q), 78 | pointer_(pointer), 79 | type_info_( 80 | &string_factory>::instance().info()) { 81 | } 82 | 83 | const struct type* type_info() const noexcept override { 84 | return type_info_; 85 | } 86 | 87 | private: 88 | T pointer_; 89 | const string* type_info_; 90 | }; 91 | 92 | class boolean_property : public property { 93 | public: 94 | boolean_property(const std::string& name, const clazz* owner, qualifier q) 95 | : property(name, owner, q), 96 | type_info_(&boolean_factory::instance().info()) {} 97 | 98 | void visit(class_visitor* visitor) override; 99 | 100 | const struct type* type_info() const noexcept override { 101 | return type_info_; 102 | } 103 | 104 | private: 105 | const boolean* type_info_; 106 | }; 107 | 108 | template 109 | class boolean_property_impl : public boolean_property { 110 | public: 111 | boolean_property_impl(const std::string& name, const clazz* owner, 112 | qualifier q, T pointer) 113 | : boolean_property(name, owner, q), pointer_(pointer) {} 114 | 115 | private: 116 | T pointer_; 117 | }; 118 | 119 | class numeric_property : public property { 120 | public: 121 | using property::property; 122 | 123 | void visit(class_visitor* visitor) override; 124 | }; 125 | 126 | template 127 | class numeric_property_impl : public numeric_property { 128 | public: 129 | numeric_property_impl(const std::string& name, const clazz* owner, 130 | qualifier q, T pointer) 131 | : numeric_property(name, owner, q), 132 | pointer_(pointer), 133 | type_info_(&numeric_factory>::instance() 134 | .info()) {} 135 | 136 | const struct type* type_info() const noexcept override { 137 | return type_info_; 138 | } 139 | 140 | private: 141 | T pointer_ = nullptr; 142 | const class numeric* type_info_; 143 | }; 144 | 145 | class clazz_property : public property { 146 | public: 147 | using property::property; 148 | 149 | void visit(class_visitor*) override; 150 | }; 151 | 152 | template 153 | class clazz_property_impl : public clazz_property { 154 | public: 155 | clazz_property_impl(const std::string& name, T accessor) 156 | : clazz_property( 157 | name, 158 | &class_factory< 159 | util::remove_cvref_t::clazz>>::instance() 160 | .info(), 161 | get_qualifier::type>()), 162 | accessor_(accessor), 163 | type_info_( 164 | &class_factory>::instance().info()) {} 165 | 166 | const struct type* type_info() const noexcept override { 167 | return type_info_; 168 | } 169 | 170 | private: 171 | T accessor_ = nullptr; 172 | const class clazz* type_info_; 173 | }; 174 | 175 | class enum_property : public property { 176 | public: 177 | explicit enum_property(const std::string& name, const clazz* owner, 178 | qualifier q, const enum_info& enum_info) 179 | : property(name, owner, q) {} 180 | 181 | void visit(class_visitor* visitor) override; 182 | }; 183 | 184 | template 185 | class enum_property_impl : public enum_property { 186 | public: 187 | enum_property_impl(const std::string& name, const clazz* owner, qualifier q, 188 | T pointer) 189 | : enum_property(name, owner, q), 190 | pointer_(pointer), 191 | type_info_( 192 | &enum_factory>::instance().info()) {} 193 | 194 | const struct type* type_info() const noexcept override { 195 | return type_info_; 196 | } 197 | 198 | private: 199 | T pointer_ = nullptr; 200 | const class enum_info* type_info_ = nullptr; 201 | }; 202 | 203 | } // namespace mirrow::drefl -------------------------------------------------------------------------------- /mirrow/mirrow/drefl/property_factory.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "mirrow/drefl/class.hpp" 4 | #include "mirrow/drefl/factory.hpp" 5 | #include "mirrow/drefl/property.hpp" 6 | #include "mirrow/util/function_traits.hpp" 7 | #include "mirrow/util/misc.hpp" 8 | #include "mirrow/util/variable_traits.hpp" 9 | #include 10 | #include 11 | 12 | 13 | namespace mirrow::drefl { 14 | 15 | template 16 | class class_factory; 17 | 18 | class enum_property_factory final { 19 | public: 20 | template 21 | enum_property_factory(const std::string& name, T accessor) { 22 | using traits = util::variable_traits; 23 | using var_type = typename traits::type; 24 | using enum_type = util::remove_cvref_t; 25 | 26 | static_assert(std::is_enum_v); 27 | 28 | auto& enum_info = enum_factory::instance().info(); 29 | 30 | property_ = std::make_shared>( 31 | name, get_qualifier(), enum_info, accessor); 32 | } 33 | 34 | auto& get() const noexcept { return property_; } 35 | 36 | private: 37 | std::shared_ptr property_; 38 | }; 39 | 40 | class numeric_property_factory final { 41 | public: 42 | template 43 | numeric_property_factory(const std::string& name, T accessor) { 44 | using traits = util::variable_traits; 45 | using var_type = typename traits::type; 46 | 47 | property_ = std::make_shared>( 48 | name, &class_factory::instance().info(), 49 | get_qualifier(), accessor); 50 | } 51 | 52 | auto& get() const noexcept { return property_; } 53 | 54 | private: 55 | std::shared_ptr property_; 56 | }; 57 | 58 | class string_property_factory final { 59 | public: 60 | template 61 | string_property_factory(const std::string& name, T accessor) { 62 | using traits = util::variable_traits; 63 | using var_type = typename traits::type; 64 | 65 | property_ = std::make_shared>( 66 | name, &class_factory::instance().info(), 67 | get_qualifier(), accessor); 68 | } 69 | 70 | auto& get() const noexcept { return property_; } 71 | 72 | private: 73 | std::shared_ptr property_; 74 | }; 75 | 76 | class boolean_property_factory final { 77 | public: 78 | template 79 | boolean_property_factory(const std::string& name, T accessor) { 80 | using traits = util::variable_traits; 81 | using var_type = typename traits::type; 82 | 83 | property_ = std::make_shared>( 84 | name, &class_factory::instance().info(), 85 | get_qualifier(), accessor); 86 | } 87 | 88 | auto& get() const noexcept { return property_; } 89 | 90 | private: 91 | std::shared_ptr property_; 92 | }; 93 | 94 | class class_property_factory final { 95 | public: 96 | template 97 | class_property_factory(const std::string& name, T accessor) { 98 | using traits = util::variable_traits; 99 | using var_type = typename traits::type; 100 | 101 | property_ = std::make_shared>(name, accessor); 102 | } 103 | 104 | auto& get() const { return property_; } 105 | 106 | private: 107 | std::shared_ptr property_; 108 | }; 109 | 110 | class property_factory final { 111 | public: 112 | template 113 | std::shared_ptr create(const std::string& name, T accessor) { 114 | if constexpr (util::is_function_v) { 115 | // TODO: use function_factory here 116 | return nullptr; 117 | } else { 118 | using traits = util::variable_traits; 119 | using type = util::remove_cvref_t; 120 | 121 | if constexpr (std::is_same_v) { 122 | return boolean_property_factory{name, accessor}.get(); 123 | } else if constexpr (std::is_enum_v) { 124 | return enum_property_factory{name, accessor}.get(); 125 | } else if constexpr (std::is_fundamental_v) { 126 | return numeric_property_factory{name, accessor}.get(); 127 | } else if constexpr (std::is_same_v || 128 | std::is_same_v) { 129 | return string_property_factory{name, accessor}.get(); 130 | } else { 131 | return class_property_factory{name, accessor}.get(); 132 | } 133 | } 134 | } 135 | }; 136 | 137 | } // namespace mirrow::drefl -------------------------------------------------------------------------------- /mirrow/mirrow/drefl/qualifier.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace mirrow::drefl { 4 | 5 | enum class qualifier { 6 | None = 0x00, 7 | Const = 0x01, // const vairble: const int, const int&, int* const 8 | Ref = 0x02, 9 | }; 10 | 11 | 12 | } -------------------------------------------------------------------------------- /mirrow/mirrow/drefl/raw_type.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "mirrow/util/misc.hpp" 4 | #include "mirrow/util/variable_traits.hpp" 5 | 6 | namespace mirrow::drefl { 7 | 8 | template 9 | using property_raw_t = util::remove_cvref_t::type>>>; 11 | 12 | template 13 | using property_no_qualifier = util::remove_cvref_t::type>; 14 | 15 | template 16 | using raw_type_t = 17 | util::remove_cvref_t>>; 18 | 19 | } -------------------------------------------------------------------------------- /mirrow/mirrow/drefl/string.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "mirrow/drefl/type.hpp" 4 | #include "mirrow/drefl/value_kind.hpp" 5 | 6 | namespace mirrow::drefl { 7 | 8 | class any; 9 | 10 | class string : public type { 11 | public: 12 | enum class string_kind { 13 | Unknown, 14 | // ConstCharList 15 | String, 16 | StringView, 17 | }; 18 | 19 | template 20 | static string create() { 21 | return {get_kind(), get_name()}; 22 | } 23 | 24 | auto string_kind() const noexcept { return kind_; } 25 | 26 | bool is_string() const noexcept { return kind_ == string_kind::String; } 27 | 28 | bool is_string_view() const noexcept { 29 | return kind_ == string_kind::StringView; 30 | } 31 | 32 | std::string get_str(const any&) const; 33 | std::string_view get_str_view(const any&) const; 34 | 35 | void set_value(any&, const std::string&) const; 36 | void set_value(any&, std::string_view&) const; 37 | 38 | private: 39 | enum string_kind kind_; 40 | 41 | string(enum string_kind skind, const std::string& name); 42 | 43 | template 44 | static auto get_kind() { 45 | if constexpr (std::is_same_v) { 46 | return string_kind::String; 47 | } 48 | if constexpr (std::is_same_v) { 49 | return string_kind::StringView; 50 | } 51 | return string_kind::Unknown; 52 | } 53 | 54 | template 55 | static std::string get_name() { 56 | if constexpr (std::is_same_v) { 57 | return "std::string"; 58 | } 59 | if constexpr (std::is_same_v) { 60 | return "std::string_view"; 61 | } 62 | 63 | return "unknown-string-type"; 64 | } 65 | }; 66 | 67 | } // namespace mirrow::drefl -------------------------------------------------------------------------------- /mirrow/mirrow/drefl/type.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "mirrow/drefl/value_kind.hpp" 4 | #include "mirrow/drefl/any.hpp" 5 | #include "mirrow/drefl/config.hpp" 6 | #include 7 | #include 8 | 9 | namespace mirrow::drefl { 10 | 11 | class clazz; 12 | class numeric; 13 | class enum_info; 14 | class boolean; 15 | class string; 16 | class pointer; 17 | class array; 18 | class optional; 19 | 20 | struct type { 21 | using default_construct_fn = std::function; 22 | 23 | type(value_kind kind, const std::string& name, default_construct_fn fn) 24 | : kind_(kind), name_(name), default_construct_(fn) {} 25 | type(value_kind kind, default_construct_fn fn): kind_(kind), default_construct_(fn) {} 26 | virtual ~type() = default; 27 | 28 | auto& attributes() const { return attrs_; } 29 | void set_attr(const std::vector& attrs) { attrs_ = attrs; } 30 | void set_attr(std::vector&& attrs) { attrs_ = std::move(attrs); 31 | } 32 | 33 | bool find_attr(attribute_t attr) const { 34 | return std::find(attrs_.begin(), attrs_.end(), attr) != 35 | std::end(attrs_); 36 | } 37 | 38 | auto kind() const noexcept { return kind_; } 39 | 40 | const clazz* as_class() const noexcept; 41 | const numeric* as_numeric() const noexcept; 42 | const enum_info* as_enum() const noexcept; 43 | const boolean* as_boolean() const noexcept; 44 | const string* as_string() const noexcept; 45 | const pointer* as_pointer() const noexcept; 46 | const array* as_array() const noexcept; 47 | const optional* as_optional() const noexcept; 48 | 49 | bool is_class() const noexcept; 50 | bool is_numeric() const noexcept; 51 | bool is_enum() const noexcept; 52 | bool is_boolean() const noexcept; 53 | bool is_string() const noexcept; 54 | bool is_pointer() const noexcept; 55 | bool is_array() const noexcept; 56 | bool is_optional() const noexcept; 57 | 58 | auto& name() const noexcept { return name_; } 59 | 60 | bool is_default_constructible() const { 61 | return default_construct_ != nullptr; 62 | } 63 | any default_construct() const { return is_default_constructible() ? default_construct_() : any{}; } 64 | 65 | protected: 66 | std::string name_; 67 | default_construct_fn default_construct_; 68 | 69 | private: 70 | value_kind kind_; 71 | std::vector attrs_; 72 | }; 73 | 74 | #define SET_VALUE_CHECK(a, type) \ 75 | ((a.access_type() == any::access_type::Ref || \ 76 | a.access_type() == any::access_type::Copy) && \ 77 | a.type_info()->kind() == type) 78 | 79 | #define COPY_VALUE_CHECK(a, type) \ 80 | (a.access_type() != any::access_type::Null && a.type_info()->kind() == type) 81 | 82 | } // namespace mirrow::drefl 83 | -------------------------------------------------------------------------------- /mirrow/mirrow/drefl/value_kind.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include "mirrow/util/misc.hpp" 7 | 8 | namespace mirrow::drefl { 9 | 10 | enum class value_kind { 11 | None, 12 | Boolean, 13 | Numeric, 14 | String, 15 | Enum, 16 | Class, 17 | Property, 18 | Pointer, 19 | Array, 20 | Optional, 21 | }; 22 | 23 | template 24 | value_kind get_kind_from_type() { 25 | if constexpr (util::is_std_array_v || util::is_vector_v || 26 | std::is_array_v) { 27 | return value_kind::Array; 28 | } 29 | if constexpr (util::is_optional_v) { 30 | return value_kind::Optional; 31 | } 32 | if constexpr (std::is_pointer_v) { 33 | return value_kind::Pointer; 34 | } 35 | if constexpr (std::is_fundamental_v) { 36 | return value_kind::Numeric; 37 | } 38 | if constexpr (std::is_same_v || 39 | std::is_same_v) { 40 | return value_kind::String; 41 | } 42 | if constexpr (std::is_enum_v) { 43 | return value_kind::Enum; 44 | } 45 | if constexpr (std::is_class_v) { 46 | return value_kind::Class; 47 | } 48 | 49 | return value_kind::None; 50 | } 51 | 52 | } // namespace mirrow::drefl -------------------------------------------------------------------------------- /mirrow/mirrow/serd/dynamic/backends/tomlplusplus.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define TOML_EXCEPTIONS 0 4 | #define TOML_HEADER_ONLY 0 5 | #include "toml++/toml.hpp" 6 | 7 | #include "mirrow/drefl/any.hpp" 8 | #include "mirrow/drefl/factory.hpp" 9 | 10 | #include 11 | #include 12 | #include 13 | 14 | 15 | namespace mirrow::serd::drefl { 16 | 17 | using namespace ::mirrow::drefl; 18 | 19 | class serialize_method_storage { 20 | public: 21 | using serialize_fn = std::function; 22 | using deserialize_fn = std::function; 23 | 24 | struct serd_methods { 25 | serialize_fn serialize = nullptr; 26 | deserialize_fn deserialize = nullptr; 27 | }; 28 | 29 | static auto& instance() { 30 | static serialize_method_storage instance; 31 | return instance; 32 | } 33 | 34 | void regist_serialize(const type* type, const serialize_fn& f) { 35 | if (auto it = methods_.find(type); it != methods_.end()) { 36 | it->second.serialize = f; 37 | } else { 38 | methods_.insert_or_assign(type, serd_methods{f, nullptr}); 39 | } 40 | } 41 | 42 | void regist_deserialize(const type* type, const deserialize_fn& f) { 43 | if (auto it = methods_.find(type); it != methods_.end()) { 44 | it->second.deserialize = f; 45 | } else { 46 | methods_.insert_or_assign(type, serd_methods{nullptr, f}); 47 | } 48 | } 49 | 50 | serialize_fn get_serialize(const type* type) { 51 | if (auto it = methods_.find(type); it != methods_.end()) { 52 | return it->second.serialize; 53 | } 54 | return nullptr; 55 | } 56 | 57 | deserialize_fn get_deserialize(const type* type) { 58 | if (auto it = methods_.find(type); it != methods_.end()) { 59 | return it->second.deserialize; 60 | } 61 | return nullptr; 62 | } 63 | 64 | private: 65 | std::unordered_map methods_; 66 | }; 67 | 68 | void serialize(toml::table& tbl, const any& value, std::string_view name); 69 | toml::table serialize_class(const any& value); 70 | void deserialize(any& obj, const toml::node& node); 71 | 72 | } // namespace mirrow::serd::drefl 73 | -------------------------------------------------------------------------------- /mirrow/mirrow/serd/static/backends/tomlplusplus.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define TOML_EXCEPTIONS 0 4 | #define TOML_HEADER_ONLY 0 5 | #include "toml++/toml.hpp" 6 | 7 | #include "mirrow/assert.hpp" 8 | #include "mirrow/srefl/reflect.hpp" 9 | #include "mirrow/util/misc.hpp" 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | namespace mirrow { 21 | 22 | namespace serd { 23 | 24 | namespace srefl { 25 | 26 | // fwd 27 | // template 28 | // auto serialize(const T&); 29 | 30 | // some serialization 31 | namespace impl { 32 | 33 | // point out which toml type should serialize to 34 | 35 | template 36 | struct serialize_destination_type { 37 | using type = toml::table; 38 | }; 39 | 40 | template <> 41 | struct serialize_destination_type { 42 | using type = toml::value; 43 | }; 44 | 45 | template 46 | struct serialize_destination_type>> { 47 | using type = toml::value; 48 | }; 49 | 50 | template 51 | struct serialize_destination_type && std::is_integral_v>> { 52 | using type = toml::value; 53 | }; 54 | 55 | template 56 | struct serialize_destination_type>> { 57 | using type = toml::value; 58 | }; 59 | 60 | template 61 | struct serialize_destination_type> { 62 | using type = toml::array; 63 | }; 64 | 65 | template 66 | struct serialize_destination_type> { 67 | using type = 68 | typename serialize_destination_type>::type; 69 | }; 70 | 71 | template 72 | struct serialize_destination_type> { 73 | using type = toml::array; 74 | }; 75 | 76 | template 77 | struct serialize_destination_type> { 78 | using type = toml::table; 79 | }; 80 | 81 | template 82 | struct serialize_destination_type> { 83 | using type = toml::array; 84 | }; 85 | 86 | // point out whether the type already has serialize_method 87 | 88 | template 89 | struct has_serialize_method { 90 | static constexpr bool value = false; 91 | }; 92 | 93 | template 94 | struct has_serialize_method< 95 | T, std::enable_if_t>> { 96 | static constexpr bool value = true; 97 | }; 98 | 99 | template 100 | struct has_serialize_method< 101 | T, std::enable_if_t>> { 102 | static constexpr bool value = true; 103 | }; 104 | 105 | template 106 | struct has_serialize_method< 107 | T, std::enable_if_t>> { 108 | static constexpr bool value = true; 109 | }; 110 | 111 | template 112 | struct has_serialize_method< 113 | T, std::enable_if_t>> { 114 | static constexpr bool value = true; 115 | }; 116 | 117 | template 118 | struct has_serialize_method< 119 | T, std::enable_if_t>> { 120 | static constexpr bool value = true; 121 | }; 122 | 123 | template 124 | struct has_serialize_method< 125 | T, std::enable_if_t>> { 126 | static constexpr bool value = true; 127 | }; 128 | 129 | } // namespace impl 130 | 131 | template 132 | using serialize_destination_type_t = 133 | typename impl::serialize_destination_type::type; 134 | 135 | template 136 | constexpr bool has_serialize_method_v = impl::has_serialize_method::value; 137 | 138 | 139 | // some SFINEA function to serialize specific type 140 | 141 | // fwd declare 142 | 143 | /* 144 | template 145 | std::enable_if_t && !std::is_same_v> 146 | serialize(const T& value, serialize_destination_type_t& node); 147 | 148 | template 149 | std::enable_if_t> serialize( 150 | const T& value, serialize_destination_type_t& node); 151 | 152 | template 153 | std::enable_if_t> serialize( 154 | const T& value, serialize_destination_type_t& node); 155 | 156 | template 157 | std::enable_if_t> serialize( 158 | const T& value, serialize_destination_type_t& node); 159 | 160 | template 161 | std::enable_if_t> serialize( 162 | const T& value, serialize_destination_type_t& node); 163 | 164 | template 165 | std::enable_if_t> serialize( 166 | const T& map, serialize_destination_type_t& tbl); 167 | 168 | template 169 | std::enable_if_t> serialize( 170 | const T& map, serialize_destination_type_t& arr); 171 | 172 | template 173 | std::enable_if_t> serialize( 174 | const T& elems, serialize_destination_type_t& arr); 175 | 176 | template 177 | std::enable_if_t> serialize( 178 | const T& elems, serialize_destination_type_t& arr); 179 | */ 180 | 181 | 182 | /** 183 | * @brief serialize a data(the data must be reflected by static reflection) 184 | * 185 | * inner support types: 186 | * - fundamental type(integer, floating point, bool) 187 | * - std::vector 188 | * - std::array 189 | * - std::unordered_map 190 | * - std::unordered_set 191 | * - std::optional 192 | * 193 | * NOTE: we don't support std::set & set::map because data in TOML isn't in 194 | * order. 195 | * @tparam T serialize type 196 | * @tparam U serialize to the type 197 | */ 198 | 199 | template 200 | std::enable_if_t> 201 | serialize( 202 | const T& value, 203 | serialize_destination_type_t& tbl); 204 | 205 | template 206 | std::enable_if_t && !std::is_same_v> 207 | serialize(const T& value, serialize_destination_type_t& node) { 208 | node.as_integer()->get() = static_cast(value); 209 | } 210 | 211 | template 212 | std::enable_if_t> serialize( 213 | const T& value, serialize_destination_type_t& node) { 214 | node.as_floating_point()->get() = static_cast(value); 215 | } 216 | 217 | template 218 | std::enable_if_t> serialize( 219 | const T& value, serialize_destination_type_t& node) { 220 | node.as_boolean()->get() = value; 221 | } 222 | 223 | template 224 | std::enable_if_t> serialize( 225 | const T& value, serialize_destination_type_t& node) { 226 | node.as_string()->get() = value; 227 | } 228 | 229 | template 230 | std::enable_if_t> serialize( 231 | const T& value, serialize_destination_type_t& node) { 232 | if (value.has_value()) { 233 | serialize>( 234 | value.value(), node); 235 | } 236 | } 237 | 238 | template 239 | std::enable_if_t> serialize( 240 | const T& map, serialize_destination_type_t& tbl) { 241 | using mapped_type = util::remove_cvref_t; 242 | 243 | static_assert(util::is_string_v, 244 | "the key of unordered_map must std::string"); 245 | 246 | for (auto& [key, value] : map) { 247 | using toml_type = serialize_destination_type_t; 248 | toml_type new_node; 249 | serialize(value, new_node); 250 | tbl.emplace(key, new_node); 251 | } 252 | } 253 | 254 | template 255 | std::enable_if_t> serialize( 256 | const T& map, serialize_destination_type_t& arr) { 257 | using value_type = util::remove_cvref_t; 258 | 259 | for (auto& elem : arr) { 260 | using toml_type = serialize_destination_type_t; 261 | toml_type new_node; 262 | serialize(elem, new_node); 263 | arr.push_back(new_node); 264 | } 265 | } 266 | 267 | template 268 | std::enable_if_t> serialize( 269 | const T& elems, serialize_destination_type_t& arr) { 270 | MIRROW_ASSERT(arr.is_array(), "des must array when serialize std::vector"); 271 | 272 | for (auto& elem : elems) { 273 | using toml_type = serialize_destination_type_t< 274 | util::remove_cvref_t>; 275 | toml_type new_node; 276 | serialize(elem, new_node); 277 | arr.push_back(std::move(new_node)); 278 | } 279 | } 280 | 281 | template 282 | std::enable_if_t> serialize( 283 | const T& elems, serialize_destination_type_t& arr) { 284 | MIRROW_ASSERT(arr.is_array(), "des must array when serialize std::vector"); 285 | 286 | for (int i = 0; i < elems.size(); i++) { 287 | using toml_type = serialize_destination_type_t< 288 | util::remove_cvref_t>; 289 | toml_type new_node; 290 | serialize(arr[i], new_node); 291 | arr.push_back(std::move(new_node)); 292 | } 293 | } 294 | 295 | template 296 | std::enable_if_t> 297 | serialize( 298 | const T& value, 299 | serialize_destination_type_t& tbl) { 300 | using type = util::remove_cvref_t; 301 | 302 | ::mirrow::srefl::reflect_info info = ::mirrow::srefl::reflect(); 303 | info.visit_member_variables([&tbl, &value](auto&& field) { 304 | auto& member = field.invoke(&value); 305 | 306 | if constexpr (util::is_optional_v< 307 | util::remove_cvref_t>) { 308 | if (!member.has_value()) { 309 | return; 310 | } 311 | 312 | serialize_destination_type_t> node; 313 | serialize(member, node); 314 | tbl.emplace(field.name(), node); 315 | } else { 316 | serialize_destination_type_t> node; 317 | serialize(member, node); 318 | tbl.emplace(field.name(), node); 319 | } 320 | }); 321 | } 322 | 323 | 324 | 325 | /******************************deserialize********************************************/ 326 | 327 | // fwd declare 328 | 329 | template 330 | std::enable_if_t> deserialize( 331 | const toml::node& node, T& instance); 332 | 333 | // functions for deserialize specific type 334 | 335 | template 336 | std::enable_if_t && !std::is_same_v> 337 | deserialize(const toml::node& node, T& elem) { 338 | if (!node.is_integer() && !node.is_floating_point()) { 339 | MIRROW_LOG( 340 | "deserialize numeric type require TOML node is numeric type"); 341 | return; 342 | } 343 | 344 | if (node.is_integer()) { 345 | elem = static_cast(node.as_integer()->get()); 346 | } else if (node.is_floating_point()) { 347 | elem = static_cast(node.as_floating_point()->get()); 348 | } 349 | } 350 | 351 | template 352 | std::enable_if_t> deserialize( 353 | const toml::node& node, T& elem) { 354 | if (!node.is_boolean()) { 355 | MIRROW_LOG("deserialize bool type require TOML node is boolean type"); 356 | return; 357 | } 358 | 359 | elem = node.as_boolean()->get(); 360 | } 361 | 362 | template 363 | std::enable_if_t> deserialize(const toml::node& node, 364 | T& elem) { 365 | if (!node.is_string()) { 366 | MIRROW_LOG("deserialize string type require TOML node is string type"); 367 | return; 368 | } 369 | 370 | elem = node.as_string()->get(); 371 | } 372 | 373 | template 374 | std::enable_if_t> deserialize(const toml::node& node, 375 | T& elems) { 376 | if (!node.is_array()) { 377 | MIRROW_LOG("deserialize std::vector require TOML node is array type"); 378 | return; 379 | } 380 | 381 | for (auto& value : *node.as_array()) { 382 | auto& elem = elems.emplace_back(); 383 | deserialize(value, elem); 384 | } 385 | } 386 | 387 | template 388 | std::enable_if_t> deserialize( 389 | const toml::node& node, T& elems) { 390 | if (!node.is_array()) { 391 | MIRROW_LOG("deserialize std::array require TOML node is array type"); 392 | return; 393 | } 394 | 395 | auto& arr = *node.as_array(); 396 | 397 | size_t suitable_size = std::min(elems.size(), arr.size()); 398 | for (int i = 0; i < suitable_size; i++) { 399 | deserialize(arr[i], elems[i]); 400 | } 401 | } 402 | 403 | template 404 | std::enable_if_t> deserialize( 405 | const toml::node& node, T& elems) { 406 | if (!node.is_table()) { 407 | MIRROW_LOG( 408 | "deserialize std::unordered_map require TOML node is table type"); 409 | return; 410 | } 411 | 412 | auto& tbl = *node.as_table(); 413 | for (auto& [name, value] : tbl) { 414 | elems.emplace(name, deserialize(value)); 415 | } 416 | } 417 | 418 | template 419 | std::enable_if_t> deserialize( 420 | const toml::node& node, T& elems) { 421 | if (!node.is_table()) { 422 | MIRROW_LOG( 423 | "deserialize std::unordered_set require TOML node is array type"); 424 | return; 425 | } 426 | 427 | auto& arr = *node.as_array(); 428 | for (auto& value : arr) { 429 | arr.insert(deserialize(value)); 430 | } 431 | } 432 | 433 | template 434 | std::enable_if_t> deserialize( 435 | const toml::node& node, T& instance) { 436 | typename T::value_type value; 437 | deserialize(node, value); 438 | instance = value; 439 | } 440 | 441 | template 442 | std::enable_if_t> deserialize( 443 | const toml::node& node, T& instance) { 444 | if (!node.is_table()) { 445 | MIRROW_LOG("deserialize class require TOML node is table type"); 446 | return; 447 | } 448 | 449 | auto& tbl = *node.as_table(); 450 | 451 | auto type_info = ::mirrow::srefl::reflect(); 452 | 453 | type_info.visit_member_variables([&tbl, &instance](auto& field) { 454 | using type = typename util::remove_cvref_t::type; 455 | 456 | auto node = tbl[field.name()]; 457 | auto& member = field.invoke(instance); 458 | if (node.node()) { 459 | deserialize>( 460 | *node.node(), field.invoke(instance)); 461 | } 462 | }); 463 | } 464 | 465 | } // namespace srefl 466 | 467 | } // namespace serd 468 | 469 | } // namespace mirrow -------------------------------------------------------------------------------- /mirrow/mirrow/srefl/reflect.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "mirrow/srefl/srefl.hpp" 4 | #include "mirrow/util/type_list.hpp" 5 | #include "mirrow/util/misc.hpp" 6 | 7 | #include 8 | #include 9 | #include 10 | 11 | namespace mirrow { 12 | 13 | namespace srefl { 14 | 15 | // some type_info traits 16 | namespace detail { 17 | 18 | template > 19 | struct has_bases final : std::false_type {}; 20 | 21 | template 22 | struct has_bases> { 23 | static constexpr bool value = 24 | !util::is_list_empty_v; 25 | }; 26 | 27 | template > 28 | struct has_fields final : std::false_type {}; 29 | 30 | template 31 | struct has_fields> { 32 | static constexpr bool value = !util::is_list_empty_v< 33 | std::remove_cv_t>>; 34 | }; 35 | 36 | template > 37 | struct has_ctors final : std::false_type {}; 38 | 39 | template 40 | struct has_ctors> { 41 | static constexpr bool value = 42 | !util::is_list_empty_v; 43 | }; 44 | 45 | } // namespace detail 46 | 47 | namespace internal { 48 | 49 | template 50 | constexpr auto pick_tuple_elements(TupleType&& tuple, 51 | std::index_sequence) { 52 | return std::make_tuple(std::get(tuple)...); 53 | } 54 | 55 | template 56 | constexpr auto inc_seq_elem(std::index_sequence seq) { 57 | return std::index_sequence<(Idx + 1)...>{}; 58 | } 59 | 60 | } // namespace internal 61 | 62 | /** 63 | * @brief get tail of tuple(the elems without first elem) 64 | */ 65 | template 66 | constexpr auto tuple_tail(TupleType&& tuple) { 67 | using tuple_type = util::remove_cvref_t; 68 | 69 | if constexpr (util::list_size_v >= 1) { 70 | return internal::pick_tuple_elements( 71 | std::forward(tuple), 72 | internal::inc_seq_elem( 73 | std::make_index_sequence - 1>{})); 74 | } else { 75 | return std::tuple<>{}; 76 | } 77 | } 78 | 79 | /** 80 | * @brief check whether a type_info has bases classes 81 | */ 82 | template 83 | constexpr bool has_bases_v = detail::has_bases::value; 84 | 85 | /** 86 | * @brief check whether a type_info has ctors 87 | */ 88 | template 89 | constexpr bool has_ctors_v = detail::has_ctors::value; 90 | 91 | /** 92 | * @brief check whether a type_info has field 93 | */ 94 | template 95 | constexpr bool has_fields_v = detail::has_fields::value; 96 | 97 | template 98 | struct field_descriptor { 99 | using clazz = T; 100 | using field = Field; 101 | }; 102 | 103 | template 104 | class reflect_info final { 105 | public: 106 | using type = type_info; 107 | 108 | /** 109 | * @brief construct a instance 110 | */ 111 | template 112 | T construct(Args&&... args) { 113 | return T{std::forward(args)...}; 114 | } 115 | 116 | /** 117 | * @brief check whether the type is class 118 | */ 119 | constexpr bool is_class() const noexcept { 120 | return std::is_class_v; 121 | } 122 | 123 | /** 124 | * @brief check whether the type is enum 125 | */ 126 | constexpr bool is_enum() const noexcept { 127 | return std::is_enum_v; 128 | } 129 | 130 | /** 131 | * @brief check whether class has base classes 132 | */ 133 | constexpr bool has_bases() const noexcept { return has_bases_v; } 134 | 135 | /** 136 | * @brief check whether class has constructors 137 | */ 138 | constexpr bool has_ctors() const noexcept { return has_ctors_v; } 139 | 140 | /** 141 | * @brief check whether class has fields 142 | */ 143 | constexpr bool has_fields() const noexcept { return has_fields_v; } 144 | 145 | constexpr decltype(auto) enum_values() const noexcept { 146 | if constexpr (std::is_enum_v) { 147 | return type::enums; 148 | } else { 149 | return std::array, 0>{}; 150 | } 151 | } 152 | 153 | /** 154 | * @brief runtime tool: visit all fields 155 | */ 156 | template 157 | void visit_fields(Function&& func) { 158 | if constexpr (has_fields_v) { 159 | std::apply( 160 | [&func](auto&&... args) { 161 | (func(std::forward(args)), ...); 162 | }, 163 | type::fields); 164 | } 165 | } 166 | 167 | /** 168 | * @brief runtime tool: visit all member variables 169 | */ 170 | template 171 | void visit_member_variables(Function&& func) { 172 | if constexpr (has_fields_v) { 173 | do_visit_member_variables<0>(std::forward(func)); 174 | } 175 | } 176 | 177 | constexpr std::string_view name() const noexcept { 178 | return type::name(); 179 | } 180 | 181 | /** 182 | * @brief runtime tool: visit all member functions 183 | */ 184 | template 185 | void visit_member_functions(Function&& func) { 186 | if constexpr (has_fields_v) { 187 | do_visit_member_functions<0>(std::forward(func)); 188 | } 189 | } 190 | 191 | private: 192 | template 193 | void do_visit_member_variables(Function&& func) { 194 | auto fields = type::fields; 195 | if constexpr (Idx < util::list_size_v>>) { 197 | auto field = std::get(fields); 198 | if constexpr (field.is_variable() && field.is_member()) { 199 | func(std::get(fields)); 200 | } 201 | do_visit_member_variables(std::forward(func)); 202 | } 203 | } 204 | 205 | template 206 | void do_visit_member_functions(Function&& func) { 207 | constexpr auto fields = type::fields; 208 | if constexpr (Idx < util::list_size_v>>) { 210 | constexpr auto field = std::get(fields); 211 | if constexpr (field.is_function() && field.is_member()) { 212 | func(std::get(fields)); 213 | } 214 | do_visit_member_functions(std::forward(func)); 215 | } 216 | } 217 | }; 218 | 219 | /** 220 | * @brief get reflected class info 221 | */ 222 | template 223 | constexpr auto reflect() { 224 | return reflect_info>{}; 225 | } 226 | 227 | } // namespace srefl 228 | 229 | } // namespace mirrow -------------------------------------------------------------------------------- /mirrow/mirrow/srefl/srefl.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "mirrow/util/function_traits.hpp" 4 | #include "mirrow/util/type_list.hpp" 5 | #include "mirrow/util/variable_traits.hpp" 6 | 7 | #include 8 | #include 9 | #include 10 | 11 | namespace mirrow { 12 | 13 | namespace srefl { 14 | 15 | /** 16 | * @brief attributes that attach to field/function 17 | */ 18 | template 19 | using attr_list = util::type_list; 20 | 21 | namespace internal { 22 | 23 | template 24 | struct basic_field_traits; 25 | 26 | template 27 | struct basic_field_traits : util::function_traits { 28 | constexpr bool is_const_member() const noexcept { 29 | return util::function_traits::is_const; 30 | } 31 | 32 | constexpr bool is_member() const noexcept { 33 | return util::function_traits::is_member; 34 | } 35 | 36 | constexpr bool is_function() const noexcept { 37 | return true; 38 | } 39 | 40 | constexpr bool is_variable() const noexcept { 41 | return false; 42 | } 43 | }; 44 | 45 | template 46 | struct basic_field_traits : util::variable_traits { 47 | constexpr bool is_const_member() const noexcept { 48 | return false; 49 | } 50 | 51 | constexpr bool is_member() const noexcept { 52 | return util::variable_traits::is_member; 53 | } 54 | 55 | constexpr bool is_function() const noexcept { 56 | return false; 57 | } 58 | 59 | constexpr bool is_variable() const noexcept { 60 | return true; 61 | } 62 | }; 63 | 64 | } // namespace internal 65 | 66 | /** 67 | * @brief strip class/function/variable name from namespace/class prefix to pure name 68 | */ 69 | inline constexpr std::string_view strip_name(std::string_view name) { 70 | std::string_view result = name; 71 | 72 | if (auto idx = name.find_last_of('&'); idx != std::string_view::npos) { 73 | name = name.substr(idx + 1, name.length()); 74 | } 75 | if (auto idx = name.find_last_of(':'); idx != std::string_view::npos) { 76 | name = name.substr(idx + 1, name.length()); 77 | } 78 | if (auto idx = name.find_first_of(')'); idx != std::string_view::npos) { 79 | name = name.substr(0, idx); 80 | } 81 | 82 | return name; 83 | } 84 | 85 | /** 86 | * @brief extract class field(member variable, member function) info 87 | * 88 | * @tparam T type 89 | * @tparam Attrs attributes 90 | */ 91 | template 92 | struct field_traits : internal::basic_field_traits> { 93 | constexpr field_traits(T&& pointer, std::string_view name, 94 | Attrs&&... attrs) 95 | : pointer_(std::forward(pointer)), 96 | name_(strip_name(name)), 97 | attrs_(std::forward(attrs)...) {} 98 | 99 | /** 100 | * @brief check whether field is a const member(class const function) 101 | */ 102 | constexpr bool is_const_member() const noexcept { 103 | return base::is_const_member(); 104 | } 105 | 106 | /** 107 | * @brief check whether field is class member or static/global 108 | */ 109 | constexpr bool is_member() const noexcept { 110 | return base::is_member(); 111 | } 112 | 113 | /** 114 | * @brief get field name 115 | */ 116 | constexpr std::string_view name() const noexcept { 117 | return name_; 118 | } 119 | 120 | /** 121 | * @brief get pointer 122 | */ 123 | constexpr auto pointer() const noexcept { 124 | return pointer_; 125 | } 126 | 127 | /** 128 | * @brief get attributes 129 | */ 130 | constexpr auto& attrs() const noexcept { 131 | return attrs_; 132 | } 133 | 134 | template 135 | decltype(auto) invoke(Args&&... args) { 136 | if constexpr (!util::is_function_v) { 137 | if constexpr (util::variable_traits::is_member) { 138 | return std::invoke(this->pointer_, std::forward(args)...); 139 | } else { 140 | return *(this->pointer_); 141 | } 142 | } else { 143 | return std::invoke(this->pointer_, std::forward(args)...); 144 | } 145 | } 146 | 147 | private: 148 | using base = internal::basic_field_traits>; 149 | 150 | T pointer_; 151 | std::string_view name_; 152 | std::tuple attrs_; 153 | }; 154 | 155 | /** 156 | * @brief store class constructor 157 | */ 158 | template 159 | struct ctor { 160 | using args = util::type_list; 161 | }; 162 | 163 | /** 164 | * @brief store base classes 165 | */ 166 | template 167 | struct base { 168 | using bases = util::type_list; 169 | }; 170 | 171 | template 172 | struct base_type_info { 173 | using type = T; 174 | static constexpr bool is_final = std::is_final_v; 175 | }; 176 | 177 | template 178 | struct enum_value { 179 | using value_type = T; 180 | // using underlying_type = std::underlying_type_t; 181 | 182 | constexpr enum_value(value_type value, std::string_view name) 183 | : value{value}, name{name} {} 184 | 185 | T value; 186 | std::string_view name; 187 | }; 188 | 189 | /** 190 | * @brief store class type info 191 | * 192 | * @tparam T type 193 | * @tparam AttrList attributes 194 | */ 195 | template 196 | struct type_info; 197 | 198 | } // namespace srefl 199 | 200 | } // namespace mirrow -------------------------------------------------------------------------------- /mirrow/mirrow/srefl/srefl_begin.hpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file srefl_macro.hpp 3 | * @brief some helpful macros to help you reflect class more eazier 4 | * @note use `srefl_end.hpp` when you finish reflect 5 | */ 6 | 7 | #include 8 | 9 | #define srefl_class(type, ...) \ 10 | template <> \ 11 | struct type_info : base_type_info { \ 12 | static constexpr std::string_view name() { \ 13 | return #type; \ 14 | } \ 15 | __VA_ARGS__ \ 16 | }; 17 | 18 | #define fields(...) \ 19 | inline static constexpr auto fields = std::make_tuple(__VA_ARGS__); 20 | 21 | #define field(pointer, ...) \ 22 | field_traits { \ 23 | pointer, #pointer, ##__VA_ARGS__ \ 24 | } 25 | 26 | #define bases(...) using bases = util::type_list<__VA_ARGS__>; 27 | 28 | #define ctors(...) using ctors = util::type_list<__VA_ARGS__>; 29 | 30 | #define ctor(...) ctor<__VA_ARGS__> 31 | 32 | #define srefl_enum(type, ...) \ 33 | template <> \ 34 | struct type_info : base_type_info { \ 35 | static constexpr std::string_view name() { \ 36 | return #type; \ 37 | } \ 38 | static constexpr std::array enums = {__VA_ARGS__}; \ 39 | }; 40 | 41 | #define enum_value(value, name) \ 42 | enum_value { \ 43 | value, name \ 44 | } 45 | 46 | #ifdef MIRROW_SREFL_BEGIN 47 | #error \ 48 | "do you forget include mirrow/srefl/srefl_end.hpp after include mirrow/srefl/srefl_begin.hpp?" 49 | #define MIRROW_SREFL_BEGIN 50 | #endif 51 | 52 | namespace mirrow::srefl { 53 | -------------------------------------------------------------------------------- /mirrow/mirrow/srefl/srefl_end.hpp: -------------------------------------------------------------------------------- 1 | #undef MIRROW_SREFL_BEGIN 2 | 3 | } 4 | 5 | #undef srefl_class 6 | #undef fields 7 | #undef field 8 | #undef bases 9 | #undef ctors 10 | #undef ctor 11 | #undef enum_value 12 | -------------------------------------------------------------------------------- /mirrow/mirrow/util/const_str.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | namespace mirrow { 7 | 8 | namespace util { 9 | 10 | template 11 | struct const_string { 12 | static constexpr Char data[] = {Chars..., 0}; 13 | static constexpr size_t length = sizeof...(Chars); 14 | 15 | constexpr std::string_view str() const noexcept { return data; } 16 | }; 17 | 18 | template 19 | constexpr decltype(auto) prepareImpl(T, std::index_sequence) { 20 | return const_string(); 21 | } 22 | 23 | template 24 | constexpr decltype(auto) prepare(T t) { 25 | return prepareImpl(t, std::make_index_sequence()); 26 | } 27 | 28 | #define CONST_STR(s) \ 29 | (::mirrow::util::prepare([] { \ 30 | struct tmp { \ 31 | static constexpr decltype(auto) get() { \ 32 | return s; \ 33 | } \ 34 | }; \ 35 | return tmp{}; \ 36 | }())) 37 | 38 | } // namespace util 39 | 40 | } // namespace mirrow -------------------------------------------------------------------------------- /mirrow/mirrow/util/function_traits.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "mirrow/util/type_list.hpp" 4 | 5 | namespace mirrow { 6 | 7 | namespace util { 8 | 9 | namespace detail { 10 | 11 | template 12 | struct function_type; 13 | 14 | template 15 | struct function_type { 16 | using type = Ret(Args...); 17 | }; 18 | 19 | template 20 | struct function_type { 21 | using type = Ret (Class::*)(Args...); 22 | }; 23 | 24 | template 25 | struct function_type { 26 | using type = Ret (Class::*)(Args...) const; 27 | }; 28 | 29 | } // namespace detail 30 | 31 | /** 32 | * @brief get function type from function pointer type 33 | * 34 | * @tparam F 35 | */ 36 | template 37 | using function_type_t = typename detail::function_type::type; 38 | 39 | namespace detail { 40 | 41 | template 42 | auto function_pointer_to_type(int, Ret (*)(Args...)) -> Ret (*)(Args...); 43 | 44 | template 45 | auto function_pointer_to_type(char, Ret (Class::*)(Args...)) 46 | -> Ret (Class::*)(Args...); 47 | 48 | template 49 | auto function_pointer_to_type(char, Ret (Class::*)(Args...) const) 50 | -> Ret (Class::*)(Args...) const; 51 | 52 | } // namespace detail 53 | 54 | /** 55 | * @brief get a function pointer type from it's function pointer 56 | * 57 | * @tparam F 58 | */ 59 | template 60 | using function_pointer_type_t = 61 | decltype(detail::function_pointer_to_type(0, F)); 62 | 63 | /** 64 | * @brief get a function type from it's function pointer 65 | * 66 | * @tparam F 67 | */ 68 | template 69 | using function_type_from_pointer_t = 70 | function_type_t; 71 | 72 | namespace detail { 73 | 74 | template 75 | struct basic_function_traits; 76 | 77 | template 78 | struct basic_function_traits { 79 | using args = util::type_list; 80 | using return_type = Ret; 81 | }; 82 | 83 | } // namespace detail 84 | 85 | /** 86 | * @brief extract function info from function type 87 | * 88 | * @tparam Func 89 | */ 90 | template 91 | struct function_traits; 92 | 93 | template 94 | struct function_traits 95 | : detail::basic_function_traits { 96 | using type = Ret(Args...); 97 | using args_with_class = type_list; 98 | using pointer = Ret(*)(Args...); 99 | static constexpr bool is_member = false; 100 | static constexpr bool is_const = false; 101 | }; 102 | 103 | template 104 | struct function_traits 105 | : detail::basic_function_traits { 106 | using type = Ret (Class::*)(Args...); 107 | using args_with_class = type_list; 108 | using pointer = Ret (Class::*)(Args...); 109 | static constexpr bool is_member = true; 110 | static constexpr bool is_const = false; 111 | }; 112 | 113 | template 114 | struct function_traits 115 | : detail::basic_function_traits { 116 | using type = Ret (Class::*)(Args...) const; 117 | using args_with_class = type_list; 118 | using pointer = Ret (Class::*)(Args...) const; 119 | static constexpr bool is_member = true; 120 | static constexpr bool is_const = true; 121 | }; 122 | 123 | namespace detail { 124 | 125 | template 126 | struct function_pointer_traits 127 | : function_traits> {}; 128 | 129 | } // namespace detail 130 | 131 | /** 132 | * @brief extract function info from function pointer 133 | * 134 | * @tparam F 135 | */ 136 | template 137 | using function_pointer_traits = detail::function_pointer_traits; 138 | 139 | /** 140 | * @brief check a type is a function or function pointer 141 | */ 142 | template 143 | constexpr bool is_function_v = std::is_function_v || std::is_member_function_pointer_v; 144 | 145 | } // namespace util 146 | 147 | } // namespace mirrow -------------------------------------------------------------------------------- /mirrow/mirrow/util/misc.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | namespace mirrow { 16 | 17 | namespace util { 18 | 19 | template 20 | using remove_cvref_t = std::remove_cv_t>; 21 | 22 | namespace detail { 23 | template 24 | struct remove_all_pointers; 25 | 26 | template 27 | struct remove_all_pointers { 28 | using type = T; 29 | }; 30 | 31 | template 32 | struct remove_all_pointers { 33 | using type = typename remove_all_pointers, 34 | std::is_pointer_v>::type; 35 | }; 36 | } // namespace detail 37 | 38 | /** 39 | * @brief remove all pointers from type 40 | */ 41 | template 42 | using remove_all_pointers_t = 43 | typename detail::remove_all_pointers::type; 44 | 45 | /** 46 | * @brief check whether a type has qualifier(const, volatile) 47 | */ 48 | template 49 | constexpr bool has_qualifier_v = std::is_volatile_v || std::is_const_v; 50 | 51 | /** 52 | * @brief check whether a type has pointer, qualifier, reference or array 53 | */ 54 | template 55 | constexpr bool is_complex_type_v = has_qualifier_v || std::is_pointer_v || 56 | std::is_array_v || std::is_reference_v; 57 | 58 | /** 59 | * @brief remove qualifier/pointer/reference/array from type 60 | */ 61 | template 62 | using strip_type_t = remove_cvref_t< 63 | remove_all_pointers_t>>>; 64 | 65 | namespace detail { 66 | 67 | template 68 | [[maybe_unused]] static auto is_container_test(int) 69 | -> decltype(std::declval().begin(), std::declval().end(), 70 | std::true_type{}); 71 | 72 | template 73 | [[maybe_unused]] static std::false_type is_container_test(...); 74 | 75 | /** 76 | * @brief check whether a type is a container(has begin() and end() member 77 | * function) 78 | */ 79 | template 80 | struct is_container : decltype(detail::is_container_test(0)) {}; 81 | 82 | template 83 | struct completely_strip_type { 84 | using type = T; 85 | }; 86 | 87 | template 88 | struct completely_strip_type>> { 89 | using type = typename completely_strip_type>::type; 90 | }; 91 | 92 | template 93 | struct is_std_array { 94 | static constexpr bool value = false; 95 | }; 96 | 97 | template 98 | struct is_std_array> { 99 | static constexpr bool value = true; 100 | }; 101 | 102 | template 103 | struct is_optional { 104 | static constexpr bool value = false; 105 | }; 106 | 107 | template 108 | struct is_optional> { 109 | static constexpr bool value = true; 110 | }; 111 | 112 | template 113 | struct is_vector { 114 | static constexpr bool value = false; 115 | }; 116 | 117 | template 118 | struct is_vector> { 119 | static constexpr bool value = true; 120 | }; 121 | 122 | template 123 | struct is_std_list { 124 | static constexpr bool value = false; 125 | }; 126 | 127 | template 128 | struct is_std_list> { 129 | static constexpr bool value = true; 130 | }; 131 | 132 | template 133 | struct is_unordered_map { 134 | static constexpr bool value = false; 135 | }; 136 | 137 | template 138 | struct is_unordered_map> { 139 | static constexpr bool value = true; 140 | }; 141 | 142 | template 143 | struct is_map { 144 | static constexpr bool value = false; 145 | }; 146 | 147 | template 148 | struct is_map> { 149 | static constexpr bool value = true; 150 | }; 151 | 152 | template 153 | struct is_set { 154 | static constexpr bool value = false; 155 | }; 156 | 157 | template 158 | struct is_set> { 159 | static constexpr bool value = true; 160 | }; 161 | 162 | template 163 | struct is_unordered_set { 164 | static constexpr bool value = false; 165 | }; 166 | 167 | template 168 | struct is_unordered_set> { 169 | static constexpr bool value = true; 170 | }; 171 | 172 | template 173 | struct is_string { 174 | static constexpr bool value = std::is_same_v; 175 | }; 176 | 177 | template 178 | struct inner_type { 179 | using type = T; 180 | }; 181 | 182 | template 183 | struct inner_type> { 184 | using type = typename T::value_type; 185 | }; 186 | 187 | template 188 | struct pointer_layer { 189 | constexpr static int value = 0; 190 | }; 191 | 192 | template 193 | struct pointer_layer { 194 | constexpr static int value = 1 + pointer_layer::value; 195 | }; 196 | 197 | template 198 | struct array_element_type { 199 | using type = typename T::value_type; 200 | }; 201 | 202 | template 203 | struct array_element_type { 204 | using type = T; 205 | }; 206 | 207 | } // namespace detail 208 | 209 | /** 210 | * Checks whether objects of the type T support member .begin() and .end() 211 | * operations. 212 | */ 213 | template 214 | constexpr bool is_container_v = detail::is_container::value; 215 | 216 | /** 217 | * @brief remove all qualifier/pointer/reference/array from type 218 | */ 219 | template 220 | using completely_strip_type_t = typename detail::completely_strip_type::type; 221 | 222 | template 223 | constexpr bool is_std_array_v = detail::is_std_array::value; 224 | 225 | template 226 | constexpr bool is_vector_v = detail::is_vector::value; 227 | 228 | template 229 | constexpr bool is_std_list_v = detail::is_std_list::value; 230 | 231 | template 232 | constexpr bool is_unordered_map_v = detail::is_unordered_map::value; 233 | 234 | template 235 | constexpr bool is_map_v = detail::is_map::value; 236 | 237 | template 238 | constexpr bool is_set_v = detail::is_set::value; 239 | 240 | template 241 | constexpr bool is_unordered_set_v = detail::is_unordered_set::value; 242 | 243 | template 244 | constexpr bool is_string_v = detail::is_string::value; 245 | 246 | template 247 | constexpr bool is_optional_v = detail::is_optional::value; 248 | 249 | template 250 | using array_element_t = typename detail::array_element_type::type; 251 | 252 | /** 253 | * @brief get container/std::optional inner type 254 | */ 255 | template 256 | using inner_type_t = typename detail::inner_type::type; 257 | 258 | template 259 | constexpr int pointer_layer_v = 260 | detail::pointer_layer>::value; 261 | 262 | } // namespace util 263 | 264 | } // namespace mirrow -------------------------------------------------------------------------------- /mirrow/mirrow/util/type_list.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | namespace mirrow { 7 | 8 | namespace util { 9 | 10 | /** 11 | * @brief a type container to store types 12 | * 13 | * @tparam Ts types 14 | */ 15 | template 16 | struct type_list { 17 | using self_type = type_list; 18 | static constexpr size_t size = sizeof...(Ts); 19 | }; 20 | 21 | namespace detail { 22 | 23 | template 24 | struct list_element; 25 | 26 | template