From f31bcb3c4981608d3fdfdb5a420161abbc471a32 Mon Sep 17 00:00:00 2001 From: Mark Gillard Date: Mon, 3 Jul 2023 19:24:17 +0300 Subject: [PATCH] implemented most of the basic container functionality - custom allocator support - `swap()` - `reserve()` - `shrink_to_fit()` - `pop_back()` - copy/move construct/assign (when supported by members + allocator) - strong exception guarantees wherever possible - `resize()` - `max_size()` - per-column accessors --- .clang-format | 200 + .editorconfig | 22 + .gitattributes | 38 +- .gitignore | 114 + .style.yapf | 22 + .vscode/c_cpp_properties.json | 22 + .vscode/settings.json | 21 + CHANGELOG.md | 5 + CONTRIBUTING.md | 42 + LICENSE.txt | 21 + README.md | 72 + examples/meson.build | 26 + examples/particles.cpp | 17 + examples/particles.hpp | 225 + examples/particles.toml | 13 + meson.build | 180 + meson_options.txt | 3 + requirements.txt | 5 + run_yapf.bat | 47 + setup.py | 89 + soagen.code-workspace | 54 + soagen/__init__.py | 11 + soagen/__main__.py | 13 + soagen/column.py | 146 + soagen/config.py | 155 + soagen/configurable.py | 43 + soagen/data/.clang-format | 200 + soagen/data/hpp/allocator.hpp | 227 + soagen/data/hpp/column_traits.hpp | 473 ++ soagen/data/hpp/compressed_pair.hpp | 258 + soagen/data/hpp/core.hpp | 374 ++ soagen/data/hpp/header_end.hpp | 29 + soagen/data/hpp/header_start.hpp | 33 + soagen/data/hpp/meson.build | 7 + soagen/data/hpp/preprocessor.hpp | 945 ++++ soagen/data/hpp/soagen.hpp | 4233 +++++++++++++++++ soagen/data/hpp/table.hpp | 1017 ++++ soagen/data/hpp/table_traits.hpp | 732 +++ .../data/hpp_templates/compressed_pair.hpp.in | 20 + soagen/data/hpp_templates/core.hpp.in | 152 + soagen/data/hpp_templates/preprocessor.hpp.in | 124 + soagen/data/hpp_templates/soagen.hpp.in | 15 + soagen/data/meson.build | 7 + soagen/data/version.txt | 1 + soagen/detect_includes.py | 497 ++ soagen/errors.py | 20 + soagen/header_file.py | 219 + soagen/header_file_old.py | 258 + soagen/injectors.py | 32 + soagen/log.py | 91 + soagen/main.py | 289 ++ soagen/meson.build | 6 + soagen/metavars.py | 117 + soagen/natvis_file.py | 143 + soagen/paths.py | 35 + soagen/preprocessor.py | 144 + soagen/row.py | 224 + soagen/schemas.py | 53 + soagen/span.py | 458 ++ soagen/stripe.py | 103 + soagen/struct.py | 530 +++ soagen/struct_old.py | 813 ++++ soagen/type_list.py | 77 + soagen/utils.py | 104 + soagen/variable.py | 91 + soagen/writer.py | 356 ++ 66 files changed, 15111 insertions(+), 2 deletions(-) create mode 100644 .clang-format create mode 100644 .editorconfig create mode 100644 .gitignore create mode 100644 .style.yapf create mode 100644 .vscode/c_cpp_properties.json create mode 100644 .vscode/settings.json create mode 100644 CHANGELOG.md create mode 100644 CONTRIBUTING.md create mode 100644 LICENSE.txt create mode 100644 README.md create mode 100644 examples/meson.build create mode 100644 examples/particles.cpp create mode 100644 examples/particles.hpp create mode 100644 examples/particles.toml create mode 100644 meson.build create mode 100644 meson_options.txt create mode 100644 requirements.txt create mode 100644 run_yapf.bat create mode 100644 setup.py create mode 100644 soagen.code-workspace create mode 100644 soagen/__init__.py create mode 100644 soagen/__main__.py create mode 100644 soagen/column.py create mode 100644 soagen/config.py create mode 100644 soagen/configurable.py create mode 100644 soagen/data/.clang-format create mode 100644 soagen/data/hpp/allocator.hpp create mode 100644 soagen/data/hpp/column_traits.hpp create mode 100644 soagen/data/hpp/compressed_pair.hpp create mode 100644 soagen/data/hpp/core.hpp create mode 100644 soagen/data/hpp/header_end.hpp create mode 100644 soagen/data/hpp/header_start.hpp create mode 100644 soagen/data/hpp/meson.build create mode 100644 soagen/data/hpp/preprocessor.hpp create mode 100644 soagen/data/hpp/soagen.hpp create mode 100644 soagen/data/hpp/table.hpp create mode 100644 soagen/data/hpp/table_traits.hpp create mode 100644 soagen/data/hpp_templates/compressed_pair.hpp.in create mode 100644 soagen/data/hpp_templates/core.hpp.in create mode 100644 soagen/data/hpp_templates/preprocessor.hpp.in create mode 100644 soagen/data/hpp_templates/soagen.hpp.in create mode 100644 soagen/data/meson.build create mode 100644 soagen/data/version.txt create mode 100644 soagen/detect_includes.py create mode 100644 soagen/errors.py create mode 100644 soagen/header_file.py create mode 100644 soagen/header_file_old.py create mode 100644 soagen/injectors.py create mode 100644 soagen/log.py create mode 100644 soagen/main.py create mode 100644 soagen/meson.build create mode 100644 soagen/metavars.py create mode 100644 soagen/natvis_file.py create mode 100644 soagen/paths.py create mode 100644 soagen/preprocessor.py create mode 100644 soagen/row.py create mode 100644 soagen/schemas.py create mode 100644 soagen/span.py create mode 100644 soagen/stripe.py create mode 100644 soagen/struct.py create mode 100644 soagen/struct_old.py create mode 100644 soagen/type_list.py create mode 100644 soagen/utils.py create mode 100644 soagen/variable.py create mode 100644 soagen/writer.py diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..b62853e --- /dev/null +++ b/.clang-format @@ -0,0 +1,200 @@ +--- +Language: Cpp +AccessModifierOffset: -2 +AlignAfterOpenBracket: Align +AlignConsecutiveMacros: Consecutive +AlignConsecutiveAssignments: Consecutive +AlignConsecutiveBitFields: Consecutive +AlignConsecutiveDeclarations: None +AlignEscapedNewlines: Right +AlignOperands: AlignAfterOperator +AlignTrailingComments: true +AllowAllArgumentsOnNextLine: false +AllowAllConstructorInitializersOnNextLine: false +AllowAllParametersOfDeclarationOnNextLine: false +AllowShortEnumsOnASingleLine: false +AllowShortBlocksOnASingleLine: Never +AllowShortCaseLabelsOnASingleLine: true +AllowShortFunctionsOnASingleLine: None +AllowShortLambdasOnASingleLine: All +AllowShortIfStatementsOnASingleLine: Never +AllowShortLoopsOnASingleLine: false +AlwaysBreakAfterDefinitionReturnType: None +AlwaysBreakAfterReturnType: None +AlwaysBreakBeforeMultilineStrings: false +AlwaysBreakTemplateDeclarations: Yes +AttributeMacros: + - SOAGEN_EMPTY_BASES + - SOAGEN_NODISCARD_CLASS + - SOAGEN_ABSTRACT_INTERFACE + - SOAGEN_TRIVIAL_ABI + - SOAGEN_VECTORCALL + - SOAGEN_CALLCONV +BinPackArguments: false +BinPackParameters: false +BraceWrapping: + AfterCaseLabel: true + AfterClass: true + AfterControlStatement: Always + AfterEnum: true + AfterFunction: true + AfterNamespace: true + AfterObjCDeclaration: false + AfterStruct: true + AfterUnion: true + AfterExternBlock: true + BeforeCatch: true + BeforeElse: true + BeforeLambdaBody: true + BeforeWhile: true + IndentBraces: false + SplitEmptyFunction: false + SplitEmptyRecord: false + SplitEmptyNamespace: true +BreakAfterAttributes: Always +BreakBeforeBinaryOperators: NonAssignment +BreakBeforeConceptDeclarations: true +BreakBeforeBraces: Custom +BreakBeforeInheritanceComma: false +BreakInheritanceList: BeforeColon +BreakBeforeTernaryOperators: true +BreakConstructorInitializersBeforeComma: false +BreakConstructorInitializers: BeforeColon +BreakAfterJavaFieldAnnotations: false +BreakStringLiterals: true +ColumnLimit: 120 +CommentPragmas: '^([/*!#]|\s*(===|---|clang-format))' +CompactNamespaces: false +ConstructorInitializerAllOnOneLineOrOnePerLine: true +ConstructorInitializerIndentWidth: 4 +ContinuationIndentWidth: 4 +Cpp11BracedListStyle: false +DeriveLineEnding: false +DerivePointerAlignment: false +DisableFormat: false +EmptyLineBeforeAccessModifier: LogicalBlock +ExperimentalAutoDetectBinPacking: false +FixNamespaceComments: false +ForEachMacros: + - foreach + - Q_FOREACH + - BOOST_FOREACH +StatementAttributeLikeMacros: + - Q_EMIT +IncludeBlocks: Preserve +IncludeCategories: + - Regex: '^"(llvm|llvm-c|clang|clang-c)/' + Priority: 2 + SortPriority: 0 + CaseSensitive: false + - Regex: '^(<|"(gtest|gmock|isl|json)/)' + Priority: 3 + SortPriority: 0 + CaseSensitive: false + - Regex: ".*" + Priority: 1 + SortPriority: 0 + CaseSensitive: false +IncludeIsMainRegex: "(Test)?$" +IncludeIsMainSourceRegex: "" +IndentCaseLabels: true +IndentCaseBlocks: false +IndentGotoLabels: true +IndentPPDirectives: BeforeHash +IndentExternBlock: Indent +IndentRequires: false +IndentWidth: 4 +IndentWrappedFunctionNames: false +InsertTrailingCommas: None +JavaScriptQuotes: Leave +JavaScriptWrapImports: true +KeepEmptyLinesAtTheStartOfBlocks: false +MacroBlockBegin: "" +MacroBlockEnd: "" +MaxEmptyLinesToKeep: 1 +NamespaceIndentation: All +ObjCBinPackProtocolList: Auto +ObjCBlockIndentWidth: 2 +ObjCBreakBeforeNestedBlockParam: true +ObjCSpaceAfterProperty: false +ObjCSpaceBeforeProtocolList: true +PenaltyBreakAssignment: 2 +PenaltyBreakBeforeFirstCallParameter: 19 +PenaltyBreakComment: 1 +PenaltyBreakFirstLessLess: 120 +PenaltyBreakString: 1000 +PenaltyBreakTemplateDeclaration: 10 +PenaltyExcessCharacter: 1000000 +PenaltyReturnTypeOnItsOwnLine: 1000000 +PenaltyIndentedWhitespace: 0 +PointerAlignment: Left +QualifierAlignment: Leave +ReflowComments: true +SortIncludes: false +SortJavaStaticImport: Before +SortUsingDeclarations: false +SpaceAfterCStyleCast: false +SpaceAfterLogicalNot: false +SpaceAfterTemplateKeyword: true +SpaceBeforeAssignmentOperators: true +SpaceBeforeCaseColon: false +SpaceBeforeCpp11BracedList: false +SpaceBeforeCtorInitializerColon: true +SpaceBeforeInheritanceColon: true +SpaceBeforeParens: ControlStatements +SpaceAroundPointerQualifiers: Default +SpaceBeforeRangeBasedForLoopColon: true +SpaceInEmptyBlock: false +SpaceInEmptyParentheses: false +SpacesBeforeTrailingComments: 1 +SpacesInAngles: false +SpacesInConditionalStatement: false +SpacesInContainerLiterals: true +SpacesInCStyleCastParentheses: false +SpacesInParentheses: false +SpacesInSquareBrackets: false +SpaceBeforeSquareBrackets: false +BitFieldColonSpacing: Both +Standard: Latest +StatementMacros: + - SOAGEN_ALIGNED_COLUMN + - SOAGEN_ALWAYS_INLINE + - SOAGEN_API + - SOAGEN_ATTR + - SOAGEN_COLUMN + - SOAGEN_CONST + - SOAGEN_CONST_GETTER + - SOAGEN_CONST_GETTER + - SOAGEN_CONST_INLINE_GETTER + - SOAGEN_CONST_INLINE_GETTER + - SOAGEN_CONSTEVAL + - SOAGEN_CONSTRAINED_TEMPLATE + - SOAGEN_CPP20_CONSTEXPR + - SOAGEN_DECLSPEC + - SOAGEN_HIDDEN_CONSTRAINT + - SOAGEN_INLINE_GETTER + - SOAGEN_MALLOC + - SOAGEN_NEVER_INLINE + - SOAGEN_NODISCARD + - SOAGEN_NODISCARD_CTOR + - SOAGEN_PURE + - SOAGEN_PURE_GETTER + - SOAGEN_PURE_INLINE_GETTER + - _Pragma + - __pragma +TabWidth: 4 +TypenameMacros: +UseCRLF: false +UseTab: Always +WhitespaceSensitiveMacros: + - STRINGIZE + - PP_STRINGIZE + - BOOST_PP_STRINGIZE + - SOAGEN_ATTR + - SOAGEN_HAS_ATTR + - SOAGEN_HAS_BUILTIN + - SOAGEN_HAS_CPP_ATTR + - SOAGEN_HAS_FEATURE + - SOAGEN_HAS_INCLUDE + - SOAGEN_LIKELY + - SOAGEN_UNLIKELY diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..0435634 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,22 @@ +root = true + +[*] +insert_final_newline = true +indent_style = tab +indent_size = 4 +tab_width = 4 +end_of_line = lf +trim_trailing_whitespace = true +charset = utf-8 + +[*.{gitattributes,yml,vcxproj,vcxproj.filters,sln,rc,clang-format}] +indent_style = space + +[{Doxyfile,Doxyfile-mcss}] +indent_style = space + +[*.{hlsl,rc,sln,vcxproj,vcxproj.filters}] +end_of_line = crlf + +[*.{sln,vcxproj,vcxproj.filters}] +charset = utf-8-bom diff --git a/.gitattributes b/.gitattributes index dfe0770..da140f1 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,2 +1,36 @@ -# Auto detect text files and perform LF normalization -* text=auto +* text eol=lf encoding=UTF-8 +*.hlsl text eol=crlf encoding=UTF-8 +*.rc text eol=crlf encoding=UTF-8 +*.sln text eol=crlf encoding=UTF-8-BOM +*.vcxproj text eol=crlf encoding=UTF-8-BOM +*.vcxproj.filters text eol=crlf encoding=UTF-8-BOM + +*.cs eol=lf diff=csharp + +*.doc diff=astextplain +*.DOC diff=astextplain +*.docx diff=astextplain +*.DOCX diff=astextplain +*.dot diff=astextplain +*.DOT diff=astextplain +*.pdf diff=astextplain +*.PDF diff=astextplain +*.rtf diff=astextplain +*.RTF diff=astextplain + +*.ai binary +*.bin binary +*.bmp binary +*.dat binary +*.gif binary +*.ico binary +*.jpeg binary +*.jpg binary +*.otf binary +*.png binary +*.psd binary +*.rc binary +*.ttf binary +*.woff binary +*.woff2 binary +*.xlsx binary diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..510c73d --- /dev/null +++ b/.gitignore @@ -0,0 +1,114 @@ +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +.hypothesis/ +.pytest_cache/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +.python-version + +# celery beat schedule file +celerybeat-schedule + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ diff --git a/.style.yapf b/.style.yapf new file mode 100644 index 0000000..0c23b60 --- /dev/null +++ b/.style.yapf @@ -0,0 +1,22 @@ +[style] +based_on_style = pep8 + +allow_multiline_dictionary_keys = true +allow_multiline_lambdas = false +allow_split_before_dict_value = true +blank_line_before_class_docstring = true +blank_line_before_module_docstring = true +blank_lines_around_top_level_definition = 3 +blank_line_before_nested_class_or_def = true +coalesce_brackets = true +column_limit = 120 +continuation_align_style = fixed +continuation_indent_width = 4 +dedent_closing_brackets = true +indent_closing_brackets = false +indent_dictionary_value = true +indent_width = 4 +split_before_arithmetic_operator = true +split_before_bitwise_operator = true +split_before_logical_operator = true +use_tabs = true diff --git a/.vscode/c_cpp_properties.json b/.vscode/c_cpp_properties.json new file mode 100644 index 0000000..3944fd0 --- /dev/null +++ b/.vscode/c_cpp_properties.json @@ -0,0 +1,22 @@ +{ + "configurations": [ + { + "name": "Win32", + "includePath": [ + "${workspaceFolder}/**", + "${workspaceFolder}/soagen/data/hpp" + ], + "defines": [ + "_DEBUG", + "UNICODE", + "_UNICODE" + ], + "windowsSdkVersion": "10.0.20348.0", + "compilerPath": "cl.exe", + "cStandard": "c17", + "cppStandard": "c++20", + "intelliSenseMode": "windows-msvc-x64" + } + ], + "version": 4 +} diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..9dc32df --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,21 @@ +{ + "prettier.useTabs": true, + "prettier.tabWidth": 4, + "editor.rulers": [120], + "editor.formatOnSave": true, + "editor.formatOnPaste": false, + "editor.formatOnType": false, + "files.eol": "\n", + "python.formatting.provider": "yapf", + "html.format.indentInnerHtml": true, + "files.exclude": { + "**/__pycache__": true, + "**/build": true, + "**/*.egg-info": true + }, + "[python]": { + "editor.codeActionsOnSave": { + "source.organizeImports": true + } + } +} diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..fa3a07d --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,5 @@ +# Changelog + +## Unreleased + +- First public release. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..0bf8ef8 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,42 @@ +# Contributing to Soagen + +Firstly: thanks! Any help is greatly appreciated. + +For most situations the easiest way for you to contribute is to simply let me know what's going on: + +- Reporting issues or requesting features: [Issues] +- Chat: [Gitter] + +If you'd like to contribute more directly via a pull request, see below. + +- [Pull Requests](#pull-requests) + - [Getting started](#getting-started) + - [Code style](#code-style) + +## Pull Requests + +### Getting started + +A prerequisite of working on Soagen with the intention of making a pull request is to have it installed +as 'editable' from a clone of the repository: + +```sh +git clone +cd soagen +pip install -r requirements.txt +pip install -e . +``` + +### Code style + +It's Python. I'm primarily a C++ programmer. I really don't care that much. +If you want to be consistent, the codebase is configured for use with [yapf], so you can point your editor +to that as an autoformatter. + +I'm not too fussy though. I'm unlikely to reject a PR on the basis of style unless you do something truly horrendous. + +

