├── .github ├── FUNDING.yml └── workflows │ ├── build.yml │ └── coverage.yml ├── .gitignore ├── AUTHORS ├── CMakeLists.txt ├── LICENSE ├── README.md ├── build └── .gitignore ├── cmake └── in │ ├── googletest.in │ ├── metaBuildConfig.cmake.in │ └── metaConfig.cmake.in ├── deps └── .gitignore ├── docs ├── CMakeLists.txt ├── CONTRIBUTING.md ├── doxy.in └── extra.dox ├── src └── meta │ ├── factory.hpp │ ├── meta.hpp │ └── policy.hpp └── test ├── CMakeLists.txt ├── meta.cpp └── odr.cpp /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: skypjack 4 | patreon: 5 | open_collective: 6 | ko_fi: 7 | tidelift: 8 | community_bridge: 9 | liberapay: 10 | issuehunt: 11 | otechie: 12 | custom: https://www.paypal.me/skypjack 13 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: build 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | 7 | linux: 8 | timeout-minutes: 5 9 | 10 | strategy: 11 | matrix: 12 | compiler: [g++, clang++] 13 | 14 | runs-on: ubuntu-latest 15 | 16 | steps: 17 | - uses: actions/checkout@v1 18 | - name: Compile tests 19 | working-directory: build 20 | env: 21 | CXX: ${{ matrix.compiler }} 22 | run: | 23 | cmake -DBUILD_TESTING=ON .. 24 | make -j4 25 | - name: Run tests 26 | working-directory: build 27 | env: 28 | CTEST_OUTPUT_ON_FAILURE: 1 29 | run: ctest --timeout 5 -C Debug -j4 30 | 31 | windows: 32 | timeout-minutes: 5 33 | 34 | strategy: 35 | matrix: 36 | generator: [Visual Studio 16 2019] 37 | 38 | runs-on: windows-latest 39 | 40 | steps: 41 | - uses: actions/checkout@v1 42 | - name: Compile tests 43 | working-directory: build 44 | run: | 45 | cmake -DBUILD_TESTING=ON -DCMAKE_CXX_FLAGS=/W1 -G"${{ matrix.generator }}" .. 46 | cmake --build . -j 4 47 | - name: Run tests 48 | working-directory: build 49 | env: 50 | CTEST_OUTPUT_ON_FAILURE: 1 51 | run: ctest --timeout 5 -C Debug -j4 52 | 53 | macos: 54 | timeout-minutes: 5 55 | runs-on: macOS-latest 56 | 57 | steps: 58 | - uses: actions/checkout@v1 59 | - name: Compile tests 60 | working-directory: build 61 | run: | 62 | cmake -DBUILD_TESTING=ON .. 63 | make -j4 64 | - name: Run tests 65 | working-directory: build 66 | env: 67 | CTEST_OUTPUT_ON_FAILURE: 1 68 | run: ctest --timeout 5 -C Debug -j4 69 | -------------------------------------------------------------------------------- /.github/workflows/coverage.yml: -------------------------------------------------------------------------------- 1 | name: coverage 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | 7 | codecov: 8 | timeout-minutes: 30 9 | runs-on: ubuntu-latest 10 | 11 | steps: 12 | - uses: actions/checkout@v1 13 | - name: Compile tests 14 | working-directory: build 15 | env: 16 | CXXFLAGS: "-O0 --coverage -fno-inline -fno-inline-small-functions -fno-default-inline" 17 | CXX: g++ 18 | run: | 19 | cmake -DBUILD_TESTING=ON .. 20 | make -j4 21 | - name: Run tests 22 | working-directory: build 23 | env: 24 | CTEST_OUTPUT_ON_FAILURE: 1 25 | run: ctest --timeout 5 -C Debug -j4 26 | - name: Upload coverage to Codecov 27 | working-directory: build 28 | env: 29 | CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} 30 | run: | 31 | wget https://codecov.io/bash -O codecov 32 | chmod +x codecov 33 | ./codecov -t $CODECOV_TOKEN -B $GITHUB_REF -s test/ 34 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.user 2 | .vs 3 | CMakeSettings.json 4 | -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | # Author 2 | 3 | skypjack 4 | 5 | # Contributors 6 | 7 | beppemarazzi 8 | iaanus 9 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # 2 | # meta 3 | # 4 | 5 | cmake_minimum_required(VERSION 3.7.2) 6 | 7 | # 8 | # Building in-tree is not allowed (we take care of your craziness). 9 | # 10 | 11 | if(${CMAKE_SOURCE_DIR} STREQUAL ${CMAKE_BINARY_DIR}) 12 | message(FATAL_ERROR "Prevented in-tree built. Please create a build directory outside of the source code and call cmake from there. Thank you.") 13 | endif() 14 | 15 | # 16 | # Project configuration 17 | # 18 | 19 | project(meta VERSION 1.4.9) 20 | 21 | include(GNUInstallDirs) 22 | 23 | if(NOT CMAKE_BUILD_TYPE) 24 | set(CMAKE_BUILD_TYPE Debug) 25 | endif() 26 | 27 | set(SETTINGS_ORGANIZATION "Michele Caini") 28 | set(SETTINGS_APPLICATION ${PROJECT_NAME}) 29 | set(PROJECT_AUTHOR "Michele Caini") 30 | set(PROJECT_AUTHOR_EMAIL "michele.caini@gmail.com") 31 | 32 | message("*") 33 | message("* ${PROJECT_NAME} v${PROJECT_VERSION} (${CMAKE_BUILD_TYPE})") 34 | message("* Copyright (c) 2018-2019 ${PROJECT_AUTHOR} <${PROJECT_AUTHOR_EMAIL}>") 35 | message("*") 36 | 37 | option(USE_LIBCPP "Use libc++ by adding -stdlib=libc++ flag if availbale." ON) 38 | option(USE_ASAN "Use address sanitizer by adding -fsanitize=address -fno-omit-frame-pointer flags" OFF) 39 | option(USE_COMPILE_OPTIONS "Use compile options from meta." ON) 40 | 41 | # 42 | # Compiler stuff 43 | # 44 | 45 | if(NOT MSVC AND USE_LIBCPP) 46 | include(CheckCXXSourceCompiles) 47 | include(CMakePushCheckState) 48 | 49 | cmake_push_check_state() 50 | 51 | set(CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS} -stdlib=libc++") 52 | 53 | check_cxx_source_compiles(" 54 | #include 55 | int main() { return std::is_same::value ? 0 : 1; } 56 | " HAS_LIBCPP) 57 | 58 | if(NOT HAS_LIBCPP) 59 | message(WARNING "The option USE_LIBCPP is set (by default) but libc++ is not available. The flag will not be added to the target.") 60 | endif() 61 | 62 | cmake_pop_check_state() 63 | endif() 64 | 65 | # 66 | # Add meta target 67 | # 68 | 69 | add_library(meta INTERFACE) 70 | 71 | target_include_directories( 72 | meta INTERFACE 73 | $ 74 | $ 75 | ) 76 | 77 | target_compile_definitions( 78 | meta 79 | INTERFACE $<$,$>>:DEBUG> 80 | INTERFACE $<$,$>>:RELEASE> 81 | ) 82 | 83 | if(USE_ASAN) 84 | target_compile_options(meta INTERFACE $<$,$>>:-fsanitize=address -fno-omit-frame-pointer>) 85 | target_link_libraries(meta INTERFACE $<$,$>>:-fsanitize=address -fno-omit-frame-pointer>) 86 | endif() 87 | 88 | if(USE_COMPILE_OPTIONS) 89 | target_compile_options( 90 | meta 91 | INTERFACE $<$,$>>:-O0 -g> 92 | # it seems that -O3 ruins a bit the performance when using clang ... 93 | INTERFACE $<$,$>:-O2> 94 | # ... on the other side, GCC is incredibly comfortable with it. 95 | INTERFACE $<$,$>:-O3> 96 | ) 97 | endif() 98 | 99 | if(HAS_LIBCPP) 100 | target_compile_options(meta BEFORE INTERFACE -stdlib=libc++) 101 | endif() 102 | 103 | target_compile_features(meta INTERFACE cxx_std_17) 104 | 105 | # 106 | # Install meta 107 | # 108 | 109 | if(${CMAKE_SYSTEM_NAME} STREQUAL "Windows") 110 | set(CUSTOM_INSTALL_CONFIGDIR cmake) 111 | else() 112 | set(CUSTOM_INSTALL_CONFIGDIR ${CMAKE_INSTALL_LIBDIR}/cmake/meta) 113 | endif() 114 | 115 | install(DIRECTORY src/ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) 116 | install(TARGETS meta EXPORT metaTargets) 117 | 118 | export(EXPORT metaTargets FILE ${meta_BINARY_DIR}/metaTargets.cmake) 119 | 120 | install( 121 | EXPORT metaTargets 122 | FILE metaTargets.cmake 123 | DESTINATION ${CUSTOM_INSTALL_CONFIGDIR} 124 | ) 125 | 126 | # 127 | # Build tree package config file 128 | # 129 | 130 | configure_file(cmake/in/metaBuildConfig.cmake.in metaConfig.cmake @ONLY) 131 | 132 | include(CMakePackageConfigHelpers) 133 | 134 | # 135 | # Install tree package config file 136 | # 137 | 138 | configure_package_config_file( 139 | cmake/in/metaConfig.cmake.in 140 | ${CUSTOM_INSTALL_CONFIGDIR}/metaConfig.cmake 141 | INSTALL_DESTINATION ${CUSTOM_INSTALL_CONFIGDIR} 142 | PATH_VARS CMAKE_INSTALL_INCLUDEDIR 143 | NO_CHECK_REQUIRED_COMPONENTS_MACRO 144 | ) 145 | 146 | write_basic_package_version_file( 147 | ${meta_BINARY_DIR}/metaConfigVersion.cmake 148 | VERSION ${PROJECT_VERSION} 149 | COMPATIBILITY AnyNewerVersion 150 | ) 151 | 152 | install( 153 | FILES 154 | ${meta_BINARY_DIR}/${CUSTOM_INSTALL_CONFIGDIR}/metaConfig.cmake 155 | ${meta_BINARY_DIR}/metaConfigVersion.cmake 156 | DESTINATION ${CUSTOM_INSTALL_CONFIGDIR} 157 | ) 158 | 159 | export(PACKAGE meta) 160 | 161 | # 162 | # Tests 163 | # 164 | 165 | option(BUILD_TESTING "Enable testing with ctest." ON) 166 | 167 | if(BUILD_TESTING) 168 | set(THREADS_PREFER_PTHREAD_FLAG ON) 169 | find_package(Threads REQUIRED) 170 | 171 | option(FIND_GTEST_PACKAGE "Enable finding gtest package." OFF) 172 | 173 | if(FIND_GTEST_PACKAGE) 174 | find_package(GTest REQUIRED) 175 | else() 176 | # gtest, gtest_main, gmock and gmock_main targets are available from now on 177 | set(GOOGLETEST_DEPS_DIR ${meta_SOURCE_DIR}/deps/googletest) 178 | configure_file(${meta_SOURCE_DIR}/cmake/in/googletest.in ${GOOGLETEST_DEPS_DIR}/CMakeLists.txt) 179 | execute_process(COMMAND ${CMAKE_COMMAND} -G "${CMAKE_GENERATOR}" . WORKING_DIRECTORY ${GOOGLETEST_DEPS_DIR}) 180 | execute_process(COMMAND ${CMAKE_COMMAND} --build . WORKING_DIRECTORY ${GOOGLETEST_DEPS_DIR}) 181 | set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) 182 | add_subdirectory(${GOOGLETEST_DEPS_DIR}/src ${GOOGLETEST_DEPS_DIR}/build) 183 | target_compile_features(gmock_main PRIVATE $) 184 | target_compile_features(gmock PRIVATE $) 185 | add_library(GTest::Main ALIAS gtest_main) 186 | endif() 187 | 188 | enable_testing() 189 | add_subdirectory(test) 190 | endif() 191 | 192 | # 193 | # Documentation 194 | # 195 | 196 | option(BUILD_DOCS "Enable building with documentation." OFF) 197 | 198 | if(BUILD_DOCS) 199 | find_package(Doxygen 1.8) 200 | 201 | if(DOXYGEN_FOUND) 202 | add_subdirectory(docs) 203 | endif() 204 | endif() 205 | 206 | # 207 | # AOB 208 | # 209 | 210 | FILE(GLOB GH_WORKFLOWS .github/workflows/*.yml) 211 | 212 | add_custom_target( 213 | meta_aob 214 | SOURCES 215 | ${GH_WORKFLOWS} 216 | .github/FUNDING.yml 217 | AUTHORS 218 | LICENSE 219 | README.md 220 | ) 221 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2018-2019 Michele Caini 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copy of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copy or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Header-only runtime reflection system in C++ 2 | 3 | 6 | [![GitHub version](https://badge.fury.io/gh/skypjack%2Fmeta.svg)](https://github.com/skypjack/meta/releases) 7 | [![Build Status](https://github.com/skypjack/meta/workflows/build/badge.svg)](https://github.com/skypjack/meta/actions) 8 | [![Coverage](https://codecov.io/gh/skypjack/meta/branch/master/graph/badge.svg)](https://codecov.io/gh/skypjack/meta) 9 | [![Donate](https://img.shields.io/badge/Donate-PayPal-green.svg)](https://www.paypal.me/skypjack) 10 | 11 | > The reflection system was born within [EnTT](https://github.com/skypjack/entt) 12 | > and is developed and enriched there. This project is designed for those who 13 | > are interested only in a header-only, full-featured, non-intrusive and macro 14 | > free reflection system which certainly deserves to be treated also separately 15 | > due to its quality and its rather peculiar features. 16 | 17 | If you use `meta` and you want to say thanks or support the project, please 18 | **consider becoming a 19 | [sponsor](https://github.com/users/skypjack/sponsorship)**.
20 | You can help me make the difference. 21 | [Many thanks](https://skypjack.github.io/sponsorship/) to those who supported me 22 | and still support me today. 23 | 24 | # Table of Contents 25 | 26 | * [Introduction](#introduction) 27 | * [Build Instructions](#build-instructions) 28 | * [Requirements](#requirements) 29 | * [Library](#library) 30 | * [Documentation](#documentation) 31 | * [Tests](#tests) 32 | * [Crash course](#crash-course) 33 | * [Names and identifiers](#names-and-identifiers) 34 | * [Reflection in a nutshell](#reflection-in-a-nutshell) 35 | * [Any as in any type](#any-as-in-any-type) 36 | * [Enjoy the runtime](#enjoy-the-runtime) 37 | * [Policies: the more, the less](#policies-the-more-the-less) 38 | * [Named constants and enums](#named-constants-and-enums) 39 | * [Properties and meta objects](#properties-and-meta-objects) 40 | * [Unregister types](#unregister-types) 41 | * [Contributors](#contributors) 42 | * [License](#license) 43 | * [Support](#support) 44 | 47 | 48 | # Introduction 49 | 50 | Reflection (or rather, its lack) is a trending topic in the C++ world. I looked 51 | for a third-party library that met my needs on the subject, but I always came 52 | across some details that I didn't like: macros, being intrusive, too many 53 | allocations.
54 | In one word: **unsatisfactory**. 55 | 56 | I finally decided to write a built-in, non-intrusive and macro-free runtime 57 | reflection system for my own.
58 | Maybe I didn't do better than others or maybe yes. Time will tell me. 59 | 60 | # Build Instructions 61 | 62 | ## Requirements 63 | 64 | To be able to use `meta`, users must provide a full-featured compiler that 65 | supports at least C++17.
66 | The requirements below are mandatory to compile the tests and to extract the 67 | documentation: 68 | 69 | * CMake version 3.2 or later. 70 | * Doxygen version 1.8 or later. 71 | 72 | If you are looking for a C++14 version of `meta`, feel free to 73 | [contact me](https://github.com/skypjack). 74 | 75 | ## Library 76 | 77 | `meta` is a header-only library. This means that including the `factory.hpp` and 78 | `meta.hpp` headers is enough to include the library as a whole and use it.
79 | It's a matter of adding the following lines to the top of a file: 80 | 81 | ```cpp 82 | #include 83 | #include 84 | ``` 85 | 86 | Then pass the proper `-I` argument to the compiler to add the `src` directory to 87 | the include paths. 88 | 89 | ## Documentation 90 | 91 | The documentation is based on [doxygen](http://www.stack.nl/~dimitri/doxygen/). 92 | To build it: 93 | 94 | $ cd build 95 | $ cmake .. -DBUILD_DOCS=ON 96 | $ make 97 | 98 | The API reference will be created in HTML format within the directory 99 | `build/docs/html`. To navigate it with your favorite browser: 100 | 101 | $ cd build 102 | $ your_favorite_browser docs/html/index.html 103 | 104 | 107 | It's also available [online](https://skypjack.github.io/meta/) for the latest 108 | version. 109 | 112 | 113 | ## Tests 114 | 115 | To compile and run the tests, `meta` requires *googletest*.
116 | `cmake` will download and compile the library before compiling anything else. 117 | In order to build without tests set CMake option `BUILD_TESTING=OFF`. 118 | 119 | To build the most basic set of tests: 120 | 121 | * `$ cd build` 122 | * `$ cmake ..` 123 | * `$ make` 124 | * `$ make test` 125 | 126 | # Crash course 127 | 128 | ## Names and identifiers 129 | 130 | The meta system doesn't force the user to use a specific tool when it comes to 131 | working with names and identifiers. It does this by offering an API that works 132 | with opaque identifiers that for example may or may not be generated by means of 133 | a hashed string.
134 | This means that users can assign any type of identifier to the meta objects, as 135 | long as they are numeric. It doesn't matter if they are generated at runtime, at 136 | compile-time or with custom functions. 137 | 138 | However, the examples in the following sections are all based on 139 | `std::hash` as provided by the standard library. Therefore, 140 | where an identifier is required, it's likely that an instance of this class is 141 | used as follows: 142 | 143 | ```cpp 144 | std::hash hash{}; 145 | auto factory = meta::reflect(hash("reflected_type")); 146 | ``` 147 | 148 | For what it's worth, this is likely completely equivalent to: 149 | 150 | ```cpp 151 | auto factory = meta::reflect(42); 152 | ``` 153 | 154 | Obviously, human-readable identifiers are more convenient to use and highly 155 | recommended. 156 | 157 | ## Reflection in a nutshell 158 | 159 | Reflection always starts from real types (users cannot reflect imaginary types 160 | and it would not make much sense, we wouldn't be talking about reflection 161 | anymore).
162 | To _reflect_ a type, the library provides the `reflect` function: 163 | 164 | ```cpp 165 | meta::factory factory = meta::reflect(hash("reflected_type")); 166 | ``` 167 | 168 | It accepts the type to reflect as a template parameter and an optional 169 | identifier as an argument. Identifiers are important because users can retrieve 170 | meta types at runtime by searching for them by _name_. However, there are cases 171 | in which users can be interested in adding features to a reflected type so that 172 | the reflection system can use it correctly under the hood, but they don't want 173 | to allow searching the type by _name_.
174 | In both cases, the returned value is a factory object to use to continue 175 | building the meta type. 176 | 177 | A factory is such that all its member functions returns the factory itself. 178 | It can be used to extend the reflected type and add the following: 179 | 180 | * _Constructors_. Actual constructors can be assigned to a reflected type by 181 | specifying their list of arguments. Free functions (namely, factories) can be 182 | used as well, as long as the return type is the expected one. From a client's 183 | point of view, nothing changes if a constructor is a free function or an 184 | actual constructor.
185 | Use the `ctor` member function for this purpose: 186 | 187 | ```cpp 188 | meta::reflect(hash("reflected")).ctor().ctor<&factory>(); 189 | ``` 190 | 191 | * _Destructors_. Free functions can be set as destructors of reflected types. 192 | The purpose is to give users the ability to free up resources that require 193 | special treatment before an object is actually destroyed.
194 | Use the `dtor` member function for this purpose: 195 | 196 | ```cpp 197 | meta::reflect(hash("reflected")).dtor<&destroy>(); 198 | ``` 199 | 200 | A function should neither delete nor explicitly invoke the destructor of a 201 | given instance. 202 | 203 | * _Data members_. Both real data members of the underlying type and static and 204 | global variables, as well as constants of any kind, can be attached to a meta 205 | type. From a client's point of view, all the variables associated with the 206 | reflected type will appear as if they were part of the type itself.
207 | Use the `data` member function for this purpose: 208 | 209 | ```cpp 210 | meta::reflect(hash("reflected")) 211 | .data<&my_type::static_variable>(hash("static")) 212 | .data<&my_type::data_member>(hash("member")) 213 | .data<&global_variable>(hash("global")); 214 | ``` 215 | 216 | This function requires as an argument the identifier to give to the meta data 217 | once created. Users can then access meta data at runtime by searching for them 218 | by _name_.
219 | Data members can be set also by means of a couple of functions, namely a 220 | setter and a getter. Setters and getters can be either free functions, member 221 | functions or mixed ones, as long as they respect the required signatures.
222 | Refer to the inline documentation for all the details. 223 | 224 | * _Member functions_. Both real member functions of the underlying type and free 225 | functions can be attached to a meta type. From a client's point of view, all 226 | the functions associated with the reflected type will appear as if they were 227 | part of the type itself.
228 | Use the `func` member function for this purpose: 229 | 230 | ```cpp 231 | meta::reflect(hash("reflected")) 232 | .func<&my_type::static_function>(hash("static")) 233 | .func<&my_type::member_function>(hash("member")) 234 | .func<&free_function>(hash("free")); 235 | ``` 236 | 237 | This function requires as an argument the identifier to give to the meta 238 | function once created. Users can then access meta functions at runtime by 239 | searching for them by _name_. 240 | 241 | * _Base classes_. A base class is such that the underlying type is actually 242 | derived from it. In this case, the reflection system tracks the relationship 243 | and allows for implicit casts at runtime when required.
244 | Use the `base` member function for this purpose: 245 | 246 | ```cpp 247 | meta::reflect(hash("derived")).base(); 248 | ``` 249 | 250 | From now on, wherever a `base_type` is required, an instance of `derived_type` 251 | will also be accepted. 252 | 253 | * _Conversion functions_. Actual types can be converted, this is a fact. Just 254 | think of the relationship between a `double` and an `int` to see it. Similar 255 | to bases, conversion functions allow users to define conversions that will be 256 | implicitly performed by the reflection system when required.
257 | Use the `conv` member function for this purpose: 258 | 259 | ```cpp 260 | meta::reflect().conv(); 261 | ``` 262 | 263 | That's all, everything users need to create meta types and enjoy the reflection 264 | system. At first glance it may not seem that much, but users usually learn to 265 | appreciate it over time.
266 | Also, do not forget what these few lines hide under the hood: a built-in, 267 | non-intrusive and macro-free system for reflection in C++. Features that are 268 | definitely worth the price, at least for me. 269 | 270 | ## Any as in any type 271 | 272 | The reflection system comes with its own meta any type. It may seem redundant 273 | since C++17 introduced `std::any`, but it is not.
274 | In fact, the _type_ returned by an `std::any` is a const reference to an 275 | `std::type_info`, an implementation defined class that's not something everyone 276 | wants to see in a software. Furthermore, the class `std::type_info` suffers from 277 | some design flaws and there is even no way to _convert_ an `std::type_info` into 278 | a meta type, thus linking the two worlds. 279 | 280 | A meta any object provides an API similar to that of its most famous counterpart 281 | and serves the same purpose of being an opaque container for any type of 282 | value.
283 | It minimizes the allocations required, which are almost absent thanks to _SBO_ 284 | techniques. In fact, unless users deal with _fat types_ and create instances of 285 | them though the reflection system, allocations are at zero. 286 | 287 | A meta any object can be created by any other object or as an empty container 288 | to initialize later: 289 | 290 | ```cpp 291 | // a meta any object that contains an int 292 | meta::any any{0}; 293 | 294 | // an empty meta any object 295 | meta::any empty{}; 296 | ``` 297 | 298 | It takes the burden of destroying the contained instance when required.
299 | Moreover, it can be used as an opaque container for unmanaged objects if needed: 300 | 301 | ```cpp 302 | int value; 303 | meta::any any{std::ref(value)}; 304 | ``` 305 | 306 | In other words, whenever `any` intercepts a `reference_wrapper`, it acts as a 307 | reference to the original instance rather than making a copy of it. The 308 | contained object is never destroyed and users must ensure that its lifetime 309 | exceeds that of the container. 310 | 311 | A meta any object has a `type` member function that returns the meta type of the 312 | contained value, if any. The member functions `try_cast`, `cast` and `convert` 313 | are used to know if the underlying object has a given type as a base or if it 314 | can be converted implicitly to it. 315 | 316 | ## Enjoy the runtime 317 | 318 | Once the web of reflected types has been constructed, it's a matter of using it 319 | at runtime where required. 320 | 321 | To search for a reflected type there are two options: by type or by _name_. In 322 | both cases, the search can be done by means of the `resolve` function: 323 | 324 | ```cpp 325 | // search for a reflected type by type 326 | meta::type by_type = meta::resolve(); 327 | 328 | // search for a reflected type by name 329 | meta::type by_name = meta::resolve(hash("reflected_type")); 330 | ``` 331 | 332 | There exits also a third overload of the `resolve` function to use to iterate 333 | all the reflected types at once: 334 | 335 | ```cpp 336 | resolve([](meta::type type) { 337 | // ... 338 | }); 339 | ``` 340 | 341 | In all cases, the returned value is an instance of `type`. This type of objects 342 | offer an API to know the _runtime identifier_ of the type, to iterate all the 343 | meta objects associated with them and even to build or destroy instances of the 344 | underlying type.
345 | Refer to the inline documentation for all the details. 346 | 347 | The meta objects that compose a meta type are accessed in the following ways: 348 | 349 | * _Meta constructors_. They are accessed by types of arguments: 350 | 351 | ```cpp 352 | meta::ctor ctor = meta::resolve().ctor(); 353 | ``` 354 | 355 | The returned type is `ctor` and may be invalid if there is no constructor that 356 | accepts the supplied arguments or at least some types from which they are 357 | derived or to which they can be converted.
358 | A meta constructor offers an API to know the number of arguments, the expected 359 | meta types and to invoke it, therefore to construct a new instance of the 360 | underlying type. 361 | 362 | * _Meta destructor_. It's returned by a dedicated function: 363 | 364 | ```cpp 365 | meta::dtor dtor = meta::resolve().dtor(); 366 | ``` 367 | 368 | The returned type is `dtor` and may be invalid if there is no custom 369 | destructor set for the given meta type.
370 | All what a meta destructor has to offer is a way to invoke it on a given 371 | instance. Be aware that the result may not be what is expected. 372 | 373 | * _Meta data_. They are accessed by _name_: 374 | 375 | ```cpp 376 | meta::data data = meta::resolve().data(hash("member")); 377 | ``` 378 | 379 | The returned type is `data` and may be invalid if there is no meta data object 380 | associated with the given identifier.
381 | A meta data object offers an API to query the underlying type (ie to know if 382 | it's a const or a static one), to get the meta type of the variable and to set 383 | or get the contained value. 384 | 385 | * _Meta functions_. They are accessed by _name_: 386 | 387 | ```cpp 388 | meta::func func = meta::resolve().func(hash("member")); 389 | ``` 390 | 391 | The returned type is `func` and may be invalid if there is no meta function 392 | object associated with the given identifier.
393 | A meta function object offers an API to query the underlying type (ie to know 394 | if it's a const or a static function), to know the number of arguments, the 395 | meta return type and the meta types of the parameters. In addition, a meta 396 | function object can be used to invoke the underlying function and then get the 397 | return value in the form of meta any object. 398 | 399 | * _Meta bases_. They are accessed through the _name_ of the base types: 400 | 401 | ```cpp 402 | meta::base base = meta::resolve().base(hash("base")); 403 | ``` 404 | 405 | The returned type is `base` and may be invalid if there is no meta base object 406 | associated with the given identifier.
407 | Meta bases aren't meant to be used directly, even though they are freely 408 | accessible. They expose only a few methods to use to know the meta type of the 409 | base class and to convert a raw pointer between types. 410 | 411 | * _Meta conversion functions_. They are accessed by type: 412 | 413 | ```cpp 414 | meta::conv conv = meta::resolve().conv(); 415 | ``` 416 | 417 | The returned type is `conv` and may be invalid if there is no meta conversion 418 | function associated with the given type.
419 | The meta conversion functions are as thin as the meta bases and with a very 420 | similar interface. The sole difference is that they return a newly created 421 | instance wrapped in a meta any object when they convert between different 422 | types. 423 | 424 | All the objects thus obtained as well as the meta types can be explicitly 425 | converted to a boolean value to check if they are valid: 426 | 427 | ```cpp 428 | meta::func func = meta::resolve().func(hash("member")); 429 | 430 | if(func) { 431 | // ... 432 | } 433 | ``` 434 | 435 | Furthermore, all meta objects with the exception of meta destructors can be 436 | iterated through an overload that accepts a callback through which to return 437 | them. As an example: 438 | 439 | ```cpp 440 | meta::resolve().data([](meta::data data) { 441 | // ... 442 | }); 443 | ``` 444 | 445 | A meta type can also be used to `construct` or `destroy` actual instances of the 446 | underlying type.
447 | In particular, the `construct` member function accepts a variable number of 448 | arguments and searches for a match. It returns a `any` object that may or may 449 | not be initialized, depending on whether a suitable constructor has been found 450 | or not. On the other side, the `destroy` member function accepts instances of 451 | `any` as well as actual objects by reference and invokes the registered 452 | destructor if any.
453 | Be aware that the result of a call to `destroy` may not be what is expected. The 454 | purpose is to give users the ability to free up resources that require special 455 | treatment and **not** to actually destroy instances. 456 | 457 | Meta types and meta objects in general contain much more than what is said: a 458 | plethora of functions in addition to those listed whose purposes and uses go 459 | unfortunately beyond the scope of this document.
460 | I invite anyone interested in the subject to look at the code, experiment and 461 | read the official documentation to get the best out of this powerful tool. 462 | 463 | ## Policies: the more, the less 464 | 465 | Policies are a kind of compile-time directives that can be used when recording 466 | reflection information.
467 | Their purpose is to require slightly different behavior than the default in some 468 | specific cases. For example, when reading a given data member, its value is 469 | returned wrapped in a `any` object which, by default, makes a copy of it. For 470 | large objects or if the caller wants to access the original instance, this 471 | behavior isn't desirable. Policies are there to offer a solution to this and 472 | other problems. 473 | 474 | There are a few alternatives available at the moment: 475 | 476 | * The _as-is_ policy, associated with the type `meta::as_is_t`.
477 | This is the default policy. In general, it should never be used explicitly, 478 | since it's implicitly selected if no other policy is specified.
479 | In this case, the return values of the functions as well as the properties 480 | exposed as data members are always returned by copy in a dedicated wrapper and 481 | therefore associated with their original meta types. 482 | 483 | * The _as-void_ policy, associated with the type `meta::as_void_t`.
484 | Its purpose is to discard the return value of a meta object, whatever it is, 485 | thus making it appear as if its type were `void`.
486 | If the use with functions is obvious, it must be said that it's also possible 487 | to use this policy with constructors and data members. In the first case, the 488 | constructor will be invoked but the returned wrapper will actually be empty. 489 | In the second case, instead, the property will not be accessible for 490 | reading. 491 | 492 | As an example of use: 493 | 494 | ```cpp 495 | meta::reflect(hash("reflected")) 496 | .func<&my_type::member_function, meta::as_void_t>(hash("member")); 497 | ``` 498 | 499 | * The _as-alias_ policy, associated with the type `meta::as_alias_t`
500 | It allows to build wrappers that act as aliases for the objects used to 501 | initialize them. Modifying the object contained in the wrapper for which the 502 | _aliasing_ was requested will make it possible to directly modify the instance 503 | used to initialize the wrapper itself.
504 | This policy works with constructors (for example, when objects are taken from 505 | an external container rather than created on demand), data members and 506 | functions in general (as long as their return types are lvalue references). 507 | 508 | As an example of use: 509 | 510 | ```cpp 511 | meta::reflect(hash("reflected")) 512 | .data<&my_type::data_member, meta::as_alias_t>(hash("member")); 513 | ``` 514 | 515 | Some uses are rather trivial, but it's useful to note that there are some less 516 | obvious corner cases that can in turn be solved with the use of policies. 517 | 518 | ## Named constants and enums 519 | 520 | A special mention should be made for constant values and enums. It wouldn't be 521 | necessary, but it will help distracted readers. 522 | 523 | As mentioned, the `data` member function can be used to reflect constants of any 524 | type among the other things.
525 | This allows users to create meta types for enums that will work exactly like any 526 | other meta type built from a class. Similarly, arithmetic types can be enriched 527 | with constants of special meaning where required.
528 | Personally, I find it very useful not to export what is the difference between 529 | enums and classes in C++ directly in the space of the reflected types. 530 | 531 | All the values thus exported will appear to users as if they were constant data 532 | members of the reflected types. 533 | 534 | Exporting constant values or elements from an enum is as simple as ever: 535 | 536 | ```cpp 537 | meta::reflect() 538 | .data(hash("a_value")) 539 | .data(hash("another_value")); 540 | 541 | meta::reflect().data<2048>(hash("max_int")); 542 | ``` 543 | 544 | It goes without saying that accessing them is trivial as well. It's a matter of 545 | doing the following, as with any other data member of a meta type: 546 | 547 | ```cpp 548 | my_enum value = meta::resolve().data(hash("a_value")).get({}).cast(); 549 | int max = meta::resolve().data(hash("max_int")).get({}).cast(); 550 | ``` 551 | 552 | As a side note, remember that all this happens behind the scenes without any 553 | allocation because of the small object optimization performed by the meta any 554 | class. 555 | 556 | ## Properties and meta objects 557 | 558 | Sometimes (for example, when it comes to creating an editor) it might be useful 559 | to be able to attach properties to the meta objects created. Fortunately, this 560 | is possible for most of them.
561 | To attach a property to a meta object, no matter what as long as it supports 562 | properties, it is sufficient to provide an object at the time of construction 563 | such that `std::get<0>` and `std::get<1>` are valid for it. In other terms, the 564 | properties are nothing more than key/value pairs users can put in an 565 | `std::pair`. As an example: 566 | 567 | ```cpp 568 | meta::reflect(hash("reflected"), std::make_pair(hash("tooltip"), "message")); 569 | ``` 570 | 571 | The meta objects that support properties offer then a couple of member functions 572 | named `prop` to iterate them at once and to search a specific property by key: 573 | 574 | ```cpp 575 | // iterate all the properties of a meta type 576 | meta::resolve().prop([](meta::prop prop) { 577 | // ... 578 | }); 579 | 580 | // search for a given property by name 581 | meta::prop prop = meta::resolve().prop(hash("tooltip")); 582 | ``` 583 | 584 | Meta properties are objects having a fairly poor interface, all in all. They 585 | only provide the `key` and the `value` member functions to be used to retrieve 586 | the key and the value contained in the form of meta any objects, respectively. 587 | 588 | ## Unregister types 589 | 590 | A type registered with the reflection system can also be unregistered. This 591 | means unregistering all its data members, member functions, conversion functions 592 | and so on. However, the base classes won't be unregistered, since they don't 593 | necessarily depend on it. Similarly, implicitly generated types (as an example, 594 | the meta types implicitly generated for function parameters when needed) won't 595 | be unregistered. 596 | 597 | To unregister a type, users can use the `unregister` function from the global 598 | namespace: 599 | 600 | ```cpp 601 | meta::unregister(); 602 | ``` 603 | 604 | This function returns a boolean value that is true if the type is actually 605 | registered with the reflection system, false otherwise.
606 | The type can be re-registered later with a completely different name and form. 607 | 608 | 611 | # Contributors 612 | 613 | Requests for features, PR, suggestions ad feedback are highly appreciated. 614 | 615 | If you find you can help me and want to contribute to the project with your 616 | experience or you do want to get part of the project for some other reasons, 617 | feel free to contact me directly (you can find the mail in the 618 | [profile](https://github.com/skypjack)).
619 | I can't promise that each and every contribution will be accepted, but I can 620 | assure that I'll do my best to take them all seriously. 621 | 622 | If you decide to participate, please see the guidelines for 623 | [contributing](docs/CONTRIBUTING.md) before to create issues or pull 624 | requests.
625 | Take also a look at the 626 | [contributors list](https://github.com/skypjack/meta/blob/master/AUTHORS) to 627 | know who has participated so far. 628 | 631 | 632 | # License 633 | 634 | Code and documentation Copyright (c) 2018-2019 Michele Caini. 635 | 636 | Code released under 637 | [the MIT license](https://github.com/skypjack/meta/blob/master/LICENSE). 638 | Documentation released under 639 | [CC BY 4.0](https://creativecommons.org/licenses/by/4.0/).
640 | 641 | 644 | # Support 645 | 646 | If you want to support this project, you can 647 | [offer me](https://github.com/users/skypjack/sponsorship) an espresso.
648 | If you find that it's not enough, feel free to 649 | [help me](https://www.paypal.me/skypjack) the way you prefer. 650 | 653 | -------------------------------------------------------------------------------- /build/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /cmake/in/googletest.in: -------------------------------------------------------------------------------- 1 | project(googletest-download NONE) 2 | cmake_minimum_required(VERSION 3.2) 3 | 4 | include(ExternalProject) 5 | 6 | ExternalProject_Add( 7 | googletest 8 | GIT_REPOSITORY https://github.com/google/googletest.git 9 | GIT_TAG master 10 | DOWNLOAD_DIR ${GOOGLETEST_DEPS_DIR} 11 | TMP_DIR ${GOOGLETEST_DEPS_DIR}/tmp 12 | STAMP_DIR ${GOOGLETEST_DEPS_DIR}/stamp 13 | SOURCE_DIR ${GOOGLETEST_DEPS_DIR}/src 14 | BINARY_DIR ${GOOGLETEST_DEPS_DIR}/build 15 | CONFIGURE_COMMAND "" 16 | BUILD_COMMAND "" 17 | INSTALL_COMMAND "" 18 | TEST_COMMAND "" 19 | ) 20 | -------------------------------------------------------------------------------- /cmake/in/metaBuildConfig.cmake.in: -------------------------------------------------------------------------------- 1 | set(META_VERSION "@PROJECT_VERSION@") 2 | set(META_INCLUDE_DIRS "@CMAKE_CURRENT_SOURCE_DIR@/src") 3 | 4 | if(NOT CMAKE_VERSION VERSION_LESS "3.0") 5 | include("${CMAKE_CURRENT_LIST_DIR}/metaTargets.cmake") 6 | endif() 7 | -------------------------------------------------------------------------------- /cmake/in/metaConfig.cmake.in: -------------------------------------------------------------------------------- 1 | set(META_VERSION "@PROJECT_VERSION@") 2 | 3 | @PACKAGE_INIT@ 4 | 5 | set_and_check(META_INCLUDE_DIRS "@PACKAGE_CMAKE_INSTALL_INCLUDEDIR@") 6 | 7 | if(NOT CMAKE_VERSION VERSION_LESS "3.0") 8 | include("${CMAKE_CURRENT_LIST_DIR}/metaTargets.cmake") 9 | endif() 10 | 11 | check_required_components("@PROJECT_NAME@") 12 | -------------------------------------------------------------------------------- /deps/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /docs/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # 2 | # Doxygen configuration (documentation) 3 | # 4 | 5 | set(DOXY_SOURCE_DIRECTORY ${meta_SOURCE_DIR}/src) 6 | set(DOXY_DOCS_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}) 7 | set(DOXY_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) 8 | 9 | configure_file(doxy.in doxy.cfg @ONLY) 10 | 11 | add_custom_target( 12 | docs ALL 13 | COMMAND ${DOXYGEN_EXECUTABLE} ${CMAKE_CURRENT_BINARY_DIR}/doxy.cfg 14 | WORKING_DIRECTORY ${meta_SOURCE_DIR} 15 | VERBATIM 16 | SOURCES doxy.in 17 | ) 18 | 19 | install( 20 | DIRECTORY ${DOXY_OUTPUT_DIRECTORY}/html 21 | DESTINATION share/${PROJECT_NAME}-${PROJECT_VERSION}/ 22 | ) 23 | 24 | add_custom_target( 25 | docs_aob 26 | SOURCES 27 | CONTRIBUTING.md 28 | ) 29 | -------------------------------------------------------------------------------- /docs/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | First of all, thank you very much for taking the time to contribute to the 4 | `meta`.
5 | How to do it mostly depends on the type of contribution: 6 | 7 | * If you have a question, **please** ensure there isn't already an answer for 8 | you by searching on GitHub under 9 | [issues](https://github.com/skypjack/meta/issues). Do not forget to search 10 | also through the closed ones. If you are unable to find a proper answer, feel 11 | free to [open a new issue](https://github.com/skypjack/meta/issues/new). 12 | Usually, questions are marked as such and closed in a few days. 13 | 14 | * If you want to fix a typo in the inline documentation or in the README file, 15 | if you want to add some new sections or if you want to help me with the 16 | language by reviewing what I wrote so far (I'm not a native speaker after 17 | all), **please** open a new 18 | [pull request](https://github.com/skypjack/meta/pulls) with your changes. 19 | 20 | * If you found a bug, **please** ensure there isn't already an answer for you by 21 | searching on GitHub under [issues](https://github.com/skypjack/meta/issues). 22 | If you are unable to find an open issue addressing the problem, feel free to 23 | [open a new one](https://github.com/skypjack/meta/issues/new). **Please**, do 24 | not forget to carefully describe how to reproduce the problem, then add all 25 | the information about the system on which you are experiencing it and point 26 | out the version of `meta` you are using (tag or commit). 27 | 28 | * If you found a bug and you wrote a patch to fix it, open a new 29 | [pull request](https://github.com/skypjack/meta/pulls) with your code. 30 | **Please**, add some tests to avoid regressions in future if possible, it 31 | would be really appreciated. 32 | 33 | * If you want to propose a new feature and you know how to code it, **please** 34 | do not issue directly a pull request. Before to do it, 35 | [create a new issue](https://github.com/skypjack/meta/issues/new) to discuss 36 | your proposal. Other users could be interested in your idea and the discussion 37 | that will follow can refine it and therefore give us a better solution. 38 | 39 | * If you want to request a new feature, I'm available for hiring. Take a look at 40 | [my profile](https://github.com/skypjack) and feel free to write me. 41 | -------------------------------------------------------------------------------- /docs/extra.dox: -------------------------------------------------------------------------------- 1 | /** 2 | * @namespace meta 3 | * 4 | * @brief `meta` default namespace. 5 | */ 6 | -------------------------------------------------------------------------------- /src/meta/factory.hpp: -------------------------------------------------------------------------------- 1 | #ifndef META_FACTORY_HPP 2 | #define META_FACTORY_HPP 3 | 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include "policy.hpp" 13 | #include "meta.hpp" 14 | 15 | 16 | namespace meta { 17 | 18 | 19 | /** 20 | * @cond TURN_OFF_DOXYGEN 21 | * Internal details not to be documented. 22 | */ 23 | 24 | 25 | namespace internal { 26 | 27 | 28 | template 29 | struct function_helper; 30 | 31 | 32 | template 33 | struct function_helper { 34 | using return_type = std::remove_cv_t>; 35 | using args_type = std::tuple>...>; 36 | 37 | static constexpr auto size = sizeof...(Args); 38 | static constexpr auto is_const = false; 39 | 40 | static auto arg(typename internal::func_node::size_type index) noexcept { 41 | return std::array{{type_info::resolve()...}}[index]; 42 | } 43 | }; 44 | 45 | 46 | template 47 | struct function_helper: function_helper { 48 | static constexpr auto is_const = true; 49 | }; 50 | 51 | 52 | template 53 | constexpr function_helper 54 | to_function_helper(Ret(Class:: *)(Args...)); 55 | 56 | 57 | template 58 | constexpr function_helper 59 | to_function_helper(Ret(Class:: *)(Args...) const); 60 | 61 | 62 | template 63 | constexpr function_helper 64 | to_function_helper(Ret(*)(Args...)); 65 | 66 | 67 | constexpr void to_function_helper(...); 68 | 69 | 70 | template 71 | using function_helper_t = decltype(to_function_helper(std::declval())); 72 | 73 | 74 | template 75 | any construct(any * const args, std::index_sequence) { 76 | [[maybe_unused]] auto direct = std::make_tuple((args+Indexes)->try_cast()...); 77 | any any{}; 78 | 79 | if(((std::get(direct) || (args+Indexes)->convert()) && ...)) { 80 | any = Type{(std::get(direct) ? *std::get(direct) : (args+Indexes)->cast())...}; 81 | } 82 | 83 | return any; 84 | } 85 | 86 | 87 | template 88 | bool setter([[maybe_unused]] handle handle, [[maybe_unused]] any index, [[maybe_unused]] any value) { 89 | bool accepted = false; 90 | 91 | if constexpr(!Const) { 92 | if constexpr(std::is_function_v> || std::is_member_function_pointer_v) { 93 | using helper_type = function_helper_t; 94 | using data_type = std::tuple_element_t, typename helper_type::args_type>; 95 | static_assert(std::is_invocable_v); 96 | auto *clazz = any{handle}.try_cast(); 97 | auto *direct = value.try_cast(); 98 | 99 | if(clazz && (direct || value.convert())) { 100 | std::invoke(Data, *clazz, direct ? *direct : value.cast()); 101 | accepted = true; 102 | } 103 | } else if constexpr(std::is_member_object_pointer_v) { 104 | using data_type = std::remove_cv_t().*Data)>>; 105 | static_assert(std::is_invocable_v); 106 | auto *clazz = any{handle}.try_cast(); 107 | 108 | if constexpr(std::is_array_v) { 109 | using underlying_type = std::remove_extent_t; 110 | auto *direct = value.try_cast(); 111 | auto *idx = index.try_cast(); 112 | 113 | if(clazz && idx && (direct || value.convert())) { 114 | std::invoke(Data, clazz)[*idx] = direct ? *direct : value.cast(); 115 | accepted = true; 116 | } 117 | } else { 118 | auto *direct = value.try_cast(); 119 | 120 | if(clazz && (direct || value.convert())) { 121 | std::invoke(Data, clazz) = (direct ? *direct : value.cast()); 122 | accepted = true; 123 | } 124 | } 125 | } else { 126 | static_assert(std::is_pointer_v); 127 | using data_type = std::remove_cv_t>; 128 | 129 | if constexpr(std::is_array_v) { 130 | using underlying_type = std::remove_extent_t; 131 | auto *direct = value.try_cast(); 132 | auto *idx = index.try_cast(); 133 | 134 | if(idx && (direct || value.convert())) { 135 | (*Data)[*idx] = (direct ? *direct : value.cast()); 136 | accepted = true; 137 | } 138 | } else { 139 | auto *direct = value.try_cast(); 140 | 141 | if(direct || value.convert()) { 142 | *Data = (direct ? *direct : value.cast()); 143 | accepted = true; 144 | } 145 | } 146 | } 147 | } 148 | 149 | return accepted; 150 | } 151 | 152 | 153 | template 154 | any getter([[maybe_unused]] handle handle, [[maybe_unused]] any index) { 155 | auto dispatch = [](auto &&value) { 156 | if constexpr(std::is_same_v) { 157 | return any{std::in_place_type}; 158 | } else if constexpr(std::is_same_v) { 159 | return any{std::ref(std::forward(value))}; 160 | } else { 161 | static_assert(std::is_same_v); 162 | return any{std::forward(value)}; 163 | } 164 | }; 165 | 166 | if constexpr(std::is_function_v> || std::is_member_function_pointer_v) { 167 | static_assert(std::is_invocable_v); 168 | auto *clazz = any{handle}.try_cast(); 169 | return clazz ? dispatch(std::invoke(Data, *clazz)) : any{}; 170 | } else if constexpr(std::is_member_object_pointer_v) { 171 | using data_type = std::remove_cv_t().*Data)>>; 172 | static_assert(std::is_invocable_v); 173 | auto *clazz = any{handle}.try_cast(); 174 | 175 | if constexpr(std::is_array_v) { 176 | auto *idx = index.try_cast(); 177 | return (clazz && idx) ? dispatch(std::invoke(Data, clazz)[*idx]) : any{}; 178 | } else { 179 | return clazz ? dispatch(std::invoke(Data, clazz)) : any{}; 180 | } 181 | } else { 182 | static_assert(std::is_pointer_v>); 183 | 184 | if constexpr(std::is_array_v>) { 185 | auto *idx = index.try_cast(); 186 | return idx ? dispatch((*Data)[*idx]) : any{}; 187 | } else { 188 | return dispatch(*Data); 189 | } 190 | } 191 | } 192 | 193 | 194 | template 195 | any invoke([[maybe_unused]] handle handle, any *args, std::index_sequence) { 196 | using helper_type = function_helper_t; 197 | 198 | auto dispatch = [](auto *... args) { 199 | if constexpr(std::is_void_v || std::is_same_v) { 200 | std::invoke(Candidate, *args...); 201 | return any{std::in_place_type}; 202 | } else if constexpr(std::is_same_v) { 203 | return any{std::ref(std::invoke(Candidate, *args...))}; 204 | } else { 205 | static_assert(std::is_same_v); 206 | return any{std::invoke(Candidate, *args...)}; 207 | } 208 | }; 209 | 210 | [[maybe_unused]] const auto direct = std::make_tuple([](meta::any *any, auto *instance) { 211 | using arg_type = std::remove_reference_t; 212 | 213 | if(!instance && any->convert()) { 214 | instance = any->try_cast(); 215 | } 216 | 217 | return instance; 218 | }(args+Indexes, (args+Indexes)->try_cast>())...); 219 | 220 | if constexpr(std::is_function_v>) { 221 | return (std::get(direct) && ...) ? dispatch(std::get(direct)...) : any{}; 222 | } else { 223 | auto *clazz = any{handle}.try_cast(); 224 | return (clazz && (std::get(direct) && ...)) ? dispatch(clazz, std::get(direct)...) : any{}; 225 | } 226 | } 227 | 228 | 229 | } 230 | 231 | 232 | /** 233 | * Internal details not to be documented. 234 | * @endcond TURN_OFF_DOXYGEN 235 | */ 236 | 237 | 238 | /** 239 | * @brief A meta factory to be used for reflection purposes. 240 | * 241 | * A meta factory is an utility class used to reflect types, data and functions 242 | * of all sorts. This class ensures that the underlying web of types is built 243 | * correctly and performs some checks in debug mode to ensure that there are no 244 | * subtle errors at runtime. 245 | * 246 | * @tparam Type Reflected type for which the factory was created. 247 | */ 248 | template 249 | class factory { 250 | template 251 | bool duplicate(const std::size_t identifier, const Node *node) noexcept { 252 | return node && (node->identifier == identifier || duplicate(identifier, node->next)); 253 | } 254 | 255 | bool duplicate(const any &key, const internal::prop_node *node) noexcept { 256 | return node && (node->key() == key || duplicate(key, node->next)); 257 | } 258 | 259 | template 260 | internal::prop_node * properties() { 261 | return nullptr; 262 | } 263 | 264 | template 265 | internal::prop_node * properties(Property &&property, Other &&... other) { 266 | static std::remove_cv_t> prop{}; 267 | 268 | static internal::prop_node node{ 269 | nullptr, 270 | []() -> any { 271 | return std::as_const(std::get<0>(prop)); 272 | }, 273 | []() -> any { 274 | return std::as_const(std::get<1>(prop)); 275 | }, 276 | []() noexcept -> meta::prop { 277 | return &node; 278 | } 279 | }; 280 | 281 | prop = std::forward(property); 282 | node.next = properties(std::forward(other)...); 283 | assert(!duplicate(any{std::get<0>(prop)}, node.next)); 284 | return &node; 285 | } 286 | 287 | void unregister_prop(internal::prop_node **prop) { 288 | while(*prop) { 289 | auto *node = *prop; 290 | *prop = node->next; 291 | node->next = nullptr; 292 | } 293 | } 294 | 295 | void unregister_dtor() { 296 | if(auto node = internal::type_info::type->dtor; node) { 297 | internal::type_info::type->dtor = nullptr; 298 | *node->underlying = nullptr; 299 | } 300 | } 301 | 302 | template 303 | auto unregister_all(int) 304 | -> decltype((internal::type_info::type->*Member)->prop, void()) { 305 | while(internal::type_info::type->*Member) { 306 | auto node = internal::type_info::type->*Member; 307 | internal::type_info::type->*Member = node->next; 308 | unregister_prop(&node->prop); 309 | node->next = nullptr; 310 | *node->underlying = nullptr; 311 | } 312 | } 313 | 314 | template 315 | void unregister_all(char) { 316 | while(internal::type_info::type->*Member) { 317 | auto node = internal::type_info::type->*Member; 318 | internal::type_info::type->*Member = node->next; 319 | node->next = nullptr; 320 | *node->underlying = nullptr; 321 | } 322 | } 323 | 324 | public: 325 | /*! @brief Default constructor. */ 326 | factory() noexcept = default; 327 | 328 | /** 329 | * @brief Extends a meta type by assigning it an identifier and properties. 330 | * @tparam Property Types of properties to assign to the meta type. 331 | * @param identifier Unique identifier. 332 | * @param property Properties to assign to the meta type. 333 | * @return A meta factory for the parent type. 334 | */ 335 | template 336 | factory type(const std::size_t identifier, Property &&... property) noexcept { 337 | assert(!internal::type_info::type); 338 | auto *node = internal::type_info::resolve(); 339 | node->identifier = identifier; 340 | node->next = internal::type_info<>::type; 341 | node->prop = properties(std::forward(property)...); 342 | assert(!duplicate(node->identifier, node->next)); 343 | internal::type_info::type = node; 344 | internal::type_info<>::type = node; 345 | 346 | return *this; 347 | } 348 | 349 | /** 350 | * @brief Assigns a meta base to a meta type. 351 | * 352 | * A reflected base class must be a real base class of the reflected type. 353 | * 354 | * @tparam Base Type of the base class to assign to the meta type. 355 | * @return A meta factory for the parent type. 356 | */ 357 | template 358 | factory base() noexcept { 359 | static_assert(std::is_base_of_v); 360 | auto * const type = internal::type_info::resolve(); 361 | 362 | static internal::base_node node{ 363 | &internal::type_info::template base, 364 | type, 365 | nullptr, 366 | &internal::type_info::resolve, 367 | [](void *instance) noexcept -> void * { 368 | return static_cast(static_cast(instance)); 369 | }, 370 | []() noexcept -> meta::base { 371 | return &node; 372 | } 373 | }; 374 | 375 | node.next = type->base; 376 | assert((!internal::type_info::template base)); 377 | internal::type_info::template base = &node; 378 | type->base = &node; 379 | 380 | return *this; 381 | } 382 | 383 | /** 384 | * @brief Assigns a meta conversion function to a meta type. 385 | * 386 | * The given type must be such that an instance of the reflected type can be 387 | * converted to it. 388 | * 389 | * @tparam To Type of the conversion function to assign to the meta type. 390 | * @return A meta factory for the parent type. 391 | */ 392 | template 393 | factory conv() noexcept { 394 | static_assert(std::is_convertible_v); 395 | auto * const type = internal::type_info::resolve(); 396 | 397 | static internal::conv_node node{ 398 | &internal::type_info::template conv, 399 | type, 400 | nullptr, 401 | &internal::type_info::resolve, 402 | [](const void *instance) -> any { 403 | return static_cast(*static_cast(instance)); 404 | }, 405 | []() noexcept -> meta::conv { 406 | return &node; 407 | } 408 | }; 409 | 410 | node.next = type->conv; 411 | assert((!internal::type_info::template conv)); 412 | internal::type_info::template conv = &node; 413 | type->conv = &node; 414 | 415 | return *this; 416 | } 417 | 418 | /** 419 | * @brief Assigns a meta conversion function to a meta type. 420 | * 421 | * Conversion functions can be either free functions or member 422 | * functions.
423 | * In case of free functions, they must accept a const reference to an 424 | * instance of the parent type as an argument. In case of member functions, 425 | * they should have no arguments at all. 426 | * 427 | * @tparam Candidate The actual function to use for the conversion. 428 | * @return A meta factory for the parent type. 429 | */ 430 | template 431 | factory conv() noexcept { 432 | using conv_type = std::invoke_result_t; 433 | auto * const type = internal::type_info::resolve(); 434 | 435 | static internal::conv_node node{ 436 | &internal::type_info::template conv, 437 | type, 438 | nullptr, 439 | &internal::type_info::resolve, 440 | [](const void *instance) -> any { 441 | return std::invoke(Candidate, *static_cast(instance)); 442 | }, 443 | []() noexcept -> meta::conv { 444 | return &node; 445 | } 446 | }; 447 | 448 | node.next = type->conv; 449 | assert((!internal::type_info::template conv)); 450 | internal::type_info::template conv = &node; 451 | type->conv = &node; 452 | 453 | return *this; 454 | } 455 | 456 | /** 457 | * @brief Assigns a meta constructor to a meta type. 458 | * 459 | * Free functions can be assigned to meta types in the role of constructors. 460 | * All that is required is that they return an instance of the underlying 461 | * type.
462 | * From a client's point of view, nothing changes if a constructor of a meta 463 | * type is a built-in one or a free function. 464 | * 465 | * @tparam Func The actual function to use as a constructor. 466 | * @tparam Policy Optional policy (no policy set by default). 467 | * @tparam Property Types of properties to assign to the meta data. 468 | * @param property Properties to assign to the meta data. 469 | * @return A meta factory for the parent type. 470 | */ 471 | template 472 | factory ctor(Property &&... property) noexcept { 473 | using helper_type = internal::function_helper_t; 474 | static_assert(std::is_same_v); 475 | auto * const type = internal::type_info::resolve(); 476 | 477 | static internal::ctor_node node{ 478 | &internal::type_info::template ctor, 479 | type, 480 | nullptr, 481 | nullptr, 482 | helper_type::size, 483 | &helper_type::arg, 484 | [](any * const any) { 485 | return internal::invoke({}, any, std::make_index_sequence{}); 486 | }, 487 | []() noexcept -> meta::ctor { 488 | return &node; 489 | } 490 | }; 491 | 492 | node.next = type->ctor; 493 | node.prop = properties(std::forward(property)...); 494 | assert((!internal::type_info::template ctor)); 495 | internal::type_info::template ctor = &node; 496 | type->ctor = &node; 497 | 498 | return *this; 499 | } 500 | 501 | /** 502 | * @brief Assigns a meta constructor to a meta type. 503 | * 504 | * A meta constructor is uniquely identified by the types of its arguments 505 | * and is such that there exists an actual constructor of the underlying 506 | * type that can be invoked with parameters whose types are those given. 507 | * 508 | * @tparam Args Types of arguments to use to construct an instance. 509 | * @tparam Property Types of properties to assign to the meta data. 510 | * @param property Properties to assign to the meta data. 511 | * @return A meta factory for the parent type. 512 | */ 513 | template 514 | factory ctor(Property &&... property) noexcept { 515 | using helper_type = internal::function_helper_t; 516 | auto * const type = internal::type_info::resolve(); 517 | 518 | static internal::ctor_node node{ 519 | &internal::type_info::template ctor, 520 | type, 521 | nullptr, 522 | nullptr, 523 | helper_type::size, 524 | &helper_type::arg, 525 | [](any * const any) { 526 | return internal::construct>...>(any, std::make_index_sequence{}); 527 | }, 528 | []() noexcept -> meta::ctor { 529 | return &node; 530 | } 531 | }; 532 | 533 | node.next = type->ctor; 534 | node.prop = properties(std::forward(property)...); 535 | assert((!internal::type_info::template ctor)); 536 | internal::type_info::template ctor = &node; 537 | type->ctor = &node; 538 | 539 | return *this; 540 | } 541 | 542 | /** 543 | * @brief Assigns a meta destructor to a meta type. 544 | * 545 | * Free functions can be assigned to meta types in the role of destructors. 546 | * The signature of the function should identical to the following: 547 | * 548 | * @code{.cpp} 549 | * void(Type &); 550 | * @endcode 551 | * 552 | * The purpose is to give users the ability to free up resources that 553 | * require special treatment before an object is actually destroyed. 554 | * 555 | * @tparam Func The actual function to use as a destructor. 556 | * @return A meta factory for the parent type. 557 | */ 558 | template 559 | factory dtor() noexcept { 560 | static_assert(std::is_invocable_v); 561 | auto * const type = internal::type_info::resolve(); 562 | 563 | static internal::dtor_node node{ 564 | &internal::type_info::template dtor, 565 | type, 566 | [](handle handle) { 567 | const auto valid = (handle.type() == internal::type_info::resolve()->clazz()); 568 | 569 | if(valid) { 570 | std::invoke(Func, *any{handle}.try_cast()); 571 | } 572 | 573 | return valid; 574 | }, 575 | []() noexcept -> meta::dtor { 576 | return &node; 577 | } 578 | }; 579 | 580 | assert(!internal::type_info::type->dtor); 581 | assert((!internal::type_info::template dtor)); 582 | internal::type_info::template dtor = &node; 583 | internal::type_info::type->dtor = &node; 584 | 585 | return *this; 586 | } 587 | 588 | /** 589 | * @brief Assigns a meta data to a meta type. 590 | * 591 | * Both data members and static and global variables, as well as constants 592 | * of any kind, can be assigned to a meta type.
593 | * From a client's point of view, all the variables associated with the 594 | * reflected object will appear as if they were part of the type itself. 595 | * 596 | * @tparam Data The actual variable to attach to the meta type. 597 | * @tparam Policy Optional policy (no policy set by default). 598 | * @tparam Property Types of properties to assign to the meta data. 599 | * @param identifier Unique identifier. 600 | * @param property Properties to assign to the meta data. 601 | * @return A meta factory for the parent type. 602 | */ 603 | template 604 | factory data(const std::size_t identifier, Property &&... property) noexcept { 605 | auto * const type = internal::type_info::resolve(); 606 | internal::data_node *curr = nullptr; 607 | 608 | if constexpr(std::is_same_v) { 609 | static_assert(std::is_same_v); 610 | 611 | static internal::data_node node{ 612 | &internal::type_info::template data, 613 | {}, 614 | type, 615 | nullptr, 616 | nullptr, 617 | true, 618 | true, 619 | &internal::type_info::resolve, 620 | [](handle, any, any) { return false; }, 621 | [](handle, any) -> any { return Data; }, 622 | []() noexcept -> meta::data { 623 | return &node; 624 | } 625 | }; 626 | 627 | node.prop = properties>(std::forward(property)...); 628 | curr = &node; 629 | } else if constexpr(std::is_member_object_pointer_v) { 630 | using data_type = std::remove_reference_t().*Data)>; 631 | 632 | static internal::data_node node{ 633 | &internal::type_info::template data, 634 | {}, 635 | type, 636 | nullptr, 637 | nullptr, 638 | std::is_const_v, 639 | !std::is_member_object_pointer_v, 640 | &internal::type_info::resolve, 641 | &internal::setter, Type, Data>, 642 | &internal::getter, 643 | []() noexcept -> meta::data { 644 | return &node; 645 | } 646 | }; 647 | 648 | node.prop = properties>(std::forward(property)...); 649 | curr = &node; 650 | } else { 651 | static_assert(std::is_pointer_v>); 652 | using data_type = std::remove_pointer_t>; 653 | 654 | static internal::data_node node{ 655 | &internal::type_info::template data, 656 | {}, 657 | type, 658 | nullptr, 659 | nullptr, 660 | std::is_const_v, 661 | !std::is_member_object_pointer_v, 662 | &internal::type_info::resolve, 663 | &internal::setter, Type, Data>, 664 | &internal::getter, 665 | []() noexcept -> meta::data { 666 | return &node; 667 | } 668 | }; 669 | 670 | node.prop = properties>(std::forward(property)...); 671 | curr = &node; 672 | } 673 | 674 | curr->identifier = identifier; 675 | curr->next = type->data; 676 | assert(!duplicate(curr->identifier, curr->next)); 677 | assert((!internal::type_info::template data)); 678 | internal::type_info::template data = curr; 679 | type->data = curr; 680 | 681 | return *this; 682 | } 683 | 684 | /** 685 | * @brief Assigns a meta data to a meta type by means of its setter and 686 | * getter. 687 | * 688 | * Setters and getters can be either free functions, member functions or a 689 | * mix of them.
690 | * In case of free functions, setters and getters must accept a reference to 691 | * an instance of the parent type as their first argument. A setter has then 692 | * an extra argument of a type convertible to that of the parameter to 693 | * set.
694 | * In case of member functions, getters have no arguments at all, while 695 | * setters has an argument of a type convertible to that of the parameter to 696 | * set. 697 | * 698 | * @tparam Setter The actual function to use as a setter. 699 | * @tparam Getter The actual function to use as a getter. 700 | * @tparam Policy Optional policy (no policy set by default). 701 | * @tparam Property Types of properties to assign to the meta data. 702 | * @param identifier Unique identifier. 703 | * @param property Properties to assign to the meta data. 704 | * @return A meta factory for the parent type. 705 | */ 706 | template 707 | factory data(const std::size_t identifier, Property &&... property) noexcept { 708 | using owner_type = std::tuple, std::integral_constant>; 709 | using underlying_type = std::invoke_result_t; 710 | static_assert(std::is_invocable_v); 711 | auto * const type = internal::type_info::resolve(); 712 | 713 | static internal::data_node node{ 714 | &internal::type_info::template data, 715 | {}, 716 | type, 717 | nullptr, 718 | nullptr, 719 | false, 720 | false, 721 | &internal::type_info::resolve, 722 | &internal::setter, 723 | &internal::getter, 724 | []() noexcept -> meta::data { 725 | return &node; 726 | } 727 | }; 728 | 729 | node.identifier = identifier; 730 | node.next = type->data; 731 | node.prop = properties(std::forward(property)...); 732 | assert(!duplicate(node.identifier, node.next)); 733 | assert((!internal::type_info::template data)); 734 | internal::type_info::template data = &node; 735 | type->data = &node; 736 | 737 | return *this; 738 | } 739 | 740 | /** 741 | * @brief Assigns a meta funcion to a meta type. 742 | * 743 | * Both member functions and free functions can be assigned to a meta 744 | * type.
745 | * From a client's point of view, all the functions associated with the 746 | * reflected object will appear as if they were part of the type itself. 747 | * 748 | * @tparam Candidate The actual function to attach to the meta type. 749 | * @tparam Policy Optional policy (no policy set by default). 750 | * @tparam Property Types of properties to assign to the meta function. 751 | * @param identifier Unique identifier. 752 | * @param property Properties to assign to the meta function. 753 | * @return A meta factory for the parent type. 754 | */ 755 | template 756 | factory func(const std::size_t identifier, Property &&... property) noexcept { 757 | using owner_type = std::integral_constant; 758 | using helper_type = internal::function_helper_t; 759 | auto * const type = internal::type_info::resolve(); 760 | 761 | static internal::func_node node{ 762 | &internal::type_info::template func, 763 | {}, 764 | type, 765 | nullptr, 766 | nullptr, 767 | helper_type::size, 768 | helper_type::is_const, 769 | !std::is_member_function_pointer_v, 770 | &internal::type_info, void, typename helper_type::return_type>>::resolve, 771 | &helper_type::arg, 772 | [](handle handle, any *any) { 773 | return internal::invoke(handle, any, std::make_index_sequence{}); 774 | }, 775 | []() noexcept -> meta::func { 776 | return &node; 777 | } 778 | }; 779 | 780 | node.identifier = identifier; 781 | node.next = type->func; 782 | node.prop = properties(std::forward(property)...); 783 | assert(!duplicate(node.identifier, node.next)); 784 | assert((!internal::type_info::template func)); 785 | internal::type_info::template func = &node; 786 | type->func = &node; 787 | 788 | return *this; 789 | } 790 | 791 | /** 792 | * @brief Unregisters a meta type and all its parts. 793 | * 794 | * This function unregisters a meta type and all its data members, member 795 | * functions and properties, as well as its constructors, destructors and 796 | * conversion functions if any.
797 | * Base classes aren't unregistered but the link between the two types is 798 | * removed. 799 | * 800 | * @return True if the meta type exists, false otherwise. 801 | */ 802 | bool unregister() noexcept { 803 | const auto registered = internal::type_info::type; 804 | 805 | if(registered) { 806 | if(auto *curr = internal::type_info<>::type; curr == internal::type_info::type) { 807 | internal::type_info<>::type = internal::type_info::type->next; 808 | } else { 809 | while(curr && curr->next != internal::type_info::type) { 810 | curr = curr->next; 811 | } 812 | 813 | if(curr) { 814 | curr->next = internal::type_info::type->next; 815 | } 816 | } 817 | 818 | unregister_prop(&internal::type_info::type->prop); 819 | unregister_all<&internal::type_node::base>(0); 820 | unregister_all<&internal::type_node::conv>(0); 821 | unregister_all<&internal::type_node::ctor>(0); 822 | unregister_all<&internal::type_node::data>(0); 823 | unregister_all<&internal::type_node::func>(0); 824 | unregister_dtor(); 825 | 826 | internal::type_info::type->identifier = {}; 827 | internal::type_info::type->next = nullptr; 828 | internal::type_info::type = nullptr; 829 | } 830 | 831 | return registered; 832 | } 833 | }; 834 | 835 | 836 | /** 837 | * @brief Utility function to use for reflection. 838 | * 839 | * This is the point from which everything starts.
840 | * By invoking this function with a type that is not yet reflected, a meta type 841 | * is created to which it will be possible to attach data and functions through 842 | * a dedicated factory. 843 | * 844 | * @tparam Type Type to reflect. 845 | * @tparam Property Types of properties to assign to the reflected type. 846 | * @param identifier Unique identifier. 847 | * @param property Properties to assign to the reflected type. 848 | * @return A meta factory for the given type. 849 | */ 850 | template 851 | inline factory reflect(const std::size_t identifier, Property &&... property) noexcept { 852 | return factory{}.type(identifier, std::forward(property)...); 853 | } 854 | 855 | 856 | /** 857 | * @brief Utility function to use for reflection. 858 | * 859 | * This is the point from which everything starts.
860 | * By invoking this function with a type that is not yet reflected, a meta type 861 | * is created to which it will be possible to attach data and functions through 862 | * a dedicated factory. 863 | * 864 | * @tparam Type Type to reflect. 865 | * @return A meta factory for the given type. 866 | */ 867 | template 868 | inline factory reflect() noexcept { 869 | return factory{}; 870 | } 871 | 872 | 873 | /** 874 | * @brief Utility function to unregister a type. 875 | * 876 | * This function unregisters a type and all its data members, member functions 877 | * and properties, as well as its constructors, destructors and conversion 878 | * functions if any.
879 | * Base classes aren't unregistered but the link between the two types is 880 | * removed. 881 | * 882 | * @tparam Type Type to unregister. 883 | * @return True if the type to unregister exists, false otherwise. 884 | */ 885 | template 886 | inline bool unregister() noexcept { 887 | return factory{}.unregister(); 888 | } 889 | 890 | 891 | /** 892 | * @brief Returns the meta type associated with a given type. 893 | * @tparam Type Type to use to search for a meta type. 894 | * @return The meta type associated with the given type, if any. 895 | */ 896 | template 897 | inline type resolve() noexcept { 898 | return internal::type_info::resolve()->clazz(); 899 | } 900 | 901 | 902 | /** 903 | * @brief Returns the meta type associated with a given identifier. 904 | * @param identifier Unique identifier. 905 | * @return The meta type associated with the given identifier, if any. 906 | */ 907 | inline type resolve(const std::size_t identifier) noexcept { 908 | const auto *curr = internal::find_if([identifier](auto *node) { 909 | return node->identifier == identifier; 910 | }, internal::type_info<>::type); 911 | 912 | return curr ? curr->clazz() : type{}; 913 | } 914 | 915 | 916 | /** 917 | * @brief Iterates all the reflected types. 918 | * @tparam Op Type of the function object to invoke. 919 | * @param op A valid function object. 920 | */ 921 | template 922 | inline std::enable_if_t, void> 923 | resolve(Op op) noexcept { 924 | internal::iterate([op = std::move(op)](auto *node) { 925 | op(node->clazz()); 926 | }, internal::type_info<>::type); 927 | } 928 | 929 | 930 | } 931 | 932 | 933 | #endif // META_FACTORY_HPP 934 | -------------------------------------------------------------------------------- /src/meta/meta.hpp: -------------------------------------------------------------------------------- 1 | #ifndef META_META_HPP 2 | #define META_META_HPP 3 | 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | 14 | namespace meta { 15 | 16 | 17 | class any; 18 | class handle; 19 | class prop; 20 | class base; 21 | class conv; 22 | class ctor; 23 | class dtor; 24 | class data; 25 | class func; 26 | class type; 27 | 28 | 29 | /** 30 | * @cond TURN_OFF_DOXYGEN 31 | * Internal details not to be documented. 32 | */ 33 | 34 | 35 | namespace internal { 36 | 37 | 38 | struct type_node; 39 | 40 | 41 | struct prop_node { 42 | prop_node * next; 43 | any(* const key)(); 44 | any(* const value)(); 45 | prop(* const clazz)() noexcept; 46 | }; 47 | 48 | 49 | struct base_node { 50 | base_node ** const underlying; 51 | type_node * const parent; 52 | base_node * next; 53 | type_node *(* const ref)() noexcept; 54 | void *(* const cast)(void *) noexcept; 55 | base(* const clazz)() noexcept; 56 | }; 57 | 58 | 59 | struct conv_node { 60 | conv_node ** const underlying; 61 | type_node * const parent; 62 | conv_node * next; 63 | type_node *(* const ref)() noexcept; 64 | any(* const convert)(const void *); 65 | conv(* const clazz)() noexcept; 66 | }; 67 | 68 | 69 | struct ctor_node { 70 | using size_type = std::size_t; 71 | ctor_node ** const underlying; 72 | type_node * const parent; 73 | ctor_node * next; 74 | prop_node * prop; 75 | const size_type size; 76 | type_node *(* const arg)(size_type) noexcept; 77 | any(* const invoke)(any * const); 78 | ctor(* const clazz)() noexcept; 79 | }; 80 | 81 | 82 | struct dtor_node { 83 | dtor_node ** const underlying; 84 | type_node * const parent; 85 | bool(* const invoke)(handle); 86 | dtor(* const clazz)() noexcept; 87 | }; 88 | 89 | 90 | struct data_node { 91 | data_node ** const underlying; 92 | std::size_t identifier; 93 | type_node * const parent; 94 | data_node * next; 95 | prop_node * prop; 96 | const bool is_const; 97 | const bool is_static; 98 | type_node *(* const ref)() noexcept; 99 | bool(* const set)(handle, any, any); 100 | any(* const get)(handle, any); 101 | data(* const clazz)() noexcept; 102 | }; 103 | 104 | 105 | struct func_node { 106 | using size_type = std::size_t; 107 | func_node ** const underlying; 108 | std::size_t identifier; 109 | type_node * const parent; 110 | func_node * next; 111 | prop_node * prop; 112 | const size_type size; 113 | const bool is_const; 114 | const bool is_static; 115 | type_node *(* const ret)() noexcept; 116 | type_node *(* const arg)(size_type) noexcept; 117 | any(* const invoke)(handle, any *); 118 | func(* const clazz)() noexcept; 119 | }; 120 | 121 | 122 | struct type_node { 123 | using size_type = std::size_t; 124 | std::size_t identifier; 125 | type_node * next; 126 | prop_node * prop; 127 | const bool is_void; 128 | const bool is_integral; 129 | const bool is_floating_point; 130 | const bool is_array; 131 | const bool is_enum; 132 | const bool is_union; 133 | const bool is_class; 134 | const bool is_pointer; 135 | const bool is_function_pointer; 136 | const bool is_member_object_pointer; 137 | const bool is_member_function_pointer; 138 | const size_type extent; 139 | bool(* const compare)(const void *, const void *); 140 | type(* const remove_pointer)() noexcept; 141 | type(* const clazz)() noexcept; 142 | base_node *base{nullptr}; 143 | conv_node *conv{nullptr}; 144 | ctor_node *ctor{nullptr}; 145 | dtor_node *dtor{nullptr}; 146 | data_node *data{nullptr}; 147 | func_node *func{nullptr}; 148 | }; 149 | 150 | 151 | template 152 | struct info_node { 153 | inline static type_node *type = nullptr; 154 | }; 155 | 156 | 157 | template 158 | struct info_node { 159 | inline static type_node *type = nullptr; 160 | 161 | template 162 | inline static base_node *base = nullptr; 163 | 164 | template 165 | inline static conv_node *conv = nullptr; 166 | 167 | template 168 | inline static ctor_node *ctor = nullptr; 169 | 170 | template 171 | inline static dtor_node *dtor = nullptr; 172 | 173 | template 174 | inline static data_node *data = nullptr; 175 | 176 | template 177 | inline static func_node *func = nullptr; 178 | 179 | inline static type_node * resolve() noexcept; 180 | }; 181 | 182 | 183 | template 184 | struct type_info: info_node>...> {}; 185 | 186 | 187 | template 188 | void iterate(Op op, const Node *curr) noexcept { 189 | while(curr) { 190 | op(curr); 191 | curr = curr->next; 192 | } 193 | } 194 | 195 | 196 | template 197 | void iterate(Op op, const type_node *node) noexcept { 198 | if(node) { 199 | auto *curr = node->base; 200 | iterate(op, node->*Member); 201 | 202 | while(curr) { 203 | iterate(op, curr->ref()); 204 | curr = curr->next; 205 | } 206 | } 207 | } 208 | 209 | 210 | template 211 | auto find_if(Op op, const Node *curr) noexcept { 212 | while(curr && !op(curr)) { 213 | curr = curr->next; 214 | } 215 | 216 | return curr; 217 | } 218 | 219 | 220 | template 221 | auto find_if(Op op, const type_node *node) noexcept 222 | -> decltype(find_if(op, node->*Member)) { 223 | decltype(find_if(op, node->*Member)) ret = nullptr; 224 | 225 | if(node) { 226 | ret = find_if(op, node->*Member); 227 | auto *curr = node->base; 228 | 229 | while(curr && !ret) { 230 | ret = find_if(op, curr->ref()); 231 | curr = curr->next; 232 | } 233 | } 234 | 235 | return ret; 236 | } 237 | 238 | 239 | template 240 | const Type * try_cast(const type_node *node, void *instance) noexcept { 241 | const auto *type = type_info::resolve(); 242 | void *ret = nullptr; 243 | 244 | if(node == type) { 245 | ret = instance; 246 | } else { 247 | const auto *base = find_if<&type_node::base>([type](auto *candidate) { 248 | return candidate->ref() == type; 249 | }, node); 250 | 251 | ret = base ? base->cast(instance) : nullptr; 252 | } 253 | 254 | return static_cast(ret); 255 | } 256 | 257 | 258 | template 259 | inline bool can_cast_or_convert(const type_node *from, const type_node *to) noexcept { 260 | return (from == to) || find_if([to](auto *node) { 261 | return node->ref() == to; 262 | }, from); 263 | } 264 | 265 | 266 | template 267 | inline auto ctor(std::index_sequence, const type_node *node) noexcept { 268 | return internal::find_if([](auto *candidate) { 269 | return candidate->size == sizeof...(Args) && 270 | (([](auto *from, auto *to) { 271 | return internal::can_cast_or_convert<&internal::type_node::base>(from, to) 272 | || internal::can_cast_or_convert<&internal::type_node::conv>(from, to); 273 | }(internal::type_info::resolve(), candidate->arg(Indexes))) && ...); 274 | }, node->ctor); 275 | } 276 | 277 | 278 | } 279 | 280 | 281 | /** 282 | * Internal details not to be documented. 283 | * @endcond TURN_OFF_DOXYGEN 284 | */ 285 | 286 | 287 | /** 288 | * @brief Meta any object. 289 | * 290 | * A meta any is an opaque container for single values of any type. 291 | * 292 | * This class uses a technique called small buffer optimization (SBO) to 293 | * completely eliminate the need to allocate memory, where possible.
294 | * From the user's point of view, nothing will change, but the elimination of 295 | * allocations will reduce the jumps in memory and therefore will avoid chasing 296 | * of pointers. This will greatly improve the use of the cache, thus increasing 297 | * the overall performance. 298 | */ 299 | class any { 300 | /*! @brief A meta handle is allowed to _inherit_ from a meta any. */ 301 | friend class handle; 302 | 303 | using storage_type = std::aligned_storage_t; 304 | using copy_fn_type = void *(storage_type &, const void *); 305 | using destroy_fn_type = void(void *); 306 | using steal_fn_type = void *(storage_type &, void *, destroy_fn_type *); 307 | 308 | template> 309 | struct type_traits { 310 | template 311 | static void * instance(storage_type &storage, Args &&... args) { 312 | auto instance = std::make_unique(std::forward(args)...); 313 | new (&storage) Type *{instance.get()}; 314 | return instance.release(); 315 | } 316 | 317 | static void destroy(void *instance) { 318 | auto *node = internal::type_info::resolve(); 319 | auto *actual = static_cast(instance); 320 | [[maybe_unused]] const bool destroyed = node->clazz().destroy(*actual); 321 | assert(destroyed); 322 | delete actual; 323 | } 324 | 325 | static void * copy(storage_type &storage, const void *other) { 326 | auto instance = std::make_unique(*static_cast(other)); 327 | new (&storage) Type *{instance.get()}; 328 | return instance.release(); 329 | } 330 | 331 | static void * steal(storage_type &to, void *from, destroy_fn_type *) { 332 | auto *instance = static_cast(from); 333 | new (&to) Type *{instance}; 334 | return instance; 335 | } 336 | }; 337 | 338 | template 339 | struct type_traits>> { 340 | template 341 | static void * instance(storage_type &storage, Args &&... args) { 342 | return new (&storage) Type{std::forward(args)...}; 343 | } 344 | 345 | static void destroy(void *instance) { 346 | auto *node = internal::type_info::resolve(); 347 | auto *actual = static_cast(instance); 348 | [[maybe_unused]] const bool destroyed = node->clazz().destroy(*actual); 349 | assert(destroyed); 350 | actual->~Type(); 351 | } 352 | 353 | static void * copy(storage_type &storage, const void *instance) { 354 | return new (&storage) Type{*static_cast(instance)}; 355 | } 356 | 357 | static void * steal(storage_type &to, void *from, destroy_fn_type *destroy_fn) { 358 | void *instance = new (&to) Type{std::move(*static_cast(from))}; 359 | destroy_fn(from); 360 | return instance; 361 | } 362 | }; 363 | 364 | public: 365 | /*! @brief Default constructor. */ 366 | any() noexcept 367 | : storage{}, 368 | instance{nullptr}, 369 | node{nullptr}, 370 | destroy_fn{nullptr}, 371 | copy_fn{nullptr}, 372 | steal_fn{nullptr} 373 | {} 374 | 375 | /** 376 | * @brief Constructs a meta any by directly initializing the new object. 377 | * @tparam Type Type of object to use to initialize the container. 378 | * @tparam Args Types of arguments to use to construct the new instance. 379 | * @param args Parameters to use to construct the instance. 380 | */ 381 | template 382 | explicit any(std::in_place_type_t, [[maybe_unused]] Args &&... args) 383 | : any{} 384 | { 385 | node = internal::type_info::resolve(); 386 | 387 | if constexpr(!std::is_void_v) { 388 | using traits_type = type_traits>>; 389 | instance = traits_type::instance(storage, std::forward(args)...); 390 | destroy_fn = &traits_type::destroy; 391 | copy_fn = &traits_type::copy; 392 | steal_fn = &traits_type::steal; 393 | } 394 | } 395 | 396 | /** 397 | * @brief Constructs a meta any that holds an unmanaged object. 398 | * @tparam Type Type of object to use to initialize the container. 399 | * @param type An instance of an object to use to initialize the container. 400 | */ 401 | template 402 | explicit any(std::reference_wrapper type) 403 | : any{} 404 | { 405 | node = internal::type_info::resolve(); 406 | instance = &type.get(); 407 | } 408 | 409 | /** 410 | * @brief Constructs a meta any from a meta handle object. 411 | * @param handle A reference to an object to use to initialize the meta any. 412 | */ 413 | inline any(handle handle) noexcept; 414 | 415 | /** 416 | * @brief Constructs a meta any from a given value. 417 | * @tparam Type Type of object to use to initialize the container. 418 | * @param type An instance of an object to use to initialize the container. 419 | */ 420 | template>, any>>> 421 | any(Type &&type) 422 | : any{std::in_place_type>>, std::forward(type)} 423 | {} 424 | 425 | /** 426 | * @brief Copy constructor. 427 | * @param other The instance to copy from. 428 | */ 429 | any(const any &other) 430 | : any{} 431 | { 432 | node = other.node; 433 | instance = other.copy_fn ? other.copy_fn(storage, other.instance) : other.instance; 434 | destroy_fn = other.destroy_fn; 435 | copy_fn = other.copy_fn; 436 | steal_fn = other.steal_fn; 437 | } 438 | 439 | /** 440 | * @brief Move constructor. 441 | * 442 | * After meta any move construction, instances that have been moved from 443 | * are placed in a valid but unspecified state. It's highly discouraged to 444 | * continue using them. 445 | * 446 | * @param other The instance to move from. 447 | */ 448 | any(any &&other) noexcept 449 | : any{} 450 | { 451 | swap(*this, other); 452 | } 453 | 454 | /*! @brief Frees the internal storage, whatever it means. */ 455 | ~any() { 456 | if(destroy_fn) { 457 | destroy_fn(instance); 458 | } 459 | } 460 | 461 | /** 462 | * @brief Assignment operator. 463 | * @tparam Type Type of object to use to initialize the container. 464 | * @param type An instance of an object to use to initialize the container. 465 | * @return This meta any object. 466 | */ 467 | template>, any>>> 468 | any & operator=(Type &&type) { 469 | return (*this = any{std::forward(type)}); 470 | } 471 | 472 | /** 473 | * @brief Copy assignment operator. 474 | * @param other The instance to assign. 475 | * @return This meta any object. 476 | */ 477 | any & operator=(const any &other) { 478 | return (*this = any{other}); 479 | } 480 | 481 | /** 482 | * @brief Move assignment operator. 483 | * @param other The instance to assign. 484 | * @return This meta any object. 485 | */ 486 | any & operator=(any &&other) noexcept { 487 | any any{std::move(other)}; 488 | swap(any, *this); 489 | return *this; 490 | } 491 | 492 | /** 493 | * @brief Returns the meta type of the underlying object. 494 | * @return The meta type of the underlying object, if any. 495 | */ 496 | inline meta::type type() const noexcept; 497 | 498 | /** 499 | * @brief Returns an opaque pointer to the contained instance. 500 | * @return An opaque pointer the contained instance, if any. 501 | */ 502 | const void * data() const noexcept { 503 | return instance; 504 | } 505 | 506 | /*! @copydoc data */ 507 | void * data() noexcept { 508 | return const_cast(std::as_const(*this).data()); 509 | } 510 | 511 | /** 512 | * @brief Tries to cast an instance to a given type. 513 | * @tparam Type Type to which to cast the instance. 514 | * @return A (possibly null) pointer to the contained instance. 515 | */ 516 | template 517 | const Type * try_cast() const noexcept { 518 | return internal::try_cast(node, instance); 519 | } 520 | 521 | /*! @copydoc try_cast */ 522 | template 523 | Type * try_cast() noexcept { 524 | return const_cast(std::as_const(*this).try_cast()); 525 | } 526 | 527 | /** 528 | * @brief Tries to cast an instance to a given type. 529 | * 530 | * The type of the instance must be such that the cast is possible. 531 | * 532 | * @warning 533 | * Attempting to perform a cast that isn't viable results in undefined 534 | * behavior.
535 | * An assertion will abort the execution at runtime in debug mode in case 536 | * the cast is not feasible. 537 | * 538 | * @tparam Type Type to which to cast the instance. 539 | * @return A reference to the contained instance. 540 | */ 541 | template 542 | const Type & cast() const noexcept { 543 | auto *actual = try_cast(); 544 | assert(actual); 545 | return *actual; 546 | } 547 | 548 | /*! @copydoc cast */ 549 | template 550 | Type & cast() noexcept { 551 | return const_cast(std::as_const(*this).cast()); 552 | } 553 | 554 | /** 555 | * @brief Tries to convert an instance to a given type and returns it. 556 | * @tparam Type Type to which to convert the instance. 557 | * @return A valid meta any object if the conversion is possible, an invalid 558 | * one otherwise. 559 | */ 560 | template 561 | any convert() const { 562 | any any{}; 563 | 564 | if(const auto *type = internal::type_info::resolve(); node == type) { 565 | any = *static_cast(instance); 566 | } else { 567 | const auto *conv = internal::find_if<&internal::type_node::conv>([type](auto *other) { 568 | return other->ref() == type; 569 | }, node); 570 | 571 | if(conv) { 572 | any = conv->convert(instance); 573 | } 574 | } 575 | 576 | return any; 577 | } 578 | 579 | /** 580 | * @brief Tries to convert an instance to a given type. 581 | * @tparam Type Type to which to convert the instance. 582 | * @return True if the conversion is possible, false otherwise. 583 | */ 584 | template 585 | bool convert() { 586 | bool valid = (node == internal::type_info::resolve()); 587 | 588 | if(!valid) { 589 | if(auto any = std::as_const(*this).convert(); any) { 590 | swap(any, *this); 591 | valid = true; 592 | } 593 | } 594 | 595 | return valid; 596 | } 597 | 598 | /** 599 | * @brief Replaces the contained object by initializing a new instance 600 | * directly. 601 | * @tparam Type Type of object to use to initialize the container. 602 | * @tparam Args Types of arguments to use to construct the new instance. 603 | * @param args Parameters to use to construct the instance. 604 | */ 605 | template 606 | void emplace(Args&& ... args) { 607 | *this = any{std::in_place_type_t{}, std::forward(args)...}; 608 | } 609 | 610 | /** 611 | * @brief Returns false if a container is empty, true otherwise. 612 | * @return False if the container is empty, true otherwise. 613 | */ 614 | explicit operator bool() const noexcept { 615 | return node; 616 | } 617 | 618 | /** 619 | * @brief Checks if two containers differ in their content. 620 | * @param other Container with which to compare. 621 | * @return False if the two containers differ in their content, true 622 | * otherwise. 623 | */ 624 | bool operator==(const any &other) const noexcept { 625 | return node == other.node && (!node || node->compare(instance, other.instance)); 626 | } 627 | 628 | /** 629 | * @brief Swaps two meta any objects. 630 | * @param lhs A valid meta any object. 631 | * @param rhs A valid meta any object. 632 | */ 633 | friend void swap(any &lhs, any &rhs) noexcept { 634 | if(lhs.steal_fn && rhs.steal_fn) { 635 | storage_type buffer; 636 | auto *temp = lhs.steal_fn(buffer, lhs.instance, lhs.destroy_fn); 637 | lhs.instance = rhs.steal_fn(lhs.storage, rhs.instance, rhs.destroy_fn); 638 | rhs.instance = lhs.steal_fn(rhs.storage, temp, lhs.destroy_fn); 639 | } else if(lhs.steal_fn) { 640 | lhs.instance = lhs.steal_fn(rhs.storage, lhs.instance, lhs.destroy_fn); 641 | std::swap(rhs.instance, lhs.instance); 642 | } else if(rhs.steal_fn) { 643 | rhs.instance = rhs.steal_fn(lhs.storage, rhs.instance, rhs.destroy_fn); 644 | std::swap(rhs.instance, lhs.instance); 645 | } else { 646 | std::swap(lhs.instance, rhs.instance); 647 | } 648 | 649 | std::swap(lhs.node, rhs.node); 650 | std::swap(lhs.destroy_fn, rhs.destroy_fn); 651 | std::swap(lhs.copy_fn, rhs.copy_fn); 652 | std::swap(lhs.steal_fn, rhs.steal_fn); 653 | } 654 | 655 | private: 656 | storage_type storage; 657 | void *instance; 658 | const internal::type_node *node; 659 | destroy_fn_type *destroy_fn; 660 | copy_fn_type *copy_fn; 661 | steal_fn_type *steal_fn; 662 | }; 663 | 664 | 665 | /** 666 | * @brief Meta handle object. 667 | * 668 | * A meta handle is an opaque pointer to an instance of any type. 669 | * 670 | * A handle doesn't perform copies and isn't responsible for the contained 671 | * object. It doesn't prolong the lifetime of the pointed instance. Users are 672 | * responsible for ensuring that the target object remains alive for the entire 673 | * interval of use of the handle. 674 | */ 675 | class handle { 676 | /*! @brief A meta any is allowed to _inherit_ from a meta handle. */ 677 | friend class any; 678 | 679 | public: 680 | /*! @brief Default constructor. */ 681 | handle() noexcept 682 | : node{nullptr}, 683 | instance{nullptr} 684 | {} 685 | 686 | /** 687 | * @brief Constructs a meta handle from a meta any object. 688 | * @param any A reference to an object to use to initialize the handle. 689 | */ 690 | handle(any &any) noexcept 691 | : node{any.node}, 692 | instance{any.instance} 693 | {} 694 | 695 | /** 696 | * @brief Constructs a meta handle from a given instance. 697 | * @tparam Type Type of object to use to initialize the handle. 698 | * @param obj A reference to an object to use to initialize the handle. 699 | */ 700 | template>, handle>>> 701 | handle(Type &obj) noexcept 702 | : node{internal::type_info::resolve()}, 703 | instance{&obj} 704 | {} 705 | 706 | /** 707 | * @brief Returns the meta type of the underlying object. 708 | * @return The meta type of the underlying object, if any. 709 | */ 710 | inline meta::type type() const noexcept; 711 | 712 | /** 713 | * @brief Returns an opaque pointer to the contained instance. 714 | * @return An opaque pointer the contained instance, if any. 715 | */ 716 | const void * data() const noexcept { 717 | return instance; 718 | } 719 | 720 | /*! @copydoc data */ 721 | void * data() noexcept { 722 | return const_cast(std::as_const(*this).data()); 723 | } 724 | 725 | /** 726 | * @brief Returns false if a handle is empty, true otherwise. 727 | * @return False if the handle is empty, true otherwise. 728 | */ 729 | explicit operator bool() const noexcept { 730 | return instance; 731 | } 732 | 733 | private: 734 | const internal::type_node *node; 735 | void *instance; 736 | }; 737 | 738 | 739 | /** 740 | * @brief Checks if two containers differ in their content. 741 | * @param lhs A meta any object, either empty or not. 742 | * @param rhs A meta any object, either empty or not. 743 | * @return True if the two containers differ in their content, false otherwise. 744 | */ 745 | inline bool operator!=(const any &lhs, const any &rhs) noexcept { 746 | return !(lhs == rhs); 747 | } 748 | 749 | 750 | /** 751 | * @brief Meta property object. 752 | * 753 | * A meta property is an opaque container for a key/value pair.
754 | * Properties are associated with any other meta object to enrich it. 755 | */ 756 | class prop { 757 | /*! @brief A meta factory is allowed to create meta objects. */ 758 | template friend class factory; 759 | 760 | prop(const internal::prop_node *curr) noexcept 761 | : node{curr} 762 | {} 763 | 764 | public: 765 | /*! @brief Default constructor. */ 766 | prop() noexcept 767 | : node{nullptr} 768 | {} 769 | 770 | /** 771 | * @brief Returns the stored key. 772 | * @return A meta any containing the key stored with the given property. 773 | */ 774 | any key() const noexcept { 775 | return node->key(); 776 | } 777 | 778 | /** 779 | * @brief Returns the stored value. 780 | * @return A meta any containing the value stored with the given property. 781 | */ 782 | any value() const noexcept { 783 | return node->value(); 784 | } 785 | 786 | /** 787 | * @brief Returns true if a meta object is valid, false otherwise. 788 | * @return True if the meta object is valid, false otherwise. 789 | */ 790 | explicit operator bool() const noexcept { 791 | return node; 792 | } 793 | 794 | /** 795 | * @brief Checks if two meta objects refer to the same node. 796 | * @param other The meta object with which to compare. 797 | * @return True if the two meta objects refer to the same node, false 798 | * otherwise. 799 | */ 800 | bool operator==(const prop &other) const noexcept { 801 | return node == other.node; 802 | } 803 | 804 | private: 805 | const internal::prop_node *node; 806 | }; 807 | 808 | 809 | /** 810 | * @brief Checks if two meta objects refer to the same node. 811 | * @param lhs A meta object, either valid or not. 812 | * @param rhs A meta object, either valid or not. 813 | * @return True if the two meta objects refer to the same node, false otherwise. 814 | */ 815 | inline bool operator!=(const prop &lhs, const prop &rhs) noexcept { 816 | return !(lhs == rhs); 817 | } 818 | 819 | 820 | /** 821 | * @brief Meta base object. 822 | * 823 | * A meta base is an opaque container for a base class to be used to walk 824 | * through hierarchies. 825 | */ 826 | class base { 827 | /*! @brief A meta factory is allowed to create meta objects. */ 828 | template friend class factory; 829 | 830 | base(const internal::base_node *curr) noexcept 831 | : node{curr} 832 | {} 833 | 834 | public: 835 | /*! @brief Default constructor. */ 836 | base() noexcept 837 | : node{nullptr} 838 | {} 839 | 840 | /** 841 | * @brief Returns the meta type to which a meta base belongs. 842 | * @return The meta type to which the meta base belongs. 843 | */ 844 | inline meta::type parent() const noexcept; 845 | 846 | /** 847 | * @brief Returns the meta type of a given meta base. 848 | * @return The meta type of the meta base. 849 | */ 850 | inline meta::type type() const noexcept; 851 | 852 | /** 853 | * @brief Casts an instance from a parent type to a base type. 854 | * @param instance The instance to cast. 855 | * @return An opaque pointer to the base type. 856 | */ 857 | void * cast(void *instance) const noexcept { 858 | return node->cast(instance); 859 | } 860 | 861 | /** 862 | * @brief Returns true if a meta object is valid, false otherwise. 863 | * @return True if the meta object is valid, false otherwise. 864 | */ 865 | explicit operator bool() const noexcept { 866 | return node; 867 | } 868 | 869 | /** 870 | * @brief Checks if two meta objects refer to the same node. 871 | * @param other The meta object with which to compare. 872 | * @return True if the two meta objects refer to the same node, false 873 | * otherwise. 874 | */ 875 | bool operator==(const base &other) const noexcept { 876 | return node == other.node; 877 | } 878 | 879 | private: 880 | const internal::base_node *node; 881 | }; 882 | 883 | 884 | /** 885 | * @brief Checks if two meta objects refer to the same node. 886 | * @param lhs A meta object, either valid or not. 887 | * @param rhs A meta object, either valid or not. 888 | * @return True if the two meta objects refer to the same node, false otherwise. 889 | */ 890 | inline bool operator!=(const base &lhs, const base &rhs) noexcept { 891 | return !(lhs == rhs); 892 | } 893 | 894 | 895 | /** 896 | * @brief Meta conversion function object. 897 | * 898 | * A meta conversion function is an opaque container for a conversion function 899 | * to be used to convert a given instance to another type. 900 | */ 901 | class conv { 902 | /*! @brief A meta factory is allowed to create meta objects. */ 903 | template friend class factory; 904 | 905 | conv(const internal::conv_node *curr) noexcept 906 | : node{curr} 907 | {} 908 | 909 | public: 910 | /*! @brief Default constructor. */ 911 | conv() noexcept 912 | : node{nullptr} 913 | {} 914 | 915 | /** 916 | * @brief Returns the meta type to which a meta conversion function belongs. 917 | * @return The meta type to which the meta conversion function belongs. 918 | */ 919 | inline meta::type parent() const noexcept; 920 | 921 | /** 922 | * @brief Returns the meta type of a given meta conversion function. 923 | * @return The meta type of the meta conversion function. 924 | */ 925 | inline meta::type type() const noexcept; 926 | 927 | /** 928 | * @brief Converts an instance to a given type. 929 | * @param instance The instance to convert. 930 | * @return An opaque pointer to the instance to convert. 931 | */ 932 | any convert(const void *instance) const noexcept { 933 | return node->convert(instance); 934 | } 935 | 936 | /** 937 | * @brief Returns true if a meta object is valid, false otherwise. 938 | * @return True if the meta object is valid, false otherwise. 939 | */ 940 | explicit operator bool() const noexcept { 941 | return node; 942 | } 943 | 944 | /** 945 | * @brief Checks if two meta objects refer to the same node. 946 | * @param other The meta object with which to compare. 947 | * @return True if the two meta objects refer to the same node, false 948 | * otherwise. 949 | */ 950 | bool operator==(const conv &other) const noexcept { 951 | return node == other.node; 952 | } 953 | 954 | private: 955 | const internal::conv_node *node; 956 | }; 957 | 958 | 959 | /** 960 | * @brief Checks if two meta objects refer to the same node. 961 | * @param lhs A meta object, either valid or not. 962 | * @param rhs A meta object, either valid or not. 963 | * @return True if the two meta objects refer to the same node, false otherwise. 964 | */ 965 | inline bool operator!=(const conv &lhs, const conv &rhs) noexcept { 966 | return !(lhs == rhs); 967 | } 968 | 969 | 970 | /** 971 | * @brief Meta constructor object. 972 | * 973 | * A meta constructor is an opaque container for a function to be used to 974 | * construct instances of a given type. 975 | */ 976 | class ctor { 977 | /*! @brief A meta factory is allowed to create meta objects. */ 978 | template friend class factory; 979 | 980 | ctor(const internal::ctor_node *curr) noexcept 981 | : node{curr} 982 | {} 983 | 984 | public: 985 | /*! @brief Unsigned integer type. */ 986 | using size_type = typename internal::ctor_node::size_type; 987 | 988 | /*! @brief Default constructor. */ 989 | ctor() noexcept 990 | : node{nullptr} 991 | {} 992 | 993 | /** 994 | * @brief Returns the meta type to which a meta constructor belongs. 995 | * @return The meta type to which the meta constructor belongs. 996 | */ 997 | inline meta::type parent() const noexcept; 998 | 999 | /** 1000 | * @brief Returns the number of arguments accepted by a meta constructor. 1001 | * @return The number of arguments accepted by the meta constructor. 1002 | */ 1003 | size_type size() const noexcept { 1004 | return node->size; 1005 | } 1006 | 1007 | /** 1008 | * @brief Returns the meta type of the i-th argument of a meta constructor. 1009 | * @param index The index of the argument of which to return the meta type. 1010 | * @return The meta type of the i-th argument of a meta constructor, if any. 1011 | */ 1012 | meta::type arg(size_type index) const noexcept; 1013 | 1014 | /** 1015 | * @brief Creates an instance of the underlying type, if possible. 1016 | * 1017 | * To create a valid instance, the types of the parameters must coincide 1018 | * exactly with those required by the underlying meta constructor. 1019 | * Otherwise, an empty and then invalid container is returned. 1020 | * 1021 | * @tparam Args Types of arguments to use to construct the instance. 1022 | * @param args Parameters to use to construct the instance. 1023 | * @return A meta any containing the new instance, if any. 1024 | */ 1025 | template 1026 | any invoke(Args &&... args) const { 1027 | std::array arguments{{std::forward(args)...}}; 1028 | any any{}; 1029 | 1030 | if(sizeof...(Args) == size()) { 1031 | any = node->invoke(arguments.data()); 1032 | } 1033 | 1034 | return any; 1035 | } 1036 | 1037 | /** 1038 | * @brief Iterates all the properties assigned to a meta constructor. 1039 | * @tparam Op Type of the function object to invoke. 1040 | * @param op A valid function object. 1041 | */ 1042 | template 1043 | std::enable_if_t, void> 1044 | prop(Op op) const noexcept { 1045 | internal::iterate([op = std::move(op)](auto *curr) { 1046 | op(curr->clazz()); 1047 | }, node->prop); 1048 | } 1049 | 1050 | /** 1051 | * @brief Returns the property associated with a given key. 1052 | * @tparam Key Type of key to use to search for a property. 1053 | * @param key The key to use to search for a property. 1054 | * @return The property associated with the given key, if any. 1055 | */ 1056 | template 1057 | std::enable_if_t, meta::prop> 1058 | prop(Key &&key) const noexcept { 1059 | const auto *curr = internal::find_if([key = any{std::forward(key)}](auto *candidate) { 1060 | return candidate->key() == key; 1061 | }, node->prop); 1062 | 1063 | return curr ? curr->clazz() : meta::prop{}; 1064 | } 1065 | 1066 | /** 1067 | * @brief Returns true if a meta object is valid, false otherwise. 1068 | * @return True if the meta object is valid, false otherwise. 1069 | */ 1070 | explicit operator bool() const noexcept { 1071 | return node; 1072 | } 1073 | 1074 | /** 1075 | * @brief Checks if two meta objects refer to the same node. 1076 | * @param other The meta object with which to compare. 1077 | * @return True if the two meta objects refer to the same node, false 1078 | * otherwise. 1079 | */ 1080 | bool operator==(const ctor &other) const noexcept { 1081 | return node == other.node; 1082 | } 1083 | 1084 | private: 1085 | const internal::ctor_node *node; 1086 | }; 1087 | 1088 | 1089 | /** 1090 | * @brief Checks if two meta objects refer to the same node. 1091 | * @param lhs A meta object, either valid or not. 1092 | * @param rhs A meta object, either valid or not. 1093 | * @return True if the two meta objects refer to the same node, false otherwise. 1094 | */ 1095 | inline bool operator!=(const ctor &lhs, const ctor &rhs) noexcept { 1096 | return !(lhs == rhs); 1097 | } 1098 | 1099 | 1100 | /** 1101 | * @brief Meta destructor object. 1102 | * 1103 | * A meta destructor is an opaque container for a function to be used to 1104 | * destroy instances of a given type. 1105 | */ 1106 | class dtor { 1107 | /*! @brief A meta factory is allowed to create meta objects. */ 1108 | template friend class factory; 1109 | 1110 | dtor(const internal::dtor_node *curr) noexcept 1111 | : node{curr} 1112 | {} 1113 | 1114 | public: 1115 | /*! @brief Default constructor. */ 1116 | dtor() noexcept 1117 | : node{nullptr} 1118 | {} 1119 | 1120 | /** 1121 | * @brief Returns the meta type to which a meta destructor belongs. 1122 | * @return The meta type to which the meta destructor belongs. 1123 | */ 1124 | inline meta::type parent() const noexcept; 1125 | 1126 | /** 1127 | * @brief Destroys an instance of the underlying type. 1128 | * 1129 | * It must be possible to cast the instance to the parent type of the meta 1130 | * destructor. Otherwise, invoking the meta destructor results in an 1131 | * undefined behavior. 1132 | * 1133 | * @param handle An opaque pointer to an instance of the underlying type. 1134 | * @return True in case of success, false otherwise. 1135 | */ 1136 | bool invoke(handle handle) const { 1137 | return node->invoke(handle); 1138 | } 1139 | 1140 | /** 1141 | * @brief Returns true if a meta object is valid, false otherwise. 1142 | * @return True if the meta object is valid, false otherwise. 1143 | */ 1144 | explicit operator bool() const noexcept { 1145 | return node; 1146 | } 1147 | 1148 | /** 1149 | * @brief Checks if two meta objects refer to the same node. 1150 | * @param other The meta object with which to compare. 1151 | * @return True if the two meta objects refer to the same node, false 1152 | * otherwise. 1153 | */ 1154 | bool operator==(const dtor &other) const noexcept { 1155 | return node == other.node; 1156 | } 1157 | 1158 | private: 1159 | const internal::dtor_node *node; 1160 | }; 1161 | 1162 | 1163 | /** 1164 | * @brief Checks if two meta objects refer to the same node. 1165 | * @param lhs A meta object, either valid or not. 1166 | * @param rhs A meta object, either valid or not. 1167 | * @return True if the two meta objects refer to the same node, false otherwise. 1168 | */ 1169 | inline bool operator!=(const dtor &lhs, const dtor &rhs) noexcept { 1170 | return !(lhs == rhs); 1171 | } 1172 | 1173 | 1174 | /** 1175 | * @brief Meta data object. 1176 | * 1177 | * A meta data is an opaque container for a data member associated with a given 1178 | * type. 1179 | */ 1180 | class data { 1181 | /*! @brief A meta factory is allowed to create meta objects. */ 1182 | template friend class factory; 1183 | 1184 | data(const internal::data_node *curr) noexcept 1185 | : node{curr} 1186 | {} 1187 | 1188 | public: 1189 | /*! @brief Default constructor. */ 1190 | data() noexcept 1191 | : node{nullptr} 1192 | {} 1193 | 1194 | /** 1195 | * @brief Returns the meta type to which a meta data belongs. 1196 | * @return The meta type to which the meta data belongs. 1197 | */ 1198 | inline meta::type parent() const noexcept; 1199 | 1200 | /** 1201 | * @brief Indicates whether a given meta data is constant or not. 1202 | * @return True if the meta data is constant, false otherwise. 1203 | */ 1204 | bool is_const() const noexcept { 1205 | return node->is_const; 1206 | } 1207 | 1208 | /** 1209 | * @brief Indicates whether a given meta data is static or not. 1210 | * 1211 | * A static meta data is such that it can be accessed using a null pointer 1212 | * as an instance. 1213 | * 1214 | * @return True if the meta data is static, false otherwise. 1215 | */ 1216 | bool is_static() const noexcept { 1217 | return node->is_static; 1218 | } 1219 | 1220 | /** 1221 | * @brief Returns the meta type of a given meta data. 1222 | * @return The meta type of the meta data. 1223 | */ 1224 | inline meta::type type() const noexcept; 1225 | 1226 | /** 1227 | * @brief Sets the value of the variable enclosed by a given meta type. 1228 | * 1229 | * It must be possible to cast the instance to the parent type of the meta 1230 | * data. Otherwise, invoking the setter results in an undefined 1231 | * behavior.
1232 | * The type of the value must coincide exactly with that of the variable 1233 | * enclosed by the meta data. Otherwise, invoking the setter does nothing. 1234 | * 1235 | * @tparam Type Type of value to assign. 1236 | * @param handle An opaque pointer to an instance of the underlying type. 1237 | * @param value Parameter to use to set the underlying variable. 1238 | * @return True in case of success, false otherwise. 1239 | */ 1240 | template 1241 | bool set(handle handle, Type &&value) const { 1242 | return node->set(handle, any{}, std::forward(value)); 1243 | } 1244 | 1245 | /** 1246 | * @brief Sets the i-th element of an array enclosed by a given meta type. 1247 | * 1248 | * It must be possible to cast the instance to the parent type of the meta 1249 | * data. Otherwise, invoking the setter results in an undefined 1250 | * behavior.
1251 | * The type of the value must coincide exactly with that of the array type 1252 | * enclosed by the meta data. Otherwise, invoking the setter does nothing. 1253 | * 1254 | * @tparam Type Type of value to assign. 1255 | * @param handle An opaque pointer to an instance of the underlying type. 1256 | * @param index Position of the underlying element to set. 1257 | * @param value Parameter to use to set the underlying element. 1258 | * @return True in case of success, false otherwise. 1259 | */ 1260 | template 1261 | bool set(handle handle, std::size_t index, Type &&value) const { 1262 | assert(index < node->ref()->extent); 1263 | return node->set(handle, index, std::forward(value)); 1264 | } 1265 | 1266 | /** 1267 | * @brief Gets the value of the variable enclosed by a given meta type. 1268 | * 1269 | * It must be possible to cast the instance to the parent type of the meta 1270 | * data. Otherwise, invoking the getter results in an undefined behavior. 1271 | * 1272 | * @param handle An opaque pointer to an instance of the underlying type. 1273 | * @return A meta any containing the value of the underlying variable. 1274 | */ 1275 | any get(handle handle) const noexcept { 1276 | return node->get(handle, any{}); 1277 | } 1278 | 1279 | /** 1280 | * @brief Gets the i-th element of an array enclosed by a given meta type. 1281 | * 1282 | * It must be possible to cast the instance to the parent type of the meta 1283 | * data. Otherwise, invoking the getter results in an undefined behavior. 1284 | * 1285 | * @param handle An opaque pointer to an instance of the underlying type. 1286 | * @param index Position of the underlying element to get. 1287 | * @return A meta any containing the value of the underlying element. 1288 | */ 1289 | any get(handle handle, std::size_t index) const noexcept { 1290 | assert(index < node->ref()->extent); 1291 | return node->get(handle, index); 1292 | } 1293 | 1294 | /** 1295 | * @brief Iterates all the properties assigned to a meta data. 1296 | * @tparam Op Type of the function object to invoke. 1297 | * @param op A valid function object. 1298 | */ 1299 | template 1300 | std::enable_if_t, void> 1301 | prop(Op op) const noexcept { 1302 | internal::iterate([op = std::move(op)](auto *curr) { 1303 | op(curr->clazz()); 1304 | }, node->prop); 1305 | } 1306 | 1307 | /** 1308 | * @brief Returns the property associated with a given key. 1309 | * @tparam Key Type of key to use to search for a property. 1310 | * @param key The key to use to search for a property. 1311 | * @return The property associated with the given key, if any. 1312 | */ 1313 | template 1314 | std::enable_if_t, meta::prop> 1315 | prop(Key &&key) const noexcept { 1316 | const auto *curr = internal::find_if([key = any{std::forward(key)}](auto *candidate) { 1317 | return candidate->key() == key; 1318 | }, node->prop); 1319 | 1320 | return curr ? curr->clazz() : meta::prop{}; 1321 | } 1322 | 1323 | /** 1324 | * @brief Returns true if a meta object is valid, false otherwise. 1325 | * @return True if the meta object is valid, false otherwise. 1326 | */ 1327 | explicit operator bool() const noexcept { 1328 | return node; 1329 | } 1330 | 1331 | /** 1332 | * @brief Checks if two meta objects refer to the same node. 1333 | * @param other The meta object with which to compare. 1334 | * @return True if the two meta objects refer to the same node, false 1335 | * otherwise. 1336 | */ 1337 | bool operator==(const data &other) const noexcept { 1338 | return node == other.node; 1339 | } 1340 | 1341 | private: 1342 | const internal::data_node *node; 1343 | }; 1344 | 1345 | 1346 | /** 1347 | * @brief Checks if two meta objects refer to the same node. 1348 | * @param lhs A meta object, either valid or not. 1349 | * @param rhs A meta object, either valid or not. 1350 | * @return True if the two meta objects refer to the same node, false otherwise. 1351 | */ 1352 | inline bool operator!=(const data &lhs, const data &rhs) noexcept { 1353 | return !(lhs == rhs); 1354 | } 1355 | 1356 | 1357 | /** 1358 | * @brief Meta function object. 1359 | * 1360 | * A meta function is an opaque container for a member function associated with 1361 | * a given type. 1362 | */ 1363 | class func { 1364 | /*! @brief A meta factory is allowed to create meta objects. */ 1365 | template friend class factory; 1366 | 1367 | func(const internal::func_node *curr) noexcept 1368 | : node{curr} 1369 | {} 1370 | 1371 | public: 1372 | /*! @brief Unsigned integer type. */ 1373 | using size_type = typename internal::func_node::size_type; 1374 | 1375 | /*! @brief Default constructor. */ 1376 | func() noexcept 1377 | : node{nullptr} 1378 | {} 1379 | 1380 | /** 1381 | * @brief Returns the meta type to which a meta function belongs. 1382 | * @return The meta type to which the meta function belongs. 1383 | */ 1384 | inline meta::type parent() const noexcept; 1385 | 1386 | /** 1387 | * @brief Returns the number of arguments accepted by a meta function. 1388 | * @return The number of arguments accepted by the meta function. 1389 | */ 1390 | size_type size() const noexcept { 1391 | return node->size; 1392 | } 1393 | 1394 | /** 1395 | * @brief Indicates whether a given meta function is constant or not. 1396 | * @return True if the meta function is constant, false otherwise. 1397 | */ 1398 | bool is_const() const noexcept { 1399 | return node->is_const; 1400 | } 1401 | 1402 | /** 1403 | * @brief Indicates whether a given meta function is static or not. 1404 | * 1405 | * A static meta function is such that it can be invoked using a null 1406 | * pointer as an instance. 1407 | * 1408 | * @return True if the meta function is static, false otherwise. 1409 | */ 1410 | bool is_static() const noexcept { 1411 | return node->is_static; 1412 | } 1413 | 1414 | /** 1415 | * @brief Returns the meta type of the return type of a meta function. 1416 | * @return The meta type of the return type of the meta function. 1417 | */ 1418 | inline meta::type ret() const noexcept; 1419 | 1420 | /** 1421 | * @brief Returns the meta type of the i-th argument of a meta function. 1422 | * @param index The index of the argument of which to return the meta type. 1423 | * @return The meta type of the i-th argument of a meta function, if any. 1424 | */ 1425 | inline meta::type arg(size_type index) const noexcept; 1426 | 1427 | /** 1428 | * @brief Invokes the underlying function, if possible. 1429 | * 1430 | * To invoke a meta function, the types of the parameters must coincide 1431 | * exactly with those required by the underlying function. Otherwise, an 1432 | * empty and then invalid container is returned.
1433 | * It must be possible to cast the instance to the parent type of the meta 1434 | * function. Otherwise, invoking the underlying function results in an 1435 | * undefined behavior. 1436 | * 1437 | * @tparam Args Types of arguments to use to invoke the function. 1438 | * @param handle An opaque pointer to an instance of the underlying type. 1439 | * @param args Parameters to use to invoke the function. 1440 | * @return A meta any containing the returned value, if any. 1441 | */ 1442 | template 1443 | any invoke(handle handle, Args &&... args) const { 1444 | // makes aliasing on the values and passes forward references if any 1445 | std::array arguments{{meta::handle{args}...}}; 1446 | any any{}; 1447 | 1448 | if(sizeof...(Args) == size()) { 1449 | any = node->invoke(handle, arguments.data()); 1450 | } 1451 | 1452 | return any; 1453 | } 1454 | 1455 | /** 1456 | * @brief Iterates all the properties assigned to a meta function. 1457 | * @tparam Op Type of the function object to invoke. 1458 | * @param op A valid function object. 1459 | */ 1460 | template 1461 | std::enable_if_t, void> 1462 | prop(Op op) const noexcept { 1463 | internal::iterate([op = std::move(op)](auto *curr) { 1464 | op(curr->clazz()); 1465 | }, node->prop); 1466 | } 1467 | 1468 | /** 1469 | * @brief Returns the property associated with a given key. 1470 | * @tparam Key Type of key to use to search for a property. 1471 | * @param key The key to use to search for a property. 1472 | * @return The property associated with the given key, if any. 1473 | */ 1474 | template 1475 | std::enable_if_t, meta::prop> 1476 | prop(Key &&key) const noexcept { 1477 | const auto *curr = internal::find_if([key = any{std::forward(key)}](auto *candidate) { 1478 | return candidate->key() == key; 1479 | }, node->prop); 1480 | 1481 | return curr ? curr->clazz() : meta::prop{}; 1482 | } 1483 | 1484 | /** 1485 | * @brief Returns true if a meta object is valid, false otherwise. 1486 | * @return True if the meta object is valid, false otherwise. 1487 | */ 1488 | explicit operator bool() const noexcept { 1489 | return node; 1490 | } 1491 | 1492 | /** 1493 | * @brief Checks if two meta objects refer to the same node. 1494 | * @param other The meta object with which to compare. 1495 | * @return True if the two meta objects refer to the same node, false 1496 | * otherwise. 1497 | */ 1498 | bool operator==(const func &other) const noexcept { 1499 | return node == other.node; 1500 | } 1501 | 1502 | private: 1503 | const internal::func_node *node; 1504 | }; 1505 | 1506 | 1507 | /** 1508 | * @brief Checks if two meta objects refer to the same node. 1509 | * @param lhs A meta object, either valid or not. 1510 | * @param rhs A meta object, either valid or not. 1511 | * @return True if the two meta objects refer to the same node, false otherwise. 1512 | */ 1513 | inline bool operator!=(const func &lhs, const func &rhs) noexcept { 1514 | return !(lhs == rhs); 1515 | } 1516 | 1517 | 1518 | /** 1519 | * @brief Meta type object. 1520 | * 1521 | * A meta type is the starting point for accessing a reflected type, thus being 1522 | * able to work through it on real objects. 1523 | */ 1524 | class type { 1525 | /*! @brief A meta node is allowed to create meta objects. */ 1526 | template friend struct internal::info_node; 1527 | 1528 | type(const internal::type_node *curr) noexcept 1529 | : node{curr} 1530 | {} 1531 | 1532 | public: 1533 | /*! @brief Unsigned integer type. */ 1534 | using size_type = typename internal::type_node::size_type; 1535 | 1536 | /*! @brief Default constructor. */ 1537 | type() noexcept 1538 | : node{nullptr} 1539 | {} 1540 | 1541 | /** 1542 | * @brief Indicates whether a given meta type refers to void or not. 1543 | * @return True if the underlying type is void, false otherwise. 1544 | */ 1545 | bool is_void() const noexcept { 1546 | return node->is_void; 1547 | } 1548 | 1549 | /** 1550 | * @brief Indicates whether a given meta type refers to an integral type or 1551 | * not. 1552 | * @return True if the underlying type is an integral type, false otherwise. 1553 | */ 1554 | bool is_integral() const noexcept { 1555 | return node->is_integral; 1556 | } 1557 | 1558 | /** 1559 | * @brief Indicates whether a given meta type refers to a floating-point 1560 | * type or not. 1561 | * @return True if the underlying type is a floating-point type, false 1562 | * otherwise. 1563 | */ 1564 | bool is_floating_point() const noexcept { 1565 | return node->is_floating_point; 1566 | } 1567 | 1568 | /** 1569 | * @brief Indicates whether a given meta type refers to an array type or 1570 | * not. 1571 | * @return True if the underlying type is an array type, false otherwise. 1572 | */ 1573 | bool is_array() const noexcept { 1574 | return node->is_array; 1575 | } 1576 | 1577 | /** 1578 | * @brief Indicates whether a given meta type refers to an enum or not. 1579 | * @return True if the underlying type is an enum, false otherwise. 1580 | */ 1581 | bool is_enum() const noexcept { 1582 | return node->is_enum; 1583 | } 1584 | 1585 | /** 1586 | * @brief Indicates whether a given meta type refers to an union or not. 1587 | * @return True if the underlying type is an union, false otherwise. 1588 | */ 1589 | bool is_union() const noexcept { 1590 | return node->is_union; 1591 | } 1592 | 1593 | /** 1594 | * @brief Indicates whether a given meta type refers to a class or not. 1595 | * @return True if the underlying type is a class, false otherwise. 1596 | */ 1597 | bool is_class() const noexcept { 1598 | return node->is_class; 1599 | } 1600 | 1601 | /** 1602 | * @brief Indicates whether a given meta type refers to a pointer or not. 1603 | * @return True if the underlying type is a pointer, false otherwise. 1604 | */ 1605 | bool is_pointer() const noexcept { 1606 | return node->is_pointer; 1607 | } 1608 | 1609 | /** 1610 | * @brief Indicates whether a given meta type refers to a function pointer 1611 | * or not. 1612 | * @return True if the underlying type is a function pointer, false 1613 | * otherwise. 1614 | */ 1615 | bool is_function_pointer() const noexcept { 1616 | return node->is_function_pointer; 1617 | } 1618 | 1619 | /** 1620 | * @brief Indicates whether a given meta type refers to a pointer to data 1621 | * member or not. 1622 | * @return True if the underlying type is a pointer to data member, false 1623 | * otherwise. 1624 | */ 1625 | bool is_member_object_pointer() const noexcept { 1626 | return node->is_member_object_pointer; 1627 | } 1628 | 1629 | /** 1630 | * @brief Indicates whether a given meta type refers to a pointer to member 1631 | * function or not. 1632 | * @return True if the underlying type is a pointer to member function, 1633 | * false otherwise. 1634 | */ 1635 | bool is_member_function_pointer() const noexcept { 1636 | return node->is_member_function_pointer; 1637 | } 1638 | 1639 | /** 1640 | * @brief If a given meta type refers to an array type, provides the number 1641 | * of elements of the array. 1642 | * @return The number of elements of the array if the underlying type is an 1643 | * array type, 0 otherwise. 1644 | */ 1645 | size_type extent() const noexcept { 1646 | return node->extent; 1647 | } 1648 | 1649 | /** 1650 | * @brief Provides the meta type for which the pointer is defined. 1651 | * @return The meta type for which the pointer is defined or this meta type 1652 | * if it doesn't refer to a pointer type. 1653 | */ 1654 | meta::type remove_pointer() const noexcept { 1655 | return node->remove_pointer(); 1656 | } 1657 | 1658 | /** 1659 | * @brief Iterates all the meta base of a meta type. 1660 | * 1661 | * Iteratively returns **all** the base classes of the given type. 1662 | * 1663 | * @tparam Op Type of the function object to invoke. 1664 | * @param op A valid function object. 1665 | */ 1666 | template 1667 | std::enable_if_t, void> 1668 | base(Op op) const noexcept { 1669 | internal::iterate<&internal::type_node::base>([op = std::move(op)](auto *curr) { 1670 | op(curr->clazz()); 1671 | }, node); 1672 | } 1673 | 1674 | /** 1675 | * @brief Returns the meta base associated with a given identifier. 1676 | * 1677 | * Searches recursively among **all** the base classes of the given type. 1678 | * 1679 | * @param identifier Unique identifier. 1680 | * @return The meta base associated with the given identifier, if any. 1681 | */ 1682 | meta::base base(const std::size_t identifier) const noexcept { 1683 | const auto *curr = internal::find_if<&internal::type_node::base>([identifier](auto *candidate) { 1684 | return candidate->ref()->identifier == identifier; 1685 | }, node); 1686 | 1687 | return curr ? curr->clazz() : meta::base{}; 1688 | } 1689 | 1690 | /** 1691 | * @brief Iterates all the meta conversion functions of a meta type. 1692 | * 1693 | * Iteratively returns **all** the meta conversion functions of the given 1694 | * type. 1695 | * 1696 | * @tparam Op Type of the function object to invoke. 1697 | * @param op A valid function object. 1698 | */ 1699 | template 1700 | void conv(Op op) const noexcept { 1701 | internal::iterate<&internal::type_node::conv>([op = std::move(op)](auto *curr) { 1702 | op(curr->clazz()); 1703 | }, node); 1704 | } 1705 | 1706 | /** 1707 | * @brief Returns the meta conversion function associated with a given type. 1708 | * 1709 | * Searches recursively among **all** the conversion functions of the given 1710 | * type. 1711 | * 1712 | * @tparam Type The type to use to search for a meta conversion function. 1713 | * @return The meta conversion function associated with the given type, if 1714 | * any. 1715 | */ 1716 | template 1717 | meta::conv conv() const noexcept { 1718 | const auto *curr = internal::find_if<&internal::type_node::conv>([type = internal::type_info::resolve()](auto *candidate) { 1719 | return candidate->ref() == type; 1720 | }, node); 1721 | 1722 | return curr ? curr->clazz() : meta::conv{}; 1723 | } 1724 | 1725 | /** 1726 | * @brief Iterates all the meta constructors of a meta type. 1727 | * @tparam Op Type of the function object to invoke. 1728 | * @param op A valid function object. 1729 | */ 1730 | template 1731 | void ctor(Op op) const noexcept { 1732 | internal::iterate([op = std::move(op)](auto *curr) { 1733 | op(curr->clazz()); 1734 | }, node->ctor); 1735 | } 1736 | 1737 | /** 1738 | * @brief Returns the meta constructor that accepts a given list of types of 1739 | * arguments. 1740 | * @return The requested meta constructor, if any. 1741 | */ 1742 | template 1743 | meta::ctor ctor() const noexcept { 1744 | const auto *curr = internal::ctor(std::make_index_sequence{}, node); 1745 | return curr ? curr->clazz() : meta::ctor{}; 1746 | } 1747 | 1748 | /** 1749 | * @brief Returns the meta destructor associated with a given type. 1750 | * @return The meta destructor associated with the given type, if any. 1751 | */ 1752 | meta::dtor dtor() const noexcept { 1753 | return node->dtor ? node->dtor->clazz() : meta::dtor{}; 1754 | } 1755 | 1756 | /** 1757 | * @brief Iterates all the meta data of a meta type. 1758 | * 1759 | * Iteratively returns **all** the meta data of the given type. This means 1760 | * that the meta data of the base classes will also be returned, if any. 1761 | * 1762 | * @tparam Op Type of the function object to invoke. 1763 | * @param op A valid function object. 1764 | */ 1765 | template 1766 | std::enable_if_t, void> 1767 | data(Op op) const noexcept { 1768 | internal::iterate<&internal::type_node::data>([op = std::move(op)](auto *curr) { 1769 | op(curr->clazz()); 1770 | }, node); 1771 | } 1772 | 1773 | /** 1774 | * @brief Returns the meta data associated with a given identifier. 1775 | * 1776 | * Searches recursively among **all** the meta data of the given type. This 1777 | * means that the meta data of the base classes will also be inspected, if 1778 | * any. 1779 | * 1780 | * @param identifier Unique identifier. 1781 | * @return The meta data associated with the given identifier, if any. 1782 | */ 1783 | meta::data data(const std::size_t identifier) const noexcept { 1784 | const auto *curr = internal::find_if<&internal::type_node::data>([identifier](auto *candidate) { 1785 | return candidate->identifier == identifier; 1786 | }, node); 1787 | 1788 | return curr ? curr->clazz() : meta::data{}; 1789 | } 1790 | 1791 | /** 1792 | * @brief Iterates all the meta functions of a meta type. 1793 | * 1794 | * Iteratively returns **all** the meta functions of the given type. This 1795 | * means that the meta functions of the base classes will also be returned, 1796 | * if any. 1797 | * 1798 | * @tparam Op Type of the function object to invoke. 1799 | * @param op A valid function object. 1800 | */ 1801 | template 1802 | std::enable_if_t, void> 1803 | func(Op op) const noexcept { 1804 | internal::iterate<&internal::type_node::func>([op = std::move(op)](auto *curr) { 1805 | op(curr->clazz()); 1806 | }, node); 1807 | } 1808 | 1809 | /** 1810 | * @brief Returns the meta function associated with a given identifier. 1811 | * 1812 | * Searches recursively among **all** the meta functions of the given type. 1813 | * This means that the meta functions of the base classes will also be 1814 | * inspected, if any. 1815 | * 1816 | * @param identifier Unique identifier. 1817 | * @return The meta function associated with the given identifier, if any. 1818 | */ 1819 | meta::func func(const std::size_t identifier) const noexcept { 1820 | const auto *curr = internal::find_if<&internal::type_node::func>([identifier](auto *candidate) { 1821 | return candidate->identifier == identifier; 1822 | }, node); 1823 | 1824 | return curr ? curr->clazz() : meta::func{}; 1825 | } 1826 | 1827 | /** 1828 | * @brief Creates an instance of the underlying type, if possible. 1829 | * 1830 | * To create a valid instance, the types of the parameters must coincide 1831 | * exactly with those required by the underlying meta constructor. 1832 | * Otherwise, an empty and then invalid container is returned. 1833 | * 1834 | * @tparam Args Types of arguments to use to construct the instance. 1835 | * @param args Parameters to use to construct the instance. 1836 | * @return A meta any containing the new instance, if any. 1837 | */ 1838 | template 1839 | any construct(Args &&... args) const { 1840 | std::array arguments{{std::forward(args)...}}; 1841 | any any{}; 1842 | 1843 | internal::find_if<&internal::type_node::ctor>([data = arguments.data(), &any](auto *curr) -> bool { 1844 | if(curr->size == sizeof...(args)) { 1845 | any = curr->invoke(data); 1846 | } 1847 | 1848 | return static_cast(any); 1849 | }, node); 1850 | 1851 | return any; 1852 | } 1853 | 1854 | /** 1855 | * @brief Destroys an instance of the underlying type. 1856 | * 1857 | * It must be possible to cast the instance to the underlying type. 1858 | * Otherwise, invoking the meta destructor results in an undefined 1859 | * behavior.
1860 | * If no destructor has been set, this function returns true without doing 1861 | * anything. 1862 | * 1863 | * @param handle An opaque pointer to an instance of the underlying type. 1864 | * @return True in case of success, false otherwise. 1865 | */ 1866 | bool destroy(handle handle) const { 1867 | return (handle.type() == node->clazz()) && (!node->dtor || node->dtor->invoke(handle)); 1868 | } 1869 | 1870 | /** 1871 | * @brief Iterates all the properties assigned to a meta type. 1872 | * 1873 | * Iteratively returns **all** the properties of the given type. This means 1874 | * that the properties of the base classes will also be returned, if any. 1875 | * 1876 | * @tparam Op Type of the function object to invoke. 1877 | * @param op A valid function object. 1878 | */ 1879 | template 1880 | std::enable_if_t, void> 1881 | prop(Op op) const noexcept { 1882 | internal::iterate<&internal::type_node::prop>([op = std::move(op)](auto *curr) { 1883 | op(curr->clazz()); 1884 | }, node); 1885 | } 1886 | 1887 | /** 1888 | * @brief Returns the property associated with a given key. 1889 | * 1890 | * Searches recursively among **all** the properties of the given type. This 1891 | * means that the properties of the base classes will also be inspected, if 1892 | * any. 1893 | * 1894 | * @tparam Key Type of key to use to search for a property. 1895 | * @param key The key to use to search for a property. 1896 | * @return The property associated with the given key, if any. 1897 | */ 1898 | template 1899 | std::enable_if_t, meta::prop> 1900 | prop(Key &&key) const noexcept { 1901 | const auto *curr = internal::find_if<&internal::type_node::prop>([key = any{std::forward(key)}](auto *candidate) { 1902 | return candidate->key() == key; 1903 | }, node); 1904 | 1905 | return curr ? curr->clazz() : meta::prop{}; 1906 | } 1907 | 1908 | /** 1909 | * @brief Returns true if a meta object is valid, false otherwise. 1910 | * @return True if the meta object is valid, false otherwise. 1911 | */ 1912 | explicit operator bool() const noexcept { 1913 | return node; 1914 | } 1915 | 1916 | /** 1917 | * @brief Checks if two meta objects refer to the same node. 1918 | * @param other The meta object with which to compare. 1919 | * @return True if the two meta objects refer to the same node, false 1920 | * otherwise. 1921 | */ 1922 | bool operator==(const type &other) const noexcept { 1923 | return node == other.node; 1924 | } 1925 | 1926 | private: 1927 | const internal::type_node *node; 1928 | }; 1929 | 1930 | 1931 | /** 1932 | * @brief Checks if two meta objects refer to the same node. 1933 | * @param lhs A meta object, either valid or not. 1934 | * @param rhs A meta object, either valid or not. 1935 | * @return True if the two meta objects refer to the same node, false otherwise. 1936 | */ 1937 | inline bool operator!=(const type &lhs, const type &rhs) noexcept { 1938 | return !(lhs == rhs); 1939 | } 1940 | 1941 | 1942 | inline any::any(handle handle) noexcept 1943 | : any{} 1944 | { 1945 | node = handle.node; 1946 | instance = handle.instance; 1947 | } 1948 | 1949 | 1950 | inline meta::type any::type() const noexcept { 1951 | return node ? node->clazz() : meta::type{}; 1952 | } 1953 | 1954 | 1955 | inline meta::type handle::type() const noexcept { 1956 | return node ? node->clazz() : meta::type{}; 1957 | } 1958 | 1959 | 1960 | inline meta::type base::parent() const noexcept { 1961 | return node->parent->clazz(); 1962 | } 1963 | 1964 | 1965 | inline meta::type base::type() const noexcept { 1966 | return node->ref()->clazz(); 1967 | } 1968 | 1969 | 1970 | inline meta::type conv::parent() const noexcept { 1971 | return node->parent->clazz(); 1972 | } 1973 | 1974 | 1975 | inline meta::type conv::type() const noexcept { 1976 | return node->ref()->clazz(); 1977 | } 1978 | 1979 | 1980 | inline meta::type ctor::parent() const noexcept { 1981 | return node->parent->clazz(); 1982 | } 1983 | 1984 | 1985 | inline meta::type ctor::arg(size_type index) const noexcept { 1986 | return index < size() ? node->arg(index)->clazz() : meta::type{}; 1987 | } 1988 | 1989 | 1990 | inline meta::type dtor::parent() const noexcept { 1991 | return node->parent->clazz(); 1992 | } 1993 | 1994 | 1995 | inline meta::type data::parent() const noexcept { 1996 | return node->parent->clazz(); 1997 | } 1998 | 1999 | 2000 | inline meta::type data::type() const noexcept { 2001 | return node->ref()->clazz(); 2002 | } 2003 | 2004 | 2005 | inline meta::type func::parent() const noexcept { 2006 | return node->parent->clazz(); 2007 | } 2008 | 2009 | 2010 | inline meta::type func::ret() const noexcept { 2011 | return node->ret()->clazz(); 2012 | } 2013 | 2014 | 2015 | inline meta::type func::arg(size_type index) const noexcept { 2016 | return index < size() ? node->arg(index)->clazz() : meta::type{}; 2017 | } 2018 | 2019 | 2020 | /** 2021 | * @cond TURN_OFF_DOXYGEN 2022 | * Internal details not to be documented. 2023 | */ 2024 | 2025 | 2026 | namespace internal { 2027 | 2028 | 2029 | template && !std::is_function_v>> 2030 | static auto compare(int, const void *lhs, const void *rhs) 2031 | -> decltype(std::declval() == std::declval(), bool{}) { 2032 | return *static_cast(lhs) == *static_cast(rhs); 2033 | } 2034 | 2035 | template 2036 | static bool compare(char, const void *lhs, const void *rhs) { 2037 | return lhs == rhs; 2038 | } 2039 | 2040 | 2041 | template 2042 | inline type_node * info_node::resolve() noexcept { 2043 | if(!type) { 2044 | static type_node node{ 2045 | {}, 2046 | nullptr, 2047 | nullptr, 2048 | std::is_void_v, 2049 | std::is_integral_v, 2050 | std::is_floating_point_v, 2051 | std::is_array_v, 2052 | std::is_enum_v, 2053 | std::is_union_v, 2054 | std::is_class_v, 2055 | std::is_pointer_v, 2056 | std::is_pointer_v && std::is_function_v>, 2057 | std::is_member_object_pointer_v, 2058 | std::is_member_function_pointer_v, 2059 | std::extent_v, 2060 | [](const void *lhs, const void *rhs) { 2061 | return compare(0, lhs, rhs); 2062 | }, 2063 | []() noexcept -> meta::type { 2064 | return internal::type_info>::resolve(); 2065 | }, 2066 | []() noexcept -> meta::type { 2067 | return &node; 2068 | } 2069 | }; 2070 | 2071 | type = &node; 2072 | } 2073 | 2074 | return type; 2075 | } 2076 | 2077 | 2078 | } 2079 | 2080 | 2081 | /** 2082 | * Internal details not to be documented. 2083 | * @endcond TURN_OFF_DOXYGEN 2084 | */ 2085 | 2086 | 2087 | } 2088 | 2089 | 2090 | #endif // META_META_HPP 2091 | -------------------------------------------------------------------------------- /src/meta/policy.hpp: -------------------------------------------------------------------------------- 1 | #ifndef META_POLICY_HPP 2 | #define META_POLICY_HPP 3 | 4 | 5 | namespace meta { 6 | 7 | 8 | /*! @brief Empty class type used to request the _as alias_ policy. */ 9 | struct as_alias_t {}; 10 | 11 | 12 | /*! @brief Disambiguation tag. */ 13 | constexpr as_alias_t as_alias; 14 | 15 | 16 | /*! @brief Empty class type used to request the _as-is_ policy. */ 17 | struct as_is_t {}; 18 | 19 | 20 | /*! @brief Empty class type used to request the _as void_ policy. */ 21 | struct as_void_t {}; 22 | 23 | 24 | } 25 | 26 | 27 | #endif // META_POLICY_HPP 28 | -------------------------------------------------------------------------------- /test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # 2 | # Tests configuration 3 | # 4 | 5 | include_directories($) 6 | add_compile_options($) 7 | 8 | add_executable(meta_test odr.cpp meta.cpp) 9 | set_target_properties(meta_test PROPERTIES CXX_EXTENSIONS OFF) 10 | target_link_libraries(meta_test PRIVATE meta GTest::Main Threads::Threads) 11 | target_compile_definitions(meta_test PRIVATE $) 12 | target_compile_features(meta_test PRIVATE $) 13 | target_compile_options(meta_test PRIVATE $<$>:-pedantic -Wall>) 14 | target_compile_options(meta_test PRIVATE $<$:/EHsc>) 15 | add_test(NAME meta_test COMMAND meta_test) 16 | -------------------------------------------------------------------------------- /test/meta.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | template 11 | void set(Type &prop, Type value) { 12 | prop = value; 13 | } 14 | 15 | template 16 | Type get(Type &prop) { 17 | return prop; 18 | } 19 | 20 | enum class properties { 21 | prop_int, 22 | prop_bool 23 | }; 24 | 25 | struct empty_type { 26 | virtual ~empty_type() = default; 27 | 28 | static void destroy(empty_type &) { 29 | ++counter; 30 | } 31 | 32 | inline static int counter = 0; 33 | }; 34 | 35 | struct fat_type: empty_type { 36 | fat_type() = default; 37 | 38 | fat_type(int *value) 39 | : foo{value}, bar{value} 40 | {} 41 | 42 | int *foo{nullptr}; 43 | int *bar{nullptr}; 44 | 45 | bool operator==(const fat_type &other) const { 46 | return foo == other.foo && bar == other.bar; 47 | } 48 | }; 49 | 50 | union union_type { 51 | int i; 52 | double d; 53 | }; 54 | 55 | struct base_type { 56 | virtual ~base_type() = default; 57 | }; 58 | 59 | struct derived_type: base_type { 60 | derived_type() = default; 61 | 62 | derived_type(const base_type &, int value, char character) 63 | : i{value}, c{character} 64 | {} 65 | 66 | int f() const { return i; } 67 | static char g(const derived_type &type) { return type.c; } 68 | 69 | const int i{}; 70 | const char c{}; 71 | }; 72 | 73 | derived_type derived_factory(const base_type &, int value) { 74 | return {derived_type{}, value, 'c'}; 75 | } 76 | 77 | struct data_type { 78 | int i{0}; 79 | const int j{1}; 80 | inline static int h{2}; 81 | inline static const int k{3}; 82 | empty_type empty{}; 83 | int v{0}; 84 | }; 85 | 86 | struct array_type { 87 | static inline int global[3]; 88 | int local[3]; 89 | }; 90 | 91 | struct func_type { 92 | int f(const base_type &, int a, int b) { return f(a, b); } 93 | int f(int a, int b) { value = a; return b*b; } 94 | int f(int v) const { return v*v; } 95 | void g(int v) { value = v*v; } 96 | 97 | static int h(int &v) { return (v *= value); } 98 | static void k(int v) { value = v; } 99 | 100 | int v(int v) const { return (value = v); } 101 | int & a() const { return value; } 102 | 103 | inline static int value = 0; 104 | }; 105 | 106 | struct setter_getter_type { 107 | int value{}; 108 | 109 | int setter(int val) { return value = val; } 110 | int getter() { return value; } 111 | 112 | int setter_with_ref(const int &val) { return value = val; } 113 | const int & getter_with_ref() { return value; } 114 | 115 | static int static_setter(setter_getter_type &type, int value) { return type.value = value; } 116 | static int static_getter(const setter_getter_type &type) { return type.value; } 117 | }; 118 | 119 | struct not_comparable_type { 120 | bool operator==(const not_comparable_type &) const = delete; 121 | }; 122 | 123 | bool operator!=(const not_comparable_type &, const not_comparable_type &) = delete; 124 | 125 | struct an_abstract_type { 126 | virtual ~an_abstract_type() = default; 127 | void f(int v) { i = v; } 128 | virtual void g(int) = 0; 129 | int i{}; 130 | }; 131 | 132 | struct another_abstract_type { 133 | virtual ~another_abstract_type() = default; 134 | virtual void h(char) = 0; 135 | char j{}; 136 | }; 137 | 138 | struct concrete_type: an_abstract_type, another_abstract_type { 139 | void f(int v) { i = v*v; } // hide, it's ok :-) 140 | void g(int v) override { i = -v; } 141 | void h(char c) override { j = c; } 142 | }; 143 | 144 | struct Meta: public ::testing::Test { 145 | static void SetUpTestCase() { 146 | meta::reflect().conv(); 147 | std::hash hash{}; 148 | 149 | meta::reflect(hash("char"), std::make_pair(properties::prop_int, 42)) 150 | .data<&set, &get>(hash("value")); 151 | 152 | meta::reflect() 153 | .data(hash("prop_bool")) 154 | .data(hash("prop_int")) 155 | .data<&set, &get>(hash("value")); 156 | 157 | meta::reflect().data<0u>(hash("min")).data<100u>(hash("max")); 158 | 159 | meta::reflect(hash("base")); 160 | 161 | meta::reflect(hash("derived"), std::make_pair(properties::prop_int, 99)) 162 | .base() 163 | .ctor(std::make_pair(properties::prop_bool, false)) 164 | .ctor<&derived_factory>(std::make_pair(properties::prop_int, 42)) 165 | .conv<&derived_type::f>() 166 | .conv<&derived_type::g>(); 167 | 168 | meta::reflect(hash("empty")) 169 | .dtor<&empty_type::destroy>(); 170 | 171 | meta::reflect(hash("fat")) 172 | .base() 173 | .dtor<&fat_type::destroy>(); 174 | 175 | meta::reflect(hash("data")) 176 | .data<&data_type::i, meta::as_alias_t>(hash("i"), std::make_pair(properties::prop_int, 0)) 177 | .data<&data_type::j>(hash("j"), std::make_pair(properties::prop_int, 1)) 178 | .data<&data_type::h>(hash("h"), std::make_pair(properties::prop_int, 2)) 179 | .data<&data_type::k>(hash("k"), std::make_pair(properties::prop_int, 3)) 180 | .data<&data_type::empty>(hash("empty")) 181 | .data<&data_type::v, meta::as_void_t>(hash("v")); 182 | 183 | meta::reflect(hash("array")) 184 | .data<&array_type::global>(hash("global")) 185 | .data<&array_type::local>(hash("local")); 186 | 187 | meta::reflect(hash("func")) 188 | .func(&func_type::f)>(hash("f3")) 189 | .func(&func_type::f)>(hash("f2"), std::make_pair(properties::prop_bool, false)) 190 | .func(&func_type::f)>(hash("f1"), std::make_pair(properties::prop_bool, false)) 191 | .func<&func_type::g>(hash("g"), std::make_pair(properties::prop_bool, false)) 192 | .func<&func_type::h>(hash("h"), std::make_pair(properties::prop_bool, false)) 193 | .func<&func_type::k>(hash("k"), std::make_pair(properties::prop_bool, false)) 194 | .func<&func_type::v, meta::as_void_t>(hash("v")) 195 | .func<&func_type::a, meta::as_alias_t>(hash("a")); 196 | 197 | meta::reflect(hash("setter_getter")) 198 | .data<&setter_getter_type::static_setter, &setter_getter_type::static_getter>(hash("x")) 199 | .data<&setter_getter_type::setter, &setter_getter_type::getter>(hash("y")) 200 | .data<&setter_getter_type::static_setter, &setter_getter_type::getter>(hash("z")) 201 | .data<&setter_getter_type::setter_with_ref, &setter_getter_type::getter_with_ref>(hash("w")); 202 | 203 | meta::reflect(hash("an_abstract_type"), std::make_pair(properties::prop_bool, false)) 204 | .data<&an_abstract_type::i>(hash("i")) 205 | .func<&an_abstract_type::f>(hash("f")) 206 | .func<&an_abstract_type::g>(hash("g")); 207 | 208 | meta::reflect(hash("another_abstract_type"), std::make_pair(properties::prop_int, 42)) 209 | .data<&another_abstract_type::j>(hash("j")) 210 | .func<&another_abstract_type::h>(hash("h")); 211 | 212 | meta::reflect(hash("concrete")) 213 | .base() 214 | .base() 215 | .func<&concrete_type::f>(hash("f")); 216 | } 217 | 218 | static void SetUpAfterUnregistration() { 219 | meta::reflect().conv(); 220 | std::hash hash{}; 221 | 222 | meta::reflect(hash("my_type"), std::make_pair(properties::prop_bool, false)) 223 | .ctor<>(); 224 | 225 | meta::reflect(hash("your_type")) 226 | .data<&another_abstract_type::j>(hash("a_data_member")) 227 | .func<&another_abstract_type::h>(hash("a_member_function")); 228 | } 229 | 230 | void SetUp() override { 231 | empty_type::counter = 0; 232 | func_type::value = 0; 233 | } 234 | }; 235 | 236 | TEST_F(Meta, Resolve) { 237 | std::hash hash{}; 238 | ASSERT_EQ(meta::resolve(), meta::resolve(hash("derived"))); 239 | 240 | bool found = false; 241 | 242 | meta::resolve([&found](auto type) { 243 | found = found || type == meta::resolve(); 244 | }); 245 | 246 | ASSERT_TRUE(found); 247 | } 248 | 249 | TEST_F(Meta, MetaAnyFromMetaHandle) { 250 | int value = 42; 251 | meta::handle handle{value}; 252 | meta::any any{handle}; 253 | any.cast() = 3; 254 | 255 | ASSERT_TRUE(any); 256 | ASSERT_EQ(any.type(), meta::resolve()); 257 | ASSERT_EQ(any.try_cast(), nullptr); 258 | ASSERT_EQ(any.try_cast(), handle.data()); 259 | ASSERT_EQ(std::as_const(any).try_cast(), handle.data()); 260 | ASSERT_EQ(any.data(), handle.data()); 261 | ASSERT_EQ(std::as_const(any).data(), handle.data()); 262 | ASSERT_EQ(value, 3); 263 | } 264 | 265 | TEST_F(Meta, MetaAnySBO) { 266 | meta::any any{'c'}; 267 | 268 | ASSERT_TRUE(any); 269 | ASSERT_FALSE(any.try_cast()); 270 | ASSERT_TRUE(any.try_cast()); 271 | ASSERT_EQ(any.cast(), 'c'); 272 | ASSERT_EQ(std::as_const(any).cast(), 'c'); 273 | ASSERT_NE(any.data(), nullptr); 274 | ASSERT_NE(std::as_const(any).data(), nullptr); 275 | ASSERT_EQ(any, meta::any{'c'}); 276 | ASSERT_NE(meta::any{'h'}, any); 277 | } 278 | 279 | TEST_F(Meta, MetaAnyNoSBO) { 280 | int value = 42; 281 | fat_type instance{&value}; 282 | meta::any any{instance}; 283 | 284 | ASSERT_TRUE(any); 285 | ASSERT_FALSE(any.try_cast()); 286 | ASSERT_TRUE(any.try_cast()); 287 | ASSERT_EQ(any.cast(), instance); 288 | ASSERT_EQ(std::as_const(any).cast(), instance); 289 | ASSERT_NE(any.data(), nullptr); 290 | ASSERT_NE(std::as_const(any).data(), nullptr); 291 | ASSERT_EQ(any, meta::any{instance}); 292 | ASSERT_NE(fat_type{}, any); 293 | } 294 | 295 | TEST_F(Meta, MetaAnyEmpty) { 296 | meta::any any{}; 297 | 298 | ASSERT_FALSE(any); 299 | ASSERT_FALSE(any.type()); 300 | ASSERT_FALSE(any.try_cast()); 301 | ASSERT_FALSE(any.try_cast()); 302 | ASSERT_EQ(any.data(), nullptr); 303 | ASSERT_EQ(std::as_const(any).data(), nullptr); 304 | ASSERT_EQ(any, meta::any{}); 305 | ASSERT_NE(meta::any{'c'}, any); 306 | } 307 | 308 | TEST_F(Meta, MetaAnySBOInPlaceTypeConstruction) { 309 | meta::any any{std::in_place_type, 42}; 310 | 311 | ASSERT_TRUE(any); 312 | ASSERT_FALSE(any.try_cast()); 313 | ASSERT_TRUE(any.try_cast()); 314 | ASSERT_EQ(any.cast(), 42); 315 | ASSERT_EQ(std::as_const(any).cast(), 42); 316 | ASSERT_NE(any.data(), nullptr); 317 | ASSERT_NE(std::as_const(any).data(), nullptr); 318 | ASSERT_EQ(any, (meta::any{std::in_place_type, 42})); 319 | ASSERT_EQ(any, meta::any{42}); 320 | ASSERT_NE(meta::any{3}, any); 321 | } 322 | 323 | TEST_F(Meta, MetaAnySBOAsAliasConstruction) { 324 | int value = 3; 325 | int other = 42; 326 | meta::any any{std::ref(value)}; 327 | 328 | ASSERT_TRUE(any); 329 | ASSERT_FALSE(any.try_cast()); 330 | ASSERT_TRUE(any.try_cast()); 331 | ASSERT_EQ(any.cast(), 3); 332 | ASSERT_EQ(std::as_const(any).cast(), 3); 333 | ASSERT_NE(any.data(), nullptr); 334 | ASSERT_NE(std::as_const(any).data(), nullptr); 335 | ASSERT_EQ(any, (meta::any{std::ref(value)})); 336 | ASSERT_NE(any, (meta::any{std::ref(other)})); 337 | ASSERT_NE(any, meta::any{42}); 338 | ASSERT_EQ(meta::any{3}, any); 339 | } 340 | 341 | TEST_F(Meta, MetaAnySBOCopyConstruction) { 342 | meta::any any{42}; 343 | meta::any other{any}; 344 | 345 | ASSERT_TRUE(any); 346 | ASSERT_TRUE(other); 347 | ASSERT_FALSE(other.try_cast()); 348 | ASSERT_TRUE(other.try_cast()); 349 | ASSERT_EQ(other.cast(), 42); 350 | ASSERT_EQ(std::as_const(other).cast(), 42); 351 | ASSERT_EQ(other, meta::any{42}); 352 | ASSERT_NE(other, meta::any{0}); 353 | } 354 | 355 | TEST_F(Meta, MetaAnySBOCopyAssignment) { 356 | meta::any any{42}; 357 | meta::any other{3}; 358 | 359 | other = any; 360 | 361 | ASSERT_TRUE(any); 362 | ASSERT_TRUE(other); 363 | ASSERT_FALSE(other.try_cast()); 364 | ASSERT_TRUE(other.try_cast()); 365 | ASSERT_EQ(other.cast(), 42); 366 | ASSERT_EQ(std::as_const(other).cast(), 42); 367 | ASSERT_EQ(other, meta::any{42}); 368 | ASSERT_NE(other, meta::any{0}); 369 | } 370 | 371 | TEST_F(Meta, MetaAnySBOMoveConstruction) { 372 | meta::any any{42}; 373 | meta::any other{std::move(any)}; 374 | 375 | ASSERT_FALSE(any); 376 | ASSERT_TRUE(other); 377 | ASSERT_FALSE(other.try_cast()); 378 | ASSERT_TRUE(other.try_cast()); 379 | ASSERT_EQ(other.cast(), 42); 380 | ASSERT_EQ(std::as_const(other).cast(), 42); 381 | ASSERT_EQ(other, meta::any{42}); 382 | ASSERT_NE(other, meta::any{0}); 383 | } 384 | 385 | TEST_F(Meta, MetaAnySBOMoveAssignment) { 386 | meta::any any{42}; 387 | meta::any other{3}; 388 | 389 | other = std::move(any); 390 | 391 | ASSERT_FALSE(any); 392 | ASSERT_TRUE(other); 393 | ASSERT_FALSE(other.try_cast()); 394 | ASSERT_TRUE(other.try_cast()); 395 | ASSERT_EQ(other.cast(), 42); 396 | ASSERT_EQ(std::as_const(other).cast(), 42); 397 | ASSERT_EQ(other, meta::any{42}); 398 | ASSERT_NE(other, meta::any{0}); 399 | } 400 | 401 | TEST_F(Meta, MetaAnySBODirectAssignment) { 402 | meta::any any{}; 403 | any = 42; 404 | 405 | ASSERT_FALSE(any.try_cast()); 406 | ASSERT_TRUE(any.try_cast()); 407 | ASSERT_EQ(any.cast(), 42); 408 | ASSERT_EQ(std::as_const(any).cast(), 42); 409 | ASSERT_EQ(any, meta::any{42}); 410 | ASSERT_NE(meta::any{0}, any); 411 | } 412 | 413 | TEST_F(Meta, MetaAnyNoSBOInPlaceTypeConstruction) { 414 | int value = 42; 415 | fat_type instance{&value}; 416 | meta::any any{std::in_place_type, instance}; 417 | 418 | ASSERT_TRUE(any); 419 | ASSERT_FALSE(any.try_cast()); 420 | ASSERT_TRUE(any.try_cast()); 421 | ASSERT_EQ(any.cast(), instance); 422 | ASSERT_EQ(std::as_const(any).cast(), instance); 423 | ASSERT_NE(any.data(), nullptr); 424 | ASSERT_NE(std::as_const(any).data(), nullptr); 425 | ASSERT_EQ(any, (meta::any{std::in_place_type, instance})); 426 | ASSERT_EQ(any, meta::any{instance}); 427 | ASSERT_NE(meta::any{fat_type{}}, any); 428 | } 429 | 430 | TEST_F(Meta, MetaAnyNoSBOAsAliasConstruction) { 431 | int value = 3; 432 | fat_type instance{&value}; 433 | meta::any any{std::ref(instance)}; 434 | 435 | ASSERT_TRUE(any); 436 | ASSERT_FALSE(any.try_cast()); 437 | ASSERT_TRUE(any.try_cast()); 438 | ASSERT_EQ(any.cast(), instance); 439 | ASSERT_EQ(std::as_const(any).cast(), instance); 440 | ASSERT_NE(any.data(), nullptr); 441 | ASSERT_NE(std::as_const(any).data(), nullptr); 442 | ASSERT_EQ(any, (meta::any{std::ref(instance)})); 443 | ASSERT_EQ(any, meta::any{instance}); 444 | ASSERT_NE(meta::any{fat_type{}}, any); 445 | } 446 | 447 | TEST_F(Meta, MetaAnyNoSBOCopyConstruction) { 448 | int value = 42; 449 | fat_type instance{&value}; 450 | meta::any any{instance}; 451 | meta::any other{any}; 452 | 453 | ASSERT_TRUE(any); 454 | ASSERT_TRUE(other); 455 | ASSERT_FALSE(other.try_cast()); 456 | ASSERT_TRUE(other.try_cast()); 457 | ASSERT_EQ(other.cast(), instance); 458 | ASSERT_EQ(std::as_const(other).cast(), instance); 459 | ASSERT_EQ(other, meta::any{instance}); 460 | ASSERT_NE(other, fat_type{}); 461 | } 462 | 463 | TEST_F(Meta, MetaAnyNoSBOCopyAssignment) { 464 | int value = 42; 465 | fat_type instance{&value}; 466 | meta::any any{instance}; 467 | meta::any other{3}; 468 | 469 | other = any; 470 | 471 | ASSERT_TRUE(any); 472 | ASSERT_TRUE(other); 473 | ASSERT_FALSE(other.try_cast()); 474 | ASSERT_TRUE(other.try_cast()); 475 | ASSERT_EQ(other.cast(), instance); 476 | ASSERT_EQ(std::as_const(other).cast(), instance); 477 | ASSERT_EQ(other, meta::any{instance}); 478 | ASSERT_NE(other, fat_type{}); 479 | } 480 | 481 | TEST_F(Meta, MetaAnyNoSBOMoveConstruction) { 482 | int value = 42; 483 | fat_type instance{&value}; 484 | meta::any any{instance}; 485 | meta::any other{std::move(any)}; 486 | 487 | ASSERT_FALSE(any); 488 | ASSERT_TRUE(other); 489 | ASSERT_FALSE(other.try_cast()); 490 | ASSERT_TRUE(other.try_cast()); 491 | ASSERT_EQ(other.cast(), instance); 492 | ASSERT_EQ(std::as_const(other).cast(), instance); 493 | ASSERT_EQ(other, meta::any{instance}); 494 | ASSERT_NE(other, fat_type{}); 495 | } 496 | 497 | TEST_F(Meta, MetaAnyNoSBOMoveAssignment) { 498 | int value = 42; 499 | fat_type instance{&value}; 500 | meta::any any{instance}; 501 | meta::any other{3}; 502 | 503 | other = std::move(any); 504 | 505 | ASSERT_FALSE(any); 506 | ASSERT_TRUE(other); 507 | ASSERT_FALSE(other.try_cast()); 508 | ASSERT_TRUE(other.try_cast()); 509 | ASSERT_EQ(other.cast(), instance); 510 | ASSERT_EQ(std::as_const(other).cast(), instance); 511 | ASSERT_EQ(other, meta::any{instance}); 512 | ASSERT_NE(other, fat_type{}); 513 | } 514 | 515 | TEST_F(Meta, MetaAnyNoSBODirectAssignment) { 516 | int value = 42; 517 | meta::any any{}; 518 | any = fat_type{&value}; 519 | 520 | ASSERT_FALSE(any.try_cast()); 521 | ASSERT_TRUE(any.try_cast()); 522 | ASSERT_EQ(any.cast(), fat_type{&value}); 523 | ASSERT_EQ(std::as_const(any).cast(), fat_type{&value}); 524 | ASSERT_EQ(any, meta::any{fat_type{&value}}); 525 | ASSERT_NE(fat_type{}, any); 526 | } 527 | 528 | TEST_F(Meta, MetaAnyVoidInPlaceTypeConstruction) { 529 | meta::any any{std::in_place_type}; 530 | 531 | ASSERT_TRUE(any); 532 | ASSERT_FALSE(any.try_cast()); 533 | ASSERT_EQ(any.data(), nullptr); 534 | ASSERT_EQ(std::as_const(any).data(), nullptr); 535 | ASSERT_EQ(any.type(), meta::resolve()); 536 | ASSERT_EQ(any, meta::any{std::in_place_type}); 537 | ASSERT_NE(meta::any{3}, any); 538 | } 539 | 540 | TEST_F(Meta, MetaAnyVoidCopyConstruction) { 541 | meta::any any{std::in_place_type}; 542 | meta::any other{any}; 543 | 544 | ASSERT_TRUE(any); 545 | ASSERT_TRUE(other); 546 | ASSERT_EQ(any.type(), meta::resolve()); 547 | ASSERT_EQ(other, meta::any{std::in_place_type}); 548 | } 549 | 550 | TEST_F(Meta, MetaAnyVoidCopyAssignment) { 551 | meta::any any{std::in_place_type}; 552 | meta::any other{std::in_place_type}; 553 | 554 | other = any; 555 | 556 | ASSERT_TRUE(any); 557 | ASSERT_TRUE(other); 558 | ASSERT_EQ(any.type(), meta::resolve()); 559 | ASSERT_EQ(other, meta::any{std::in_place_type}); 560 | } 561 | 562 | TEST_F(Meta, MetaAnyVoidMoveConstruction) { 563 | meta::any any{std::in_place_type}; 564 | meta::any other{std::move(any)}; 565 | 566 | ASSERT_FALSE(any); 567 | ASSERT_TRUE(other); 568 | ASSERT_EQ(other.type(), meta::resolve()); 569 | ASSERT_EQ(other, meta::any{std::in_place_type}); 570 | } 571 | 572 | TEST_F(Meta, MetaAnyVoidMoveAssignment) { 573 | meta::any any{std::in_place_type}; 574 | meta::any other{std::in_place_type}; 575 | 576 | other = std::move(any); 577 | 578 | ASSERT_FALSE(any); 579 | ASSERT_TRUE(other); 580 | ASSERT_EQ(other.type(), meta::resolve()); 581 | ASSERT_EQ(other, meta::any{std::in_place_type}); 582 | } 583 | 584 | TEST_F(Meta, MetaAnySBOMoveInvalidate) { 585 | meta::any any{42}; 586 | meta::any other{std::move(any)}; 587 | meta::any valid = std::move(other); 588 | 589 | ASSERT_FALSE(any); 590 | ASSERT_FALSE(other); 591 | ASSERT_TRUE(valid); 592 | } 593 | 594 | TEST_F(Meta, MetaAnyNoSBOMoveInvalidate) { 595 | int value = 42; 596 | fat_type instance{&value}; 597 | meta::any any{instance}; 598 | meta::any other{std::move(any)}; 599 | meta::any valid = std::move(other); 600 | 601 | ASSERT_FALSE(any); 602 | ASSERT_FALSE(other); 603 | ASSERT_TRUE(valid); 604 | } 605 | 606 | TEST_F(Meta, MetaAnyVoidMoveInvalidate) { 607 | meta::any any{std::in_place_type}; 608 | meta::any other{std::move(any)}; 609 | meta::any valid = std::move(other); 610 | 611 | ASSERT_FALSE(any); 612 | ASSERT_FALSE(other); 613 | ASSERT_TRUE(valid); 614 | } 615 | 616 | TEST_F(Meta, MetaAnySBODestruction) { 617 | ASSERT_EQ(empty_type::counter, 0); 618 | { meta::any any{empty_type{}}; } 619 | ASSERT_EQ(empty_type::counter, 1); 620 | } 621 | 622 | TEST_F(Meta, MetaAnyNoSBODestruction) { 623 | ASSERT_EQ(fat_type::counter, 0); 624 | { meta::any any{fat_type{}}; } 625 | ASSERT_EQ(fat_type::counter, 1); 626 | } 627 | 628 | TEST_F(Meta, MetaAnyVoidDestruction) { 629 | // just let asan tell us if everything is ok here 630 | [[maybe_unused]] meta::any any{std::in_place_type}; 631 | } 632 | 633 | TEST_F(Meta, MetaAnyEmplace) { 634 | meta::any any{}; 635 | any.emplace(42); 636 | 637 | ASSERT_TRUE(any); 638 | ASSERT_FALSE(any.try_cast()); 639 | ASSERT_TRUE(any.try_cast()); 640 | ASSERT_EQ(any.cast(), 42); 641 | ASSERT_EQ(std::as_const(any).cast(), 42); 642 | ASSERT_NE(any.data(), nullptr); 643 | ASSERT_NE(std::as_const(any).data(), nullptr); 644 | ASSERT_EQ(any, (meta::any{std::in_place_type, 42})); 645 | ASSERT_EQ(any, meta::any{42}); 646 | ASSERT_NE(meta::any{3}, any); 647 | } 648 | 649 | TEST_F(Meta, MetaAnyEmplaceVoid) { 650 | meta::any any{}; 651 | any.emplace(); 652 | 653 | ASSERT_TRUE(any); 654 | ASSERT_EQ(any.data(), nullptr); 655 | ASSERT_EQ(std::as_const(any).data(), nullptr); 656 | ASSERT_EQ(any.type(), meta::resolve()); 657 | ASSERT_EQ(any, (meta::any{std::in_place_type})); 658 | } 659 | 660 | TEST_F(Meta, MetaAnySBOSwap) { 661 | meta::any lhs{'c'}; 662 | meta::any rhs{42}; 663 | 664 | std::swap(lhs, rhs); 665 | 666 | ASSERT_TRUE(lhs.try_cast()); 667 | ASSERT_EQ(lhs.cast(), 42); 668 | ASSERT_TRUE(rhs.try_cast()); 669 | ASSERT_EQ(rhs.cast(), 'c'); 670 | } 671 | 672 | TEST_F(Meta, MetaAnyNoSBOSwap) { 673 | int i, j; 674 | meta::any lhs{fat_type{&i}}; 675 | meta::any rhs{fat_type{&j}}; 676 | 677 | std::swap(lhs, rhs); 678 | 679 | ASSERT_EQ(lhs.cast().foo, &j); 680 | ASSERT_EQ(rhs.cast().bar, &i); 681 | } 682 | 683 | TEST_F(Meta, MetaAnyVoidSwap) { 684 | meta::any lhs{std::in_place_type}; 685 | meta::any rhs{std::in_place_type}; 686 | const auto *pre = lhs.data(); 687 | 688 | std::swap(lhs, rhs); 689 | 690 | ASSERT_EQ(pre, lhs.data()); 691 | } 692 | 693 | TEST_F(Meta, MetaAnySBOWithNoSBOSwap) { 694 | int value = 42; 695 | meta::any lhs{fat_type{&value}}; 696 | meta::any rhs{'c'}; 697 | 698 | std::swap(lhs, rhs); 699 | 700 | ASSERT_TRUE(lhs.try_cast()); 701 | ASSERT_EQ(lhs.cast(), 'c'); 702 | ASSERT_TRUE(rhs.try_cast()); 703 | ASSERT_EQ(rhs.cast().foo, &value); 704 | ASSERT_EQ(rhs.cast().bar, &value); 705 | } 706 | 707 | TEST_F(Meta, MetaAnySBOWithEmptySwap) { 708 | meta::any lhs{'c'}; 709 | meta::any rhs{}; 710 | 711 | std::swap(lhs, rhs); 712 | 713 | ASSERT_FALSE(lhs); 714 | ASSERT_TRUE(rhs.try_cast()); 715 | ASSERT_EQ(rhs.cast(), 'c'); 716 | 717 | std::swap(lhs, rhs); 718 | 719 | ASSERT_FALSE(rhs); 720 | ASSERT_TRUE(lhs.try_cast()); 721 | ASSERT_EQ(lhs.cast(), 'c'); 722 | } 723 | 724 | TEST_F(Meta, MetaAnySBOWithVoidSwap) { 725 | meta::any lhs{'c'}; 726 | meta::any rhs{std::in_place_type}; 727 | 728 | std::swap(lhs, rhs); 729 | 730 | ASSERT_EQ(lhs.type(), meta::resolve()); 731 | ASSERT_TRUE(rhs.try_cast()); 732 | ASSERT_EQ(rhs.cast(), 'c'); 733 | } 734 | 735 | TEST_F(Meta, MetaAnyNoSBOWithEmptySwap) { 736 | int i; 737 | meta::any lhs{fat_type{&i}}; 738 | meta::any rhs{}; 739 | 740 | std::swap(lhs, rhs); 741 | 742 | ASSERT_EQ(rhs.cast().bar, &i); 743 | 744 | std::swap(lhs, rhs); 745 | 746 | ASSERT_EQ(lhs.cast().bar, &i); 747 | } 748 | 749 | TEST_F(Meta, MetaAnyNoSBOWithVoidSwap) { 750 | int i; 751 | meta::any lhs{fat_type{&i}}; 752 | meta::any rhs{std::in_place_type}; 753 | 754 | std::swap(lhs, rhs); 755 | 756 | ASSERT_EQ(rhs.cast().bar, &i); 757 | 758 | std::swap(lhs, rhs); 759 | 760 | ASSERT_EQ(lhs.cast().bar, &i); 761 | } 762 | 763 | TEST_F(Meta, MetaAnyComparable) { 764 | meta::any any{'c'}; 765 | 766 | ASSERT_EQ(any, any); 767 | ASSERT_EQ(any, meta::any{'c'}); 768 | ASSERT_NE(meta::any{'a'}, any); 769 | ASSERT_NE(any, meta::any{}); 770 | 771 | ASSERT_TRUE(any == any); 772 | ASSERT_TRUE(any == meta::any{'c'}); 773 | ASSERT_FALSE(any == meta::any{'a'}); 774 | ASSERT_TRUE(any != meta::any{'a'}); 775 | ASSERT_TRUE(any != meta::any{}); 776 | } 777 | 778 | TEST_F(Meta, MetaAnyNotComparable) { 779 | meta::any any{not_comparable_type{}}; 780 | 781 | ASSERT_EQ(any, any); 782 | ASSERT_NE(any, meta::any{not_comparable_type{}}); 783 | ASSERT_NE(meta::any{}, any); 784 | 785 | ASSERT_TRUE(any == any); 786 | ASSERT_FALSE(any == meta::any{not_comparable_type{}}); 787 | ASSERT_TRUE(any != meta::any{}); 788 | } 789 | 790 | TEST_F(Meta, MetaAnyCompareVoid) { 791 | meta::any any{std::in_place_type}; 792 | 793 | ASSERT_EQ(any, any); 794 | ASSERT_EQ(any, meta::any{std::in_place_type}); 795 | ASSERT_NE(meta::any{'a'}, any); 796 | ASSERT_NE(any, meta::any{}); 797 | 798 | ASSERT_TRUE(any == any); 799 | ASSERT_TRUE(any == meta::any{std::in_place_type}); 800 | ASSERT_FALSE(any == meta::any{'a'}); 801 | ASSERT_TRUE(any != meta::any{'a'}); 802 | ASSERT_TRUE(any != meta::any{}); 803 | } 804 | 805 | TEST_F(Meta, MetaAnyTryCast) { 806 | meta::any any{derived_type{}}; 807 | 808 | ASSERT_TRUE(any); 809 | ASSERT_EQ(any.type(), meta::resolve()); 810 | ASSERT_EQ(any.try_cast(), nullptr); 811 | ASSERT_NE(any.try_cast(), nullptr); 812 | ASSERT_EQ(any.try_cast(), any.data()); 813 | ASSERT_EQ(std::as_const(any).try_cast(), any.try_cast()); 814 | ASSERT_EQ(std::as_const(any).try_cast(), any.data()); 815 | } 816 | 817 | TEST_F(Meta, MetaAnyCast) { 818 | meta::any any{derived_type{}}; 819 | 820 | ASSERT_TRUE(any); 821 | ASSERT_EQ(any.type(), meta::resolve()); 822 | ASSERT_EQ(any.try_cast(), nullptr); 823 | ASSERT_NE(any.try_cast(), nullptr); 824 | ASSERT_EQ(any.try_cast(), any.data()); 825 | ASSERT_EQ(std::as_const(any).try_cast(), any.try_cast()); 826 | ASSERT_EQ(std::as_const(any).try_cast(), any.data()); 827 | } 828 | 829 | TEST_F(Meta, MetaAnyConvert) { 830 | meta::any any{42.}; 831 | 832 | ASSERT_TRUE(any); 833 | ASSERT_EQ(any.type(), meta::resolve()); 834 | ASSERT_TRUE(any.convert()); 835 | ASSERT_FALSE(any.convert()); 836 | ASSERT_EQ(any.type(), meta::resolve()); 837 | ASSERT_EQ(any.cast(), 42.); 838 | ASSERT_TRUE(any.convert()); 839 | ASSERT_EQ(any.type(), meta::resolve()); 840 | ASSERT_EQ(any.cast(), 42); 841 | } 842 | 843 | TEST_F(Meta, MetaAnyConstConvert) { 844 | const meta::any any{42.}; 845 | 846 | ASSERT_TRUE(any); 847 | ASSERT_EQ(any.type(), meta::resolve()); 848 | ASSERT_TRUE(any.convert()); 849 | ASSERT_FALSE(any.convert()); 850 | ASSERT_EQ(any.type(), meta::resolve()); 851 | ASSERT_EQ(any.cast(), 42.); 852 | 853 | auto other = any.convert(); 854 | 855 | ASSERT_EQ(any.type(), meta::resolve()); 856 | ASSERT_EQ(any.cast(), 42.); 857 | ASSERT_EQ(other.type(), meta::resolve()); 858 | ASSERT_EQ(other.cast(), 42); 859 | } 860 | 861 | TEST_F(Meta, MetaHandleFromObject) { 862 | empty_type empty{}; 863 | meta::handle handle{empty}; 864 | 865 | ASSERT_TRUE(handle); 866 | ASSERT_EQ(handle.type(), meta::resolve()); 867 | ASSERT_EQ(std::as_const(handle).data(), &empty); 868 | ASSERT_EQ(handle.data(), &empty); 869 | } 870 | 871 | TEST_F(Meta, MetaHandleFromMetaAny) { 872 | meta::any any{42}; 873 | meta::handle handle{any}; 874 | 875 | ASSERT_TRUE(handle); 876 | ASSERT_EQ(handle.type(), meta::resolve()); 877 | ASSERT_EQ(std::as_const(handle).data(), any.data()); 878 | ASSERT_EQ(handle.data(), any.data()); 879 | } 880 | 881 | TEST_F(Meta, MetaHandleEmpty) { 882 | meta::handle handle{}; 883 | 884 | ASSERT_FALSE(handle); 885 | ASSERT_FALSE(handle.type()); 886 | ASSERT_EQ(std::as_const(handle).data(), nullptr); 887 | ASSERT_EQ(handle.data(), nullptr); 888 | } 889 | 890 | TEST_F(Meta, MetaProp) { 891 | auto prop = meta::resolve().prop(properties::prop_int); 892 | 893 | ASSERT_TRUE(prop); 894 | ASSERT_NE(prop, meta::prop{}); 895 | ASSERT_EQ(prop.key(), properties::prop_int); 896 | ASSERT_EQ(prop.value(), 42); 897 | } 898 | 899 | TEST_F(Meta, MetaBase) { 900 | std::hash hash{}; 901 | auto base = meta::resolve().base(hash("base")); 902 | derived_type derived{}; 903 | 904 | ASSERT_TRUE(base); 905 | ASSERT_NE(base, meta::base{}); 906 | ASSERT_EQ(base.parent(), meta::resolve(hash("derived"))); 907 | ASSERT_EQ(base.type(), meta::resolve()); 908 | ASSERT_EQ(base.cast(&derived), static_cast(&derived)); 909 | } 910 | 911 | TEST_F(Meta, MetaConv) { 912 | auto conv = meta::resolve().conv(); 913 | double value = 3.; 914 | 915 | ASSERT_TRUE(conv); 916 | ASSERT_NE(conv, meta::conv{}); 917 | ASSERT_EQ(conv.parent(), meta::resolve()); 918 | ASSERT_EQ(conv.type(), meta::resolve()); 919 | 920 | auto any = conv.convert(&value); 921 | 922 | ASSERT_TRUE(any); 923 | ASSERT_EQ(any.type(), meta::resolve()); 924 | ASSERT_EQ(any.cast(), 3); 925 | } 926 | 927 | TEST_F(Meta, MetaConvAsFreeFunctions) { 928 | auto conv = meta::resolve().conv(); 929 | derived_type derived{derived_type{}, 42, 'c'}; 930 | 931 | ASSERT_TRUE(conv); 932 | ASSERT_NE(conv, meta::conv{}); 933 | ASSERT_EQ(conv.parent(), meta::resolve()); 934 | ASSERT_EQ(conv.type(), meta::resolve()); 935 | 936 | auto any = conv.convert(&derived); 937 | 938 | ASSERT_TRUE(any); 939 | ASSERT_EQ(any.type(), meta::resolve()); 940 | ASSERT_EQ(any.cast(), 42); 941 | } 942 | 943 | TEST_F(Meta, MetaConvAsMemberFunctions) { 944 | auto conv = meta::resolve().conv(); 945 | derived_type derived{derived_type{}, 42, 'c'}; 946 | 947 | ASSERT_TRUE(conv); 948 | ASSERT_NE(conv, meta::conv{}); 949 | ASSERT_EQ(conv.parent(), meta::resolve()); 950 | ASSERT_EQ(conv.type(), meta::resolve()); 951 | 952 | auto any = conv.convert(&derived); 953 | 954 | ASSERT_TRUE(any); 955 | ASSERT_EQ(any.type(), meta::resolve()); 956 | ASSERT_EQ(any.cast(), 'c'); 957 | } 958 | 959 | TEST_F(Meta, MetaCtor) { 960 | auto ctor = meta::resolve().ctor(); 961 | std::hash hash{}; 962 | 963 | ASSERT_TRUE(ctor); 964 | ASSERT_NE(ctor, meta::ctor{}); 965 | ASSERT_EQ(ctor.parent(), meta::resolve(hash("derived"))); 966 | ASSERT_EQ(ctor.size(), meta::ctor::size_type{3}); 967 | ASSERT_EQ(ctor.arg(meta::ctor::size_type{0}), meta::resolve()); 968 | ASSERT_EQ(ctor.arg(meta::ctor::size_type{1}), meta::resolve()); 969 | ASSERT_EQ(ctor.arg(meta::ctor::size_type{2}), meta::resolve()); 970 | ASSERT_FALSE(ctor.arg(meta::ctor::size_type{3})); 971 | 972 | auto any = ctor.invoke(base_type{}, 42, 'c'); 973 | auto empty = ctor.invoke(); 974 | 975 | ASSERT_FALSE(empty); 976 | ASSERT_TRUE(any); 977 | ASSERT_TRUE(any.try_cast()); 978 | ASSERT_EQ(any.cast().i, 42); 979 | ASSERT_EQ(any.cast().c, 'c'); 980 | 981 | ctor.prop([](auto prop) { 982 | ASSERT_TRUE(prop); 983 | ASSERT_EQ(prop.key(), properties::prop_bool); 984 | ASSERT_FALSE(prop.value().template cast()); 985 | }); 986 | 987 | ASSERT_FALSE(ctor.prop(properties::prop_int)); 988 | 989 | auto prop = ctor.prop(properties::prop_bool); 990 | 991 | ASSERT_TRUE(prop); 992 | ASSERT_EQ(prop.key(), properties::prop_bool); 993 | ASSERT_FALSE(prop.value().template cast()); 994 | } 995 | 996 | TEST_F(Meta, MetaCtorFunc) { 997 | auto ctor = meta::resolve().ctor(); 998 | std::hash hash{}; 999 | 1000 | ASSERT_TRUE(ctor); 1001 | ASSERT_EQ(ctor.parent(), meta::resolve(hash("derived"))); 1002 | ASSERT_EQ(ctor.size(), meta::ctor::size_type{2}); 1003 | ASSERT_EQ(ctor.arg(meta::ctor::size_type{0}), meta::resolve()); 1004 | ASSERT_EQ(ctor.arg(meta::ctor::size_type{1}), meta::resolve()); 1005 | ASSERT_FALSE(ctor.arg(meta::ctor::size_type{2})); 1006 | 1007 | auto any = ctor.invoke(derived_type{}, 42); 1008 | auto empty = ctor.invoke(3, 'c'); 1009 | 1010 | ASSERT_FALSE(empty); 1011 | ASSERT_TRUE(any); 1012 | ASSERT_TRUE(any.try_cast()); 1013 | ASSERT_EQ(any.cast().i, 42); 1014 | ASSERT_EQ(any.cast().c, 'c'); 1015 | 1016 | ctor.prop([](auto prop) { 1017 | ASSERT_TRUE(prop); 1018 | ASSERT_EQ(prop.key(), properties::prop_int); 1019 | ASSERT_EQ(prop.value(), 42); 1020 | }); 1021 | 1022 | ASSERT_FALSE(ctor.prop(properties::prop_bool)); 1023 | 1024 | auto prop = ctor.prop(properties::prop_int); 1025 | 1026 | ASSERT_TRUE(prop); 1027 | ASSERT_EQ(prop.key(), properties::prop_int); 1028 | ASSERT_EQ(prop.value(), 42); 1029 | } 1030 | 1031 | TEST_F(Meta, MetaCtorMetaAnyArgs) { 1032 | auto ctor = meta::resolve().ctor(); 1033 | auto any = ctor.invoke(base_type{}, meta::any{42}, meta::any{'c'}); 1034 | 1035 | ASSERT_TRUE(any); 1036 | ASSERT_TRUE(any.try_cast()); 1037 | ASSERT_EQ(any.cast().i, 42); 1038 | ASSERT_EQ(any.cast().c, 'c'); 1039 | } 1040 | 1041 | TEST_F(Meta, MetaCtorInvalidArgs) { 1042 | auto ctor = meta::resolve().ctor(); 1043 | ASSERT_FALSE(ctor.invoke(base_type{}, meta::any{'c'}, meta::any{42})); 1044 | } 1045 | 1046 | TEST_F(Meta, MetaCtorCastAndConvert) { 1047 | auto ctor = meta::resolve().ctor(); 1048 | auto any = ctor.invoke(meta::any{derived_type{}}, meta::any{42.}, meta::any{'c'}); 1049 | 1050 | ASSERT_TRUE(any); 1051 | ASSERT_TRUE(any.try_cast()); 1052 | ASSERT_EQ(any.cast().i, 42); 1053 | ASSERT_EQ(any.cast().c, 'c'); 1054 | } 1055 | 1056 | TEST_F(Meta, MetaCtorFuncMetaAnyArgs) { 1057 | auto ctor = meta::resolve().ctor(); 1058 | auto any = ctor.invoke(base_type{}, meta::any{42}); 1059 | 1060 | ASSERT_TRUE(any); 1061 | ASSERT_TRUE(any.try_cast()); 1062 | ASSERT_EQ(any.cast().i, 42); 1063 | ASSERT_EQ(any.cast().c, 'c'); 1064 | } 1065 | 1066 | TEST_F(Meta, MetaCtorFuncInvalidArgs) { 1067 | auto ctor = meta::resolve().ctor(); 1068 | ASSERT_FALSE(ctor.invoke(base_type{}, meta::any{'c'})); 1069 | } 1070 | 1071 | TEST_F(Meta, MetaCtorFuncCastAndConvert) { 1072 | auto ctor = meta::resolve().ctor(); 1073 | auto any = ctor.invoke(meta::any{derived_type{}}, meta::any{42.}); 1074 | 1075 | ASSERT_TRUE(any); 1076 | ASSERT_TRUE(any.try_cast()); 1077 | ASSERT_EQ(any.cast().i, 42); 1078 | ASSERT_EQ(any.cast().c, 'c'); 1079 | } 1080 | 1081 | TEST_F(Meta, MetaDtor) { 1082 | std::hash hash{}; 1083 | auto dtor = meta::resolve().dtor(); 1084 | empty_type empty{}; 1085 | 1086 | ASSERT_TRUE(dtor); 1087 | ASSERT_NE(dtor, meta::dtor{}); 1088 | ASSERT_EQ(dtor.parent(), meta::resolve(hash("empty"))); 1089 | ASSERT_EQ(empty_type::counter, 0); 1090 | ASSERT_TRUE(dtor.invoke(empty)); 1091 | ASSERT_EQ(empty_type::counter, 1); 1092 | } 1093 | 1094 | TEST_F(Meta, MetaDtorMetaAnyArg) { 1095 | auto dtor = meta::resolve().dtor(); 1096 | meta::any any{empty_type{}}; 1097 | 1098 | ASSERT_EQ(empty_type::counter, 0); 1099 | ASSERT_TRUE(dtor.invoke(any)); 1100 | ASSERT_EQ(empty_type::counter, 1); 1101 | } 1102 | 1103 | TEST_F(Meta, MetaDtorMetaAnyInvalidArg) { 1104 | auto instance = 0; 1105 | ASSERT_FALSE(meta::resolve().dtor().invoke(instance)); 1106 | } 1107 | 1108 | 1109 | TEST_F(Meta, MetaData) { 1110 | std::hash hash{}; 1111 | auto data = meta::resolve().data(hash("i")); 1112 | data_type instance{}; 1113 | 1114 | ASSERT_TRUE(data); 1115 | ASSERT_NE(data, meta::data{}); 1116 | ASSERT_EQ(data.parent(), meta::resolve(hash("data"))); 1117 | ASSERT_EQ(data.type(), meta::resolve()); 1118 | ASSERT_FALSE(data.is_const()); 1119 | ASSERT_FALSE(data.is_static()); 1120 | ASSERT_EQ(data.get(instance).cast(), 0); 1121 | ASSERT_TRUE(data.set(instance, 42)); 1122 | ASSERT_EQ(data.get(instance).cast(), 42); 1123 | 1124 | data.prop([](auto prop) { 1125 | ASSERT_TRUE(prop); 1126 | ASSERT_EQ(prop.key(), properties::prop_int); 1127 | ASSERT_EQ(prop.value(), 0); 1128 | }); 1129 | 1130 | ASSERT_FALSE(data.prop(properties::prop_bool)); 1131 | 1132 | auto prop = data.prop(properties::prop_int); 1133 | 1134 | ASSERT_TRUE(prop); 1135 | ASSERT_EQ(prop.key(), properties::prop_int); 1136 | ASSERT_EQ(prop.value(), 0); 1137 | } 1138 | 1139 | TEST_F(Meta, MetaDataConst) { 1140 | std::hash hash{}; 1141 | auto data = meta::resolve().data(hash("j")); 1142 | data_type instance{}; 1143 | 1144 | ASSERT_TRUE(data); 1145 | ASSERT_EQ(data.parent(), meta::resolve(hash("data"))); 1146 | ASSERT_EQ(data.type(), meta::resolve()); 1147 | ASSERT_TRUE(data.is_const()); 1148 | ASSERT_FALSE(data.is_static()); 1149 | ASSERT_EQ(data.get(instance).cast(), 1); 1150 | ASSERT_FALSE(data.set(instance, 42)); 1151 | ASSERT_EQ(data.get(instance).cast(), 1); 1152 | 1153 | data.prop([](auto prop) { 1154 | ASSERT_TRUE(prop); 1155 | ASSERT_EQ(prop.key(), properties::prop_int); 1156 | ASSERT_EQ(prop.value(), 1); 1157 | }); 1158 | 1159 | ASSERT_FALSE(data.prop(properties::prop_bool)); 1160 | 1161 | auto prop = data.prop(properties::prop_int); 1162 | 1163 | ASSERT_TRUE(prop); 1164 | ASSERT_EQ(prop.key(), properties::prop_int); 1165 | ASSERT_EQ(prop.value(), 1); 1166 | } 1167 | 1168 | TEST_F(Meta, MetaDataStatic) { 1169 | std::hash hash{}; 1170 | auto data = meta::resolve().data(hash("h")); 1171 | 1172 | ASSERT_TRUE(data); 1173 | ASSERT_EQ(data.parent(), meta::resolve(hash("data"))); 1174 | ASSERT_EQ(data.type(), meta::resolve()); 1175 | ASSERT_FALSE(data.is_const()); 1176 | ASSERT_TRUE(data.is_static()); 1177 | ASSERT_EQ(data.get({}).cast(), 2); 1178 | ASSERT_TRUE(data.set({}, 42)); 1179 | ASSERT_EQ(data.get({}).cast(), 42); 1180 | 1181 | data.prop([](auto prop) { 1182 | ASSERT_TRUE(prop); 1183 | ASSERT_EQ(prop.key(), properties::prop_int); 1184 | ASSERT_EQ(prop.value(), 2); 1185 | }); 1186 | 1187 | ASSERT_FALSE(data.prop(properties::prop_bool)); 1188 | 1189 | auto prop = data.prop(properties::prop_int); 1190 | 1191 | ASSERT_TRUE(prop); 1192 | ASSERT_EQ(prop.key(), properties::prop_int); 1193 | ASSERT_EQ(prop.value(), 2); 1194 | } 1195 | 1196 | TEST_F(Meta, MetaDataConstStatic) { 1197 | std::hash hash{}; 1198 | auto data = meta::resolve().data(hash("k")); 1199 | 1200 | ASSERT_TRUE(data); 1201 | ASSERT_EQ(data.parent(), meta::resolve(hash("data"))); 1202 | ASSERT_EQ(data.type(), meta::resolve()); 1203 | ASSERT_TRUE(data.is_const()); 1204 | ASSERT_TRUE(data.is_static()); 1205 | ASSERT_EQ(data.get({}).cast(), 3); 1206 | ASSERT_FALSE(data.set({}, 42)); 1207 | ASSERT_EQ(data.get({}).cast(), 3); 1208 | 1209 | data.prop([](auto prop) { 1210 | ASSERT_TRUE(prop); 1211 | ASSERT_EQ(prop.key(), properties::prop_int); 1212 | ASSERT_EQ(prop.value(), 3); 1213 | }); 1214 | 1215 | ASSERT_FALSE(data.prop(properties::prop_bool)); 1216 | 1217 | auto prop = data.prop(properties::prop_int); 1218 | 1219 | ASSERT_TRUE(prop); 1220 | ASSERT_EQ(prop.key(), properties::prop_int); 1221 | ASSERT_EQ(prop.value(), 3); 1222 | } 1223 | 1224 | TEST_F(Meta, MetaDataGetMetaAnyArg) { 1225 | std::hash hash{}; 1226 | auto data = meta::resolve().data(hash("i")); 1227 | meta::any any{data_type{}}; 1228 | any.cast().i = 99; 1229 | const auto value = data.get(any); 1230 | 1231 | ASSERT_TRUE(value); 1232 | ASSERT_TRUE(value.cast()); 1233 | ASSERT_EQ(value.cast(), 99); 1234 | } 1235 | 1236 | TEST_F(Meta, MetaDataGetInvalidArg) { 1237 | std::hash hash{}; 1238 | auto instance = 0; 1239 | ASSERT_FALSE(meta::resolve().data(hash("i")).get(instance)); 1240 | } 1241 | 1242 | TEST_F(Meta, MetaDataSetMetaAnyArg) { 1243 | std::hash hash{}; 1244 | auto data = meta::resolve().data(hash("i")); 1245 | meta::any any{data_type{}}; 1246 | meta::any value{42}; 1247 | 1248 | ASSERT_EQ(any.cast().i, 0); 1249 | ASSERT_TRUE(data.set(any, value)); 1250 | ASSERT_EQ(any.cast().i, 42); 1251 | } 1252 | 1253 | TEST_F(Meta, MetaDataSetInvalidArg) { 1254 | std::hash hash{}; 1255 | ASSERT_FALSE(meta::resolve().data(hash("i")).set({}, 'c')); 1256 | } 1257 | 1258 | TEST_F(Meta, MetaDataSetCast) { 1259 | std::hash hash{}; 1260 | auto data = meta::resolve().data(hash("empty")); 1261 | data_type instance{}; 1262 | 1263 | ASSERT_EQ(empty_type::counter, 0); 1264 | ASSERT_TRUE(data.set(instance, fat_type{})); 1265 | ASSERT_EQ(empty_type::counter, 1); 1266 | } 1267 | 1268 | TEST_F(Meta, MetaDataSetConvert) { 1269 | std::hash hash{}; 1270 | auto data = meta::resolve().data(hash("i")); 1271 | data_type instance{}; 1272 | 1273 | ASSERT_EQ(instance.i, 0); 1274 | ASSERT_TRUE(data.set(instance, 3.)); 1275 | ASSERT_EQ(instance.i, 3); 1276 | } 1277 | 1278 | TEST_F(Meta, MetaDataSetterGetterAsFreeFunctions) { 1279 | std::hash hash{}; 1280 | auto data = meta::resolve().data(hash("x")); 1281 | setter_getter_type instance{}; 1282 | 1283 | ASSERT_TRUE(data); 1284 | ASSERT_NE(data, meta::data{}); 1285 | ASSERT_EQ(data.parent(), meta::resolve(hash("setter_getter"))); 1286 | ASSERT_EQ(data.type(), meta::resolve()); 1287 | ASSERT_FALSE(data.is_const()); 1288 | ASSERT_FALSE(data.is_static()); 1289 | ASSERT_EQ(data.get(instance).cast(), 0); 1290 | ASSERT_TRUE(data.set(instance, 42)); 1291 | ASSERT_EQ(data.get(instance).cast(), 42); 1292 | } 1293 | 1294 | TEST_F(Meta, MetaDataSetterGetterAsMemberFunctions) { 1295 | std::hash hash{}; 1296 | auto data = meta::resolve().data(hash("y")); 1297 | setter_getter_type instance{}; 1298 | 1299 | ASSERT_TRUE(data); 1300 | ASSERT_NE(data, meta::data{}); 1301 | ASSERT_EQ(data.parent(), meta::resolve(hash("setter_getter"))); 1302 | ASSERT_EQ(data.type(), meta::resolve()); 1303 | ASSERT_FALSE(data.is_const()); 1304 | ASSERT_FALSE(data.is_static()); 1305 | ASSERT_EQ(data.get(instance).cast(), 0); 1306 | ASSERT_TRUE(data.set(instance, 42)); 1307 | ASSERT_EQ(data.get(instance).cast(), 42); 1308 | } 1309 | 1310 | TEST_F(Meta, MetaDataSetterGetterWithRefAsMemberFunctions) { 1311 | std::hash hash{}; 1312 | auto data = meta::resolve().data(hash("w")); 1313 | setter_getter_type instance{}; 1314 | 1315 | ASSERT_TRUE(data); 1316 | ASSERT_NE(data, meta::data{}); 1317 | ASSERT_EQ(data.parent(), meta::resolve(hash("setter_getter"))); 1318 | ASSERT_EQ(data.type(), meta::resolve()); 1319 | ASSERT_FALSE(data.is_const()); 1320 | ASSERT_FALSE(data.is_static()); 1321 | ASSERT_EQ(data.get(instance).cast(), 0); 1322 | ASSERT_TRUE(data.set(instance, 42)); 1323 | ASSERT_EQ(data.get(instance).cast(), 42); 1324 | } 1325 | 1326 | TEST_F(Meta, MetaDataSetterGetterMixed) { 1327 | std::hash hash{}; 1328 | auto data = meta::resolve().data(hash("z")); 1329 | setter_getter_type instance{}; 1330 | 1331 | ASSERT_TRUE(data); 1332 | ASSERT_NE(data, meta::data{}); 1333 | ASSERT_EQ(data.parent(), meta::resolve(hash("setter_getter"))); 1334 | ASSERT_EQ(data.type(), meta::resolve()); 1335 | ASSERT_FALSE(data.is_const()); 1336 | ASSERT_FALSE(data.is_static()); 1337 | ASSERT_EQ(data.get(instance).cast(), 0); 1338 | ASSERT_TRUE(data.set(instance, 42)); 1339 | ASSERT_EQ(data.get(instance).cast(), 42); 1340 | } 1341 | 1342 | TEST_F(Meta, MetaDataArrayStatic) { 1343 | std::hash hash{}; 1344 | auto data = meta::resolve().data(hash("global")); 1345 | 1346 | array_type::global[0] = 3; 1347 | array_type::global[1] = 5; 1348 | array_type::global[2] = 7; 1349 | 1350 | ASSERT_TRUE(data); 1351 | ASSERT_NE(data, meta::data{}); 1352 | ASSERT_EQ(data.parent(), meta::resolve(hash("array"))); 1353 | ASSERT_EQ(data.type(), meta::resolve()); 1354 | ASSERT_FALSE(data.is_const()); 1355 | ASSERT_TRUE(data.is_static()); 1356 | ASSERT_TRUE(data.type().is_array()); 1357 | ASSERT_EQ(data.type().extent(), 3); 1358 | ASSERT_EQ(data.get({}, 0).cast(), 3); 1359 | ASSERT_EQ(data.get({}, 1).cast(), 5); 1360 | ASSERT_EQ(data.get({}, 2).cast(), 7); 1361 | ASSERT_FALSE(data.set({}, 0, 'c')); 1362 | ASSERT_EQ(data.get({}, 0).cast(), 3); 1363 | ASSERT_TRUE(data.set({}, 0, data.get({}, 0).cast()+2)); 1364 | ASSERT_TRUE(data.set({}, 1, data.get({}, 1).cast()+2)); 1365 | ASSERT_TRUE(data.set({}, 2, data.get({}, 2).cast()+2)); 1366 | ASSERT_EQ(data.get({}, 0).cast(), 5); 1367 | ASSERT_EQ(data.get({}, 1).cast(), 7); 1368 | ASSERT_EQ(data.get({}, 2).cast(), 9); 1369 | } 1370 | 1371 | TEST_F(Meta, MetaDataArray) { 1372 | std::hash hash{}; 1373 | auto data = meta::resolve().data(hash("local")); 1374 | array_type instance; 1375 | 1376 | instance.local[0] = 3; 1377 | instance.local[1] = 5; 1378 | instance.local[2] = 7; 1379 | 1380 | ASSERT_TRUE(data); 1381 | ASSERT_NE(data, meta::data{}); 1382 | ASSERT_EQ(data.parent(), meta::resolve(hash("array"))); 1383 | ASSERT_EQ(data.type(), meta::resolve()); 1384 | ASSERT_FALSE(data.is_const()); 1385 | ASSERT_FALSE(data.is_static()); 1386 | ASSERT_TRUE(data.type().is_array()); 1387 | ASSERT_EQ(data.type().extent(), 3); 1388 | ASSERT_EQ(data.get(instance, 0).cast(), 3); 1389 | ASSERT_EQ(data.get(instance, 1).cast(), 5); 1390 | ASSERT_EQ(data.get(instance, 2).cast(), 7); 1391 | ASSERT_FALSE(data.set(instance, 0, 'c')); 1392 | ASSERT_EQ(data.get(instance, 0).cast(), 3); 1393 | ASSERT_TRUE(data.set(instance, 0, data.get(instance, 0).cast()+2)); 1394 | ASSERT_TRUE(data.set(instance, 1, data.get(instance, 1).cast()+2)); 1395 | ASSERT_TRUE(data.set(instance, 2, data.get(instance, 2).cast()+2)); 1396 | ASSERT_EQ(data.get(instance, 0).cast(), 5); 1397 | ASSERT_EQ(data.get(instance, 1).cast(), 7); 1398 | ASSERT_EQ(data.get(instance, 2).cast(), 9); 1399 | } 1400 | 1401 | TEST_F(Meta, MetaDataAsVoid) { 1402 | std::hash hash{}; 1403 | auto data = meta::resolve().data(hash("v")); 1404 | data_type instance{}; 1405 | 1406 | ASSERT_TRUE(data.set(instance, 42)); 1407 | ASSERT_EQ(instance.v, 42); 1408 | ASSERT_EQ(data.get(instance), meta::any{std::in_place_type}); 1409 | } 1410 | 1411 | TEST_F(Meta, MetaDataAsAlias) { 1412 | std::hash hash{}; 1413 | data_type instance{}; 1414 | auto h_data = meta::resolve().data(hash("h")); 1415 | auto i_data = meta::resolve().data(hash("i")); 1416 | 1417 | h_data.get(instance).cast() = 3; 1418 | i_data.get(instance).cast() = 3; 1419 | 1420 | ASSERT_EQ(h_data.type(), meta::resolve()); 1421 | ASSERT_EQ(i_data.type(), meta::resolve()); 1422 | ASSERT_NE(instance.h, 3); 1423 | ASSERT_EQ(instance.i, 3); 1424 | } 1425 | 1426 | TEST_F(Meta, MetaFunc) { 1427 | std::hash hash{}; 1428 | auto func = meta::resolve().func(hash("f2")); 1429 | func_type instance{}; 1430 | 1431 | ASSERT_TRUE(func); 1432 | ASSERT_NE(func, meta::func{}); 1433 | ASSERT_EQ(func.parent(), meta::resolve(hash("func"))); 1434 | ASSERT_EQ(func.size(), meta::func::size_type{2}); 1435 | ASSERT_FALSE(func.is_const()); 1436 | ASSERT_FALSE(func.is_static()); 1437 | ASSERT_EQ(func.ret(), meta::resolve()); 1438 | ASSERT_EQ(func.arg(meta::func::size_type{0}), meta::resolve()); 1439 | ASSERT_EQ(func.arg(meta::func::size_type{1}), meta::resolve()); 1440 | ASSERT_FALSE(func.arg(meta::func::size_type{2})); 1441 | 1442 | auto any = func.invoke(instance, 3, 2); 1443 | auto empty = func.invoke(instance); 1444 | 1445 | ASSERT_FALSE(empty); 1446 | ASSERT_TRUE(any); 1447 | ASSERT_EQ(any.type(), meta::resolve()); 1448 | ASSERT_EQ(any.cast(), 4); 1449 | ASSERT_EQ(func_type::value, 3); 1450 | 1451 | func.prop([](auto prop) { 1452 | ASSERT_TRUE(prop); 1453 | ASSERT_EQ(prop.key(), properties::prop_bool); 1454 | ASSERT_FALSE(prop.value().template cast()); 1455 | }); 1456 | 1457 | ASSERT_FALSE(func.prop(properties::prop_int)); 1458 | 1459 | auto prop = func.prop(properties::prop_bool); 1460 | 1461 | ASSERT_TRUE(prop); 1462 | ASSERT_EQ(prop.key(), properties::prop_bool); 1463 | ASSERT_FALSE(prop.value().cast()); 1464 | } 1465 | 1466 | TEST_F(Meta, MetaFuncConst) { 1467 | std::hash hash{}; 1468 | auto func = meta::resolve().func(hash("f1")); 1469 | func_type instance{}; 1470 | 1471 | ASSERT_TRUE(func); 1472 | ASSERT_EQ(func.parent(), meta::resolve(hash("func"))); 1473 | ASSERT_EQ(func.size(), meta::func::size_type{1}); 1474 | ASSERT_TRUE(func.is_const()); 1475 | ASSERT_FALSE(func.is_static()); 1476 | ASSERT_EQ(func.ret(), meta::resolve()); 1477 | ASSERT_EQ(func.arg(meta::func::size_type{0}), meta::resolve()); 1478 | ASSERT_FALSE(func.arg(meta::func::size_type{1})); 1479 | 1480 | auto any = func.invoke(instance, 4); 1481 | auto empty = func.invoke(instance, 'c'); 1482 | 1483 | ASSERT_FALSE(empty); 1484 | ASSERT_TRUE(any); 1485 | ASSERT_EQ(any.type(), meta::resolve()); 1486 | ASSERT_EQ(any.cast(), 16); 1487 | 1488 | func.prop([](auto prop) { 1489 | ASSERT_TRUE(prop); 1490 | ASSERT_EQ(prop.key(), properties::prop_bool); 1491 | ASSERT_FALSE(prop.value().template cast()); 1492 | }); 1493 | 1494 | ASSERT_FALSE(func.prop(properties::prop_int)); 1495 | 1496 | auto prop = func.prop(properties::prop_bool); 1497 | 1498 | ASSERT_TRUE(prop); 1499 | ASSERT_EQ(prop.key(), properties::prop_bool); 1500 | ASSERT_FALSE(prop.value().cast()); 1501 | } 1502 | 1503 | TEST_F(Meta, MetaFuncRetVoid) { 1504 | std::hash hash{}; 1505 | auto func = meta::resolve().func(hash("g")); 1506 | func_type instance{}; 1507 | 1508 | ASSERT_TRUE(func); 1509 | ASSERT_EQ(func.parent(), meta::resolve(hash("func"))); 1510 | ASSERT_EQ(func.size(), meta::func::size_type{1}); 1511 | ASSERT_FALSE(func.is_const()); 1512 | ASSERT_FALSE(func.is_static()); 1513 | ASSERT_EQ(func.ret(), meta::resolve()); 1514 | ASSERT_EQ(func.arg(meta::func::size_type{0}), meta::resolve()); 1515 | ASSERT_FALSE(func.arg(meta::func::size_type{1})); 1516 | 1517 | auto any = func.invoke(instance, 5); 1518 | 1519 | ASSERT_TRUE(any); 1520 | ASSERT_EQ(any.type(), meta::resolve()); 1521 | ASSERT_EQ(func_type::value, 25); 1522 | 1523 | func.prop([](auto prop) { 1524 | ASSERT_TRUE(prop); 1525 | ASSERT_EQ(prop.key(), properties::prop_bool); 1526 | ASSERT_FALSE(prop.value().template cast()); 1527 | }); 1528 | 1529 | ASSERT_FALSE(func.prop(properties::prop_int)); 1530 | 1531 | auto prop = func.prop(properties::prop_bool); 1532 | 1533 | ASSERT_TRUE(prop); 1534 | ASSERT_EQ(prop.key(), properties::prop_bool); 1535 | ASSERT_FALSE(prop.value().cast()); 1536 | } 1537 | 1538 | TEST_F(Meta, MetaFuncStatic) { 1539 | std::hash hash{}; 1540 | auto func = meta::resolve().func(hash("h")); 1541 | func_type::value = 2; 1542 | 1543 | ASSERT_TRUE(func); 1544 | ASSERT_EQ(func.parent(), meta::resolve(hash("func"))); 1545 | ASSERT_EQ(func.size(), meta::func::size_type{1}); 1546 | ASSERT_FALSE(func.is_const()); 1547 | ASSERT_TRUE(func.is_static()); 1548 | ASSERT_EQ(func.ret(), meta::resolve()); 1549 | ASSERT_EQ(func.arg(meta::func::size_type{0}), meta::resolve()); 1550 | ASSERT_FALSE(func.arg(meta::func::size_type{1})); 1551 | 1552 | auto any = func.invoke({}, 3); 1553 | auto empty = func.invoke({}, 'c'); 1554 | 1555 | ASSERT_FALSE(empty); 1556 | ASSERT_TRUE(any); 1557 | ASSERT_EQ(any.type(), meta::resolve()); 1558 | ASSERT_EQ(any.cast(), 6); 1559 | 1560 | func.prop([](auto prop) { 1561 | ASSERT_TRUE(prop); 1562 | ASSERT_EQ(prop.key(), properties::prop_bool); 1563 | ASSERT_FALSE(prop.value().template cast()); 1564 | }); 1565 | 1566 | ASSERT_FALSE(func.prop(properties::prop_int)); 1567 | 1568 | auto prop = func.prop(properties::prop_bool); 1569 | 1570 | ASSERT_TRUE(prop); 1571 | ASSERT_EQ(prop.key(), properties::prop_bool); 1572 | ASSERT_FALSE(prop.value().cast()); 1573 | } 1574 | 1575 | TEST_F(Meta, MetaFuncStaticRetVoid) { 1576 | std::hash hash{}; 1577 | auto func = meta::resolve().func(hash("k")); 1578 | 1579 | ASSERT_TRUE(func); 1580 | ASSERT_EQ(func.parent(), meta::resolve(hash("func"))); 1581 | ASSERT_EQ(func.size(), meta::func::size_type{1}); 1582 | ASSERT_FALSE(func.is_const()); 1583 | ASSERT_TRUE(func.is_static()); 1584 | ASSERT_EQ(func.ret(), meta::resolve()); 1585 | ASSERT_EQ(func.arg(meta::func::size_type{0}), meta::resolve()); 1586 | ASSERT_FALSE(func.arg(meta::func::size_type{1})); 1587 | 1588 | auto any = func.invoke({}, 42); 1589 | 1590 | ASSERT_TRUE(any); 1591 | ASSERT_EQ(any.type(), meta::resolve()); 1592 | ASSERT_EQ(func_type::value, 42); 1593 | 1594 | func.prop([](auto *prop) { 1595 | ASSERT_TRUE(prop); 1596 | ASSERT_EQ(prop->key(), properties::prop_bool); 1597 | ASSERT_FALSE(prop->value().template cast()); 1598 | }); 1599 | 1600 | ASSERT_FALSE(func.prop(properties::prop_int)); 1601 | 1602 | auto prop = func.prop(properties::prop_bool); 1603 | 1604 | ASSERT_TRUE(prop); 1605 | ASSERT_EQ(prop.key(), properties::prop_bool); 1606 | ASSERT_FALSE(prop.value().cast()); 1607 | } 1608 | 1609 | TEST_F(Meta, MetaFuncMetaAnyArgs) { 1610 | std::hash hash{}; 1611 | auto func = meta::resolve().func(hash("f1")); 1612 | func_type instance; 1613 | 1614 | auto any = func.invoke(instance, meta::any{3}); 1615 | 1616 | ASSERT_TRUE(any); 1617 | ASSERT_EQ(any.type(), meta::resolve()); 1618 | ASSERT_EQ(any.cast(), 9); 1619 | } 1620 | 1621 | TEST_F(Meta, MetaFuncInvalidArgs) { 1622 | std::hash hash{}; 1623 | auto func = meta::resolve().func(hash("f1")); 1624 | empty_type instance; 1625 | 1626 | ASSERT_FALSE(func.invoke(instance, meta::any{'c'})); 1627 | } 1628 | 1629 | TEST_F(Meta, MetaFuncCastAndConvert) { 1630 | std::hash hash{}; 1631 | auto func = meta::resolve().func(hash("f3")); 1632 | func_type instance; 1633 | 1634 | auto any = func.invoke(instance, derived_type{}, 0, 3.); 1635 | 1636 | ASSERT_TRUE(any); 1637 | ASSERT_EQ(any.type(), meta::resolve()); 1638 | ASSERT_EQ(any.cast(), 9); 1639 | } 1640 | 1641 | TEST_F(Meta, MetaFuncAsVoid) { 1642 | std::hash hash{}; 1643 | auto func = meta::resolve().func(hash("v")); 1644 | func_type instance{}; 1645 | 1646 | ASSERT_EQ(func.invoke(instance, 42), meta::any{std::in_place_type}); 1647 | ASSERT_EQ(func.ret(), meta::resolve()); 1648 | ASSERT_EQ(instance.value, 42); 1649 | } 1650 | 1651 | TEST_F(Meta, MetaFuncAsAlias) { 1652 | std::hash hash{}; 1653 | func_type instance{}; 1654 | auto func = meta::resolve().func(hash("a")); 1655 | func.invoke(instance).cast() = 3; 1656 | 1657 | ASSERT_EQ(func.ret(), meta::resolve()); 1658 | ASSERT_EQ(instance.value, 3); 1659 | } 1660 | 1661 | TEST_F(Meta, MetaFuncByReference) { 1662 | std::hash hash{}; 1663 | auto func = meta::resolve().func(hash("h")); 1664 | func_type::value = 2; 1665 | meta::any any{3}; 1666 | int value = 4; 1667 | 1668 | ASSERT_EQ(func.invoke({}, value).cast(), 8); 1669 | ASSERT_EQ(func.invoke({}, any).cast(), 6); 1670 | ASSERT_EQ(any.cast(), 6); 1671 | ASSERT_EQ(value, 8); 1672 | } 1673 | 1674 | TEST_F(Meta, MetaType) { 1675 | auto type = meta::resolve(); 1676 | 1677 | ASSERT_TRUE(type); 1678 | ASSERT_NE(type, meta::type{}); 1679 | 1680 | type.prop([](auto prop) { 1681 | ASSERT_TRUE(prop); 1682 | ASSERT_EQ(prop.key(), properties::prop_int); 1683 | ASSERT_EQ(prop.value(), 99); 1684 | }); 1685 | 1686 | ASSERT_FALSE(type.prop(properties::prop_bool)); 1687 | 1688 | auto prop = type.prop(properties::prop_int); 1689 | 1690 | ASSERT_TRUE(prop); 1691 | ASSERT_EQ(prop.key(), properties::prop_int); 1692 | ASSERT_EQ(prop.value(), 99); 1693 | } 1694 | 1695 | TEST_F(Meta, MetaTypeTraits) { 1696 | ASSERT_TRUE(meta::resolve().is_void()); 1697 | ASSERT_TRUE(meta::resolve().is_integral()); 1698 | ASSERT_TRUE(meta::resolve().is_floating_point()); 1699 | ASSERT_TRUE(meta::resolve().is_enum()); 1700 | ASSERT_TRUE(meta::resolve().is_union()); 1701 | ASSERT_TRUE(meta::resolve().is_class()); 1702 | ASSERT_TRUE(meta::resolve().is_pointer()); 1703 | ASSERT_TRUE(meta::resolve().is_function_pointer()); 1704 | ASSERT_TRUE(meta::resolve().is_member_object_pointer()); 1705 | ASSERT_TRUE(meta::resolve().is_member_function_pointer()); 1706 | } 1707 | 1708 | TEST_F(Meta, MetaTypeRemovePointer) { 1709 | ASSERT_EQ(meta::resolve().remove_pointer(), meta::resolve()); 1710 | ASSERT_EQ(meta::resolve().remove_pointer(), meta::resolve()); 1711 | ASSERT_EQ(meta::resolve().remove_pointer(), meta::resolve()); 1712 | } 1713 | 1714 | TEST_F(Meta, MetaTypeBase) { 1715 | std::hash hash{}; 1716 | auto type = meta::resolve(); 1717 | bool iterate = false; 1718 | 1719 | type.base([&iterate](auto base) { 1720 | ASSERT_EQ(base.type(), meta::resolve()); 1721 | iterate = true; 1722 | }); 1723 | 1724 | ASSERT_TRUE(iterate); 1725 | ASSERT_EQ(type.base(hash("base")).type(), meta::resolve()); 1726 | } 1727 | 1728 | TEST_F(Meta, MetaTypeConv) { 1729 | auto type = meta::resolve(); 1730 | bool iterate = false; 1731 | 1732 | type.conv([&iterate](auto conv) { 1733 | ASSERT_EQ(conv.type(), meta::resolve()); 1734 | iterate = true; 1735 | }); 1736 | 1737 | ASSERT_TRUE(iterate); 1738 | 1739 | auto conv = type.conv(); 1740 | 1741 | ASSERT_EQ(conv.type(), meta::resolve()); 1742 | ASSERT_FALSE(type.conv()); 1743 | } 1744 | 1745 | TEST_F(Meta, MetaTypeCtor) { 1746 | auto type = meta::resolve(); 1747 | int counter{}; 1748 | 1749 | type.ctor([&counter](auto) { 1750 | ++counter; 1751 | }); 1752 | 1753 | ASSERT_EQ(counter, 2); 1754 | ASSERT_TRUE((type.ctor())); 1755 | ASSERT_TRUE((type.ctor())); 1756 | } 1757 | 1758 | TEST_F(Meta, MetaTypeDtor) { 1759 | ASSERT_TRUE(meta::resolve().dtor()); 1760 | ASSERT_FALSE(meta::resolve().dtor()); 1761 | } 1762 | 1763 | TEST_F(Meta, MetaTypeData) { 1764 | std::hash hash{}; 1765 | auto type = meta::resolve(); 1766 | int counter{}; 1767 | 1768 | type.data([&counter](auto) { 1769 | ++counter; 1770 | }); 1771 | 1772 | ASSERT_EQ(counter, 6); 1773 | ASSERT_TRUE(type.data(hash("i"))); 1774 | } 1775 | 1776 | TEST_F(Meta, MetaTypeFunc) { 1777 | std::hash hash{}; 1778 | auto type = meta::resolve(); 1779 | int counter{}; 1780 | 1781 | type.func([&counter](auto) { 1782 | ++counter; 1783 | }); 1784 | 1785 | ASSERT_EQ(counter, 8); 1786 | ASSERT_TRUE(type.func(hash("f1"))); 1787 | } 1788 | 1789 | TEST_F(Meta, MetaTypeConstruct) { 1790 | auto type = meta::resolve(); 1791 | auto any = type.construct(base_type{}, 42, 'c'); 1792 | 1793 | ASSERT_TRUE(any); 1794 | ASSERT_TRUE(any.try_cast()); 1795 | ASSERT_EQ(any.cast().i, 42); 1796 | ASSERT_EQ(any.cast().c, 'c'); 1797 | } 1798 | 1799 | TEST_F(Meta, MetaTypeConstructMetaAnyArgs) { 1800 | auto type = meta::resolve(); 1801 | auto any = type.construct(meta::any{base_type{}}, meta::any{42}, meta::any{'c'}); 1802 | 1803 | ASSERT_TRUE(any); 1804 | ASSERT_TRUE(any.try_cast()); 1805 | ASSERT_EQ(any.cast().i, 42); 1806 | ASSERT_EQ(any.cast().c, 'c'); 1807 | } 1808 | 1809 | TEST_F(Meta, MetaTypeConstructInvalidArgs) { 1810 | auto type = meta::resolve(); 1811 | auto any = type.construct(meta::any{base_type{}}, meta::any{'c'}, meta::any{42}); 1812 | ASSERT_FALSE(any); 1813 | } 1814 | 1815 | TEST_F(Meta, MetaTypeLessArgs) { 1816 | auto type = meta::resolve(); 1817 | auto any = type.construct(base_type{}); 1818 | ASSERT_FALSE(any); 1819 | } 1820 | 1821 | TEST_F(Meta, MetaTypeConstructCastAndConvert) { 1822 | auto type = meta::resolve(); 1823 | auto any = type.construct(meta::any{derived_type{}}, meta::any{42.}, meta::any{'c'}); 1824 | 1825 | ASSERT_TRUE(any); 1826 | ASSERT_TRUE(any.try_cast()); 1827 | ASSERT_EQ(any.cast().i, 42); 1828 | ASSERT_EQ(any.cast().c, 'c'); 1829 | } 1830 | 1831 | TEST_F(Meta, MetaTypeDestroyDtor) { 1832 | auto type = meta::resolve(); 1833 | empty_type instance; 1834 | 1835 | ASSERT_EQ(empty_type::counter, 0); 1836 | ASSERT_TRUE(type.destroy(instance)); 1837 | ASSERT_EQ(empty_type::counter, 1); 1838 | } 1839 | 1840 | TEST_F(Meta, MetaTypeDestroyDtorInvalidArg) { 1841 | auto type = meta::resolve(); 1842 | auto instance = 'c'; 1843 | 1844 | ASSERT_EQ(empty_type::counter, 0); 1845 | ASSERT_FALSE(type.destroy(instance)); 1846 | ASSERT_EQ(empty_type::counter, 0); 1847 | } 1848 | 1849 | TEST_F(Meta, MetaTypeDestroyDtorCastAndConvert) { 1850 | auto type = meta::resolve(); 1851 | fat_type instance{}; 1852 | 1853 | ASSERT_EQ(empty_type::counter, 0); 1854 | ASSERT_FALSE(type.destroy(instance)); 1855 | ASSERT_EQ(empty_type::counter, 0); 1856 | } 1857 | 1858 | TEST_F(Meta, MetaTypeDestroyNoDtor) { 1859 | auto instance = 'c'; 1860 | ASSERT_TRUE(meta::resolve().destroy(instance)); 1861 | } 1862 | 1863 | TEST_F(Meta, MetaTypeDestroyNoDtorInvalidArg) { 1864 | auto instance = 42; 1865 | ASSERT_FALSE(meta::resolve().destroy(instance)); 1866 | } 1867 | 1868 | TEST_F(Meta, MetaTypeDestroyNoDtorVoid) { 1869 | ASSERT_FALSE(meta::resolve().destroy({})); 1870 | } 1871 | 1872 | TEST_F(Meta, MetaTypeDestroyNoDtorCastAndConvert) { 1873 | auto instance = 42.; 1874 | ASSERT_FALSE(meta::resolve().destroy(instance)); 1875 | } 1876 | 1877 | TEST_F(Meta, MetaDataFromBase) { 1878 | std::hash hash{}; 1879 | auto type = meta::resolve(); 1880 | concrete_type instance; 1881 | 1882 | ASSERT_TRUE(type.data(hash("i"))); 1883 | ASSERT_TRUE(type.data(hash("j"))); 1884 | 1885 | ASSERT_EQ(instance.i, 0); 1886 | ASSERT_EQ(instance.j, char{}); 1887 | ASSERT_TRUE(type.data(hash("i")).set(instance, 3)); 1888 | ASSERT_TRUE(type.data(hash("j")).set(instance, 'c')); 1889 | ASSERT_EQ(instance.i, 3); 1890 | ASSERT_EQ(instance.j, 'c'); 1891 | } 1892 | 1893 | TEST_F(Meta, MetaFuncFromBase) { 1894 | std::hash hash{}; 1895 | auto type = meta::resolve(); 1896 | auto base = meta::resolve(); 1897 | concrete_type instance; 1898 | 1899 | ASSERT_TRUE(type.func(hash("f"))); 1900 | ASSERT_TRUE(type.func(hash("g"))); 1901 | ASSERT_TRUE(type.func(hash("h"))); 1902 | 1903 | ASSERT_EQ(type.func(hash("f")).parent(), meta::resolve()); 1904 | ASSERT_EQ(type.func(hash("g")).parent(), meta::resolve()); 1905 | ASSERT_EQ(type.func(hash("h")).parent(), meta::resolve()); 1906 | 1907 | ASSERT_EQ(instance.i, 0); 1908 | ASSERT_EQ(instance.j, char{}); 1909 | 1910 | type.func(hash("f")).invoke(instance, 3); 1911 | type.func(hash("h")).invoke(instance, 'c'); 1912 | 1913 | ASSERT_EQ(instance.i, 9); 1914 | ASSERT_EQ(instance.j, 'c'); 1915 | 1916 | base.func(hash("g")).invoke(instance, 3); 1917 | 1918 | ASSERT_EQ(instance.i, -3); 1919 | } 1920 | 1921 | TEST_F(Meta, MetaPropFromBase) { 1922 | auto type = meta::resolve(); 1923 | auto prop_bool = type.prop(properties::prop_bool); 1924 | auto prop_int = type.prop(properties::prop_int); 1925 | 1926 | ASSERT_TRUE(prop_bool); 1927 | ASSERT_TRUE(prop_int); 1928 | 1929 | ASSERT_FALSE(prop_bool.value().cast()); 1930 | ASSERT_EQ(prop_int.value().cast(), 42); 1931 | } 1932 | 1933 | TEST_F(Meta, AbstractClass) { 1934 | std::hash hash{}; 1935 | auto type = meta::resolve(); 1936 | concrete_type instance; 1937 | 1938 | ASSERT_EQ(instance.i, 0); 1939 | 1940 | type.func(hash("f")).invoke(instance, 3); 1941 | 1942 | ASSERT_EQ(instance.i, 3); 1943 | 1944 | type.func(hash("g")).invoke(instance, 3); 1945 | 1946 | ASSERT_EQ(instance.i, -3); 1947 | } 1948 | 1949 | TEST_F(Meta, EnumAndNamedConstants) { 1950 | std::hash hash{}; 1951 | auto type = meta::resolve(); 1952 | 1953 | ASSERT_TRUE(type.data(hash("prop_bool"))); 1954 | ASSERT_TRUE(type.data(hash("prop_int"))); 1955 | 1956 | ASSERT_EQ(type.data(hash("prop_bool")).type(), type); 1957 | ASSERT_EQ(type.data(hash("prop_int")).type(), type); 1958 | 1959 | ASSERT_FALSE(type.data(hash("prop_bool")).set({}, properties::prop_int)); 1960 | ASSERT_FALSE(type.data(hash("prop_int")).set({}, properties::prop_bool)); 1961 | 1962 | ASSERT_EQ(type.data(hash("prop_bool")).get({}).cast(), properties::prop_bool); 1963 | ASSERT_EQ(type.data(hash("prop_int")).get({}).cast(), properties::prop_int); 1964 | } 1965 | 1966 | TEST_F(Meta, ArithmeticTypeAndNamedConstants) { 1967 | std::hash hash{}; 1968 | auto type = meta::resolve(); 1969 | 1970 | ASSERT_TRUE(type.data(hash("min"))); 1971 | ASSERT_TRUE(type.data(hash("max"))); 1972 | 1973 | ASSERT_EQ(type.data(hash("min")).type(), type); 1974 | ASSERT_EQ(type.data(hash("max")).type(), type); 1975 | 1976 | ASSERT_FALSE(type.data(hash("min")).set({}, 100u)); 1977 | ASSERT_FALSE(type.data(hash("max")).set({}, 0u)); 1978 | 1979 | ASSERT_EQ(type.data(hash("min")).get({}).cast(), 0u); 1980 | ASSERT_EQ(type.data(hash("max")).get({}).cast(), 100u); 1981 | } 1982 | 1983 | TEST_F(Meta, Variables) { 1984 | std::hash hash{}; 1985 | auto p_data = meta::resolve().data(hash("value")); 1986 | auto c_data = meta::resolve(hash("char")).data(hash("value")); 1987 | 1988 | properties prop{properties::prop_int}; 1989 | char c = 'c'; 1990 | 1991 | p_data.set(prop, properties::prop_bool); 1992 | c_data.set(c, 'x'); 1993 | 1994 | ASSERT_EQ(p_data.get(prop).cast(), properties::prop_bool); 1995 | ASSERT_EQ(c_data.get(c).cast(), 'x'); 1996 | ASSERT_EQ(prop, properties::prop_bool); 1997 | ASSERT_EQ(c, 'x'); 1998 | } 1999 | 2000 | TEST_F(Meta, Unregister) { 2001 | std::hash hash{}; 2002 | 2003 | ASSERT_FALSE(meta::unregister()); 2004 | ASSERT_TRUE(meta::unregister()); 2005 | ASSERT_TRUE(meta::unregister()); 2006 | ASSERT_TRUE(meta::unregister()); 2007 | ASSERT_TRUE(meta::unregister()); 2008 | ASSERT_TRUE(meta::unregister()); 2009 | ASSERT_TRUE(meta::unregister()); 2010 | ASSERT_TRUE(meta::unregister()); 2011 | ASSERT_TRUE(meta::unregister()); 2012 | ASSERT_TRUE(meta::unregister()); 2013 | ASSERT_TRUE(meta::unregister()); 2014 | ASSERT_TRUE(meta::unregister()); 2015 | ASSERT_TRUE(meta::unregister()); 2016 | ASSERT_TRUE(meta::unregister()); 2017 | ASSERT_TRUE(meta::unregister()); 2018 | ASSERT_FALSE(meta::unregister()); 2019 | 2020 | ASSERT_FALSE(meta::resolve(hash("char"))); 2021 | ASSERT_FALSE(meta::resolve(hash("base"))); 2022 | ASSERT_FALSE(meta::resolve(hash("derived"))); 2023 | ASSERT_FALSE(meta::resolve(hash("empty"))); 2024 | ASSERT_FALSE(meta::resolve(hash("fat"))); 2025 | ASSERT_FALSE(meta::resolve(hash("data"))); 2026 | ASSERT_FALSE(meta::resolve(hash("func"))); 2027 | ASSERT_FALSE(meta::resolve(hash("setter_getter"))); 2028 | ASSERT_FALSE(meta::resolve(hash("an_abstract_type"))); 2029 | ASSERT_FALSE(meta::resolve(hash("another_abstract_type"))); 2030 | ASSERT_FALSE(meta::resolve(hash("concrete"))); 2031 | 2032 | Meta::SetUpAfterUnregistration(); 2033 | meta::any any{42.}; 2034 | 2035 | ASSERT_TRUE(any); 2036 | ASSERT_FALSE(any.convert()); 2037 | ASSERT_TRUE(any.convert()); 2038 | 2039 | ASSERT_FALSE(meta::resolve(hash("derived"))); 2040 | ASSERT_TRUE(meta::resolve(hash("my_type"))); 2041 | 2042 | meta::resolve().prop([](auto prop) { 2043 | ASSERT_TRUE(prop); 2044 | ASSERT_EQ(prop.key(), properties::prop_bool); 2045 | ASSERT_FALSE(prop.value().template cast()); 2046 | }); 2047 | 2048 | ASSERT_FALSE((meta::resolve().ctor())); 2049 | ASSERT_TRUE((meta::resolve().ctor<>())); 2050 | 2051 | ASSERT_TRUE(meta::resolve(hash("your_type")).data(hash("a_data_member"))); 2052 | ASSERT_FALSE(meta::resolve(hash("your_type")).data(hash("another_data_member"))); 2053 | 2054 | ASSERT_TRUE(meta::resolve(hash("your_type")).func(hash("a_member_function"))); 2055 | ASSERT_FALSE(meta::resolve(hash("your_type")).func(hash("another_member_function"))); 2056 | } 2057 | -------------------------------------------------------------------------------- /test/odr.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | --------------------------------------------------------------------------------