├── .github ├── FUNDING.yml └── workflows │ └── ci.yaml ├── meson_options.txt ├── subprojects └── catch2.wrap ├── include ├── meson.build └── mz │ ├── tagged_ptr.hpp.in │ └── tagged_ptr.hpp ├── tagged_ptr.code-workspace ├── CHANGELOG.md ├── docs └── images │ ├── badge-license-MIT.svg │ ├── badge-gitter.svg │ ├── badge-c++17.svg │ └── badge-sponsor.svg ├── .editorconfig ├── tests ├── meson.build └── main.cpp ├── meson.build ├── .vscode └── settings.json ├── LICENSE ├── .gitattributes ├── .gitignore ├── .clang-format └── README.md /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: marzer 2 | -------------------------------------------------------------------------------- /meson_options.txt: -------------------------------------------------------------------------------- 1 | option('build_tests', type: 'boolean', value: true, description: 'Build tests') 2 | -------------------------------------------------------------------------------- /subprojects/catch2.wrap: -------------------------------------------------------------------------------- 1 | [wrap-file] 2 | directory = Catch2-3.2.0 3 | source_url = https://github.com/catchorg/Catch2/archive/v3.2.0.tar.gz 4 | source_filename = Catch2-3.2.0.tar.gz 5 | source_hash = feee04647e28ac3cbeff46cb42abc8ee2d8d5f646d36e3fb3ba274b8c69a58ea 6 | wrapdb_version = 3.2.0-1 7 | 8 | [provide] 9 | catch2 = catch2_dep 10 | catch2-with-main = catch2_with_main_dep 11 | -------------------------------------------------------------------------------- /include/meson.build: -------------------------------------------------------------------------------- 1 | # This file is a part of marzer/tagged_ptr and is subject to the the terms of the MIT license. 2 | # Copyright (c) Mark Gillard 3 | # See https://github.com/marzer/tagged_ptr/blob/master/LICENSE for the full license text. 4 | # SPDX-License-Identifier: MIT 5 | 6 | include_dir = include_directories('.') 7 | 8 | if not meson.is_subproject() 9 | install_subdir('mz', install_dir: get_option('includedir')) 10 | endif 11 | -------------------------------------------------------------------------------- /tagged_ptr.code-workspace: -------------------------------------------------------------------------------- 1 | { 2 | "folders": [ 3 | { 4 | "path": "." 5 | } 6 | ], 7 | "settings": { 8 | "files.associations": { 9 | "type_traits": "cpp", 10 | "concepts": "cpp", 11 | "cstddef": "cpp", 12 | "cstdint": "cpp", 13 | "cstdlib": "cpp", 14 | "initializer_list": "cpp", 15 | "xstddef": "cpp", 16 | "xtr1common": "cpp", 17 | "version": "cpp", 18 | "xstring": "cpp" 19 | }, 20 | "explorer.sortOrder": "type" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## v0.4.0 4 | 5 | - made all macros overridable 6 | - minor refactors 7 | 8 | ## v0.3.0 9 | 10 | - fixed incorrect `__builtin_assume_aligned` check 11 | 12 | ## v0.2.0 13 | 14 | - removed `constexpr` where it would be non-portable 15 | - made use of extra bits (e.g. on AMD64) opt-in via `MZ_TAGGED_PTR_BITS` 16 | - made use of `assert()` configurable via `MZ_ASSERT()` 17 | 18 | ## v0.1.0 19 | 20 | - Initial release 🎉️ 21 | -------------------------------------------------------------------------------- /docs/images/badge-license-MIT.svg: -------------------------------------------------------------------------------- 1 | licenseMIT -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | insert_final_newline = true 5 | indent_style = tab 6 | indent_size = 4 7 | tab_width = 4 8 | end_of_line = lf 9 | trim_trailing_whitespace = true 10 | charset = utf-8 11 | 12 | [*.{md,markdown}] 13 | trim_trailing_whitespace = false 14 | 15 | [*.{gitattributes,yaml,yml,vcxproj,vcxproj.filters,sln,rc,clang-format,toml}] 16 | indent_style = space 17 | 18 | [{Doxyfile,Doxyfile-mcss}] 19 | indent_style = space 20 | 21 | [*.{hlsl,rc,sln,vcxproj,vcxproj.filters}] 22 | end_of_line = crlf 23 | 24 | [*.{sln,vcxproj,vcxproj.filters}] 25 | charset = utf-8-bom 26 | -------------------------------------------------------------------------------- /docs/images/badge-gitter.svg: -------------------------------------------------------------------------------- 1 | chat: on gitterchaton gitter -------------------------------------------------------------------------------- /tests/meson.build: -------------------------------------------------------------------------------- 1 | # This file is a part of marzer/tagged_ptr and is subject to the the terms of the MIT license. 2 | # Copyright (c) Mark Gillard 3 | # See https://github.com/marzer/tagged_ptr/blob/master/LICENSE for the full license text. 4 | # SPDX-License-Identifier: MIT 5 | 6 | test_exe = executable( 7 | 'test_exe', 8 | files('main.cpp'), 9 | dependencies: 10 | [ 11 | tagged_ptr_dep, 12 | subproject('catch2', default_options: [ 'warning_level=0', 'werror=false' ]).get_variable('catch2_with_main_dep') 13 | ], 14 | override_options: [ 'warning_level=3', 'werror=true' ], 15 | install: false 16 | ) 17 | 18 | test('test', test_exe) 19 | -------------------------------------------------------------------------------- /meson.build: -------------------------------------------------------------------------------- 1 | # This file is a part of marzer/tagged_ptr and is subject to the the terms of the MIT license. 2 | # Copyright (c) Mark Gillard 3 | # See https://github.com/marzer/tagged_ptr/blob/master/LICENSE for the full license text. 4 | # SPDX-License-Identifier: MIT 5 | 6 | project( 7 | 'tagged_ptr', 8 | 'cpp', 9 | version : '0.3.0', 10 | meson_version : '>=0.60.0', 11 | license : 'MIT', 12 | default_options : [ 'cpp_std=c++17' ] 13 | ) 14 | 15 | subdir('include') 16 | 17 | tagged_ptr_dep = declare_dependency( 18 | include_directories: include_dir 19 | ) 20 | 21 | if not meson.is_subproject() and get_option('build_tests') 22 | subdir('tests') 23 | endif 24 | 25 | 26 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "prettier.useTabs": true, 3 | "prettier.tabWidth": 4, 4 | "editor.rulers": [120], 5 | "editor.formatOnSave": true, 6 | "files.eol": "\n", 7 | "python.formatting.provider": "yapf", 8 | "html.format.indentInnerHtml": true, 9 | "files.exclude": { 10 | "**/__pycache__": true, 11 | "**/build": true, 12 | "**/*.egg-info": true 13 | }, 14 | "[cpp]": { 15 | "editor.formatOnSave": true 16 | }, 17 | "mesonbuild.configureOnOpen": false, 18 | "files.associations": { 19 | "bit": "cpp", 20 | "compare": "cpp", 21 | "concepts": "cpp", 22 | "cstddef": "cpp", 23 | "cstdint": "cpp", 24 | "cstdio": "cpp", 25 | "cstdlib": "cpp", 26 | "cwchar": "cpp", 27 | "initializer_list": "cpp", 28 | "limits": "cpp", 29 | "tuple": "cpp", 30 | "type_traits": "cpp", 31 | "utility": "cpp", 32 | "xstddef": "cpp", 33 | "xtr1common": "cpp", 34 | "array": "cpp", 35 | "format": "cpp", 36 | "functional": "cpp", 37 | "future": "cpp", 38 | "memory": "cpp", 39 | "ratio": "cpp", 40 | "regex": "cpp", 41 | "variant": "cpp", 42 | "xmemory": "cpp", 43 | "xstring": "cpp", 44 | "xutility": "cpp" 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) Mark Gillard 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /docs/images/badge-c++17.svg: -------------------------------------------------------------------------------- 1 | standardC++17 -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text eol=lf encoding=UTF-8 2 | *.h text eol=lf encoding=UTF-8 linguist-language=C++ 3 | *.hlsl text eol=crlf encoding=UTF-8 4 | *.hpp text eol=lf encoding=UTF-8 linguist-language=C++ 5 | *.rc text eol=crlf encoding=UTF-8 6 | *.sln text eol=crlf encoding=UTF-8-BOM 7 | *.vcxproj text eol=crlf encoding=UTF-8-BOM 8 | *.vcxproj.filters text eol=crlf encoding=UTF-8-BOM 9 | 10 | *.cs eol=lf diff=csharp 11 | 12 | *.doc diff=astextplain 13 | *.DOC diff=astextplain 14 | *.docx diff=astextplain 15 | *.DOCX diff=astextplain 16 | *.dot diff=astextplain 17 | *.DOT diff=astextplain 18 | *.pdf diff=astextplain 19 | *.PDF diff=astextplain 20 | *.rtf diff=astextplain 21 | *.RTF diff=astextplain 22 | 23 | *.ai binary 24 | *.bin binary 25 | *.bmp binary 26 | *.dat binary 27 | *.gif binary 28 | *.ico binary 29 | *.jpeg binary 30 | *.jpg binary 31 | *.otf binary 32 | *.png binary 33 | *.psd binary 34 | *.rc binary 35 | *.ttf binary 36 | *.woff binary 37 | *.woff2 binary 38 | *.xlsx binary 39 | 40 | vendor/* linguist-vendored 41 | external/* linguist-vendored 42 | subprojects/* linguist-vendored 43 | submodules/* linguist-vendored 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /docs/images/badge-sponsor.svg: -------------------------------------------------------------------------------- 1 | sponsor: ❤sponsor -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Prerequisites 2 | *.d 3 | 4 | # Compiled Object files 5 | *.slo 6 | *.lo 7 | *.o 8 | *.obj 9 | 10 | # Precompiled Headers 11 | *.gch 12 | *.pch 13 | 14 | # Compiled Dynamic libraries 15 | *.so 16 | *.dylib 17 | *.dll 18 | 19 | # Fortran module files 20 | *.mod 21 | *.smod 22 | 23 | # Compiled Static libraries 24 | *.lai 25 | *.la 26 | *.a 27 | *.lib 28 | 29 | # Executables 30 | *.exe 31 | *.out 32 | *.app 33 | 34 | # documentation 35 | *.xml 36 | *.xslt 37 | *.xsd 38 | *.html 39 | *.temp 40 | docs/html/* 41 | docs/xml/* 42 | 43 | # visual studio 44 | .vs/ 45 | *.Designer.cs.dll 46 | *.VC.VC.opendb 47 | *.VC.db 48 | *.aps 49 | *.exp 50 | *.cachefile 51 | *.ilk 52 | *.iobj 53 | *.ipdb 54 | *.lastbuildstate 55 | *.log 56 | *.meta 57 | *.ncb 58 | *.obj 59 | *.opendb 60 | *.opensdf 61 | *.pch 62 | *.pdb 63 | *.pgc 64 | *.pgd 65 | *.pidb 66 | *.psess 67 | *.rsp 68 | *.sbr 69 | *.scc 70 | *.sdf 71 | *.sln.docstates 72 | *.suo 73 | *.svclog 74 | *.tlb 75 | *.tlh 76 | *.tli 77 | *.tlog 78 | *.tmp 79 | *.tmp_proj 80 | *.user 81 | *.userosscache 82 | *.vsidx 83 | *.vsp 84 | *.vspscc 85 | *.vspx 86 | *.vssscc 87 | *_i.c 88 | *_i.h 89 | *_p.c 90 | .builds 91 | ipch/ 92 | unsuccessfulbuild 93 | 94 | # meson 95 | meson-info/ 96 | meson-logs/ 97 | meson-private/ 98 | 99 | # Including strong name files can present a security risk 100 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 101 | *.snk 102 | 103 | # python 104 | __pycache__/ 105 | *.pyc 106 | 107 | # Windows 108 | Thumbs.db 109 | ehthumbs.db 110 | Desktop.ini 111 | $RECYCLE.BIN/ 112 | *.cab 113 | *.msi 114 | *.msm 115 | *.msp 116 | *.lnk 117 | 118 | # pitchfork 119 | build*/ 120 | _build*/ 121 | !build-aux/ 122 | 123 | # meson subprojects 124 | subprojects/*/ 125 | 126 | # sidle 127 | /sidledb.json 128 | -------------------------------------------------------------------------------- /include/mz/tagged_ptr.hpp.in: -------------------------------------------------------------------------------- 1 | //---------------------------------------------------------------------------------------------------------------------- 2 | // 3 | // {% namespaces::main %}::tagged_ptr 4 | // https://github.com/marzer/tagged_ptr 5 | // SPDX-License-Identifier: MIT 6 | // 7 | //---------------------------------------------------------------------------------------------------------------------- 8 | //% generated_header_preamble 9 | //---------------------------------------------------------------------------------------------------------------------- 10 | #ifndef {% macros::prefix %}TAGGED_PTR_HPP 11 | #define {% macros::prefix %}TAGGED_PTR_HPP 12 | 13 | #define {% macros::prefix %}TAGGED_PTR_VERSION_MAJOR 0 14 | #define {% macros::prefix %}TAGGED_PTR_VERSION_MINOR 4 15 | #define {% macros::prefix %}TAGGED_PTR_VERSION_PATCH 0 16 | 17 | //% preprocessor::cpp 18 | //% preprocessor::compilers 19 | //% preprocessor::arch::amd64 20 | 21 | //% preprocessor::has_builtin 22 | //% preprocessor::has_cpp_attr 23 | //% preprocessor::has_attr 24 | //% preprocessor::attr 25 | //% preprocessor::declspec 26 | //% preprocessor::always_inline 27 | //% preprocessor::nodiscard 28 | //% preprocessor::assume 29 | //% preprocessor::getters 30 | //% preprocessor::trivial_abi 31 | //% preprocessor::hidden 32 | //% preprocessor::sfinae 33 | 34 | //% preprocessor::has_if_consteval 35 | //% preprocessor::if_consteval 36 | 37 | #include // CHAR_BIT 38 | #include // size_t 39 | #include // std::memcpy 40 | #include 41 | #include 42 | 43 | #ifndef NDEBUG 44 | #ifndef {% macros::prefix %}ASSERT 45 | #include 46 | #define {% macros::prefix %}ASSERT(...) assert(__VA_ARGS__) 47 | #endif 48 | #else 49 | #define {% macros::prefix %}ASSERT(...) static_cast(0) 50 | #endif 51 | 52 | #ifndef {% macros::prefix %}TAGGED_PTR_HAS_TRAITS 53 | #define {% macros::prefix %}TAGGED_PTR_HAS_TRAITS 1 54 | #endif 55 | #if {% macros::prefix %}TAGGED_PTR_HAS_TRAITS 56 | #include // std::pointer_traits 57 | #endif 58 | 59 | namespace {% namespaces::main %} 60 | { 61 | using std::size_t; 62 | using std::uintptr_t; 63 | 64 | //% meta::remove_cvref 65 | //% meta::remove_enum guarded 66 | //% meta::is_cvref guarded 67 | //% meta::is_unsigned guarded 68 | 69 | //% max guarded 70 | //% clamp guarded 71 | 72 | //% countl_zero_naive guarded 73 | //% bit_width guarded 74 | //% bit_ceil guarded 75 | //% bit_floor guarded 76 | //% bit_fill_right guarded 77 | //% has_single_bit guarded 78 | //% is_constant_evaluated guarded 79 | //% assume_aligned guarded 80 | } 81 | 82 | //% tagged_ptr::impl 83 | //% tagged_ptr 84 | 85 | #if {% macros::prefix %}TAGGED_PTR_HAS_TRAITS 86 | //% tagged_ptr::traits 87 | #endif 88 | 89 | #endif // {% macros::prefix %}TAGGED_PTR_HPP 90 | -------------------------------------------------------------------------------- /.github/workflows/ci.yaml: -------------------------------------------------------------------------------- 1 | name: ci 2 | 3 | on: 4 | push: 5 | branches-ignore: 6 | - "gh-pages" 7 | paths: 8 | - "**.h" 9 | - "**.hpp" 10 | - "**.cpp" 11 | - "**.inl" 12 | - "**/meson.build" 13 | - "**/workflows/**.yaml" 14 | pull_request: 15 | branches-ignore: 16 | - "gh-pages" 17 | paths: 18 | - "**.h" 19 | - "**.hpp" 20 | - "**.cpp" 21 | - "**.inl" 22 | - "**/meson.build" 23 | - "**/workflows/**.yaml" 24 | workflow_dispatch: 25 | 26 | concurrency: 27 | group: ${{ github.workflow }}-${{ github.ref }} 28 | cancel-in-progress: true 29 | 30 | env: 31 | clang_version: "14" 32 | gcc_version: "11" 33 | 34 | jobs: 35 | linux: 36 | strategy: 37 | fail-fast: false 38 | matrix: 39 | compiler: 40 | - "clang" 41 | - "gcc" 42 | linker: 43 | - "lld" 44 | type: 45 | - "debug" 46 | - "release" 47 | 48 | runs-on: ubuntu-22.04 49 | 50 | defaults: 51 | run: 52 | shell: bash 53 | 54 | steps: 55 | - name: Install base dependencies 56 | run: | 57 | sudo apt -y update 58 | sudo apt -y install --no-install-recommends git python3 python3-pip ninja-build gettext libstdc++-${{ env.gcc_version }}-dev 59 | 60 | - name: Install lld 61 | if: ${{ startsWith(matrix.linker, 'lld') }} 62 | run: | 63 | sudo apt -y install --no-install-recommends lld-${{ env.clang_version }} 64 | sudo update-alternatives --install /usr/bin/ld.lld ld.lld /usr/bin/ld.lld-${{ env.clang_version }} 1000 65 | 66 | - name: Install clang 67 | if: ${{ startsWith(matrix.compiler, 'clang') }} 68 | run: | 69 | sudo apt -y install --no-install-recommends clang-${{ env.clang_version }} 70 | sudo update-alternatives --install /usr/bin/c++ c++ /usr/bin/clang++-${{ env.clang_version }} 1000 71 | sudo update-alternatives --install /usr/bin/cc cc /usr/bin/clang-${{ env.clang_version }} 1000 72 | 73 | - name: Install gcc 74 | if: ${{ startsWith(matrix.compiler, 'gcc') }} 75 | run: | 76 | sudo apt -y install --no-install-recommends gcc-${{ env.gcc_version }} g++-${{ env.gcc_version }} 77 | sudo update-alternatives --install /usr/bin/c++ c++ /usr/bin/g++-${{ env.gcc_version }} 1000 78 | sudo update-alternatives --install /usr/bin/cc cc /usr/bin/gcc-${{ env.gcc_version }} 1000 79 | 80 | - uses: actions/checkout@v3 81 | 82 | - name: Install python dependencies 83 | run: | 84 | sudo -H pip3 install --no-cache-dir --upgrade meson 85 | 86 | - name: Configure Meson 87 | run: | 88 | CC=cc CC_LD=${{ matrix.linker }} CXX=c++ CXX_LD=${{ matrix.linker }} meson setup build --buildtype=${{ matrix.type }} -Dbuild_tests=true 89 | 90 | - name: Build 91 | run: meson compile -C build 92 | 93 | - name: Test 94 | run: meson test -C build --verbose 95 | -------------------------------------------------------------------------------- /.clang-format: -------------------------------------------------------------------------------- 1 | --- 2 | Language: Cpp 3 | AccessModifierOffset: -2 4 | AlignAfterOpenBracket: Align 5 | AlignConsecutiveMacros: Consecutive 6 | AlignConsecutiveAssignments: Consecutive 7 | AlignConsecutiveBitFields: Consecutive 8 | AlignConsecutiveDeclarations: None 9 | AlignEscapedNewlines: Right 10 | AlignOperands: AlignAfterOperator 11 | AlignTrailingComments: true 12 | AllowAllArgumentsOnNextLine: false 13 | AllowAllConstructorInitializersOnNextLine: false 14 | AllowAllParametersOfDeclarationOnNextLine: false 15 | AllowShortEnumsOnASingleLine: false 16 | AllowShortBlocksOnASingleLine: Never 17 | AllowShortCaseLabelsOnASingleLine: true 18 | AllowShortFunctionsOnASingleLine: None 19 | AllowShortLambdasOnASingleLine: All 20 | AllowShortIfStatementsOnASingleLine: Never 21 | AllowShortLoopsOnASingleLine: false 22 | AlwaysBreakAfterDefinitionReturnType: None 23 | AlwaysBreakAfterReturnType: None 24 | AlwaysBreakBeforeMultilineStrings: false 25 | AlwaysBreakTemplateDeclarations: Yes 26 | AttributeMacros: 27 | - MZ_EMPTY_BASES 28 | - MZ_NODISCARD_CLASS 29 | BinPackArguments: false 30 | BinPackParameters: false 31 | BraceWrapping: 32 | AfterCaseLabel: true 33 | AfterClass: true 34 | AfterControlStatement: Always 35 | AfterEnum: true 36 | AfterFunction: true 37 | AfterNamespace: true 38 | AfterObjCDeclaration: false 39 | AfterStruct: true 40 | AfterUnion: true 41 | AfterExternBlock: true 42 | BeforeCatch: true 43 | BeforeElse: true 44 | BeforeLambdaBody: true 45 | BeforeWhile: true 46 | IndentBraces: false 47 | SplitEmptyFunction: false 48 | SplitEmptyRecord: false 49 | SplitEmptyNamespace: true 50 | BreakBeforeBinaryOperators: NonAssignment 51 | BreakBeforeConceptDeclarations: true 52 | BreakBeforeBraces: Custom 53 | BreakBeforeInheritanceComma: false 54 | BreakInheritanceList: BeforeColon 55 | BreakBeforeTernaryOperators: true 56 | BreakConstructorInitializersBeforeComma: false 57 | BreakConstructorInitializers: BeforeColon 58 | BreakAfterJavaFieldAnnotations: false 59 | BreakStringLiterals: true 60 | ColumnLimit: 120 61 | CommentPragmas: '^([/*!#]|\s*(===|---|clang-format))' 62 | CompactNamespaces: false 63 | ConstructorInitializerAllOnOneLineOrOnePerLine: true 64 | ConstructorInitializerIndentWidth: 4 65 | ContinuationIndentWidth: 4 66 | Cpp11BracedListStyle: false 67 | DeriveLineEnding: false 68 | DerivePointerAlignment: false 69 | DisableFormat: false 70 | EmptyLineBeforeAccessModifier: LogicalBlock 71 | ExperimentalAutoDetectBinPacking: false 72 | FixNamespaceComments: false 73 | ForEachMacros: 74 | - foreach 75 | - Q_FOREACH 76 | - BOOST_FOREACH 77 | StatementAttributeLikeMacros: 78 | - Q_EMIT 79 | IncludeBlocks: Preserve 80 | IncludeCategories: 81 | - Regex: '^"(llvm|llvm-c|clang|clang-c)/' 82 | Priority: 2 83 | SortPriority: 0 84 | CaseSensitive: false 85 | - Regex: '^(<|"(gtest|gmock|isl|json)/)' 86 | Priority: 3 87 | SortPriority: 0 88 | CaseSensitive: false 89 | - Regex: ".*" 90 | Priority: 1 91 | SortPriority: 0 92 | CaseSensitive: false 93 | IncludeIsMainRegex: "(Test)?$" 94 | IncludeIsMainSourceRegex: "" 95 | IndentCaseLabels: true 96 | IndentCaseBlocks: false 97 | IndentGotoLabels: true 98 | IndentPPDirectives: BeforeHash 99 | IndentExternBlock: Indent 100 | IndentRequires: false 101 | IndentWidth: 4 102 | IndentWrappedFunctionNames: false 103 | InsertTrailingCommas: None 104 | JavaScriptQuotes: Leave 105 | JavaScriptWrapImports: true 106 | KeepEmptyLinesAtTheStartOfBlocks: false 107 | MacroBlockBegin: "" 108 | MacroBlockEnd: "" 109 | MaxEmptyLinesToKeep: 1 110 | NamespaceIndentation: All 111 | ObjCBinPackProtocolList: Auto 112 | ObjCBlockIndentWidth: 2 113 | ObjCBreakBeforeNestedBlockParam: true 114 | ObjCSpaceAfterProperty: false 115 | ObjCSpaceBeforeProtocolList: true 116 | PenaltyBreakAssignment: 2 117 | PenaltyBreakBeforeFirstCallParameter: 19 118 | PenaltyBreakComment: 1 119 | PenaltyBreakFirstLessLess: 120 120 | PenaltyBreakString: 1000 121 | PenaltyBreakTemplateDeclaration: 10 122 | PenaltyExcessCharacter: 1000000 123 | PenaltyReturnTypeOnItsOwnLine: 1000000 124 | PenaltyIndentedWhitespace: 0 125 | PointerAlignment: Left 126 | QualifierAlignment: Leave 127 | ReflowComments: true 128 | SortIncludes: false 129 | SortJavaStaticImport: Before 130 | SortUsingDeclarations: false 131 | SpaceAfterCStyleCast: false 132 | SpaceAfterLogicalNot: false 133 | SpaceAfterTemplateKeyword: true 134 | SpaceBeforeAssignmentOperators: true 135 | SpaceBeforeCaseColon: false 136 | SpaceBeforeCpp11BracedList: false 137 | SpaceBeforeCtorInitializerColon: true 138 | SpaceBeforeInheritanceColon: true 139 | SpaceBeforeParens: ControlStatements 140 | SpaceAroundPointerQualifiers: Default 141 | SpaceBeforeRangeBasedForLoopColon: true 142 | SpaceInEmptyBlock: false 143 | SpaceInEmptyParentheses: false 144 | SpacesBeforeTrailingComments: 1 145 | SpacesInAngles: false 146 | SpacesInConditionalStatement: false 147 | SpacesInContainerLiterals: true 148 | SpacesInCStyleCastParentheses: false 149 | SpacesInParentheses: false 150 | SpacesInSquareBrackets: false 151 | SpaceBeforeSquareBrackets: false 152 | BitFieldColonSpacing: Both 153 | Standard: Latest 154 | StatementMacros: 155 | - MZ_ALWAYS_INLINE 156 | - MZ_CONSTRAINED_TEMPLATE 157 | - MZ_CONST_GETTER 158 | - MZ_CONST_INLINE_GETTER 159 | - MZ_NODISCARD 160 | - MZ_NODISCARD_CTOR 161 | - MZ_PURE_GETTER 162 | - MZ_PURE_INLINE_GETTER 163 | TabWidth: 4 164 | TypenameMacros: 165 | UseCRLF: false 166 | UseTab: Always 167 | WhitespaceSensitiveMacros: 168 | - MZ_CONCAT 169 | - MZ_CONSTRAINED_TEMPLATE 170 | - MZ_FOR_EACH 171 | -------------------------------------------------------------------------------- /tests/main.cpp: -------------------------------------------------------------------------------- 1 | // This file is a part of marzer/tagged_ptr and is subject to the the terms of the MIT license. 2 | // Copyright (c) Mark Gillard 3 | // See https://github.com/marzer/tagged_ptr/blob/master/LICENSE for the full license text. 4 | // SPDX-License-Identifier: MIT 5 | 6 | #include 7 | 8 | #ifdef _MSC_VER 9 | #pragma warning(push, 0) 10 | #endif 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | #ifdef _MSC_VER 18 | #pragma warning(pop) 19 | #endif 20 | 21 | using namespace mz; 22 | using mz::tagged_ptr; 23 | 24 | namespace 25 | { 26 | template 27 | [[nodiscard]] static constexpr T* apply_offset(T* ptr, Offset offset) noexcept 28 | { 29 | return const_cast( // 30 | reinterpret_cast( // 31 | reinterpret_cast( // 32 | static_cast(ptr)) 33 | + offset)); 34 | } 35 | } 36 | 37 | // check all static invariants 38 | template 39 | struct tagged_ptr_static_checks final 40 | { 41 | // type traits 42 | using tptr = tagged_ptr; 43 | static_assert(sizeof(tptr) == sizeof(T*)); 44 | static_assert(std::is_trivially_copyable_v); 45 | static_assert(std::is_trivially_destructible_v); 46 | static_assert(std::is_standard_layout_v); 47 | 48 | // static members and typedefs 49 | static_assert(std::is_same_v); 50 | static_assert(std::is_same_v); 51 | static_assert(std::is_same_v); 52 | static_assert(tptr::alignment == min_align); 53 | static_assert(tptr::tag_bit_count >= bit_width(min_align - 1u)); 54 | 55 | // tag size 56 | using tag_type = typename tptr::tag_type; 57 | static_assert(sizeof(tag_type) * CHAR_BIT >= tptr::tag_bit_count); 58 | static_assert(tptr::max_tag == bit_fill_right(tptr::tag_bit_count)); 59 | 60 | // to_address 61 | #if MZ_CPP >= 20 62 | static_assert(std::is_same_v())), T*>); 63 | #endif 64 | }; 65 | 66 | #define CHECK_TRAITS(type, min_align) \ 67 | template struct tagged_ptr_static_checks; \ 68 | template struct tagged_ptr_static_checks; \ 69 | template struct tagged_ptr_static_checks; \ 70 | template struct tagged_ptr_static_checks 71 | 72 | CHECK_TRAITS(void, 2); 73 | CHECK_TRAITS(void, 4); 74 | CHECK_TRAITS(void, 8); 75 | CHECK_TRAITS(void, 16); 76 | CHECK_TRAITS(void, 32); 77 | CHECK_TRAITS(void, 64); 78 | CHECK_TRAITS(void, 128); 79 | CHECK_TRAITS(void, 256); 80 | CHECK_TRAITS(void, 512); 81 | CHECK_TRAITS(void, 1024); 82 | CHECK_TRAITS(void, 2048); 83 | CHECK_TRAITS(void, 4096); 84 | CHECK_TRAITS(void, 8192); 85 | CHECK_TRAITS(void, 16384); 86 | CHECK_TRAITS(void, 32768); 87 | CHECK_TRAITS(int32_t, 4); 88 | CHECK_TRAITS(int32_t, 8); 89 | CHECK_TRAITS(int32_t, 16); 90 | CHECK_TRAITS(int32_t, 32); 91 | CHECK_TRAITS(int32_t, 64); 92 | CHECK_TRAITS(int32_t, 128); 93 | CHECK_TRAITS(int32_t, 256); 94 | CHECK_TRAITS(int32_t, 512); 95 | CHECK_TRAITS(int32_t, 1024); 96 | CHECK_TRAITS(int32_t, 2048); 97 | CHECK_TRAITS(int32_t, 4096); 98 | CHECK_TRAITS(int32_t, 8192); 99 | CHECK_TRAITS(int32_t, 16384); 100 | CHECK_TRAITS(int32_t, 32768); 101 | CHECK_TRAITS(int64_t, 8); 102 | CHECK_TRAITS(int64_t, 16); 103 | CHECK_TRAITS(int64_t, 32); 104 | CHECK_TRAITS(int64_t, 64); 105 | CHECK_TRAITS(int64_t, 128); 106 | CHECK_TRAITS(int64_t, 256); 107 | CHECK_TRAITS(int64_t, 512); 108 | CHECK_TRAITS(int64_t, 1024); 109 | CHECK_TRAITS(int64_t, 2048); 110 | CHECK_TRAITS(int64_t, 4096); 111 | CHECK_TRAITS(int64_t, 8192); 112 | CHECK_TRAITS(int64_t, 16384); 113 | CHECK_TRAITS(int64_t, 32768); 114 | 115 | // check that invocation and noexcept propagation works correctly for function pointers 116 | static_assert(std::is_invocable_v>); 117 | static_assert(std::is_invocable_v>); 118 | static_assert(std::is_nothrow_invocable_v>); 119 | #if !MZ_ICC 120 | static_assert(!std::is_nothrow_invocable_v>); 121 | #endif 122 | 123 | // check deduction guides 124 | static_assert(std::is_same_v{}, 0u }), tagged_ptr>); 125 | static_assert(std::is_same_v{} }), tagged_ptr>); 126 | 127 | namespace 128 | { 129 | template 130 | struct aligned 131 | { 132 | alignas(align) unsigned char kek; 133 | }; 134 | static_assert(alignof(aligned<32>) == 32); 135 | } 136 | 137 | TEST_CASE("tagged_ptr - basic initialization") 138 | { 139 | using tp = tagged_ptr; // 4 free bits 140 | static_assert(sizeof(tp) == sizeof(void*)); 141 | 142 | tp val; 143 | CHECK(val.ptr() == static_cast(nullptr)); 144 | CHECK(val == static_cast(nullptr)); 145 | CHECK(val == static_cast(nullptr)); 146 | CHECK(static_cast(nullptr) == val); 147 | CHECK(static_cast(nullptr) == val); 148 | CHECK(val.tag() == 0u); 149 | CHECK(tp::can_store_ptr(nullptr)); 150 | CHECK(tp::can_store_tag(0u)); 151 | 152 | auto ptr = reinterpret_cast(uintptr_t{ 0x12345670u }); 153 | val = tp{ ptr }; 154 | CHECK(val.ptr() == ptr); 155 | CHECK(val.tag() == 0u); 156 | 157 | val = tp{ ptr, 0b1010u }; 158 | CHECK(val.ptr() == ptr); 159 | CHECK(val.tag() == 0b1010u); 160 | 161 | if constexpr (MZ_ARCH_AMD64) 162 | { 163 | // see https://en.wikipedia.org/wiki/X86-64#Virtual_address_space_details 164 | ptr = reinterpret_cast(static_cast(0xFFFF800000000000ull)); 165 | val = tp{ ptr }; 166 | CHECK(val.ptr() == ptr); 167 | CHECK(val.tag() == 0u); 168 | 169 | val = tp{ ptr, 0b1010u }; 170 | CHECK(val.ptr() == ptr); 171 | CHECK(val.tag() == 0b1010u); 172 | } 173 | } 174 | 175 | TEST_CASE("tagged_ptr - integral tags") 176 | { 177 | using tp = tagged_ptr; // 4 free bits 178 | static_assert(sizeof(tp) == sizeof(void*)); 179 | 180 | auto ptr = reinterpret_cast(uintptr_t{ 0x12345670u }); 181 | tp val{ ptr, 0b1100u }; 182 | CHECK(val.ptr() == ptr); 183 | CHECK(val == ptr); 184 | CHECK(val.tag() == 0b1100u); 185 | 186 | val.tag(0b1111u); 187 | CHECK(val.tag() == 0b1111u); 188 | val.tag(0b0000u); 189 | CHECK(val.tag() == 0b0000u); 190 | } 191 | 192 | TEST_CASE("tagged_ptr - enum tags") 193 | { 194 | using tp = tagged_ptr; // 4 free bits 195 | static_assert(sizeof(tp) == sizeof(void*)); 196 | 197 | enum class an_enum : unsigned 198 | { 199 | zero = 0, 200 | first = 0b1100u, 201 | second = 0b1111u 202 | }; 203 | 204 | auto ptr = reinterpret_cast(uintptr_t{ 0x12345670u }); 205 | tp val{ ptr, an_enum::first }; 206 | CHECK(val.ptr() == ptr); 207 | CHECK(val == ptr); 208 | CHECK(val.tag() == an_enum::first); 209 | 210 | val.tag(an_enum::second); 211 | CHECK(val.tag() == an_enum::second); 212 | val.tag(an_enum::zero); 213 | CHECK(val.tag() == an_enum::zero); 214 | } 215 | 216 | TEST_CASE("tagged_ptr - pod tags") 217 | { 218 | struct data 219 | { 220 | char val; 221 | }; 222 | static_assert(sizeof(data) == 1); 223 | 224 | using align_big = aligned<(1u << (CHAR_BIT * sizeof(data)))>; 225 | 226 | tagged_ptr ptr; 227 | static_assert(sizeof(ptr) == sizeof(void*)); 228 | static_assert(decltype(ptr)::tag_bit_count >= sizeof(data) * CHAR_BIT); 229 | 230 | CHECK(ptr.ptr() == nullptr); 231 | CHECK(ptr == nullptr); 232 | CHECK(ptr.tag() == 0u); 233 | 234 | ptr.tag(data{ 'k' }); 235 | CHECK(ptr.ptr() == nullptr); 236 | CHECK(ptr.tag() != 0u); 237 | CHECK(ptr.tag().val == 'k'); 238 | 239 | std::vector> ptrs; 240 | ptrs.reserve(1024u); 241 | for (size_t i = 0; i < 1024; i++) 242 | { 243 | ptrs.push_back(std::make_unique()); 244 | ptr = ptrs.back().get(); 245 | CHECK(ptr.ptr() == ptrs.back().get()); 246 | CHECK(ptr.tag().val == 'k'); 247 | } 248 | 249 | ptr = tagged_ptr{}; 250 | CHECK(ptr.ptr() == nullptr); 251 | CHECK(ptr.tag() == 0u); 252 | 253 | ptr = { ptrs.back().get(), data{ 'k' } }; 254 | CHECK(ptr.ptr() == ptrs.back().get()); 255 | CHECK(ptr.tag().val == 'k'); 256 | } 257 | 258 | TEST_CASE("tagged_ptr - operators") 259 | { 260 | struct vec3i 261 | { 262 | int32_t x, y, z; 263 | }; 264 | std::array vecs; 265 | tagged_ptr ptr{ &vecs[0], tagged_ptr::max_tag }; 266 | tagged_ptr ptr2{ &vecs[5], tagged_ptr::max_tag }; 267 | static_assert(sizeof(ptr) == sizeof(void*)); 268 | 269 | CHECK(ptr); 270 | CHECK(!!ptr); 271 | CHECK(&vecs[0] == ptr); 272 | CHECK(&vecs[5] == ptr2); 273 | 274 | ptr2 = &vecs[8]; 275 | CHECK(&vecs[8] == ptr2); 276 | CHECK(ptr2.ptr() - &vecs[0] == 8); 277 | CHECK(&vecs[0] - ptr2.ptr() == -8); 278 | ptr2 = ptr2.ptr() + 1; 279 | CHECK(&vecs[9] == ptr2); 280 | CHECK(ptr2.ptr() - &vecs[0] == 9); 281 | CHECK(&vecs[0] - ptr2.ptr() == -9); 282 | ptr2 = ptr2.ptr() - 3; 283 | CHECK(&vecs[6] == ptr2); 284 | CHECK(ptr2.ptr() - &vecs[0] == 6); 285 | CHECK(&vecs[0] - ptr2.ptr() == -6); 286 | ptr2 = ptr2.ptr() - 1; 287 | CHECK(&vecs[5] == ptr2); 288 | CHECK(ptr2.ptr() - &vecs[0] == 5); 289 | CHECK(&vecs[0] - ptr2.ptr() == -5); 290 | ptr2 = ptr2.ptr() + 1; 291 | CHECK(&vecs[6] == ptr2); 292 | CHECK(ptr2.ptr() - &vecs[0] == 6); 293 | CHECK(&vecs[0] - ptr2.ptr() == -6); 294 | 295 | CHECK(&vecs[0] == &(*ptr)); 296 | CHECK(&vecs[6] == &(*ptr2)); 297 | 298 | vecs[0] = { 1, 2, 3 }; 299 | CHECK((*ptr).x == 1); 300 | CHECK((*ptr).y == 2); 301 | CHECK((*ptr).z == 3); 302 | CHECK(ptr->x == 1); 303 | CHECK(ptr->y == 2); 304 | CHECK(ptr->z == 3); 305 | vecs[6] = { 4, 5, 6 }; 306 | CHECK((*ptr2).x == 4); 307 | CHECK((*ptr2).y == 5); 308 | CHECK((*ptr2).z == 6); 309 | CHECK(ptr2->x == 4); 310 | CHECK(ptr2->y == 5); 311 | CHECK(ptr2->z == 6); 312 | 313 | tagged_ptr ptr3{ &vecs[0] }; 314 | CHECK(ptr3 == &vecs[0]); 315 | ptr3 = apply_offset(ptr3.ptr(), sizeof(vec3i) * 2); 316 | CHECK(ptr3 == &vecs[2]); 317 | ptr3 = apply_offset(ptr3.ptr(), static_cast(sizeof(vec3i)) * -1); 318 | CHECK(ptr3 == &vecs[1]); 319 | } 320 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # `mz::tagged_ptr` [![MIT license](docs/images/badge-license-MIT.svg)](./LICENSE) [![C++17](docs/images/badge-c++17.svg)][cpp_compilers] [![Sponsor](docs/images/badge-sponsor.svg)][sponsor] [![Gitter](docs/images/badge-gitter.svg)][gitter] 2 | 3 | A non-owning tagged pointer type for C++. 4 | 5 | Requires C++17. 6 | 7 |
8 | 9 | ## _"What is a tagged pointer?"_ 10 | 11 | A special pointer that uses some otherwise-unused bits of its memory representation to stash some extra data without taking up any more space (i.e. it still has `sizeof(void*)`). 12 | 13 | From [wikipedia/tagged_pointer](https://en.wikipedia.org/wiki/Tagged_pointer): 14 | 15 | > In computer science, a tagged pointer is a pointer (concretely a memory address) with additional data associated with it, such as an indirection bit or reference count. This additional data is often "folded" into the pointer, meaning stored inline in the data representing the address, taking advantage of certain properties of memory addressing. 16 | 17 |
18 | 19 | ## Features 20 | 21 | - Familiar `std::unique_ptr`-like interface 22 | - Support for storing enums and trivially-copyable structs in the tag data 23 | - Lots of static checks and debug assertions to make sure you don't do The Bad™ 24 | 25 |
26 | 27 | ## Synopsis 28 | 29 | ```cpp 30 | namespace mz 31 | { 32 | // template params: 33 | // T the pointed-to type 34 | // Align the minimum alignment of any value stored in the pointer 35 | // 36 | // note: 37 | // functions and `void` do not have a default alignment; 38 | // you must explicitly specify Align for pointers to these types. 39 | template 40 | class tagged_ptr 41 | { 42 | //------------------------------------------ 43 | // typedefs + constants 44 | //------------------------------------------ 45 | 46 | using element_type = T; 47 | using pointer = T*; 48 | using const_pointer = const T*; 49 | using tag_type = /* unsigned integer large enough to store the tag bits */; 50 | 51 | static constexpr size_t alignment = Align; 52 | static constexpr size_t tag_bit_count = /* the number of tag bits that may be stored */; 53 | static constexpr tag_type max_tag = /* the largest tag value for this pointer */; 54 | 55 | //------------------------------------------ 56 | // construction, copying, destruction 57 | //------------------------------------------ 58 | 59 | // default construct the pointer and tag bits to zero 60 | constexpr tagged_ptr() noexcept = default; 61 | 62 | // initialize using nullptr 63 | constexpr tagged_ptr(nullptr_t) noexcept; 64 | 65 | // construct from a pointer, set tag bits to zero 66 | explicit tagged_ptr(pointer value) noexcept; 67 | 68 | // construct from a pointer and tag bits 69 | // 70 | // tag_value may be an integer, enum, or trivial object type small enough 71 | template 72 | tagged_ptr(pointer value, const U& tag_value) noexcept; 73 | 74 | // tagged_ptr is trivially-copyable and trivially-destructible 75 | constexpr tagged_ptr(const tagged_ptr&) noexcept = default; 76 | constexpr tagged_ptr& operator=(const tagged_ptr&) noexcept = default; 77 | ~tagged_ptr() noexcept = default; 78 | 79 | //------------------------------------------ 80 | // retrieving the pointer value 81 | //------------------------------------------ 82 | 83 | // gets the pointer value 84 | pointer ptr() const noexcept; 85 | 86 | // gets the pointer value (alias for ptr()) 87 | pointer get() const noexcept; 88 | 89 | // gets the pointer value 90 | explicit operator pointer() const noexcept; 91 | 92 | // returns a reference to the pointed object 93 | // 94 | // this is only available when T is an object type 95 | element_type& operator*() const noexcept; 96 | 97 | // invokes the -> operator on the pointed object 98 | // 99 | // this is only available when T is a class type 100 | pointer operator->() const noexcept; 101 | 102 | //------------------------------------------ 103 | // changing the pointer 104 | //------------------------------------------ 105 | 106 | // changes the pointer value without changing the tag bits 107 | tagged_ptr& ptr(pointer value) noexcept; 108 | 109 | // changes the pointer value without changing the tag bits 110 | tagged_ptr& operator=(pointer rhs) noexcept; 111 | 112 | // clears the pointer value without changing the tag bits 113 | constexpr tagged_ptr& clear_ptr() noexcept; 114 | 115 | // checks if a raw pointer can be stored without clipping into the tag bits 116 | static bool can_store_ptr(pointer value) noexcept; 117 | 118 | //------------------------------------------ 119 | // retrieving the tag bits 120 | //------------------------------------------ 121 | 122 | // gets the tag bits 123 | // 124 | // U defaults to tag_type, but can be any compatible unsigned integer/enum 125 | // or trivially-copyable type 126 | template 127 | U tag() const noexcept; 128 | 129 | // gets the value of a particular tag bit 130 | bool tag_bit(size_t tag_bit_index) const noexcept; 131 | 132 | //------------------------------------------ 133 | // changing the tag 134 | //------------------------------------------ 135 | 136 | // sets the tag bits 137 | // 138 | // tag_value may be an unsigned integer/enum or a trivially-copyable type small enough 139 | template 140 | tagged_ptr& tag(const U& tag_value) noexcept; 141 | 142 | // sets the value of a particular tag bit 143 | tagged_ptr& tag_bit(size_t tag_bit_index, bool val) noexcept; 144 | 145 | // clears the tag bits 146 | constexpr tagged_ptr& clear_tag() noexcept; 147 | 148 | // checks if a tag value has compatible traits (copyable, small enough, etc.) 149 | // and can be stored without clipping into the pointer bits 150 | template 151 | static bool can_store_tag(const U& tag_value) noexcept; 152 | 153 | //------------------------------------------ 154 | // reset() 155 | //------------------------------------------ 156 | 157 | // resets both the pointer value and tag bits to zero 158 | constexpr tagged_ptr& reset() noexcept; 159 | 160 | // overrides the pointer value and resets the tag bits to zero 161 | tagged_ptr& reset(pointer value) noexcept; 162 | 163 | // overrides both the pointer value and the tag bits 164 | // 165 | // tag_value may be an unsigned integer/enum or a trivially-copyable type small enough 166 | template 167 | tagged_ptr& reset(pointer value, const U& tag_value) noexcept; 168 | 169 | //------------------------------------------ 170 | // comparison 171 | //------------------------------------------ 172 | 173 | // returns true if the pointer value is non-null (tag bits are ignored) 174 | explicit operator bool() const noexcept; 175 | 176 | // compares two tagged pointers for exact equality (tag bits are NOT ignored) 177 | friend constexpr bool operator==(tagged_ptr lhs, tagged_ptr rhs) noexcept; 178 | friend constexpr bool operator!=(tagged_ptr lhs, tagged_ptr rhs) noexcept; 179 | 180 | // compares a tagged pointer with a raw pointer of the same type (tag bits are ignored) 181 | friend bool operator==(tagged_ptr lhs, const_pointer rhs) noexcept; 182 | friend bool operator!=(tagged_ptr lhs, const_pointer rhs) noexcept; 183 | friend bool operator==(const_pointer lhs, tagged_ptr rhs) noexcept; 184 | friend bool operator!=(const_pointer lhs, tagged_ptr rhs) noexcept; 185 | 186 | //------------------------------------------ 187 | // function pointers 188 | //------------------------------------------ 189 | 190 | // invokes the function call operator on the pointed function 191 | // 192 | // this is only available when T is a function 193 | template 194 | decltype(auto) operator()(U&&... args) const noexcept(/*...*/); 195 | }; 196 | 197 | // deduction guides 198 | template 199 | tagged_ptr(T*) -> tagged_ptr; 200 | 201 | template 202 | tagged_ptr(T*, U) -> tagged_ptr; 203 | } 204 | 205 | // std::pointer_traits specialization 206 | namespace std 207 | { 208 | template 209 | struct pointer_traits>; 210 | } 211 | 212 | ``` 213 | 214 |
215 | 216 | ## Usage 217 | 218 | The library is a single-header so the easiest way to use it is to drop [tagged_ptr.hpp] somewhere in your project. 219 | 220 | Alternatively you can add `include` to your include paths then `#include ` 221 | 222 | There is also support for use as a `meson.build` subproject. 223 | 224 |
225 | 226 | ## Configuration 227 | 228 | Macros you can define to customize how mz::tagged_ptr works. `#define` them in your build system or before including the header. 229 | 230 | | Define | Type | Description | Default | 231 | | -------------------------- | -------------- | -------------------------------------------------------------------------------------------- | -------------------------- | 232 | | `MZ_ASSERT()` | Function macro | Debug assert function. | `assert()` | 233 | | `MZ_TAGGED_PTR_BITS` | Integer | The number of pointer bits actually used by the environment. [1](#config-note-1) | `sizeof(void*) * CHAR_BIT` | 234 | | `MZ_TAGGED_PTR_HAS_TRAITS` | Boolean | Should a specialization of `std::pointer_traits` be included? [2](#config-note-2) | `1` | 235 | 236 | 1. Some platforms will not use the full range of bits in the pointer, leaving some region of high bits essentially unused (e.g. [AMD64] may only use 48 or 57 bits). Defining this will allow `mz::tagged_pointer` to take advantage of these extra bits by shifting them down into the tag section. ⚠️ Very non-portable; use with caution! 237 | 2. Implies `#include ` - users wishing to keep compile times lower and not needing the pointer traits might wish to set this to `0`. 238 | 239 |
240 | 241 | ## Caveats 242 | 243 | - ⚠️ So far I've only been able to test this on x86 and AMD64. I welcome help testing it on other platforms! (see [Contributing]) 244 | - ⚠️ Absolutely no idea if this will work on big-endian systems. Help welcome! (see [Contributing]) 245 | - ⚠️ Some environments will perform pointer tagging natively (e.g. [Android on Armv8 AArch64](https://source.android.com/docs/security/test/tagged-pointers)); I recommend not using this class in those contexts. 246 | 247 |
248 | 249 | ## Contributing 250 | 251 | There are three ways you can contribute: 252 | 253 | 1. Reporting bug or making feature requests [here](https://github.com/marzer/tagged_ptr/issues/new) 254 | 2. Opening a pull request (see below) 255 | 3. Becoming a [sponsor] ❤️ 256 | 257 | ### Pull requests 258 | 259 | `tagged_ptr.hpp` is programmatically extracted from a much larger project so I won't accept pull requests made for this repository directly; if you wish to contribute a bugfix or a feature, please find the `tagged_ptr` implementation [in this project](https://github.com/marzer/muu) and propose your changes there instead. I will then propagate them to this satellite library when they are merged. 260 | 261 |
262 | 263 | ## License 264 | 265 | MIT. See [LICENSE](LICENSE). 266 | 267 | [tagged_ptr.hpp]: include/mz/tagged_ptr.hpp 268 | [license]: ./LICENSE 269 | [cpp_compilers]: https://en.cppreference.com/w/cpp/compiler_support 270 | [gitter]: https://gitter.im/marzer/community 271 | [sponsor]: https://github.com/sponsors/marzer 272 | [contributing]: #Contributing 273 | [amd64]: https://en.wikipedia.org/wiki/X86-64 274 | -------------------------------------------------------------------------------- /include/mz/tagged_ptr.hpp: -------------------------------------------------------------------------------- 1 | //---------------------------------------------------------------------------------------------------------------------- 2 | // 3 | // mz::tagged_ptr 4 | // https://github.com/marzer/tagged_ptr 5 | // SPDX-License-Identifier: MIT 6 | // 7 | //---------------------------------------------------------------------------------------------------------------------- 8 | // !!!!! THIS FILE WAS ASSEMBLED FROM MULTIPLE HEADER FILES BY A SCRIPT - PLEASE DON'T EDIT IT DIRECTLY !!!!! 9 | //---------------------------------------------------------------------------------------------------------------------- 10 | // 11 | // MIT License 12 | // 13 | // Copyright (c) Mark Gillard 14 | // 15 | // Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated 16 | // documentation files (the "Software"), to deal in the Software without restriction, including without limitation the 17 | // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to 18 | // permit persons to whom the Software is furnished to do so, subject to the following conditions: 19 | // 20 | // The above copyright notice and this permission notice shall be included in all copies or substantial portions of 21 | // the Software. 22 | // 23 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO 24 | // THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 25 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 26 | // TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 27 | // SOFTWARE. 28 | // 29 | //---------------------------------------------------------------------------------------------------------------------- 30 | #ifndef MZ_TAGGED_PTR_HPP 31 | #define MZ_TAGGED_PTR_HPP 32 | 33 | #define MZ_TAGGED_PTR_VERSION_MAJOR 0 34 | #define MZ_TAGGED_PTR_VERSION_MINOR 4 35 | #define MZ_TAGGED_PTR_VERSION_PATCH 0 36 | 37 | #ifndef MZ_CPP 38 | #ifdef _MSVC_LANG 39 | #if _MSVC_LANG > __cplusplus 40 | #define MZ_CPP _MSVC_LANG 41 | #endif 42 | #endif 43 | #ifndef MZ_CPP 44 | #define MZ_CPP __cplusplus 45 | #endif 46 | #if MZ_CPP >= 202900L 47 | #undef MZ_CPP 48 | #define MZ_CPP 29 49 | #elif MZ_CPP >= 202600L 50 | #undef MZ_CPP 51 | #define MZ_CPP 26 52 | #elif MZ_CPP >= 202302L 53 | #undef MZ_CPP 54 | #define MZ_CPP 23 55 | #elif MZ_CPP >= 202002L 56 | #undef MZ_CPP 57 | #define MZ_CPP 20 58 | #elif MZ_CPP >= 201703L 59 | #undef MZ_CPP 60 | #define MZ_CPP 17 61 | #elif MZ_CPP >= 201402L 62 | #undef MZ_CPP 63 | #define MZ_CPP 14 64 | #elif MZ_CPP >= 201103L 65 | #undef MZ_CPP 66 | #define MZ_CPP 11 67 | #else 68 | #undef MZ_CPP 69 | #define MZ_CPP 0 70 | #endif 71 | #endif 72 | 73 | #ifndef MZ_MAKE_VERSION 74 | #define MZ_MAKE_VERSION(major, minor, patch) (((major)*10000) + ((minor)*100) + ((patch))) 75 | #endif 76 | 77 | #ifndef MZ_INTELLISENSE 78 | #ifdef __INTELLISENSE__ 79 | #define MZ_INTELLISENSE 1 80 | #else 81 | #define MZ_INTELLISENSE 0 82 | #endif 83 | #endif 84 | 85 | #ifndef MZ_DOXYGEN 86 | #if defined(DOXYGEN) || defined(__DOXYGEN) || defined(__DOXYGEN__) || defined(__doxygen__) || defined(__POXY__) \ 87 | || defined(__poxy__) 88 | #define MZ_DOXYGEN 1 89 | #else 90 | #define MZ_DOXYGEN 0 91 | #endif 92 | #endif 93 | 94 | #ifndef MZ_CLANG 95 | #ifdef __clang__ 96 | #define MZ_CLANG __clang_major__ 97 | #else 98 | #define MZ_CLANG 0 99 | #endif 100 | 101 | // special handling for apple clang; see: 102 | // - https://github.com/marzer/tomlplusplus/issues/189 103 | // - https://en.wikipedia.org/wiki/Xcode 104 | // - 105 | // https://stackoverflow.com/questions/19387043/how-can-i-reliably-detect-the-version-of-clang-at-preprocessing-time 106 | #if MZ_CLANG && defined(__apple_build_version__) 107 | #undef MZ_CLANG 108 | #define MZ_CLANG_VERSION MZ_MAKE_VERSION(__clang_major__, __clang_minor__, __clang_patchlevel__) 109 | #if MZ_CLANG_VERSION >= MZ_MAKE_VERSION(15, 0, 0) 110 | #define MZ_CLANG 16 111 | #elif MZ_CLANG_VERSION >= MZ_MAKE_VERSION(14, 3, 0) 112 | #define MZ_CLANG 15 113 | #elif MZ_CLANG_VERSION >= MZ_MAKE_VERSION(14, 0, 0) 114 | #define MZ_CLANG 14 115 | #elif MZ_CLANG_VERSION >= MZ_MAKE_VERSION(13, 1, 6) 116 | #define MZ_CLANG 13 117 | #elif MZ_CLANG_VERSION >= MZ_MAKE_VERSION(13, 0, 0) 118 | #define MZ_CLANG 12 119 | #elif MZ_CLANG_VERSION >= MZ_MAKE_VERSION(12, 0, 5) 120 | #define MZ_CLANG 11 121 | #elif MZ_CLANG_VERSION >= MZ_MAKE_VERSION(12, 0, 0) 122 | #define MZ_CLANG 10 123 | #elif MZ_CLANG_VERSION >= MZ_MAKE_VERSION(11, 0, 3) 124 | #define MZ_CLANG 9 125 | #elif MZ_CLANG_VERSION >= MZ_MAKE_VERSION(11, 0, 0) 126 | #define MZ_CLANG 8 127 | #elif MZ_CLANG_VERSION >= MZ_MAKE_VERSION(10, 0, 1) 128 | #define MZ_CLANG 7 129 | #else 130 | #define MZ_CLANG 6 // not strictly correct but doesn't matter below this 131 | #endif 132 | #undef MZ_CLANG_VERSION 133 | #endif 134 | #endif 135 | 136 | #ifndef MZ_ICC 137 | #ifdef __INTEL_COMPILER 138 | #define MZ_ICC __INTEL_COMPILER 139 | #ifdef __ICL 140 | #define MZ_ICC_CL MZ_ICC 141 | #else 142 | #define MZ_ICC_CL 0 143 | #endif 144 | #else 145 | #define MZ_ICC 0 146 | #define MZ_ICC_CL 0 147 | #endif 148 | #endif 149 | 150 | #ifndef MZ_MSVC_LIKE 151 | #ifdef _MSC_VER 152 | #define MZ_MSVC_LIKE _MSC_VER 153 | #else 154 | #define MZ_MSVC_LIKE 0 155 | #endif 156 | #endif 157 | 158 | #ifndef MZ_MSVC 159 | #if MZ_MSVC_LIKE && !MZ_CLANG && !MZ_ICC 160 | #define MZ_MSVC MZ_MSVC_LIKE 161 | #else 162 | #define MZ_MSVC 0 163 | #endif 164 | #endif 165 | 166 | #ifndef MZ_GCC_LIKE 167 | #ifdef __GNUC__ 168 | #define MZ_GCC_LIKE __GNUC__ 169 | #else 170 | #define MZ_GCC_LIKE 0 171 | #endif 172 | #endif 173 | 174 | #ifndef MZ_GCC 175 | #if MZ_GCC_LIKE && !MZ_CLANG && !MZ_ICC 176 | #define MZ_GCC MZ_GCC_LIKE 177 | #else 178 | #define MZ_GCC 0 179 | #endif 180 | #endif 181 | 182 | #ifndef MZ_CUDA 183 | #if defined(__CUDACC__) || defined(__CUDA_ARCH__) || defined(__CUDA_LIBDEVICE__) 184 | #define MZ_CUDA 1 185 | #else 186 | #define MZ_CUDA 0 187 | #endif 188 | #endif 189 | 190 | #ifndef MZ_ARCH_AMD64 191 | #if defined(__amd64__) || defined(__amd64) || defined(__x86_64__) || defined(__x86_64) || defined(_M_AMD64) 192 | #define MZ_ARCH_AMD64 1 193 | #define MZ_ARCH_BITNESS 64 194 | #else 195 | #define MZ_ARCH_AMD64 0 196 | #endif 197 | #endif 198 | 199 | #ifndef MZ_HAS_BUILTIN 200 | #ifdef __has_builtin 201 | #define MZ_HAS_BUILTIN(name) __has_builtin(name) 202 | #else 203 | #define MZ_HAS_BUILTIN(name) 0 204 | #endif 205 | #endif 206 | 207 | #ifndef MZ_HAS_CPP_ATTR 208 | #ifdef __has_cpp_attribute 209 | #define MZ_HAS_CPP_ATTR(attr) __has_cpp_attribute(attr) 210 | #else 211 | #define MZ_HAS_CPP_ATTR(attr) 0 212 | #endif 213 | #endif 214 | 215 | #ifndef MZ_HAS_ATTR 216 | #ifdef __has_attribute 217 | #define MZ_HAS_ATTR(attr) __has_attribute(attr) 218 | #else 219 | #define MZ_HAS_ATTR(attr) 0 220 | #endif 221 | #endif 222 | 223 | #ifndef MZ_ATTR 224 | #if MZ_CLANG || MZ_GCC_LIKE 225 | #define MZ_ATTR(...) __attribute__((__VA_ARGS__)) 226 | #else 227 | #define MZ_ATTR(...) 228 | #endif 229 | #endif 230 | 231 | #ifndef MZ_DECLSPEC 232 | #if MZ_MSVC_LIKE 233 | #define MZ_DECLSPEC(...) __declspec(__VA_ARGS__) 234 | #else 235 | #define MZ_DECLSPEC(...) 236 | #endif 237 | #endif 238 | 239 | #ifndef MZ_ALWAYS_INLINE 240 | #if MZ_MSVC_LIKE 241 | #define MZ_ALWAYS_INLINE __forceinline 242 | #elif MZ_CLANG || MZ_GCC_LIKE || MZ_HAS_ATTR(__always_inline__) 243 | #define MZ_ALWAYS_INLINE \ 244 | MZ_ATTR(__always_inline__) \ 245 | inline 246 | #else 247 | #define MZ_ALWAYS_INLINE inline 248 | #endif 249 | #endif 250 | 251 | #ifndef MZ_NODISCARD 252 | #if MZ_HAS_CPP_ATTR(nodiscard) >= 201603 253 | #define MZ_NODISCARD [[nodiscard]] 254 | #define MZ_NODISCARD_CLASS [[nodiscard]] 255 | #elif MZ_CLANG || MZ_GCC_LIKE || MZ_HAS_ATTR(__warn_unused_result__) 256 | #define MZ_NODISCARD MZ_ATTR(__warn_unused_result__) 257 | #else 258 | #define MZ_NODISCARD 259 | #endif 260 | #ifndef MZ_NODISCARD_CLASS 261 | #define MZ_NODISCARD_CLASS 262 | #endif 263 | #if MZ_HAS_CPP_ATTR(nodiscard) >= 201907 264 | #define MZ_NODISCARD_CTOR [[nodiscard]] 265 | #else 266 | #define MZ_NODISCARD_CTOR 267 | #endif 268 | #endif 269 | 270 | #ifndef MZ_ASSUME 271 | #if MZ_MSVC_LIKE 272 | #define MZ_ASSUME(expr) __assume(expr) 273 | #elif MZ_ICC || MZ_CLANG || MZ_HAS_BUILTIN(__builtin_assume) 274 | #define MZ_ASSUME(expr) __builtin_assume(expr) 275 | #elif MZ_HAS_CPP_ATTR(assume) >= 202207 276 | #define MZ_ASSUME(expr) [[assume(expr)]] 277 | #elif MZ_HAS_ATTR(__assume__) 278 | #define MZ_ASSUME(expr) __attribute__((__assume__(expr))) 279 | #else 280 | #define MZ_ASSUME(expr) static_cast(0) 281 | #endif 282 | #endif 283 | 284 | #ifndef MZ_PURE 285 | #ifdef NDEBUG 286 | #define MZ_PURE MZ_DECLSPEC(noalias) MZ_ATTR(pure) 287 | #else 288 | #define MZ_PURE 289 | #endif 290 | #endif 291 | #ifndef MZ_CONST 292 | #ifdef NDEBUG 293 | #define MZ_CONST MZ_DECLSPEC(noalias) MZ_ATTR(const) 294 | #else 295 | #define MZ_CONST 296 | #endif 297 | #endif 298 | #ifndef MZ_INLINE_GETTER 299 | #define MZ_INLINE_GETTER \ 300 | MZ_NODISCARD \ 301 | MZ_ALWAYS_INLINE 302 | #endif 303 | #ifndef MZ_PURE_GETTER 304 | #define MZ_PURE_GETTER \ 305 | MZ_NODISCARD \ 306 | MZ_PURE 307 | #endif 308 | #ifndef MZ_PURE_INLINE_GETTER 309 | #define MZ_PURE_INLINE_GETTER \ 310 | MZ_NODISCARD \ 311 | MZ_ALWAYS_INLINE \ 312 | MZ_PURE 313 | #endif 314 | #ifndef MZ_CONST_GETTER 315 | #define MZ_CONST_GETTER \ 316 | MZ_NODISCARD \ 317 | MZ_CONST 318 | #endif 319 | #ifndef MZ_CONST_INLINE_GETTER 320 | #define MZ_CONST_INLINE_GETTER \ 321 | MZ_NODISCARD \ 322 | MZ_ALWAYS_INLINE \ 323 | MZ_CONST 324 | #endif 325 | 326 | #ifndef MZ_TRIVIAL_ABI 327 | #if MZ_CLANG || MZ_HAS_ATTR(__trivial_abi__) 328 | #define MZ_TRIVIAL_ABI MZ_ATTR(__trivial_abi__) 329 | #else 330 | #define MZ_TRIVIAL_ABI 331 | #endif 332 | #endif 333 | 334 | #ifndef MZ_HIDDEN 335 | #if MZ_DOXYGEN 336 | #define MZ_HIDDEN(...) 337 | #define MZ_HIDDEN_BASE(...) 338 | #define MZ_DOXYGEN_ONLY(...) __VA_ARGS__ 339 | #define MZ_IF_DOXYGEN(A, B) A 340 | #else 341 | #define MZ_HIDDEN(...) __VA_ARGS__ 342 | #define MZ_HIDDEN_BASE(...) : __VA_ARGS__ 343 | #define MZ_DOXYGEN_ONLY(...) 344 | #define MZ_IF_DOXYGEN(A, B) B 345 | #endif 346 | #endif 347 | #define MZ_HIDDEN_PARAM(...) MZ_HIDDEN(MZ_COMMA __VA_ARGS__) 348 | 349 | #ifndef MZ_CONCEPTS 350 | #if defined(__cpp_concepts) && __cpp_concepts >= 201907 351 | #define MZ_CONCEPTS 1 352 | #else 353 | #define MZ_CONCEPTS 0 354 | #endif 355 | #endif 356 | 357 | #ifndef MZ_REQUIRES 358 | #if !MZ_DOXYGEN && MZ_CONCEPTS 359 | #define MZ_REQUIRES(...) requires(__VA_ARGS__) 360 | #else 361 | #define MZ_REQUIRES(...) 362 | #endif 363 | #endif 364 | 365 | #ifndef MZ_STD_CONCEPT 366 | #if !MZ_DOXYGEN && defined(__cpp_lib_concepts) && __cpp_lib_concepts >= 202002 367 | #define MZ_STD_CONCEPT(...) __VA_ARGS__ 368 | #else 369 | #define MZ_STD_CONCEPT(...) true 370 | #endif 371 | #endif 372 | 373 | #ifndef MZ_ENABLE_IF 374 | #if !MZ_DOXYGEN 375 | #define MZ_ENABLE_IF_T(T, ...) std::enable_if_t 376 | #define MZ_ENABLE_IF(...) , MZ_ENABLE_IF_T(int, __VA_ARGS__) = 0 377 | #else 378 | #define MZ_ENABLE_IF_T(T, ...) 379 | #define MZ_ENABLE_IF(...) 380 | #endif 381 | #endif 382 | 383 | #ifndef MZ_CONSTRAINED_TEMPLATE 384 | #if !MZ_DOXYGEN 385 | #define MZ_CONSTRAINED_TEMPLATE(condition, ...) \ 386 | template <__VA_ARGS__ MZ_ENABLE_IF(!!(condition))> \ 387 | MZ_REQUIRES(!!(condition)) 388 | #else 389 | #define MZ_CONSTRAINED_TEMPLATE(condition, ...) template <__VA_ARGS__> 390 | #endif 391 | #endif 392 | 393 | #ifndef MZ_HIDDEN_CONSTRAINT 394 | #if !MZ_DOXYGEN 395 | #define MZ_HIDDEN_CONSTRAINT(condition, ...) MZ_CONSTRAINED_TEMPLATE(condition, __VA_ARGS__) 396 | #else 397 | #define MZ_HIDDEN_CONSTRAINT(condition, ...) 398 | #endif 399 | #endif 400 | 401 | #ifndef MZ_HAS_IF_CONSTEVAL 402 | #if defined(__cpp_if_consteval) && __cpp_if_consteval >= 202106 403 | #define MZ_HAS_IF_CONSTEVAL 1 404 | #else 405 | #define MZ_HAS_IF_CONSTEVAL 0 406 | #endif 407 | #endif 408 | 409 | #ifndef MZ_IF_CONSTEVAL 410 | #if MZ_HAS_IF_CONSTEVAL 411 | #define MZ_IF_CONSTEVAL if consteval 412 | #define MZ_IF_RUNTIME if !consteval 413 | #else 414 | #define MZ_IF_CONSTEVAL if (::mz::is_constant_evaluated()) 415 | #define MZ_IF_RUNTIME if (!::mz::is_constant_evaluated()) 416 | #endif 417 | #endif 418 | 419 | #include // CHAR_BIT 420 | #include // size_t 421 | #include // std::memcpy 422 | #include 423 | #include 424 | 425 | #ifndef NDEBUG 426 | #ifndef MZ_ASSERT 427 | #include 428 | #define MZ_ASSERT(...) assert(__VA_ARGS__) 429 | #endif 430 | #else 431 | #define MZ_ASSERT(...) static_cast(0) 432 | #endif 433 | 434 | #ifndef MZ_TAGGED_PTR_HAS_TRAITS 435 | #define MZ_TAGGED_PTR_HAS_TRAITS 1 436 | #endif 437 | #if MZ_TAGGED_PTR_HAS_TRAITS 438 | #include // std::pointer_traits 439 | #endif 440 | 441 | namespace mz 442 | { 443 | using std::size_t; 444 | using std::uintptr_t; 445 | 446 | template 447 | using remove_cvref = std::remove_cv_t>; 448 | 449 | #ifndef MZ_HAS_SNIPPET_META_REMOVE_ENUM 450 | #define MZ_HAS_SNIPPET_META_REMOVE_ENUM 451 | 452 | namespace detail 453 | { 454 | template >> 455 | struct remove_enum_ 456 | { 457 | using type = std::underlying_type_t; 458 | }; 459 | template 460 | struct remove_enum_ 461 | { 462 | using type = T; 463 | }; 464 | template 465 | struct remove_enum_ 466 | { 467 | using type = const volatile typename remove_enum_::type; 468 | }; 469 | template 470 | struct remove_enum_ 471 | { 472 | using type = volatile typename remove_enum_::type; 473 | }; 474 | template 475 | struct remove_enum_ 476 | { 477 | using type = const typename remove_enum_::type; 478 | }; 479 | template 480 | struct remove_enum_ 481 | { 482 | using type = std::add_lvalue_reference_t::type>; 483 | }; 484 | template 485 | struct remove_enum_ 486 | { 487 | using type = std::add_rvalue_reference_t::type>; 488 | }; 489 | } 490 | 491 | template 492 | using remove_enum = typename detail::remove_enum_::type; 493 | 494 | #endif // MZ_HAS_SNIPPET_META_REMOVE_ENUM 495 | 496 | #ifndef MZ_HAS_SNIPPET_META_IS_CVREF 497 | #define MZ_HAS_SNIPPET_META_IS_CVREF 498 | 499 | template 500 | inline constexpr bool is_cvref = std::is_const_v || std::is_volatile_v || std::is_reference_v; 501 | 502 | #endif // MZ_HAS_SNIPPET_META_IS_CVREF 503 | 504 | #ifndef MZ_HAS_SNIPPET_META_IS_UNSIGNED 505 | #define MZ_HAS_SNIPPET_META_IS_UNSIGNED 506 | 507 | template 508 | inline constexpr bool is_unsigned = std::is_unsigned_v>>; 509 | 510 | #endif // MZ_HAS_SNIPPET_META_IS_UNSIGNED 511 | 512 | #ifndef MZ_HAS_SNIPPET_MAX 513 | #define MZ_HAS_SNIPPET_MAX 514 | 515 | template 516 | MZ_PURE_GETTER 517 | constexpr const T& max(const T& val1, const T& val2, const U&... vals) noexcept 518 | { 519 | if constexpr (sizeof...(vals) == 0u) 520 | { 521 | return val1 < val2 ? val2 : val1; 522 | } 523 | else if constexpr (sizeof...(vals) == 2u) 524 | { 525 | return mz::max(mz::max(val1, val2), mz::max(vals...)); 526 | } 527 | else 528 | { 529 | return mz::max(mz::max(val1, val2), vals...); 530 | } 531 | } 532 | 533 | #endif // MZ_HAS_SNIPPET_MAX 534 | 535 | #ifndef MZ_HAS_SNIPPET_CLAMP 536 | #define MZ_HAS_SNIPPET_CLAMP 537 | 538 | template 539 | MZ_PURE_GETTER 540 | constexpr const T& clamp(const T& val, const T& low, const T& high) noexcept 541 | { 542 | return val < low ? low : ((high < val) ? high : val); 543 | } 544 | 545 | #endif // MZ_HAS_SNIPPET_CLAMP 546 | 547 | #ifndef MZ_HAS_SNIPPET_COUNTL_ZERO_NAIVE 548 | #define MZ_HAS_SNIPPET_COUNTL_ZERO_NAIVE 549 | 550 | MZ_CONSTRAINED_TEMPLATE(is_unsigned, typename T) 551 | MZ_CONST_GETTER 552 | constexpr int countl_zero(T val) noexcept 553 | { 554 | static_assert(!is_cvref); 555 | 556 | if (!val) 557 | return static_cast(sizeof(T) * CHAR_BIT); 558 | 559 | using bit_type = std::conditional_t<(sizeof(unsigned) > sizeof(T)), unsigned, T>; 560 | int count = 0; 561 | bit_type bit = bit_type{ 1 } << (sizeof(T) * CHAR_BIT - 1); 562 | while (true) 563 | { 564 | if ((bit & val)) 565 | break; 566 | count++; 567 | bit >>= 1; 568 | } 569 | return count; 570 | } 571 | 572 | #endif // MZ_HAS_SNIPPET_COUNTL_ZERO_NAIVE 573 | 574 | #ifndef MZ_HAS_SNIPPET_BIT_WIDTH 575 | #define MZ_HAS_SNIPPET_BIT_WIDTH 576 | 577 | MZ_CONSTRAINED_TEMPLATE(is_unsigned, typename T) 578 | MZ_CONST_GETTER 579 | constexpr T bit_width(T val) noexcept 580 | { 581 | static_assert(!is_cvref); 582 | 583 | if constexpr (std::is_enum_v) 584 | return static_cast(bit_width(static_cast>(val))); 585 | else 586 | return static_cast(sizeof(T) * CHAR_BIT - static_cast(countl_zero(val))); 587 | } 588 | 589 | #endif // MZ_HAS_SNIPPET_BIT_WIDTH 590 | 591 | #ifndef MZ_HAS_SNIPPET_BIT_CEIL 592 | #define MZ_HAS_SNIPPET_BIT_CEIL 593 | 594 | MZ_CONSTRAINED_TEMPLATE(is_unsigned, typename T) 595 | MZ_CONST_GETTER 596 | constexpr T bit_ceil(T val) noexcept 597 | { 598 | static_assert(!is_cvref); 599 | 600 | if constexpr (std::is_enum_v) 601 | return static_cast(bit_ceil(static_cast>(val))); 602 | else 603 | { 604 | if (!val) 605 | return T{ 1 }; 606 | return T{ 1 } << (sizeof(T) * size_t{ CHAR_BIT } 607 | - static_cast(countl_zero(static_cast(val - T{ 1 })))); 608 | } 609 | } 610 | 611 | #endif // MZ_HAS_SNIPPET_BIT_CEIL 612 | 613 | #ifndef MZ_HAS_SNIPPET_BIT_FLOOR 614 | #define MZ_HAS_SNIPPET_BIT_FLOOR 615 | 616 | MZ_CONSTRAINED_TEMPLATE(is_unsigned, typename T) 617 | MZ_CONST_GETTER 618 | constexpr T bit_floor(T val) noexcept 619 | { 620 | static_assert(!is_cvref); 621 | 622 | if constexpr (std::is_enum_v) 623 | return static_cast(bit_floor(static_cast>(val))); 624 | else 625 | { 626 | if (!val) 627 | return T{ 0 }; 628 | return T{ 1 } << (sizeof(T) * size_t{ CHAR_BIT } - size_t{ 1 } - static_cast(countl_zero(val))); 629 | } 630 | } 631 | 632 | #endif // MZ_HAS_SNIPPET_BIT_FLOOR 633 | 634 | #ifndef MZ_HAS_SNIPPET_BIT_FILL_RIGHT 635 | #define MZ_HAS_SNIPPET_BIT_FILL_RIGHT 636 | 637 | MZ_CONSTRAINED_TEMPLATE(is_unsigned, typename T) 638 | MZ_CONST_GETTER 639 | constexpr T bit_fill_right(size_t count) noexcept 640 | { 641 | static_assert(!is_cvref); 642 | 643 | if constexpr (std::is_enum_v) 644 | return T{ bit_fill_right>(count) }; 645 | else 646 | { 647 | if (!count) 648 | return T{}; 649 | if (count >= CHAR_BIT * sizeof(T)) 650 | return static_cast(~T{}); 651 | return static_cast((T{ 1 } << count) - T{ 1 }); 652 | } 653 | } 654 | 655 | #endif // MZ_HAS_SNIPPET_BIT_FILL_RIGHT 656 | 657 | #ifndef MZ_HAS_SNIPPET_HAS_SINGLE_BIT 658 | #define MZ_HAS_SNIPPET_HAS_SINGLE_BIT 659 | 660 | MZ_CONSTRAINED_TEMPLATE(is_unsigned, typename T) 661 | MZ_CONST_GETTER 662 | constexpr bool has_single_bit(T val) noexcept 663 | { 664 | static_assert(!is_cvref); 665 | 666 | if constexpr (std::is_enum_v) 667 | return has_single_bit(static_cast>(val)); 668 | else 669 | { 670 | return val != T{} && (val & (val - T{ 1 })) == T{}; 671 | } 672 | } 673 | 674 | #endif // MZ_HAS_SNIPPET_HAS_SINGLE_BIT 675 | 676 | #ifndef MZ_HAS_SNIPPET_IS_CONSTANT_EVALUATED 677 | #define MZ_HAS_SNIPPET_IS_CONSTANT_EVALUATED 678 | 679 | MZ_CONST_INLINE_GETTER 680 | constexpr bool is_constant_evaluated() noexcept 681 | { 682 | #if MZ_HAS_IF_CONSTEVAL 683 | 684 | if consteval 685 | { 686 | return true; 687 | } 688 | else 689 | { 690 | return false; 691 | } 692 | 693 | #elif MZ_CLANG >= 9 || MZ_GCC >= 9 || MZ_MSVC >= 1925 || MZ_HAS_BUILTIN(is_constant_evaluated) 694 | 695 | return __builtin_is_constant_evaluated(); 696 | 697 | #elif defined(__cpp_lib_is_constant_evaluated) && __cpp_lib_is_constant_evaluated >= 201811 698 | 699 | return std::is_constant_evaluated(); 700 | 701 | #else 702 | 703 | return false; 704 | 705 | #endif 706 | } 707 | 708 | namespace build 709 | { 710 | inline constexpr bool supports_is_constant_evaluated = is_constant_evaluated(); 711 | } 712 | 713 | #endif // MZ_HAS_SNIPPET_IS_CONSTANT_EVALUATED 714 | 715 | #ifndef MZ_HAS_SNIPPET_ASSUME_ALIGNED 716 | #define MZ_HAS_SNIPPET_ASSUME_ALIGNED 717 | 718 | template 719 | MZ_CONST_INLINE_GETTER 720 | MZ_ATTR(assume_aligned(N)) 721 | constexpr T* assume_aligned(T* ptr) noexcept 722 | { 723 | static_assert(N > 0 && (N & (N - 1u)) == 0u, "assume_aligned() requires a power-of-two alignment value."); 724 | static_assert(!std::is_function_v, "assume_aligned may not be used on functions."); 725 | 726 | MZ_IF_CONSTEVAL 727 | { 728 | return ptr; 729 | } 730 | else 731 | { 732 | MZ_ASSUME((reinterpret_cast(ptr) & (N - uintptr_t{ 1 })) == 0); 733 | 734 | if constexpr (std::is_volatile_v) 735 | { 736 | return static_cast(mz::assume_aligned(const_cast*>(ptr))); 737 | } 738 | else 739 | { 740 | #if MZ_CLANG || MZ_GCC || MZ_HAS_BUILTIN(__builtin_assume_aligned) 741 | 742 | return static_cast(__builtin_assume_aligned(ptr, N)); 743 | 744 | #elif MZ_MSVC 745 | 746 | if constexpr (N < 16384) 747 | return static_cast(__builtin_assume_aligned(ptr, N)); 748 | else 749 | return ptr; 750 | 751 | #elif MZ_ICC 752 | 753 | __assume_aligned(ptr, N); 754 | return ptr; 755 | 756 | #elif defined(__cpp_lib_assume_aligned) 757 | 758 | return std::assume_aligned(ptr); 759 | 760 | #else 761 | 762 | return ptr; 763 | 764 | #endif 765 | } 766 | } 767 | } 768 | 769 | #endif // MZ_HAS_SNIPPET_ASSUME_ALIGNED 770 | 771 | } 772 | 773 | #ifndef MZ_TAGGED_PTR_BITS 774 | #define MZ_TAGGED_PTR_BITS 0 // 0 == "all the bits" 775 | #endif 776 | #if MZ_ARCH_AMD64 && MZ_TAGGED_PTR_BITS \ 777 | && !(MZ_TAGGED_PTR_BITS == 48 || MZ_TAGGED_PTR_BITS == 57 || MZ_TAGGED_PTR_BITS == 64) 778 | #error MZ_TAGGED_PTR_BITS must be 48, 57 or 64 on AMD64. See: https://en.wikipedia.org/wiki/X86-64 779 | #endif 780 | 781 | #define MZ_TAGGED_PTR_TAG_TYPE_CHECKS(Tag) \ 782 | static_assert(!is_cvref, "Tag type may not be explicitly cvref-qualified"); \ 783 | static_assert(std::is_trivially_default_constructible_v, "Tag type must be trivially default-constructible"); \ 784 | static_assert(std::is_trivially_copyable_v, "Tag type must be trivially copyable") 785 | 786 | #define MZ_TAGGED_PTR_TAG_OBJECT_CHECKS(Tag) \ 787 | MZ_TAGGED_PTR_TAG_TYPE_CHECKS(Tag); \ 788 | static_assert((sizeof(Tag) * CHAR_BIT) <= tag_bits, "Tag type must fit in the available tag bits") 789 | 790 | #if MZ_MSVC 791 | #pragma warning(push) 792 | #pragma warning(disable : 4296) // condition is always false/true 793 | #endif 794 | 795 | namespace mz::detail 796 | { 797 | static_assert(MZ_TAGGED_PTR_BITS < sizeof(uintptr_t) * CHAR_BIT); 798 | 799 | inline constexpr size_t tptr_used_bits = MZ_TAGGED_PTR_BITS ? MZ_TAGGED_PTR_BITS : sizeof(uintptr_t) * CHAR_BIT; 800 | inline constexpr size_t tptr_free_bits = sizeof(uintptr_t) * CHAR_BIT - tptr_used_bits; 801 | 802 | // clang-format off 803 | 804 | template 805 | using tptr_uint_for_bits = 806 | std::conditional_t<(Bits <= sizeof(unsigned char) * CHAR_BIT), unsigned char, 807 | std::conditional_t<(Bits <= sizeof(unsigned short) * CHAR_BIT), unsigned short, 808 | std::conditional_t<(Bits <= sizeof(unsigned int) * CHAR_BIT), unsigned int, 809 | std::conditional_t<(Bits <= sizeof(unsigned long) * CHAR_BIT), unsigned long, 810 | std::conditional_t<(Bits <= sizeof(unsigned long long) * CHAR_BIT), unsigned long long, 811 | void 812 | >>>>>; 813 | 814 | template 815 | using tptr_is_enum_or_integer_ = std::disjunction< 816 | std::is_enum, 817 | std::is_same, 818 | std::is_same, 819 | std::is_same, 820 | std::is_same, 821 | std::is_same, 822 | std::is_same, 823 | std::is_same, 824 | std::is_same, 825 | std::is_same, 826 | std::is_same 827 | >; 828 | 829 | // clang-format on 830 | 831 | template 832 | inline constexpr bool tptr_is_enum_or_integer = tptr_is_enum_or_integer_>::value; 833 | 834 | template > 835 | struct tptr_make_unsigned_ 836 | { 837 | using type = std::make_unsigned_t; 838 | }; 839 | template 840 | struct tptr_make_unsigned_ 841 | { 842 | static_assert(tptr_is_enum_or_integer); 843 | 844 | using type = std::make_unsigned_t>; 845 | }; 846 | template 847 | using tptr_make_unsigned = typename tptr_make_unsigned_::type; 848 | 849 | class MZ_TRIVIAL_ABI tptr_base 850 | { 851 | protected: 852 | uintptr_t bits_ = {}; 853 | 854 | MZ_NODISCARD_CTOR 855 | constexpr tptr_base(uintptr_t bits) noexcept // 856 | : bits_{ bits } 857 | {} 858 | 859 | MZ_CONST_INLINE_GETTER 860 | static constexpr uintptr_t pack_ptr_unchecked(uintptr_t ptr) noexcept 861 | { 862 | if constexpr (tptr_free_bits > 0) 863 | return (ptr << tptr_free_bits); 864 | else 865 | return ptr; 866 | } 867 | 868 | public: 869 | MZ_NODISCARD_CTOR 870 | constexpr tptr_base() noexcept = default; 871 | }; 872 | 873 | template 874 | class MZ_TRIVIAL_ABI tptr_aligned_base : public tptr_base 875 | { 876 | protected: 877 | static constexpr size_t tag_bits = (mz::max(mz::bit_width(Align), 1u) - 1u) + detail::tptr_free_bits; 878 | static constexpr uintptr_t tag_mask = bit_fill_right(tag_bits); 879 | static constexpr uintptr_t ptr_mask = ~tag_mask; 880 | 881 | using tag_type = 882 | tptr_uint_for_bits(bit_ceil(tag_bits), CHAR_BIT, sizeof(uintptr_t) * CHAR_BIT)>; 883 | static_assert(sizeof(tag_type) <= sizeof(uintptr_t)); 884 | 885 | using base = tptr_base; 886 | using base::bits_; 887 | using base::base; 888 | 889 | MZ_CONST_GETTER 890 | static constexpr bool can_store_ptr(uintptr_t ptr) noexcept 891 | { 892 | if constexpr (!tag_bits) 893 | { 894 | return true; 895 | } 896 | else 897 | { 898 | return !(base::pack_ptr_unchecked(ptr) & tag_mask); 899 | } 900 | } 901 | 902 | template 903 | MZ_PURE_GETTER 904 | static constexpr bool can_store_tag([[maybe_unused]] const Tag& tag) noexcept 905 | { 906 | if constexpr (!tag_bits) 907 | { 908 | return false; 909 | } 910 | else if constexpr ((sizeof(Tag) * CHAR_BIT) <= tag_bits // 911 | && std::is_trivially_default_constructible_v // 912 | && std::is_trivially_copyable_v) 913 | { 914 | return true; // this branch works for both ints and pod types 915 | } 916 | else if constexpr (tptr_is_enum_or_integer) 917 | { 918 | return !(static_cast>(tag) & ptr_mask); 919 | } 920 | else 921 | { 922 | return false; // oversized/non-POD 923 | } 924 | } 925 | 926 | MZ_CONST_GETTER 927 | static uintptr_t pack_ptr(uintptr_t ptr) noexcept 928 | { 929 | MZ_ASSERT((!ptr || mz::bit_floor(ptr) >= Align) && "The pointer's address is aligned too strictly aligned"); 930 | 931 | return base::pack_ptr_unchecked(ptr); 932 | } 933 | 934 | template 935 | MZ_PURE_GETTER 936 | static uintptr_t pack_both(uintptr_t ptr, const Tag& tag) noexcept 937 | { 938 | MZ_TAGGED_PTR_TAG_TYPE_CHECKS(Tag); 939 | 940 | if constexpr (std::is_enum_v) 941 | { 942 | return pack_both(ptr, static_cast>(tag)); 943 | } 944 | else 945 | { 946 | if constexpr (std::is_integral_v) 947 | { 948 | if constexpr ((sizeof(Tag) * CHAR_BIT) > tag_bits) 949 | { 950 | MZ_ASSERT(can_store_tag(tag) && "Tag value cannot be used without truncation"); 951 | 952 | return pack_ptr(ptr) | (static_cast(tag) & tag_mask); 953 | } 954 | else 955 | { 956 | return pack_ptr(ptr) | static_cast(tag); 957 | } 958 | } 959 | else // some pod type 960 | { 961 | MZ_TAGGED_PTR_TAG_OBJECT_CHECKS(Tag); 962 | 963 | uintptr_t bits; 964 | if constexpr ((sizeof(Tag) * CHAR_BIT) < tag_bits) 965 | { 966 | bits = pack_ptr(ptr) & ptr_mask; 967 | } 968 | else 969 | { 970 | bits = pack_ptr(ptr); 971 | } 972 | std::memcpy(&bits, &tag, sizeof(tag)); 973 | return bits; 974 | } 975 | } 976 | } 977 | 978 | MZ_CONST_GETTER 979 | static uintptr_t set_ptr(uintptr_t bits, uintptr_t ptr) noexcept 980 | { 981 | if constexpr (tag_bits) 982 | { 983 | return pack_ptr(ptr) | (bits & tag_mask); 984 | } 985 | else 986 | { 987 | return pack_ptr(ptr); 988 | } 989 | } 990 | 991 | MZ_CONSTRAINED_TEMPLATE(tptr_is_enum_or_integer, typename Tag) 992 | MZ_CONST_GETTER 993 | static uintptr_t set_tag(uintptr_t bits, Tag tag) noexcept 994 | { 995 | MZ_TAGGED_PTR_TAG_TYPE_CHECKS(Tag); 996 | 997 | if constexpr (std::is_enum_v) 998 | { 999 | return set_tag(bits, static_cast>(tag)); 1000 | } 1001 | else if constexpr ((sizeof(Tag) * CHAR_BIT) > tag_bits) 1002 | { 1003 | MZ_ASSERT(can_store_tag(tag) && "Tag value cannot be used without truncation"); 1004 | 1005 | return (bits & ptr_mask) | (static_cast(tag) & tag_mask); 1006 | } 1007 | else 1008 | { 1009 | return (bits & ptr_mask) | static_cast(tag); 1010 | } 1011 | } 1012 | 1013 | MZ_CONSTRAINED_TEMPLATE(!tptr_is_enum_or_integer, typename Tag) 1014 | MZ_PURE_GETTER 1015 | static uintptr_t set_tag(uintptr_t bits, const Tag& tag) noexcept 1016 | { 1017 | MZ_TAGGED_PTR_TAG_OBJECT_CHECKS(Tag); 1018 | 1019 | if constexpr ((sizeof(Tag) * CHAR_BIT) < tag_bits) 1020 | { 1021 | bits &= ptr_mask; 1022 | } 1023 | std::memcpy(&bits, &tag, sizeof(tag)); 1024 | return bits; 1025 | } 1026 | 1027 | MZ_CONST_INLINE_GETTER 1028 | static uintptr_t get_tag([[maybe_unused]] uintptr_t bits) noexcept 1029 | { 1030 | if constexpr (tag_bits) 1031 | { 1032 | return bits & tag_mask; 1033 | } 1034 | else 1035 | { 1036 | return {}; 1037 | } 1038 | } 1039 | 1040 | MZ_CONST_GETTER 1041 | static bool get_tag_bit(uintptr_t bits, size_t index) noexcept 1042 | { 1043 | MZ_ASSERT(index < tag_bits && "Tag bit index out-of-bounds"); 1044 | 1045 | return bits & (uintptr_t{ 1 } << index); 1046 | } 1047 | 1048 | MZ_CONST_GETTER 1049 | static uintptr_t set_tag_bit(uintptr_t bits, size_t index, bool state) noexcept 1050 | { 1051 | MZ_ASSERT(index < tag_bits && "Tag bit index out-of-bounds"); 1052 | 1053 | if (state) 1054 | return bits | (uintptr_t{ 1 } << index); 1055 | else 1056 | return bits & (~(uintptr_t{ 1 } << index)); 1057 | } 1058 | 1059 | template 1060 | MZ_CONST_GETTER 1061 | static Tag get_tag_as_object(uintptr_t bits) noexcept 1062 | { 1063 | MZ_TAGGED_PTR_TAG_OBJECT_CHECKS(Tag); 1064 | 1065 | Tag tag; 1066 | std::memcpy(&tag, &bits, sizeof(tag)); 1067 | return tag; 1068 | } 1069 | 1070 | private: 1071 | // Q: why does get_ptr_impl exist? 1072 | // 1073 | // A: compilers that evaluate the if constexpr branches over-eagerly will issue warnings about 1074 | // right-shifting >= sizeof(uintptr_t) when tptr_free_bits == 0; sticking the logic in a 1075 | // separate template forces the branch evaluation to be delayed so the dead ones get pruned 1076 | // properly without the warning. 1077 | template 1078 | MZ_CONST_GETTER 1079 | static uintptr_t get_ptr_impl(uintptr_t bits) noexcept 1080 | { 1081 | static_assert((FreeBits + UsedBits) == sizeof(uintptr_t) * CHAR_BIT); 1082 | 1083 | bits &= ptr_mask; 1084 | if constexpr (FreeBits > 0) 1085 | { 1086 | bits >>= FreeBits; 1087 | 1088 | #if MZ_ARCH_AMD64 1089 | static constexpr uintptr_t canon_test = uintptr_t{ 1 } << (UsedBits - 1u); 1090 | if (bits & canon_test) 1091 | { 1092 | static constexpr uintptr_t canon_mask = bit_fill_right(FreeBits) << UsedBits; 1093 | bits |= canon_mask; 1094 | } 1095 | #endif 1096 | } 1097 | return bits; 1098 | } 1099 | 1100 | public: 1101 | MZ_CONST_INLINE_GETTER 1102 | static uintptr_t get_ptr(uintptr_t bits) noexcept 1103 | { 1104 | return get_ptr_impl(bits); 1105 | } 1106 | 1107 | public: 1108 | MZ_NODISCARD_CTOR 1109 | constexpr tptr_aligned_base() noexcept = default; 1110 | }; 1111 | 1112 | // primary template; T is a function 1113 | template > 1114 | class MZ_TRIVIAL_ABI tptr_to_function : public tptr_aligned_base 1115 | { 1116 | protected: 1117 | using base = tptr_aligned_base; 1118 | using base::bits_; 1119 | using base::base; 1120 | 1121 | public: 1122 | MZ_NODISCARD_CTOR 1123 | constexpr tptr_to_function() noexcept = default; 1124 | 1125 | MZ_PURE_GETTER 1126 | T* ptr() const noexcept 1127 | { 1128 | return reinterpret_cast(base::get_ptr(bits_)); 1129 | } 1130 | 1131 | MZ_PURE_INLINE_GETTER 1132 | T* get() const noexcept 1133 | { 1134 | return ptr(); 1135 | } 1136 | 1137 | MZ_PURE_INLINE_GETTER 1138 | explicit operator T*() const noexcept 1139 | { 1140 | return ptr(); 1141 | } 1142 | 1143 | template 1144 | decltype(auto) operator()(U&&... args) const noexcept(std::is_nothrow_invocable_v) 1145 | { 1146 | static_assert(std::is_invocable_v); 1147 | 1148 | return ptr()(static_cast(args)...); 1149 | } 1150 | }; 1151 | 1152 | // specialization; T is an object or void 1153 | template 1154 | struct MZ_TRIVIAL_ABI tptr_to_function : public tptr_aligned_base 1155 | { 1156 | protected: 1157 | using base = tptr_aligned_base; 1158 | using base::bits_; 1159 | using base::base; 1160 | 1161 | public: 1162 | MZ_NODISCARD_CTOR 1163 | constexpr tptr_to_function() noexcept = default; 1164 | 1165 | MZ_PURE_GETTER 1166 | MZ_ATTR(assume_aligned(Align)) 1167 | T* ptr() const noexcept 1168 | { 1169 | return mz::assume_aligned(reinterpret_cast(base::get_ptr(bits_))); 1170 | } 1171 | 1172 | MZ_PURE_INLINE_GETTER 1173 | MZ_ATTR(assume_aligned(Align)) 1174 | T* get() const noexcept 1175 | { 1176 | return ptr(); 1177 | } 1178 | 1179 | MZ_PURE_INLINE_GETTER 1180 | MZ_ATTR(assume_aligned(Align)) 1181 | explicit operator T*() const noexcept 1182 | { 1183 | return ptr(); 1184 | } 1185 | }; 1186 | 1187 | // primary template; T is an object 1188 | template && !std::is_void_v)> 1189 | struct MZ_TRIVIAL_ABI tptr_to_object : public tptr_to_function 1190 | { 1191 | protected: 1192 | using base = tptr_to_function; 1193 | using base::bits_; 1194 | using base::base; 1195 | 1196 | public: 1197 | MZ_NODISCARD_CTOR 1198 | constexpr tptr_to_object() noexcept = default; 1199 | 1200 | MZ_PURE_INLINE_GETTER 1201 | T& operator*() const noexcept 1202 | { 1203 | return *base::ptr(); 1204 | } 1205 | 1206 | MZ_PURE_INLINE_GETTER 1207 | T* operator->() const noexcept 1208 | { 1209 | return base::ptr(); 1210 | } 1211 | }; 1212 | 1213 | // specialization; T is a function or void 1214 | template 1215 | struct MZ_TRIVIAL_ABI tptr_to_object : public tptr_to_function 1216 | { 1217 | protected: 1218 | using base = tptr_to_function; 1219 | using base::bits_; 1220 | using base::base; 1221 | 1222 | public: 1223 | MZ_NODISCARD_CTOR 1224 | constexpr tptr_to_object() noexcept = default; 1225 | }; 1226 | 1227 | struct tptr_nullptr_deduced_tag 1228 | {}; 1229 | 1230 | template || std::is_function_v)> 1231 | inline constexpr size_t tptr_min_align = alignof(T); 1232 | template 1233 | inline constexpr size_t tptr_min_align = 1; 1234 | } 1235 | 1236 | #if MZ_MSVC 1237 | #pragma warning(pop) 1238 | #endif 1239 | 1240 | namespace mz 1241 | { 1242 | template > 1243 | class MZ_TRIVIAL_ABI tagged_ptr // 1244 | : public detail::tptr_to_object 1245 | { 1246 | private: 1247 | using base = detail::tptr_to_object; 1248 | using base::bits_; 1249 | using base::base; 1250 | 1251 | static_assert(!std::is_same_v, 1252 | "Tagged pointers cannot have their type deduced from a nullptr" 1253 | " (a nullptr is meaningless in this context)"); 1254 | 1255 | static_assert(!std::is_reference_v, "Tagged pointers cannot store references"); 1256 | 1257 | static_assert(!std::is_function_v || !is_cvref, "Tagged pointers to functions cannot be cv-qualified"); 1258 | 1259 | static_assert(has_single_bit(Align), "Alignment must be a power of two"); 1260 | 1261 | static_assert(Align >= detail::tptr_min_align, 1262 | "Alignment cannot be smaller than the type's actual alignment"); 1263 | 1264 | static_assert(Align > 1 || base::tag_bits > 0, 1265 | "Types aligned on a single byte cannot be pointed to by a tagged pointer on this platform"); 1266 | 1267 | public: 1268 | using element_type = T; 1269 | 1270 | using pointer = std::add_pointer_t; 1271 | static_assert(sizeof(pointer) == sizeof(uintptr_t), "unexpected pointer size"); 1272 | 1273 | using const_pointer = std::add_pointer_t>; 1274 | 1275 | using tag_type = typename base::tag_type; 1276 | 1277 | static constexpr size_t alignment = Align; 1278 | 1279 | static constexpr size_t tag_bit_count = base::tag_bits; 1280 | 1281 | static constexpr tag_type max_tag = bit_fill_right(tag_bit_count); 1282 | 1283 | MZ_NODISCARD_CTOR 1284 | constexpr tagged_ptr() noexcept = default; 1285 | 1286 | MZ_NODISCARD_CTOR 1287 | constexpr tagged_ptr(std::nullptr_t) noexcept 1288 | {} 1289 | 1290 | MZ_NODISCARD_CTOR 1291 | explicit tagged_ptr(pointer value) noexcept // 1292 | : base{ base::pack_ptr(reinterpret_cast(value)) } 1293 | {} 1294 | 1295 | template 1296 | MZ_NODISCARD_CTOR 1297 | tagged_ptr(pointer value, const Tag& tag_value) noexcept // 1298 | : base{ base::pack_both(reinterpret_cast(value), tag_value) } 1299 | {} 1300 | 1301 | MZ_NODISCARD_CTOR 1302 | constexpr tagged_ptr(const tagged_ptr&) noexcept = default; 1303 | 1304 | constexpr tagged_ptr& operator=(const tagged_ptr&) noexcept = default; 1305 | 1306 | ~tagged_ptr() noexcept = default; 1307 | 1308 | constexpr tagged_ptr& reset() noexcept 1309 | { 1310 | bits_ = {}; 1311 | return *this; 1312 | } 1313 | 1314 | tagged_ptr& reset(pointer value) noexcept 1315 | { 1316 | bits_ = base::pack_ptr(reinterpret_cast(value)); 1317 | return *this; 1318 | } 1319 | 1320 | template 1321 | tagged_ptr& reset(pointer value, const Tag& tag_value) noexcept 1322 | { 1323 | bits_ = base::pack_both(reinterpret_cast(value), tag_value); 1324 | return *this; 1325 | } 1326 | 1327 | #if !MZ_DOXYGEN 1328 | 1329 | using base::ptr; // prevent the setter from hiding the getter in the base 1330 | 1331 | #else 1332 | #endif 1333 | 1334 | MZ_CONST_INLINE_GETTER 1335 | static bool can_store_ptr(pointer value) noexcept 1336 | { 1337 | return base::can_store_ptr(reinterpret_cast(value)); 1338 | } 1339 | 1340 | tagged_ptr& ptr(pointer value) noexcept 1341 | { 1342 | bits_ = base::set_ptr(bits_, reinterpret_cast(value)); 1343 | return *this; 1344 | } 1345 | 1346 | MZ_ALWAYS_INLINE 1347 | tagged_ptr& operator=(pointer rhs) noexcept 1348 | { 1349 | return ptr(rhs); 1350 | } 1351 | 1352 | constexpr tagged_ptr& clear_ptr() noexcept 1353 | { 1354 | bits_ &= base::tag_mask; 1355 | return *this; 1356 | } 1357 | 1358 | template 1359 | MZ_PURE_GETTER 1360 | Tag tag() const noexcept 1361 | { 1362 | MZ_TAGGED_PTR_TAG_TYPE_CHECKS(Tag); 1363 | 1364 | if constexpr (detail::tptr_is_enum_or_integer) 1365 | { 1366 | static_assert(sizeof(Tag) * CHAR_BIT >= tag_bit_count, 1367 | "The destination type is not large enough to store the tag without a loss of data"); 1368 | 1369 | return static_cast(base::get_tag(bits_)); 1370 | } 1371 | else 1372 | { 1373 | return base::template get_tag_as_object(bits_); 1374 | } 1375 | } 1376 | 1377 | template 1378 | MZ_PURE_INLINE_GETTER 1379 | static constexpr bool can_store_tag(const Tag& tag_value) noexcept 1380 | { 1381 | return base::can_store_tag(tag_value); 1382 | } 1383 | 1384 | template 1385 | tagged_ptr& tag(const Tag& tag_value) noexcept 1386 | { 1387 | bits_ = base::set_tag(bits_, tag_value); 1388 | return *this; 1389 | } 1390 | 1391 | MZ_PURE_GETTER 1392 | bool tag_bit(size_t tag_bit_index) const noexcept 1393 | { 1394 | return base::get_tag_bit(bits_, tag_bit_index); 1395 | } 1396 | 1397 | tagged_ptr& tag_bit(size_t tag_bit_index, bool val) noexcept 1398 | { 1399 | bits_ = base::set_tag_bit(bits_, tag_bit_index, val); 1400 | return *this; 1401 | } 1402 | 1403 | constexpr tagged_ptr& clear_tag() noexcept 1404 | { 1405 | bits_ &= base::ptr_mask; 1406 | return *this; 1407 | } 1408 | 1409 | MZ_PURE_INLINE_GETTER 1410 | explicit constexpr operator bool() const noexcept 1411 | { 1412 | return bits_ & base::ptr_mask; 1413 | } 1414 | 1415 | MZ_CONST_INLINE_GETTER 1416 | friend bool operator==(tagged_ptr lhs, const_pointer rhs) noexcept 1417 | { 1418 | return lhs.ptr() == rhs; 1419 | } 1420 | 1421 | MZ_CONST_INLINE_GETTER 1422 | friend bool operator!=(tagged_ptr lhs, const_pointer rhs) noexcept 1423 | { 1424 | return lhs.ptr() != rhs; 1425 | } 1426 | 1427 | MZ_CONST_INLINE_GETTER 1428 | friend bool operator==(const_pointer lhs, tagged_ptr rhs) noexcept 1429 | { 1430 | return lhs == rhs.ptr(); 1431 | } 1432 | 1433 | MZ_CONST_INLINE_GETTER 1434 | friend bool operator!=(const_pointer lhs, tagged_ptr rhs) noexcept 1435 | { 1436 | return lhs != rhs.ptr(); 1437 | } 1438 | 1439 | MZ_CONST_INLINE_GETTER 1440 | friend constexpr bool operator==(tagged_ptr lhs, tagged_ptr rhs) noexcept 1441 | { 1442 | return lhs.bits_ == rhs.bits_; 1443 | } 1444 | 1445 | MZ_CONST_INLINE_GETTER 1446 | friend constexpr bool operator!=(tagged_ptr lhs, tagged_ptr rhs) noexcept 1447 | { 1448 | return lhs.bits_ != rhs.bits_; 1449 | } 1450 | }; 1451 | 1452 | tagged_ptr(std::nullptr_t) -> tagged_ptr; 1453 | template 1454 | tagged_ptr(std::nullptr_t, T) -> tagged_ptr; 1455 | template 1456 | tagged_ptr(T*, U) -> tagged_ptr; 1457 | template 1458 | tagged_ptr(T*) -> tagged_ptr; 1459 | 1460 | } 1461 | 1462 | #undef MZ_TAGGED_PTR_TAG_TYPE_CHECKS 1463 | #undef MZ_TAGGED_PTR_TAG_OBJECT_CHECKS 1464 | 1465 | #if MZ_TAGGED_PTR_HAS_TRAITS 1466 | 1467 | namespace mz::detail 1468 | { 1469 | template 1470 | struct tagged_pointer_traits_base 1471 | {}; 1472 | 1473 | template 1474 | struct tagged_pointer_traits 1475 | {}; 1476 | 1477 | template