+ +[issues]: https://github.com/marzer/soagen/issues +[gitter]: https://gitter.im/marzer/community +[yapf]: https://github.com/google/yapf diff --git a/LICENSE.txt b/LICENSE.txt new file mode 100644 index 0000000..158f437 --- /dev/null +++ b/LICENSE.txt @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) Mark Gillard + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..2f3a11f --- /dev/null +++ b/README.md @@ -0,0 +1,72 @@ +# soagen + +Struct-of-Arrays generator for C++ projects. + +[![Sponsor](https://img.shields.io/static/v1?label=sponsor&message=%E2%9D%A4&logo=GitHub&color=%23fe8e86&style=flat-square)][sponsor] +[![Gitter](https://badges.gitter.im/marzer/community.svg)][gitter] + +- [Installation](#installation) +- [Usage](#usage) +- [Config file options](#config-file-options) +- [License and Attribution](#license-and-attribution) + +

+ +## Installation + +### Prerequisites: + +- Python 3.9+ + +### Then: + +``` +pip install soagen +``` + +

+ +## Usage + +Soagen is a command-line application. + +``` +usage: soagen [-h] [-v] [--version] [--werror | --no-werror] [--clang-format | --no-clang-format] [--bug-report] files [files ...] + + ___ ___ __ _ __ _ ___ _ __ + / __|/ _ \ / _` |/ _` |/ _ \ '_ \ + \__ \ (_) | (_| | (_| | __/ | | | + |___/\___/ \__,_|\__, |\___|_| |_| + __/ | + |___/ v0.1.0 - github.com/marzer/soagen + +Struct-of-Arrays generator for C++ projects. + +positional arguments: + files + +options: + -h, --help show this help message and exit + -v, --verbose enable very noisy diagnostic output + --version print the version and exit + --werror, --no-werror + treat warnings as errors (default: False) + --clang-format, --no-clang-format + run clang-format on generated code if it is available (default: True) + --bug-report captures all inputs and output in a zip file for easier bug reporting. +``` + +## Config file options + +See the [Configuration options] wiki page. + +

+ +## License and Attribution + +This project is published under the terms of the [MIT license](https://github.com/marzer/soagen/blob/main/LICENSE.txt). + +[configuration options]: https://github.com/marzer/soagen/wiki/Configuration-options +[feature request]: https://github.com/marzer/soagen/issues/new +[gitter]: https://gitter.im/marzer/community +[sponsor]: https://github.com/sponsors/marzer diff --git a/examples/meson.build b/examples/meson.build new file mode 100644 index 0000000..7211828 --- /dev/null +++ b/examples/meson.build @@ -0,0 +1,26 @@ +# This file is a part of marzer/soagen and is subject to the the terms of the MIT license. +# Copyright (c) Mark Gillard +# See https://github.com/marzer/soagen/blob/master/LICENSE for the full license text. +# SPDX-License-Identifier: MIT + +example_args = [] +example_args += global_args + +examples = [ + 'particles' +] + +example_executables = [] +foreach example : examples + example_executables += [[ + example, + executable( + example, + [ example + '.cpp' ], + dependencies: soagen_dep, + cpp_args: example_args, + link_args: global_link_args, + override_options: global_overrides + ) + ]] +endforeach diff --git a/examples/particles.cpp b/examples/particles.cpp new file mode 100644 index 0000000..29ec21e --- /dev/null +++ b/examples/particles.cpp @@ -0,0 +1,17 @@ +#include "particles.hpp" + +int main() // +{ + foo::particles p; + foo::particles p2; //{ p }; + + p.reserve(10u); + + p = std::move(p2); + p.swap(p2); + p2 = p; + + p.pop_back(); + + return 0; +} diff --git a/examples/particles.hpp b/examples/particles.hpp new file mode 100644 index 0000000..7a74ff0 --- /dev/null +++ b/examples/particles.hpp @@ -0,0 +1,225 @@ +//---------------------------------------------------------------------------------------------------------------------- +// This file was generated by soagen - do not modify it directly +// https://github.com/marzer/soagen +//---------------------------------------------------------------------------------------------------------------------- +#pragma once + +/// \file +/// \brief Contains the definition of foo::particles. + +#include + +SOAGEN_PUSH_WARNINGS; +SOAGEN_DISABLE_SPAM_WARNINGS; +#if SOAGEN_MSVC + #pragma inline_recursion(on) +#endif +#if SOAGEN_MSVC_LIKE + #pragma push_macro("min") + #pragma push_macro("max") + #undef min + #undef max +#endif + +//---------------------------------------------------------------------------------------------------------------------- +// forward declarations +//---------------------------------------------------------------------------------------------------------------------- + +/// \cond + +namespace foo +{ + class particles; +} + +/// \endcond + +//---------------------------------------------------------------------------------------------------------------------- +// particles +//---------------------------------------------------------------------------------------------------------------------- + +namespace foo +{ + /// \addtogroup soa Struct-of-Arrays + /// @{ + + /// \addtogroup soa_particles particles + /// @{ + + /// \brief particles + class particles + { + public: + using size_type = std::size_t; + using difference_type = std::ptrdiff_t; + using allocator_type = soagen::allocator; + + using table_traits = + soagen::table_traits, 32>, + soagen::column_traits, 32>, + soagen::column_traits, 32>, + soagen::column_traits, 32>, + soagen::column_traits, 32>, + soagen::column_traits, 32>, + soagen::column_traits>; + + template + using column_traits = typename table_traits::template column; + + template + using column_type = typename column_traits::value_type; + + static constexpr size_type aligned_stride = table_traits::aligned_stride; + + struct column_indices + { + static constexpr size_type position_x = 0; + static constexpr size_type position_y = 1; + static constexpr size_type position_z = 2; + static constexpr size_type velocity_x = 3; + static constexpr size_type velocity_y = 4; + static constexpr size_type velocity_z = 5; + static constexpr size_type mass = 6; + }; + + template + static constexpr const char column_name[] = ""; + template <> + static constexpr const char column_name<0>[] = "position_x"; + template <> + static constexpr const char column_name<1>[] = "position_y"; + template <> + static constexpr const char column_name<2>[] = "position_z"; + template <> + static constexpr const char column_name<3>[] = "velocity_x"; + template <> + static constexpr const char column_name<4>[] = "velocity_y"; + template <> + static constexpr const char column_name<5>[] = "velocity_z"; + template <> + static constexpr const char column_name<6>[] = "mass"; + + static constexpr float default_mass = 500.0; + + private: + /// \cond + + using table_type = soagen::table; + table_type table_; + + /// \endcond + + public: + SOAGEN_NODISCARD_CTOR + particles() = default; + + SOAGEN_NODISCARD_CTOR + particles(const particles&) = default; + + SOAGEN_NODISCARD_CTOR + particles(particles&&) = default; + + particles& operator=(const particles&) = default; + + particles& operator=(particles&&) = default; + + ~particles() = default; + + SOAGEN_NODISCARD_CTOR + constexpr explicit particles(const allocator_type& alloc) noexcept // + : table_{ alloc } + {} + + SOAGEN_PURE_INLINE_GETTER + constexpr size_type size() const noexcept + { + return table_.size(); + } + + SOAGEN_PURE_INLINE_GETTER + constexpr bool empty() const noexcept + { + return table_.empty(); + } + + SOAGEN_PURE_INLINE_GETTER + constexpr size_type capacity() const noexcept + { + return table_.capacity(); + } + + SOAGEN_ALWAYS_INLINE + SOAGEN_CPP20_CONSTEXPR + void clear() noexcept + { + return table_.clear(); + } + + SOAGEN_ALWAYS_INLINE + SOAGEN_CPP20_CONSTEXPR + void reserve(size_type new_cap) noexcept(noexcept(std::declval().reserve(size_type{}))) + { + table_.reserve(new_cap); + } + + SOAGEN_ALWAYS_INLINE + SOAGEN_CPP20_CONSTEXPR + void shrink_to_fit() noexcept(noexcept(std::declval().shrink_to_fit())) + { + table_.shrink_to_fit(); + } + + SOAGEN_ALWAYS_INLINE + SOAGEN_CPP20_CONSTEXPR + void pop_back(size_type num = 1) noexcept(noexcept(std::declval().pop_back(size_type{}))) + { + table_.pop_back(num); + } + + SOAGEN_INLINE_GETTER + constexpr allocator_type get_allocator() const noexcept + { + return table_.get_allocator(); + } + + SOAGEN_HIDDEN_CONSTRAINT(sfinae, bool sfinae = soagen::has_swap_member) + SOAGEN_ALWAYS_INLINE + constexpr void swap(particles& other) // + noexcept(noexcept(std::declval().swap(std::declval()))) + { + table_.swap(other.table_); + } + }; + + SOAGEN_HIDDEN_CONSTRAINT(sfinae, bool sfinae = soagen::has_swap_member) + SOAGEN_ALWAYS_INLINE + constexpr void swap(particles& lhs, particles& rhs) // + noexcept(noexcept(std::declval().swap(std::declval()))) + { + lhs.swap(rhs); + } + + static_assert(std::is_default_constructible_v == particles::table_traits::all_default_constructible); + static_assert(std::is_move_constructible_v == particles::table_traits::all_move_constructible); + static_assert(std::is_move_assignable_v == particles::table_traits::all_move_assignable); + static_assert(std::is_copy_constructible_v == particles::table_traits::all_copy_constructible); + static_assert(std::is_copy_assignable_v == particles::table_traits::all_copy_assignable); + static_assert(std::is_nothrow_destructible_v); + + /// \cond + + /// \endcond + + /// @} + + /// @} +} + +#if SOAGEN_MSVC_LIKE + #pragma pop_macro("min") + #pragma pop_macro("max") +#endif +#if SOAGEN_MSVC + #pragma inline_recursion(off) +#endif +SOAGEN_POP_WARNINGS; diff --git a/examples/particles.toml b/examples/particles.toml new file mode 100644 index 0000000..598e2e7 --- /dev/null +++ b/examples/particles.toml @@ -0,0 +1,13 @@ +namespace = 'foo' + +[structs.particles] +static_variables = [{ name = 'default_mass', type = 'float', value = 500.0 }] +variables = [ + { name = 'position_x', type = 'float', alignment = 32 }, + { name = 'position_y', type = 'float', alignment = 32 }, + { name = 'position_z', type = 'float', alignment = 32 }, + { name = 'velocity_x', type = 'float', alignment = 32 }, + { name = 'velocity_y', type = 'float', alignment = 32 }, + { name = 'velocity_z', type = 'float', alignment = 32 }, + { name = 'mass', type = 'float', default = '{% struct::scope %}default_mass' }, +] diff --git a/meson.build b/meson.build new file mode 100644 index 0000000..eac9143 --- /dev/null +++ b/meson.build @@ -0,0 +1,180 @@ +# This file is a part of marzer/soagen and is subject to the the terms of the MIT license. +# Copyright (c) Mark Gillard +# See https://github.com/marzer/soagen/blob/master/LICENSE for the full license text. +# SPDX-License-Identifier: MIT + +project( + 'soagen', + 'cpp', + version : '1.0.0', + meson_version : '>=0.60.0', + license : 'MIT', + default_options : [ 'cpp_std=c++17', 'b_ndebug=if-release', 'buildtype=release' ] +) + +#----------------------------------------------------------------------------------------------------------------------- +# global vars + imports +#----------------------------------------------------------------------------------------------------------------------- + +compiler = meson.get_compiler('cpp') +message('target cpu_family: @0@'.format(host_machine.cpu_family())) +message('target cpu: @0@'.format(host_machine.cpu())) +message('target system: @0@'.format(host_machine.system())) +message('target endian: @0@'.format(host_machine.endian())) + +is_debug = get_option('debug') +is_release = not is_debug +is_windows = host_machine.system() == 'windows' +is_x64 = host_machine.cpu_family() == 'x86_64' +is_pedantic = get_option('pedantic') or is_devel +is_subproject = meson.is_subproject() + +cpp = meson.get_compiler('cpp') +is_gcc = cpp.get_id() == 'gcc' +is_clang = cpp.get_id() == 'clang' +is_msvc = cpp.get_id() == 'msvc' +is_icc_cl = cpp.get_id() == 'intel-cl' +is_icc = is_icc_cl or cpp.get_id() == 'intel' +is_lld = cpp.get_linker_id() == 'ld.lld' +has_exceptions = get_option('cpp_eh') != 'none' + +build_tests = get_option('build_tests') and not is_subproject +build_examples = get_option('build_examples') and not is_subproject + +#----------------------------------------------------------------------------------------------------------------------- +# global_args +# +# these are the arguments common to everything in the project +# *** they are not forwarded to dependents when using this as a submodule. *** +#----------------------------------------------------------------------------------------------------------------------- + +global_args = cpp.get_supported_arguments( + # clang/gcc + '-ferror-limit=5', + '-fmax-errors=5', + '-Wno-unused-command-line-argument', + '-Wno-reserved-macro-identifier', + '-Wno-init-list-lifetime', + '-fchar8_t', + # msvc + '/bigobj', + '/Gy', # function-level linking + '/GF', # string pooling + '/openmp-', + '/permissive-', + '/utf-8', + '/volatile:iso', + '/Zc:__cplusplus', + '/Zc:inline', + '/Zc:externConstexpr', + '/Zc:preprocessor' +) +if has_exceptions + global_args += cpp.get_supported_arguments('/Zc:throwingNew', '-D_HAS_EXCEPTIONS=1') +else + global_args += cpp.get_supported_arguments('-D_HAS_EXCEPTIONS=0') +endif +if is_pedantic + global_args += cpp.get_supported_arguments( + # clang + '-Weverything', + # gcc + '-Wcast-align', + '-Wcast-qual', + '-Wctor-dtor-privacy', + '-Wdisabled-optimization', + '-Wfloat-equal', + '-Wimport', + '-Winit-self', + '-Wlogical-op', + '-Wmissing-declarations', + '-Wmissing-field-initializers', + '-Wmissing-format-attribute', + '-Wmissing-include-dirs', + '-Wmissing-noreturn', + '-Wold-style-cast', + '-Woverloaded-virtual', + '-Wpacked', + '-Wpointer-arith', + '-Wredundant-decls', + '-Wshadow', + '-Wsign-conversion', + '-Wsign-promo', + '-Wstack-protector', + '-Wstrict-null-sentinel', + '-Wswitch-default', + '-Wswitch-enum', + '-Wundef', + '-Wunreachable-code', + '-Wunused', + '-Wunused-parameter', + '-Wuseless-cast', + '-Wvariadic-macros', + '-Wwrite-strings', + '-Wmissing-noreturn' + ) +endif +# unnecessary pedantry: +global_args += cpp.get_supported_arguments( + '-Wno-c++98-compat', + '-Wno-c++98-compat-pedantic', + '-Wno-documentation', + '-Wno-documentation-unknown-command', + '-Wno-switch-enum', + '-Wno-covered-switch-default', + '-Wno-padded', + '-Wno-float-equal' +) + +#----------------------------------------------------------------------------------------------------------------------- +# global_link_args +# +# these are the linker arguments common to everything in the projectwhen compiling shared libraries and executables. +# *** they are not forwarded to dependents when using this as a submodule. *** +#----------------------------------------------------------------------------------------------------------------------- + +global_link_args = [] + +if is_release + global_link_args += cpp.get_supported_link_arguments( + # msvc + '/OPT:REF,ICF=3', + '/INCREMENTAL:NO', + ) +endif + +#----------------------------------------------------------------------------------------------------------------------- +# global_overrides +# +# these are the meson overrides common to everything in the project +# *** they are not forwarded to dependents when using this as a submodule. *** +#----------------------------------------------------------------------------------------------------------------------- + +global_overrides = [ ] +if is_pedantic + global_overrides += [ + 'warning_level=3', + 'werror=true', + ] +endif + +#----------------------------------------------------------------------------------------------------------------------- +# subdirectories + dependencies +#----------------------------------------------------------------------------------------------------------------------- + +subdir('soagen') + +soagen_dep = declare_dependency(include_directories: include_dir) +meson.override_dependency(meson.project_name(), soagen_dep) + +if not meson.is_subproject() and get_option('build_examples') + subdir('examples') +endif + + + + + + + + diff --git a/meson_options.txt b/meson_options.txt new file mode 100644 index 0000000..9d7055f --- /dev/null +++ b/meson_options.txt @@ -0,0 +1,3 @@ +option('build_tests', type: 'boolean', value: true, description: 'Build tests') +option('build_examples', type: 'boolean', value: true, description: 'Build examples') +option('pedantic', type: 'boolean', value: true, description: 'Pedantic build mode.') diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..e8b4e29 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,5 @@ +misk>=0.7.0 +tomli +schema!=0.7.5 +colorama +trieregex diff --git a/run_yapf.bat b/run_yapf.bat new file mode 100644 index 0000000..a080c58 --- /dev/null +++ b/run_yapf.bat @@ -0,0 +1,47 @@ +@ECHO off +SETLOCAL enableextensions enabledelayedexpansion +PUSHD . +CD /d "%~dp0\.." + +REM -------------------------------------------------------------------------------------- +REM Runs yapf format on all the python files in the project +REM -------------------------------------------------------------------------------------- + +WHERE /Q yapf +IF %ERRORLEVEL% NEQ 0 ( + ECHO Could not find yapf + PAUSE + POPD + ENDLOCAL + EXIT /B !ERRORLEVEL! +) + +yapf --version +CALL :RunYapfOnDirectories ^ + soagen + +POPD +@ENDLOCAL +EXIT /B 0 + +:RunYapfOnDirectories +( + FOR %%I IN (%*) DO ( + IF EXIST "%%~I" ( + ECHO Formatting files in "%%~I" + FOR %%X IN (py) DO ( + FOR /F %%J IN ('DIR /B /S "%%~I\*.%%X" 2^> nul') DO ( + yapf -i "%%J" + IF !ERRORLEVEL! NEQ 0 ( + ECHO Error formatting %%J + PAUSE + POPD + ENDLOCAL + EXIT /B !ERRORLEVEL! + ) + ) + ) + ) + ) + EXIT /B +) diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..3b3325b --- /dev/null +++ b/setup.py @@ -0,0 +1,89 @@ +#!/usr/bin/env python3 +# This file is a part of marzer/soagen and is subject to the the terms of the MIT license. +# Copyright (c) Mark Gillard +# See https://github.com/marzer/soagen/blob/master/LICENSE.txt for the full license text. +# SPDX-License-Identifier: MIT + +# set up based on this: https://thucnc.medium.com/how-to-publish-your-own-python-package-to-pypi-4318868210f9 +# windows: +# py setup.py sdist bdist_wheel && twine upload dist/* && rmdir /S /Q dist + +import re +from setuptools import setup, find_packages +from pathlib import Path + + + +def enum_subdirs(root): + root = Path(root).resolve() + assert root.is_dir() + subdirs = [] + for p in root.iterdir(): + if p.is_dir(): + subdirs.append(p) + subdirs = subdirs + enum_subdirs(p) + return subdirs + + + +package_dir = str(Path(Path(__file__).parent, r'soagen').resolve()) +data_dir = Path(package_dir, r'data') +data_subdirs = enum_subdirs(data_dir) +data_subdirs = [str(d)[len(package_dir):].strip('\\/').replace('\\', '/') for d in data_subdirs] +data_subdirs = [rf'{d}/*' for d in data_subdirs] + +README = '' +with open(r'README.md', encoding='utf-8') as file: + README = file.read().strip() + +CHANGELOG = '' +with open(r'CHANGELOG.md', encoding='utf-8') as file: + CHANGELOG = f'\n\n{file.read()}\n\n' +CHANGELOG = re.sub(r'\n#+\s*Changelog\s*?\n', '\n## Changelog\n', CHANGELOG, flags=re.I).strip() + +VERSION = '' +with open(Path(data_dir, r'version.txt'), encoding='utf-8') as file: + VERSION = file.read().strip() + +SETUP_ARGS = { + 'name': r'soagen', + 'version': VERSION, + 'description': r'Struct-of-Arrays generator for C++ projects.', + 'long_description_content_type': r'text/markdown', + 'long_description': f'{README}\n

\n{CHANGELOG}'.strip(), + 'license': r'MIT', + 'packages': find_packages(), + 'author': r'Mark Gillard', + 'author_email': r'mark.gillard@outlook.com.au', + 'keywords': [r'c++', r'soa', r'struct-of-arrays'], + 'url': r'https://github.com/marzer/soagen', + 'download_url': r'https://pypi.org/project/soagen/', + 'classifiers': [ + r'Development Status :: 3 - Alpha', # + r'License :: OSI Approved :: MIT License', + r'Programming Language :: C++', + r'Topic :: Software Development :: Code Generators', + r'Topic :: Utilities' + ], + 'project_urls': { + r'Source': r'https://github.com/marzer/soagen', + r'Tracker': r'https://github.com/marzer/soagen/issues' + }, + r'python_requires': r'>=3.9', + 'package_data': { + r'soagen': [r'soagen/*', *data_subdirs] + }, + 'exclude_package_data': { + r'soagen': [r'.git*', r'.istanbul.yaml', r'*.rst', r'*.pyc'] + }, + 'entry_points': { + r'console_scripts': [r'soagen = soagen.main:main'] + } +} + +REQUIRES = None +with open(r'requirements.txt', encoding='utf-8') as file: + REQUIRES = file.read().strip().split() + +if __name__ == '__main__': + setup(**SETUP_ARGS, install_requires=REQUIRES) diff --git a/soagen.code-workspace b/soagen.code-workspace new file mode 100644 index 0000000..8a0e40e --- /dev/null +++ b/soagen.code-workspace @@ -0,0 +1,54 @@ +{ + "folders": [ + { + "path": "." + } + ], + "settings": { + "files.associations": { + "*.in": "plaintext", + "type_traits": "cpp", + "concepts": "cpp", + "cstddef": "cpp", + "cstdint": "cpp", + "cstdlib": "cpp", + "initializer_list": "cpp", + "xstddef": "cpp", + "xtr1common": "cpp", + "version": "cpp", + "numeric": "cpp", + "bit": "cpp", + "compare": "cpp", + "cstdio": "cpp", + "cstring": "cpp", + "cwchar": "cpp", + "limits": "cpp", + "utility": "cpp", + "xutility": "cpp", + "exception": "cpp", + "new": "cpp", + "xmemory": "cpp", + "atomic": "cpp", + "ctime": "cpp", + "iosfwd": "cpp", + "memory": "cpp", + "tuple": "cpp", + "typeinfo": "cpp" + }, + "C_Cpp.default.compileCommands": "builddir\\compile_commands.json", + "todo-tree.general.tags": [ + "bug:", + "hack:", + "fixme:", + "todo:", + "BUG", + "HACK", + "FIXME", + "TODO", + "XXX", + "[ ]", + "[x]", + "bug:" + ] + } +} diff --git a/soagen/__init__.py b/soagen/__init__.py new file mode 100644 index 0000000..3e7546c --- /dev/null +++ b/soagen/__init__.py @@ -0,0 +1,11 @@ +#!/usr/bin/env python3 +# This file is a part of marzer/soagen and is subject to the the terms of the MIT license. +# Copyright (c) Mark Gillard +# See https://github.com/marzer/soagen/blob/master/LICENSE for the full license text. +# SPDX-License-Identifier: MIT + +from .utils import lib_version + +__all__ = [r'lib_version'] + +__version__ = r'.'.join([str(v) for v in lib_version()]) diff --git a/soagen/__main__.py b/soagen/__main__.py new file mode 100644 index 0000000..9c8c399 --- /dev/null +++ b/soagen/__main__.py @@ -0,0 +1,13 @@ +#!/usr/bin/env python3 +# This file is a part of marzer/soagen and is subject to the the terms of the MIT license. +# Copyright (c) Mark Gillard +# See https://github.com/marzer/soagen/blob/master/LICENSE for the full license text. +# SPDX-License-Identifier: MIT + +try: + from . import main as m +except ImportError: + import soagen.main as m + +if __name__ == '__main__': + m.main() diff --git a/soagen/column.py b/soagen/column.py new file mode 100644 index 0000000..542a5d9 --- /dev/null +++ b/soagen/column.py @@ -0,0 +1,146 @@ +#!/usr/bin/env python3 +# This file is a part of marzer/soagen and is subject to the the terms of the MIT license. +# Copyright (c) Mark Gillard +# See https://github.com/marzer/soagen/blob/master/LICENSE for the full license text. +# SPDX-License-Identifier: MIT + +from .configurable import Configurable +from .metavars import * +from .writer import * + + + +class Column(Configurable): + + def __init__(self, var, prev=False): + super().__init__(var) + self.variable = var + self.struct = var.struct + self.index = -1 # set by the struct + self.name = 'previous_' + var.name if prev else var.name + self.type = var.type + self.param_type = var.param_type + self.alignment = var.alignment + + def detect_additional_includes(self) -> list[str]: + return self.variable.detect_additional_includes() + + + +class ColumnProvider(Configurable): + + def __init__(self, struct, const=False): + super().__init__(struct) + self.struct = struct + self.const = "const " if bool(const) else "" + self.column_types = struct.const_column_types if self.const else struct.column_types + + self.meta = MetaVars() + self.meta.push('name', rf'soa_columns') + self.meta.push('type', rf'soa_columns<{self.const}{self.struct.type}, Derived>') + self.meta.push('struct::scope', rf'{self.struct.type}::') + + @classmethod + def write_template_forward_declaration(cls, o: Writer): + o(r'''template + struct soa_columns;''') + + def write_class_forward_declaration(self, o: Writer): + pass + + def write_class_definition(self, o: Writer): + with MetaScope(self.config.meta_stack, self.meta): + o() + if self.config.documentation: + o(rf'/// \brief CRTP {self.const}column provider for {self.struct.qualified_type}.') + o(r'template ') + with ClassDefinition(o, f'struct soa_columns<{self.const}{self.struct.type}, Derived>'): + o( + rf''' + using column_indices = {self.struct.type}::column_indices; + + using column_types = type_list<{self.column_types}>; + + template + using column_type = typename column_types::template select; + + template + static constexpr size_t column_alignment = {self.struct.type}::template column_alignment; + + static constexpr size_t perfect_stride = {self.struct.type}::perfect_stride;''' + ) + + with DoxygenMemberGroup(o, 'Columns'), PreprocessorRegion(o): + for col in self.struct.columns: + for const in ('', 'const '): + if self.const and not const: + continue + type = rf'column_type<{col.index}>' + if len(type) > len(col.type): + type = col.type + o( + rf''' + SOAGEN_ALIGNED_COLUMN({col.index}) + {const}{col.type}* {col.name}() {const}noexcept + {{ + return soagen::assume_aligned>( + static_cast<{const}Derived*>(this)->template column<{col.index}>() + ); + }} + + SOAGEN_PURE_INLINE_GETTER + span<{const}{col.type}> {col.name}_span() {const}noexcept + {{ + return static_cast<{const}Derived*>(this)->template column_span<{col.index}>(); + }}''' + ) + + if not self.const: + + o( + rf''' + template + SOAGEN_ALWAYS_INLINE + void zerofill_column() noexcept + {{ + static_assert(I < {len(self.struct.columns)}, "column index out of range"); + static_assert(is_safe_to_memset>, "the target column must be memset-safe"); + + std::memset(static_cast(this)->template column(), 0, sizeof(column_type) * static_cast(this)->size()); + }} + ''' + ) + + for col in self.struct.columns: + o( + rf''' + SOAGEN_CONSTRAINED_COLUMN({col.index}, is_safe_to_memset>) + SOAGEN_ALWAYS_INLINE + void zerofill_{col.name}() noexcept + {{ + zerofill_column<{col.index}>(); + }}''' + ) + + if self.struct.stripes: + o() + with DoxygenMemberGroup(o, 'Stripes'), PreprocessorRegion(o): + for stripes in self.struct.stripes: + for stripe in stripes: + if self.const and not stripe.const: + continue + o( + rf''' + SOAGEN_PURE_INLINE_GETTER + {stripe.type} {stripe.name}({self.struct.type}::index_type idx = {{}}) {stripe.const}noexcept''' + ) + with Block(o): + with Block(o, pre=rf'return {stripe.type}', post=';'): + for col in stripe.columns: + o( + rf'''static_cast<{stripe.const}Derived*>(this)->template column() + idx,''' + ) + + + +__all__ = [r'Column', r'ColumnProvider'] diff --git a/soagen/config.py b/soagen/config.py new file mode 100644 index 0000000..294cf98 --- /dev/null +++ b/soagen/config.py @@ -0,0 +1,155 @@ +#!/usr/bin/env python3 +# This file is a part of marzer/soagen and is subject to the the terms of the MIT license. +# Copyright (c) Mark Gillard +# See https://github.com/marzer/soagen/blob/master/LICENSE for the full license text. +# SPDX-License-Identifier: MIT + +import copy +import re +from pathlib import Path + +from . import log, utils +from .configurable import ConfigBase +from .errors import Error +from .header_file import HeaderFile +from .injectors import StructInjector +from .metavars import * +from .natvis_file import NatvisFile +from .schemas import And, Optional, Schema, Stripped, Use +from .struct import Struct +from .type_list import * + +try: + import pytomlpp as toml # fast; based on toml++ (C++) +except ImportError: + try: + import tomllib as toml # PEP 680 + except ImportError: + import tomli as toml + + + +class Config(ConfigBase): + + # yapf: disable + __schema = Schema({ + Optional(r'hpp', default=dict) : { object : object }, + Optional(r'cpp', default=dict) : { object : object }, + Optional(r'namespace', default='') : And( + str, + Use(lambda x: '::'.join([part.strip(': \t') for part in x.strip(': \t').split('::')])), + Stripped(str) + ), + Optional(r'structs', default=dict) : { + And(Stripped(str, allow_empty=False, name=r'struct name'), Use(utils.make_snake_case)) : { object : object } + }, + Optional(r'doxygen', default=True) : bool, + Optional(r'clang_format', default=True) : bool, + Optional(r'allocator', default=r'soagen::allocator') : Stripped(str), + Optional(r'all_structs', default=dict) : { object : object }, + Optional(r'all_structs_and_spans', default=dict) : { object : object }, + Optional(r'all_structs_and_mutable_spans', default=dict) : { object : object }, + Optional(r'all_structs_and_const_spans', default=dict) : { object : object }, + Optional(r'all_spans', default=dict) : { object : object }, + Optional(r'all_mutable_spans', default=dict) : { object : object }, + Optional(r'all_const_spans', default=dict) : { object : object }, + Optional(r'all_rows', default=dict) : { object : object }, + Optional(r'all_lvalue_rows', default=dict) : { object : object }, + Optional(r'all_mutable_lvalue_rows', default=dict) : { object : object }, + Optional(r'all_const_lvalue_rows', default=dict) : { object : object }, + Optional(r'all_rvalue_rows', default=dict) : { object : object }, + Optional(r'all_mutable_rows', default=dict) : { object : object }, + Optional(r'all_const_rows', default=dict) : { object : object } + }) + # yapf: enable + + def __init__(self, path): + assert isinstance(path, Path) + self.path = path.resolve() + self.dir = path.parent + self.meta_stack = MetaStack() + self.meta = MetaVars() + self.meta_stack.push(self.meta) + + text = None + try: + text = toml.loads(utils.read_all_text_from_file(self.path, logger=log.i)) + except Exception as err: + log.e(err) + return + self.__dict__.update(Config.__schema.validate(text)) + + # namespace + if self.namespace: + self.meta.push('namespace', self.namespace) + self.meta.push('namespace::name', self.namespace) + self.meta.push('namespace::start', f'namespace {self.namespace}\n{{') + self.meta.push('namespace::end', r'}') + self.meta.push('namespace::scope', rf'{self.namespace}::') + else: + self.meta.push('namespace', '') + self.meta.push('namespace::name', '') + self.meta.push('namespace::start', '') + self.meta.push('namespace::end', '') + self.meta.push('namespace::scope', '') + self.namespace_macro_alias = re.sub(r'__+', '_', self.namespace.upper().replace('::', '_')) + + # injectors for the 'all_X' sections + self.all_structs = StructInjector(self, self.all_structs) + self.all_structs_and_spans = StructInjector(self, self.all_structs_and_spans) + self.all_structs_and_mutable_spans = StructInjector(self, self.all_structs_and_mutable_spans) + self.all_structs_and_const_spans = StructInjector(self, self.all_structs_and_const_spans) + self.all_spans = StructInjector(self, self.all_spans) + self.all_mutable_spans = StructInjector(self, self.all_mutable_spans) + self.all_const_spans = StructInjector(self, self.all_const_spans) + self.all_rows = StructInjector(self, self.all_rows) + self.all_lvalue_rows = StructInjector(self, self.all_lvalue_rows) + self.all_mutable_lvalue_rows = StructInjector(self, self.all_mutable_lvalue_rows) + self.all_const_lvalue_rows = StructInjector(self, self.all_const_lvalue_rows) + self.all_rvalue_rows = StructInjector(self, self.all_rvalue_rows) + self.all_mutable_rows = StructInjector(self, self.all_mutable_rows) + self.all_const_rows = StructInjector(self, self.all_const_rows) + + # structs + self.structs = [Struct(self, k, v) for k, v in self.structs.items()] + self.structs.sort(key=lambda s: s.name) + self.struct_types = TypeList([s.type for s in self.structs]) + self.meta.push('struct_names', ', '.join([s.name for s in self.structs])) + self.meta.push('struct_types', ', '.join([s.type for s in self.structs])) + self.meta.push('qualified_struct_types', ', '.join([s.qualified_type for s in self.structs])) + self.has_column_providers = False + self.has_stripes = False + self.has_rows = False + self.has_spans = False + self.has_strong_indices = False + index = 0 + for struct in self.structs: + struct.set_index(index) + self.has_column_providers = self.has_column_providers or struct.column_providers + self.has_stripes = self.has_stripes or struct.stripes + self.has_rows = self.has_rows or struct.rows + self.has_spans = self.has_spans or struct.spans + self.has_strong_indices = self.has_strong_indices or struct.strong_indices + index += 1 + + # output file configs + self.hpp = [HeaderFile(self, self.structs, self.hpp)] + self.natvis = NatvisFile(self, self.structs) + self.all_outputs = [self.natvis, *self.hpp] + log.d('\n -> '.join([str(self.path)] + [str(o.path) for o in self.all_outputs])) + for o in self.all_outputs: + if o.path.is_dir(): + raise Error(rf"invalid output '{o.path}': outputs cannot be existing directories") + if o.path == self.path: + raise Error(rf"invalid output '{o.path}': outputs cannot overwrite input") + if o.path.name.lower() == r'soagen.hpp': + raise Error(rf"invalid output '{o.path}': soagen.hpp is a reserved name") + + # includes + for i in range(len(self.hpp)): + log.d(rf'{self.hpp[i].path.name}.internal_includes:', self.hpp[i].internal_includes) + log.d(rf'{self.hpp[i].path.name}.external_includes:', self.hpp[i].external_includes) + + + +__all__ = [r'Config'] diff --git a/soagen/configurable.py b/soagen/configurable.py new file mode 100644 index 0000000..02a6a2e --- /dev/null +++ b/soagen/configurable.py @@ -0,0 +1,43 @@ +#!/usr/bin/env python3 +# This file is a part of marzer/soagen and is subject to the the terms of the MIT license. +# Copyright (c) Mark Gillard +# See https://github.com/marzer/soagen/blob/master/LICENSE for the full license text. +# SPDX-License-Identifier: MIT + + + +class ConfigBase(object): + pass + + + +class Configurable(object): + + def __init__(self, cfg): + assert cfg is not None + + if isinstance(cfg, ConfigBase): + self.__cfg = cfg + return + + try: + c = cfg.config() + if isinstance(c, ConfigBase): + self.__cfg = c + return + except: + pass + + try: + c = cfg.config + if isinstance(c, ConfigBase): + self.__cfg = c + return + except: + pass + + self.__cfg = None + + @property + def config(self) -> ConfigBase: + return self.__cfg diff --git a/soagen/data/.clang-format b/soagen/data/.clang-format new file mode 100644 index 0000000..b62853e --- /dev/null +++ b/soagen/data/.clang-format @@ -0,0 +1,200 @@ +--- +Language: Cpp +AccessModifierOffset: -2 +AlignAfterOpenBracket: Align +AlignConsecutiveMacros: Consecutive +AlignConsecutiveAssignments: Consecutive +AlignConsecutiveBitFields: Consecutive +AlignConsecutiveDeclarations: None +AlignEscapedNewlines: Right +AlignOperands: AlignAfterOperator +AlignTrailingComments: true +AllowAllArgumentsOnNextLine: false +AllowAllConstructorInitializersOnNextLine: false +AllowAllParametersOfDeclarationOnNextLine: false +AllowShortEnumsOnASingleLine: false +AllowShortBlocksOnASingleLine: Never +AllowShortCaseLabelsOnASingleLine: true +AllowShortFunctionsOnASingleLine: None +AllowShortLambdasOnASingleLine: All +AllowShortIfStatementsOnASingleLine: Never +AllowShortLoopsOnASingleLine: false +AlwaysBreakAfterDefinitionReturnType: None +AlwaysBreakAfterReturnType: None +AlwaysBreakBeforeMultilineStrings: false +AlwaysBreakTemplateDeclarations: Yes +AttributeMacros: + - SOAGEN_EMPTY_BASES + - SOAGEN_NODISCARD_CLASS + - SOAGEN_ABSTRACT_INTERFACE + - SOAGEN_TRIVIAL_ABI + - SOAGEN_VECTORCALL + - SOAGEN_CALLCONV +BinPackArguments: false +BinPackParameters: false +BraceWrapping: + AfterCaseLabel: true + AfterClass: true + AfterControlStatement: Always + AfterEnum: true + AfterFunction: true + AfterNamespace: true + AfterObjCDeclaration: false + AfterStruct: true + AfterUnion: true + AfterExternBlock: true + BeforeCatch: true + BeforeElse: true + BeforeLambdaBody: true + BeforeWhile: true + IndentBraces: false + SplitEmptyFunction: false + SplitEmptyRecord: false + SplitEmptyNamespace: true +BreakAfterAttributes: Always +BreakBeforeBinaryOperators: NonAssignment +BreakBeforeConceptDeclarations: true +BreakBeforeBraces: Custom +BreakBeforeInheritanceComma: false +BreakInheritanceList: BeforeColon +BreakBeforeTernaryOperators: true +BreakConstructorInitializersBeforeComma: false +BreakConstructorInitializers: BeforeColon +BreakAfterJavaFieldAnnotations: false +BreakStringLiterals: true +ColumnLimit: 120 +CommentPragmas: '^([/*!#]|\s*(===|---|clang-format))' +CompactNamespaces: false +ConstructorInitializerAllOnOneLineOrOnePerLine: true +ConstructorInitializerIndentWidth: 4 +ContinuationIndentWidth: 4 +Cpp11BracedListStyle: false +DeriveLineEnding: false +DerivePointerAlignment: false +DisableFormat: false +EmptyLineBeforeAccessModifier: LogicalBlock +ExperimentalAutoDetectBinPacking: false +FixNamespaceComments: false +ForEachMacros: + - foreach + - Q_FOREACH + - BOOST_FOREACH +StatementAttributeLikeMacros: + - Q_EMIT +IncludeBlocks: Preserve +IncludeCategories: + - Regex: '^"(llvm|llvm-c|clang|clang-c)/' + Priority: 2 + SortPriority: 0 + CaseSensitive: false + - Regex: '^(<|"(gtest|gmock|isl|json)/)' + Priority: 3 + SortPriority: 0 + CaseSensitive: false + - Regex: ".*" + Priority: 1 + SortPriority: 0 + CaseSensitive: false +IncludeIsMainRegex: "(Test)?$" +IncludeIsMainSourceRegex: "" +IndentCaseLabels: true +IndentCaseBlocks: false +IndentGotoLabels: true +IndentPPDirectives: BeforeHash +IndentExternBlock: Indent +IndentRequires: false +IndentWidth: 4 +IndentWrappedFunctionNames: false +InsertTrailingCommas: None +JavaScriptQuotes: Leave +JavaScriptWrapImports: true +KeepEmptyLinesAtTheStartOfBlocks: false +MacroBlockBegin: "" +MacroBlockEnd: "" +MaxEmptyLinesToKeep: 1 +NamespaceIndentation: All +ObjCBinPackProtocolList: Auto +ObjCBlockIndentWidth: 2 +ObjCBreakBeforeNestedBlockParam: true +ObjCSpaceAfterProperty: false +ObjCSpaceBeforeProtocolList: true +PenaltyBreakAssignment: 2 +PenaltyBreakBeforeFirstCallParameter: 19 +PenaltyBreakComment: 1 +PenaltyBreakFirstLessLess: 120 +PenaltyBreakString: 1000 +PenaltyBreakTemplateDeclaration: 10 +PenaltyExcessCharacter: 1000000 +PenaltyReturnTypeOnItsOwnLine: 1000000 +PenaltyIndentedWhitespace: 0 +PointerAlignment: Left +QualifierAlignment: Leave +ReflowComments: true +SortIncludes: false +SortJavaStaticImport: Before +SortUsingDeclarations: false +SpaceAfterCStyleCast: false +SpaceAfterLogicalNot: false +SpaceAfterTemplateKeyword: true +SpaceBeforeAssignmentOperators: true +SpaceBeforeCaseColon: false +SpaceBeforeCpp11BracedList: false +SpaceBeforeCtorInitializerColon: true +SpaceBeforeInheritanceColon: true +SpaceBeforeParens: ControlStatements +SpaceAroundPointerQualifiers: Default +SpaceBeforeRangeBasedForLoopColon: true +SpaceInEmptyBlock: false +SpaceInEmptyParentheses: false +SpacesBeforeTrailingComments: 1 +SpacesInAngles: false +SpacesInConditionalStatement: false +SpacesInContainerLiterals: true +SpacesInCStyleCastParentheses: false +SpacesInParentheses: false +SpacesInSquareBrackets: false +SpaceBeforeSquareBrackets: false +BitFieldColonSpacing: Both +Standard: Latest +StatementMacros: + - SOAGEN_ALIGNED_COLUMN + - SOAGEN_ALWAYS_INLINE + - SOAGEN_API + - SOAGEN_ATTR + - SOAGEN_COLUMN + - SOAGEN_CONST + - SOAGEN_CONST_GETTER + - SOAGEN_CONST_GETTER + - SOAGEN_CONST_INLINE_GETTER + - SOAGEN_CONST_INLINE_GETTER + - SOAGEN_CONSTEVAL + - SOAGEN_CONSTRAINED_TEMPLATE + - SOAGEN_CPP20_CONSTEXPR + - SOAGEN_DECLSPEC + - SOAGEN_HIDDEN_CONSTRAINT + - SOAGEN_INLINE_GETTER + - SOAGEN_MALLOC + - SOAGEN_NEVER_INLINE + - SOAGEN_NODISCARD + - SOAGEN_NODISCARD_CTOR + - SOAGEN_PURE + - SOAGEN_PURE_GETTER + - SOAGEN_PURE_INLINE_GETTER + - _Pragma + - __pragma +TabWidth: 4 +TypenameMacros: +UseCRLF: false +UseTab: Always +WhitespaceSensitiveMacros: + - STRINGIZE + - PP_STRINGIZE + - BOOST_PP_STRINGIZE + - SOAGEN_ATTR + - SOAGEN_HAS_ATTR + - SOAGEN_HAS_BUILTIN + - SOAGEN_HAS_CPP_ATTR + - SOAGEN_HAS_FEATURE + - SOAGEN_HAS_INCLUDE + - SOAGEN_LIKELY + - SOAGEN_UNLIKELY diff --git a/soagen/data/hpp/allocator.hpp b/soagen/data/hpp/allocator.hpp new file mode 100644 index 0000000..2a8a6a1 --- /dev/null +++ b/soagen/data/hpp/allocator.hpp @@ -0,0 +1,227 @@ +//# This file is a part of marzer/soagen and is subject to the the terms of the MIT license. +//# Copyright (c) Mark Gillard +//# See https://github.com/marzer/soagen/blob/master/LICENSE for the full license text. +//# SPDX-License-Identifier: MIT +#pragma once + +#include "core.hpp" + +#include "header_start.hpp" +#if SOAGEN_CLANG >= 16 + #pragma clang diagnostic ignored "-Wunsafe-buffer-usage" +#endif + +namespace soagen +{ + struct allocator + { + using value_type = std::byte; + using pointer = value_type*; + using const_pointer = const value_type*; + using void_pointer = std::byte*; + using const_void_pointer = const std::byte*; + using size_type = size_t; + using difference_type = ptrdiff_t; + using is_always_equal = std::true_type; + using propagate_on_container_copy_assignment = std::false_type; + using propagate_on_container_move_assignment = std::false_type; + using propagate_on_container_swap = std::false_type; + + static constexpr size_t min_alignment = + max(size_t{ __STDCPP_DEFAULT_NEW_ALIGNMENT__ }, alignof(std::max_align_t), alignof(value_type)); + + SOAGEN_NODISCARD + SOAGEN_ALWAYS_INLINE + SOAGEN_DECLSPEC(noalias) + SOAGEN_DECLSPEC(restrict) + SOAGEN_ATTR(assume_aligned(min_alignment)) + SOAGEN_ATTR(returns_nonnull) + SOAGEN_ATTR(malloc) + value_type* allocate(size_t size, std::align_val_t alignment = std::align_val_t{ min_alignment }) + { +#if SOAGEN_WINDOWS + auto ptr = _aligned_malloc(size, max(static_cast(alignment), min_alignment)); +#else + auto ptr = std::aligned_alloc(max(static_cast(alignment), min_alignment), size); +#endif + if (!ptr) + throw std::bad_alloc{}; + + return soagen::assume_aligned(static_cast(ptr)); + } + + SOAGEN_ALWAYS_INLINE + SOAGEN_ATTR(nonnull) + void deallocate(value_type* ptr, size_t /* size */) noexcept + { + SOAGEN_ASSUME(ptr != nullptr); + +#if SOAGEN_WINDOWS + _aligned_free(ptr); +#else + std::free(ptr); +#endif + } + + private: + SOAGEN_CONST_INLINE_GETTER + friend bool operator==(const allocator&, const allocator&) noexcept + { + return true; + } + + SOAGEN_CONST_INLINE_GETTER + friend bool operator!=(const allocator&, const allocator&) noexcept + { + return false; + } + }; + static_assert(std::is_trivially_default_constructible_v); + static_assert(std::is_trivially_move_constructible_v); + static_assert(std::is_trivially_copy_constructible_v); + static_assert(std::is_trivially_move_assignable_v); + static_assert(std::is_trivially_copy_assignable_v); + static_assert(std::is_trivially_destructible_v); + static_assert(std::is_nothrow_swappable_v); + + /// \cond + namespace detail + { + // can we specify alignment when allocating? + + template + using has_aligned_allocate_ = decltype(std::declval().allocate(size_t{}, std::align_val_t{})); + + template + inline constexpr bool has_aligned_allocate = is_detected; + + // does the allocator know it's minimum possible alignment value? + + template + using has_min_alignment_ = decltype(Allocator::min_alignment); + + template + inline constexpr bool has_min_alignment = is_detected; + + // what is the _actual_ minimum alignment value that makes sense? + + template > + inline constexpr size_t alloc_min_alignment = + max(Allocator::min_alignment, alignof(typename Allocator::value_type)); + template + inline constexpr size_t alloc_min_alignment = alignof(typename Allocator::value_type); + + // internal base type + + template + struct allocator_traits_base : public std::allocator_traits + { + static_assert(!is_cvref, "allocators must not be cvref-qualified"); + static_assert(any_same, + "allocators must have either std::byte, char or unsigned char as their value_type"); + + using base_traits = std::allocator_traits; + + static constexpr size_t min_alignment = detail::alloc_min_alignment; + static_assert(has_single_bit(min_alignment), "allocator min_alignment must be a power of two"); + + SOAGEN_PURE_INLINE_GETTER + static constexpr bool equal([[maybe_unused]] const Allocator& a, + [[maybe_unused]] const Allocator& b) noexcept + { + if constexpr (base_traits::is_always_equal::value) + return true; + else + return a == b; + } + + // always take ownership (de-allocate existing + move allocation)? + // 1. propagating, equal yes + // 2. propagating, non-equal yes + // 3. non-propagating, equal yes + // 4. non-propagating, non-equal no (need to re-use existing capacity + move elementwise) + static constexpr bool container_move_assign_always_takes_ownership = + base_traits::propagate_on_container_move_assignment::value || base_traits::is_always_equal::value; + }; + } + /// \endcond + + // primary template - allocator has an aligned-allocation overload + template > + struct allocator_traits : public detail::allocator_traits_base + { + using base_traits = detail::allocator_traits_base; + using typename base_traits::value_type; + using typename base_traits::size_type; + + SOAGEN_NODISCARD + SOAGEN_DECLSPEC(noalias) + SOAGEN_DECLSPEC(restrict) + SOAGEN_ATTR(assume_aligned(base_traits::min_alignment)) + SOAGEN_ATTR(returns_nonnull) + SOAGEN_ATTR(malloc) + static constexpr value_type* allocate(Allocator& alloc, size_type num, std::align_val_t alignment) // + noexcept(noexcept(std::declval().allocate(size_type{}, std::align_val_t{}))) + { + return soagen::assume_aligned( + alloc.allocate(num, + std::align_val_t{ max(static_cast(alignment), base_traits::min_alignment) })); + } + }; + + // secondary template - we have to implement the alignment management manually :(:( + template + struct allocator_traits : public detail::allocator_traits_base + { + using base_traits = detail::allocator_traits_base; + using typename base_traits::value_type; + using typename base_traits::size_type; + using typename base_traits::pointer; + + private: + struct alloc_info + { + size_type requested_size; + size_type actual_size; + pointer ptr; + }; + + public: + SOAGEN_NODISCARD + SOAGEN_DECLSPEC(noalias) + SOAGEN_DECLSPEC(restrict) + SOAGEN_ATTR(assume_aligned(base_traits::min_alignment)) + SOAGEN_ATTR(returns_nonnull) + SOAGEN_ATTR(malloc) + static constexpr value_type* allocate(Allocator& alloc, size_type n, std::align_val_t alignment) // + noexcept(noexcept(std::declval().allocate(size_type{}))) + { + static_assert(sizeof(typename Allocator::value_type) == 1u); + + alignment = std::align_val_t{ max(static_cast(alignment), base_traits::min_alignment) }; + + const size_type offset = (static_cast(alignment) - 1u) + sizeof(alloc_info); + pointer ptr = alloc.allocate(n + offset); + SOAGEN_ASSUME(ptr != nullptr); + + alloc_info* info = reinterpret_cast((reinterpret_cast(ptr) + offset) + & ~(static_cast(alignment) - 1u)); + info[-1] = { n, n + offset, ptr }; + return soagen::assume_aligned(reinterpret_cast(info)); + } + + // note that this hides std::allocator_traits::deallocate - this is intentional + SOAGEN_ATTR(nonnull) + static constexpr void deallocate(Allocator& alloc, value_type* ptr, [[maybe_unused]] size_type n) noexcept + { + SOAGEN_ASSUME(ptr != nullptr); + + const auto info = reinterpret_cast(ptr)[-1]; + SOAGEN_ASSERT(n == info.requested_size); + + alloc.deallocate(info.ptr, info.actual_size); + } + }; +} + +#include "header_end.hpp" diff --git a/soagen/data/hpp/column_traits.hpp b/soagen/data/hpp/column_traits.hpp new file mode 100644 index 0000000..cf4683a --- /dev/null +++ b/soagen/data/hpp/column_traits.hpp @@ -0,0 +1,473 @@ +//# This file is a part of marzer/soagen and is subject to the the terms of the MIT license. +//# Copyright (c) Mark Gillard +//# See https://github.com/marzer/soagen/blob/master/LICENSE for the full license text. +//# SPDX-License-Identifier: MIT +#pragma once + +#include "core.hpp" +#include "header_start.hpp" +#if SOAGEN_CLANG >= 16 + #pragma clang diagnostic ignored "-Wunsafe-buffer-usage" +#endif + +/// \cond +namespace soagen::detail +{ + // a base class for the column traits that handles all the non-alignment-dependent stuff + // (to minimize template instantiation explosion) + template + struct column_traits_base + { + using storage_type = StorageType; + static_assert(!is_cvref, "column storage_type may not be cvref-qualified"); + static_assert(!std::is_void_v, "column storage_type may not be void"); + static_assert(std::is_destructible_v, "column storage_type must be destructible"); + + //--- dereferencing -------------------------------------------------------------------------------------------- + + SOAGEN_PURE_GETTER + SOAGEN_ATTR(nonnull) + static constexpr storage_type& get(std::byte* ptr) noexcept + { + SOAGEN_ASSUME(ptr != nullptr); + + return *SOAGEN_LAUNDER(reinterpret_cast(soagen::assume_aligned(ptr))); + } + + SOAGEN_PURE_GETTER + SOAGEN_ATTR(nonnull) + static constexpr const storage_type& get(const std::byte* ptr) noexcept + { + SOAGEN_ASSUME(ptr != nullptr); + + return *SOAGEN_LAUNDER( + reinterpret_cast(soagen::assume_aligned(ptr))); + } + + //--- construction --------------------------------------------------------------------------------------------- + + template + SOAGEN_ATTR(nonnull) + static constexpr storage_type& construct(std::byte* destination, Args&&... args) // + noexcept(std::is_nothrow_constructible_v) + { + SOAGEN_ASSUME(destination != nullptr); + + if constexpr (std::is_aggregate_v) + { + return *(::new (static_cast(soagen::assume_aligned(destination))) + storage_type{ static_cast(args)... }); + } + else + { + return *(::new (static_cast(soagen::assume_aligned(destination))) + storage_type(static_cast(args)...)); + } + } + + template + SOAGEN_ATTR(nonnull) + static constexpr storage_type& construct_at(std::byte* buffer, size_t element_index, Args&&... args) // + noexcept(std::is_nothrow_constructible_v) + { + SOAGEN_ASSUME(buffer != nullptr); + + return construct(buffer + element_index * sizeof(storage_type), static_cast(args)...); + } + + //--- move-construction ---------------------------------------------------------------------------------------- + + static constexpr bool is_move_constructible = + std::is_move_constructible_v + || (std::is_default_constructible_v && std::is_move_assignable_v); + + static constexpr bool is_nothrow_move_constructible = std::is_move_constructible_v + ? std::is_nothrow_move_constructible_v + : (std::is_nothrow_default_constructible_v + && std::is_nothrow_move_assignable_v); + + static constexpr bool is_trivially_move_constructible = + std::is_move_constructible_v ? std::is_trivially_move_constructible_v + : (std::is_trivially_default_constructible_v + && std::is_trivially_move_assignable_v); + + SOAGEN_HIDDEN_CONSTRAINT(sfinae, auto sfinae = is_move_constructible) + SOAGEN_ATTR(nonnull) + SOAGEN_CPP20_CONSTEXPR + static storage_type& move_construct(std::byte* destination, std::byte* source) // + noexcept(is_nothrow_move_constructible) + { + SOAGEN_ASSUME(destination != nullptr); + SOAGEN_ASSUME(source != nullptr); + + if constexpr (std::is_move_constructible_v) + { + return construct(destination, static_cast(get(source))); + } + else + { + static_assert(std::is_default_constructible_v); + static_assert(std::is_move_assignable_v); + + construct(destination); + + if constexpr (std::is_nothrow_move_assignable_v) + { + return move_assign(destination, source); + } + else + { + try + { + return move_assign(destination, source); + } + catch (...) + { + destruct(destination); + throw; + } + } + } + } + + SOAGEN_HIDDEN_CONSTRAINT(sfinae, auto sfinae = std::is_move_constructible_v) + SOAGEN_ATTR(nonnull) + static constexpr storage_type& move_construct_at(std::byte* dest_buffer, + size_t dest_element_index, + std::byte* source_buffer, + size_t source_element_index) // + noexcept(std::is_nothrow_move_constructible_v) + { + SOAGEN_ASSUME(dest_buffer != nullptr); + SOAGEN_ASSUME(source_buffer != nullptr); + + return move_construct(dest_buffer + dest_element_index * sizeof(storage_type), + source_buffer + source_element_index * sizeof(storage_type)); + } + + //--- copy-construction ---------------------------------------------------------------------------------------- + + static constexpr bool is_copy_constructible = + std::is_copy_constructible_v + || (std::is_default_constructible_v && std::is_copy_assignable_v); + + static constexpr bool is_nothrow_copy_constructible = std::is_copy_constructible_v + ? std::is_nothrow_copy_constructible_v + : (std::is_nothrow_default_constructible_v + && std::is_nothrow_copy_assignable_v); + + static constexpr bool is_trivially_copy_constructible = + std::is_copy_constructible_v ? std::is_trivially_copy_constructible_v + : (std::is_trivially_default_constructible_v + && std::is_trivially_copy_assignable_v); + + SOAGEN_HIDDEN_CONSTRAINT(sfinae, auto sfinae = is_copy_constructible) + SOAGEN_ATTR(nonnull) + SOAGEN_CPP20_CONSTEXPR + static storage_type& copy_construct(std::byte* destination, std::byte* source) // + noexcept(is_nothrow_copy_constructible) + { + SOAGEN_ASSUME(destination != nullptr); + SOAGEN_ASSUME(source != nullptr); + + if constexpr (std::is_copy_constructible_v) + { + return construct(destination, static_cast(get(source))); + } + else + { + static_assert(std::is_default_constructible_v); + static_assert(std::is_copy_assignable_v); + + construct(destination); + + if constexpr (std::is_nothrow_copy_assignable_v) + { + return copy_assign(destination, source); + } + else + { + try + { + return copy_assign(destination, source); + } + catch (...) + { + destruct(destination); + throw; + } + } + } + } + + SOAGEN_HIDDEN_CONSTRAINT(sfinae, auto sfinae = is_copy_constructible) + SOAGEN_ATTR(nonnull) + static constexpr storage_type& copy_construct_at(std::byte* dest_buffer, + size_t dest_element_index, + std::byte* source_buffer, + size_t source_element_index) // + noexcept(is_nothrow_copy_constructible) + { + SOAGEN_ASSUME(dest_buffer != nullptr); + SOAGEN_ASSUME(source_buffer != nullptr); + + return copy_construct(dest_buffer + dest_element_index * sizeof(storage_type), + source_buffer + source_element_index * sizeof(storage_type)); + } + + //--- destruction ---------------------------------------------------------------------------------------------- + + SOAGEN_ATTR(nonnull) + static constexpr void destruct([[maybe_unused]] std::byte* target) // + noexcept(std::is_nothrow_destructible_v) + { + SOAGEN_ASSUME(target != nullptr); + + if constexpr (!std::is_trivially_destructible_v) + { + get(target).~storage_type(); + } + } + + SOAGEN_ATTR(nonnull) + static constexpr void destruct_at([[maybe_unused]] std::byte* buffer, // + [[maybe_unused]] size_t element_index) // + noexcept(std::is_nothrow_destructible_v) + { + SOAGEN_ASSUME(buffer != nullptr); + + if constexpr (!std::is_trivially_destructible_v) + { + destruct(buffer + element_index * sizeof(storage_type)); + } + } + + //--- move-assignment ------------------------------------------------------------------------------------------ + + static constexpr bool is_move_assignable = + std::is_move_assignable_v + || (std::is_nothrow_destructible_v && std::is_nothrow_move_constructible_v); + + static constexpr bool is_nothrow_move_assignable = + std::is_move_assignable_v + ? std::is_nothrow_move_assignable_v + : (std::is_nothrow_destructible_v && std::is_nothrow_move_constructible_v); + + static constexpr bool is_trivially_move_assignable = + std::is_move_assignable_v ? std::is_trivially_move_assignable_v + : (std::is_trivially_destructible_v + && std::is_trivially_move_constructible_v); + + SOAGEN_HIDDEN_CONSTRAINT(sfinae, auto sfinae = is_move_assignable) + SOAGEN_ATTR(nonnull) + static constexpr storage_type& move_assign(std::byte* destination, std::byte* source) // + noexcept(is_nothrow_move_assignable) + { + SOAGEN_ASSUME(destination != nullptr); + SOAGEN_ASSUME(source != nullptr); + + if constexpr (std::is_move_assignable_v) + { + return get(destination) = static_cast(get(source)); + } + else + { + // note we only fallback to this if they're nothrow because we don't want to leave the destination + // in a half-constructed state (it existed before the assignment, it should still exist after) + static_assert(std::is_nothrow_destructible_v); + static_assert(std::is_nothrow_move_constructible_v); + + destruct(destination); + return move_construct(destination, source); + } + } + + SOAGEN_HIDDEN_CONSTRAINT(sfinae, auto sfinae = is_move_assignable) + SOAGEN_ATTR(nonnull) + static constexpr storage_type& move_assign_at(std::byte* dest_buffer, + size_t dest_element_index, + std::byte* source_buffer, + size_t source_element_index) // + noexcept(is_nothrow_move_assignable) + { + SOAGEN_ASSUME(dest_buffer != nullptr); + SOAGEN_ASSUME(source_buffer != nullptr); + + return move_assign(dest_buffer + dest_element_index * sizeof(storage_type), + source_buffer + source_element_index * sizeof(storage_type)); + } + + //--- copy-assignment ------------------------------------------------------------------------------------------ + + static constexpr bool is_copy_assignable = + std::is_copy_assignable_v + || (std::is_nothrow_destructible_v && std::is_nothrow_copy_constructible_v); + + static constexpr bool is_nothrow_copy_assignable = + std::is_copy_assignable_v + ? std::is_nothrow_copy_assignable_v + : (std::is_nothrow_destructible_v && std::is_nothrow_copy_constructible_v); + + static constexpr bool is_trivially_copy_assignable = + std::is_copy_assignable_v ? std::is_trivially_copy_assignable_v + : (std::is_trivially_destructible_v + && std::is_trivially_copy_constructible_v); + + SOAGEN_HIDDEN_CONSTRAINT(sfinae, auto sfinae = is_copy_assignable) + SOAGEN_ATTR(nonnull) + static constexpr storage_type& copy_assign(std::byte* destination, std::byte* source) // + noexcept(is_nothrow_copy_assignable) + { + SOAGEN_ASSUME(destination != nullptr); + SOAGEN_ASSUME(source != nullptr); + + if constexpr (std::is_copy_assignable_v) + { + return get(destination) = static_cast(get(source)); + } + else + { + // note we only fallback to this if they're nothrow because we don't want to leave the destination + // in a half-constructed state (it existed before the assignment, it should still exist after) + static_assert(std::is_nothrow_destructible_v); + static_assert(std::is_nothrow_move_constructible_v); + + destruct(destination); + return copy_construct(destination, source); + } + } + + SOAGEN_HIDDEN_CONSTRAINT(sfinae, auto sfinae = is_copy_assignable) + SOAGEN_ATTR(nonnull) + static constexpr storage_type& copy_assign_at(std::byte* dest_buffer, + size_t dest_element_index, + std::byte* source_buffer, + size_t source_element_index) // + noexcept(is_nothrow_copy_assignable) + { + SOAGEN_ASSUME(dest_buffer != nullptr); + SOAGEN_ASSUME(source_buffer != nullptr); + + return copy_assign(dest_buffer + dest_element_index * sizeof(storage_type), + source_buffer + source_element_index * sizeof(storage_type)); + } + }; + + // trait for determining the actual storage type for a column. + // we can strip off const/volatile and coerce all pointers to be void* to reduce template instantiation burden + template + struct storage_type_ + { + using type = ValueType; + }; + template + struct storage_type_ + { + using type = void*; + }; + template + struct storage_type_ : public storage_type_ + {}; + template + struct storage_type_ : public storage_type_ + {}; + template + struct storage_type_ : public storage_type_ + {}; + template + struct storage_type_ : public storage_type_ + {}; + template + struct storage_type_ : public storage_type_ + {}; + template + struct storage_type_ : public storage_type_ + {}; + template + using storage_type = typename detail::storage_type_::type; + + // trait for determining the default parameter type for a column. + // ideally we want to pass small+fast things by value, move-only things by rvalue, + // and everything else as const lvalue. + template // + || std::is_fundamental_v // + || (std::is_trivially_copyable_v && sizeof(ValueType) <= sizeof(void*) * 2), + bool Move = !std::is_copy_constructible_v && std::is_move_constructible_v> + struct param_type_ + { + using type = ValueType; + }; + template + struct param_type_ + { + using type = std::add_rvalue_reference_t; + }; + template + struct param_type_ + { + using type = std::add_lvalue_reference_t>; + }; +} +/// \endcond + +namespace soagen +{ + template + using param_type = typename detail::param_type_::type; + + template , + size_t Align = alignof(ValueType), + typename Base = detail::column_traits_base>> + struct column_traits : public Base + { + using value_type = ValueType; + static_assert(!std::is_reference_v, "column value_type may not be a reference"); + static_assert(!std::is_void_v, "column value_type may not be void"); + static_assert(alignof(value_type) == alignof(typename Base::storage_type)); + static_assert(sizeof(value_type) == sizeof(typename Base::storage_type)); + + using param_type = ParamType; + static_assert(!std::is_void_v, "column param_type may not be void"); + static_assert(std::is_convertible_v || std::is_constructible_v + || (std::is_pointer_v && std::is_same_v), + "column value_type must be constructible or convertible from param_type"); + + static constexpr size_t alignment = max(Align, alignof(value_type)); + static_assert(has_single_bit(alignment), "column alignment must be a power of two"); + + static constexpr size_t max_capacity = static_cast(-1) / sizeof(value_type); + + static constexpr size_t aligned_stride = lcm(alignment, sizeof(value_type)) / sizeof(value_type); + }; + + template + inline constexpr bool is_column_traits = false; + + template + inline constexpr bool is_column_traits> = true; + + template + inline constexpr bool is_column_traits> = is_column_traits; + +} + +/// \cond +namespace soagen::detail +{ + template + struct to_base_traits_; + + template + struct to_base_traits_> + { + using type = column_traits_base; + }; + + template + using to_base_traits = typename to_base_traits_::type; +} +/// \endcond + +#include "header_end.hpp" diff --git a/soagen/data/hpp/compressed_pair.hpp b/soagen/data/hpp/compressed_pair.hpp new file mode 100644 index 0000000..ceed6b5 --- /dev/null +++ b/soagen/data/hpp/compressed_pair.hpp @@ -0,0 +1,258 @@ +//#--------------------------------------------------------------------------------------------------------------------- +//# !!!!! THIS FILE WAS ASSEMBLED FROM MULTIPLE HEADER FILES BY A SCRIPT - PLEASE DON'T EDIT IT DIRECTLY !!!!! +//#--------------------------------------------------------------------------------------------------------------------- + +#pragma once + +#include "core.hpp" +#include "header_start.hpp" + +namespace soagen +{ + namespace detail + { +#define SOAGEN_COMPRESSED_PAIR_BASE_GETTERS(T, name, expression) \ + SOAGEN_PURE_INLINE_GETTER \ + constexpr T& name()& noexcept \ + { \ + return static_cast(expression); \ + } \ + SOAGEN_PURE_INLINE_GETTER \ + constexpr const T& name() const& noexcept \ + { \ + return static_cast(expression); \ + } \ + SOAGEN_PURE_INLINE_GETTER \ + constexpr T&& name()&& noexcept \ + { \ + return static_cast(expression); \ + } \ + SOAGEN_PURE_INLINE_GETTER \ + constexpr const T&& name() const&& noexcept \ + { \ + return static_cast(expression); \ + } \ + static_assert(true) + + // primary template - neither element can be a base + template && !std::is_final_v, + bool SecondCanBeBase = std::is_empty_v && !std::is_final_v> + class compressed_pair_base + { + private: + static_assert(!FirstCanBeBase); + static_assert(!SecondCanBeBase); + SOAGEN_NO_UNIQUE_ADDRESS First first_; + SOAGEN_NO_UNIQUE_ADDRESS Second second_; + + public: + compressed_pair_base() = default; + SOAGEN_DEFAULT_MOVE(compressed_pair_base); + SOAGEN_DEFAULT_COPY(compressed_pair_base); + + SOAGEN_CONSTRAINED_TEMPLATE((std::is_constructible_v && std::is_constructible_v), + typename F, + typename S) + constexpr compressed_pair_base(F&& first_init, S&& second_init) // + noexcept(std::is_nothrow_constructible_v&& std::is_nothrow_constructible_v) + : first_{ static_cast(first_init) }, + second_{ static_cast(second_init) } + {} + + SOAGEN_COMPRESSED_PAIR_BASE_GETTERS(First, first, first_); + SOAGEN_COMPRESSED_PAIR_BASE_GETTERS(Second, second, second_); + }; + + // secondary template - First is a base + template + class SOAGEN_EMPTY_BASES compressed_pair_base // + : private First + { + private: + SOAGEN_NO_UNIQUE_ADDRESS Second second_; + + public: + compressed_pair_base() = default; + SOAGEN_DEFAULT_MOVE(compressed_pair_base); + SOAGEN_DEFAULT_COPY(compressed_pair_base); + + SOAGEN_CONSTRAINED_TEMPLATE((std::is_constructible_v && std::is_constructible_v), + typename F, + typename S) + constexpr compressed_pair_base(F&& first_init, S&& second_init) // + noexcept(std::is_nothrow_constructible_v&& std::is_nothrow_constructible_v) + : First{ static_cast(first_init) }, + second_{ static_cast(second_init) } + {} + + SOAGEN_COMPRESSED_PAIR_BASE_GETTERS(First, first, *this); + SOAGEN_COMPRESSED_PAIR_BASE_GETTERS(Second, second, second_); + }; + + // secondary template - Second is a base + template + class SOAGEN_EMPTY_BASES compressed_pair_base // + : private Second + { + private: + SOAGEN_NO_UNIQUE_ADDRESS First first_; + + public: + compressed_pair_base() = default; + SOAGEN_DEFAULT_MOVE(compressed_pair_base); + SOAGEN_DEFAULT_COPY(compressed_pair_base); + + SOAGEN_CONSTRAINED_TEMPLATE((std::is_constructible_v && std::is_constructible_v), + typename F, + typename S) + constexpr compressed_pair_base(F&& first_init, S&& second_init) // + noexcept(std::is_nothrow_constructible_v&& std::is_nothrow_constructible_v) + : Second{ static_cast(second_init) }, + first_{ static_cast(first_init) } + + {} + + SOAGEN_COMPRESSED_PAIR_BASE_GETTERS(First, first, first_); + SOAGEN_COMPRESSED_PAIR_BASE_GETTERS(Second, second, *this); + }; + + // secondary template - both are bases + template + class SOAGEN_EMPTY_BASES compressed_pair_base // + : private First, private Second + { + public: + compressed_pair_base() = default; + SOAGEN_DEFAULT_MOVE(compressed_pair_base); + SOAGEN_DEFAULT_COPY(compressed_pair_base); + + SOAGEN_CONSTRAINED_TEMPLATE((std::is_constructible_v && std::is_constructible_v), + typename F, + typename S) + constexpr compressed_pair_base(F&& first_init, S&& second_init) // + noexcept(std::is_nothrow_constructible_v&& std::is_nothrow_constructible_v) + : First{ static_cast(first_init) }, + Second{ static_cast(second_init) } + {} + + SOAGEN_COMPRESSED_PAIR_BASE_GETTERS(First, first, *this); + SOAGEN_COMPRESSED_PAIR_BASE_GETTERS(Second, second, *this); + }; + +#undef SOAGEN_COMPRESSED_PAIR_BASE_DEFAULTS +#undef SOAGEN_COMPRESSED_PAIR_BASE_GETTERS + } + template + class SOAGEN_EMPTY_BASES compressed_pair // + : public detail::compressed_pair_base + { + private: + using base = detail::compressed_pair_base; + + public: + using first_type = First; + using second_type = Second; + + SOAGEN_NODISCARD_CTOR + compressed_pair() = default; + + SOAGEN_NODISCARD_CTOR + compressed_pair(const compressed_pair&) = default; + + SOAGEN_NODISCARD_CTOR + compressed_pair(compressed_pair&&) = default; + + compressed_pair& operator=(const compressed_pair&) = default; + + compressed_pair& operator=(compressed_pair&&) = default; + +#if SOAGEN_DOXYGEN +#else + using detail::compressed_pair_base::compressed_pair_base; // inherit constructor +#endif + + private: + template + SOAGEN_PURE_INLINE_GETTER + static constexpr decltype(auto) do_get(T&& cp) noexcept + { + static_assert(I <= 1); + if constexpr (I == 0) + return static_cast(cp).first(); + else + return static_cast(cp).second(); + } + + public: + template + SOAGEN_PURE_INLINE_GETTER + constexpr auto& get() & noexcept + { + return do_get(*this); + } + + template + SOAGEN_PURE_INLINE_GETTER + constexpr auto&& get() && noexcept + { + return do_get(static_cast(*this)); + } + + template + SOAGEN_PURE_INLINE_GETTER + constexpr const auto& get() const& noexcept + { + return do_get(*this); + } + + template + SOAGEN_PURE_INLINE_GETTER + constexpr const auto&& get() const&& noexcept + { + return do_get(static_cast(*this)); + } + + SOAGEN_HIDDEN_CONSTRAINT(std::is_swappable_v&& std::is_swappable_v, + typename F = First, + typename S = Second) + void swap(compressed_pair& other) noexcept(std::is_nothrow_swappable_v&& std::is_nothrow_swappable_v) + { + using std::swap; + swap(base::first(), other.first()); + swap(base::second(), other.second()); + } + }; + + template + compressed_pair(F&&, S&&) -> compressed_pair, remove_cvref>; + + SOAGEN_CONSTRAINED_TEMPLATE(std::is_swappable_v&& std::is_swappable_v, typename F, typename S) + SOAGEN_ALWAYS_INLINE + void swap(compressed_pair& lhs, + compressed_pair& rhs) noexcept(std::is_nothrow_swappable_v&& std::is_nothrow_swappable_v) + { + lhs.swap(rhs); + } + +} + +namespace std +{ + template + struct tuple_size> + { + static constexpr size_t value = 2; + }; + + template + struct tuple_element> + { + static_assert(I < 2); + using type = std::conditional_t; + }; + +} + +#include "header_end.hpp" diff --git a/soagen/data/hpp/core.hpp b/soagen/data/hpp/core.hpp new file mode 100644 index 0000000..566adb0 --- /dev/null +++ b/soagen/data/hpp/core.hpp @@ -0,0 +1,374 @@ +//#--------------------------------------------------------------------------------------------------------------------- +//# !!!!! THIS FILE WAS ASSEMBLED FROM MULTIPLE HEADER FILES BY A SCRIPT - PLEASE DON'T EDIT IT DIRECTLY !!!!! +//#--------------------------------------------------------------------------------------------------------------------- + +#pragma once + +#include "preprocessor.hpp" + +SOAGEN_DISABLE_WARNINGS; +#include +#include +#include +#include +#include +#include +#include +#include +#if SOAGEN_CPP >= 20 && defined(__cpp_lib_bit_cast) && __cpp_lib_bit_cast >= 201806 + #include +#endif +SOAGEN_ENABLE_WARNINGS; + +#include "header_start.hpp" + +#if defined(__cpp_lib_launder) && __cpp_lib_launder >= 201606 + #define SOAGEN_LAUNDER(...) std::launder(__VA_ARGS__) +#elif SOAGEN_CLANG >= 8 || SOAGEN_GCC >= 7 || SOAGEN_ICC >= 1910 || SOAGEN_MSVC >= 1914 \ + || SOAGEN_HAS_BUILTIN(__builtin_launder) + #define SOAGEN_LAUNDER(...) __builtin_launder(__VA_ARGS__) +#else + #define SOAGEN_LAUNDER(...) __VA_ARGS__ +#endif + +//--- typedefs and type traits ----------------------------------------------------------------------------------------- + +namespace soagen +{ + using std::size_t; + using std::ptrdiff_t; + using std::intptr_t; + using std::uintptr_t; + using std::nullptr_t; + + template + using remove_cvref = std::remove_cv_t>; + + template + inline constexpr bool is_cv = !std::is_same_v, T>; + + template + inline constexpr bool is_cvref = !std::is_same_v, T>; + + template + inline constexpr bool is_integer = std::is_integral_v && !std::is_same_v; + + template + inline constexpr bool all_integer = (!!sizeof...(T) && ... && is_integer); + + template + inline constexpr bool is_unsigned = is_integer && std::is_unsigned_v; + + template + inline constexpr bool any_same = (false || ... || std::is_same_v); + + template + using index_tag = std::integral_constant(Value)>; + +#if SOAGEN_HAS_BUILTIN(__type_pack_element) + + template + using type_at_index = __type_pack_element; + +#else + + namespace detail + { + template + struct type_at_index_impl + { + using type = typename type_at_index_impl::type; + }; + + template + struct type_at_index_impl<0, T, U...> + { + using type = T; + }; + } + template + using type_at_index = typename detail::type_at_index_impl::type; + +#endif + + template + inline constexpr bool is_trivially_manifestable = + (std::is_class_v> || std::is_union_v>) // + &&std::is_empty_v> // + && std::is_trivially_default_constructible_v> // + && std::is_trivially_destructible_v>; + + namespace detail + { + template