diff --git a/.clang-format b/.clang-format index b62853e..6d43dbc 100644 --- a/.clang-format +++ b/.clang-format @@ -24,12 +24,12 @@ AlwaysBreakAfterReturnType: None AlwaysBreakBeforeMultilineStrings: false AlwaysBreakTemplateDeclarations: Yes AttributeMacros: + - SOAGEN_ABSTRACT_INTERFACE + - SOAGEN_CALLCONV - SOAGEN_EMPTY_BASES - SOAGEN_NODISCARD_CLASS - - SOAGEN_ABSTRACT_INTERFACE - SOAGEN_TRIVIAL_ABI - SOAGEN_VECTORCALL - - SOAGEN_CALLCONV BinPackArguments: false BinPackParameters: false BraceWrapping: @@ -171,6 +171,7 @@ StatementMacros: - SOAGEN_CONSTRAINED_TEMPLATE - SOAGEN_CPP20_CONSTEXPR - SOAGEN_DECLSPEC + - SOAGEN_HIDDEN_BASE - SOAGEN_HIDDEN_CONSTRAINT - SOAGEN_INLINE_GETTER - SOAGEN_MALLOC @@ -184,6 +185,7 @@ StatementMacros: - __pragma TabWidth: 4 TypenameMacros: + - SOAGEN_ENABLE_IF_T UseCRLF: false UseTab: Always WhitespaceSensitiveMacros: diff --git a/.editorconfig b/.editorconfig index 0435634..2094b44 100644 --- a/.editorconfig +++ b/.editorconfig @@ -8,8 +8,9 @@ tab_width = 4 end_of_line = lf trim_trailing_whitespace = true charset = utf-8 +max_line_length = 120 -[*.{gitattributes,yml,vcxproj,vcxproj.filters,sln,rc,clang-format}] +[*.{gitattributes,yml,vcxproj,vcxproj.filters,sln,rc,clang-format,py}] indent_style = space [{Doxyfile,Doxyfile-mcss}] diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 0000000..c1c954e --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1 @@ +github: marzer diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 0000000..0bb53fc --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,85 @@ +--- +name: Bug report +about: Regular ol' bugs. +title: "" +labels: ["bug"] +assignees: marzer +--- + + + +## Environment + +**soagen version** + + + +**Compiler:** + + + +**C++ standard mode:** + + + +**Target arch:** + + + +**Preprocessor overrides:** + + + +**Relevant compilation flags:** + + + +## Describe the bug + + + +## Steps to reproduce (or a small repro code sample) + + + +## Additional information + + diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 0000000..3ba13e0 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1 @@ +blank_issues_enabled: false diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 0000000..f950f3f --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,34 @@ +--- +name: Feature request +about: Want to see something added or improved? Tell me all about it. +title: "" +labels: ["feature"] +assignees: marzer +--- + + + +**Is your feature request related to a problem? Please describe.** + + + +**Describe the solution you'd like** + + + +**Additional context** + + diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 0000000..474ffe7 --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,33 @@ + + +**What does this change do?** + + + +**Is it related to an exisiting bug report or feature request?** + + + +**Pre-merge checklist** + + + +- [ ] I've read [CONTRIBUTING.md] +- [ ] I've rebased my changes against the current HEAD of `origin/main` (if necessary) +- [ ] I've added new test cases to verify my change (if applicable) +- [ ] I've updated any affected documentation +- [ ] I've tested my changes +- [ ] I've regenerated the single-header version of `soagen.hpp` ([how-to]) + +[CONTRIBUTING.md]: https://github.com/marzer/soagen/blob/master/CONTRIBUTING.md +[how-to]: https://github.com/marzer/soagen/blob/master/CONTRIBUTING.md#modifying-soagenhpp diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml new file mode 100644 index 0000000..a6af462 --- /dev/null +++ b/.github/workflows/ci.yaml @@ -0,0 +1,125 @@ +name: ci + +on: + push: + branches-ignore: + - "gh-pages" + paths: + - "**.hpp" + - "**.cpp" + - "**.inl" + - "**/meson.build" + - "**/workflows/**.yaml" + pull_request: + branches-ignore: + - "gh-pages" + paths: + - "**.hpp" + - "**.cpp" + - "**.inl" + - "**/meson.build" + - "**/workflows/**.yaml" + workflow_dispatch: + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +env: + clang_version: "15" + gcc_version: "12" + +jobs: + linux: + strategy: + matrix: + compiler: + - "clang" + - "gcc" + linker: + - "lld" + type: + - "debug" + - "release" + + runs-on: ubuntu-22.04 + + defaults: + run: + shell: bash + + steps: + - name: Install base dependencies + run: | + sudo apt -y update + sudo apt -y install --no-install-recommends git python3 python3-pip ninja-build libstdc++-${{ env.gcc_version }}-dev + sudo -H pip3 install --no-cache-dir --upgrade meson + + - name: Install lld + if: ${{ startsWith(matrix.linker, 'lld') }} + run: | + sudo apt -y install --no-install-recommends lld-${{ env.clang_version }} + sudo update-alternatives --install /usr/bin/ld.lld ld.lld /usr/bin/ld.lld-${{ env.clang_version }} 1000 + + - name: Install clang + if: ${{ startsWith(matrix.compiler, 'clang') }} + run: | + sudo apt -y install --no-install-recommends clang-${{ env.clang_version }} + sudo update-alternatives --install /usr/bin/c++ c++ /usr/bin/clang++-${{ env.clang_version }} 1000 + sudo update-alternatives --install /usr/bin/cc cc /usr/bin/clang-${{ env.clang_version }} 1000 + + - name: Install gcc + if: ${{ startsWith(matrix.compiler, 'gcc') }} + run: | + sudo apt -y install --no-install-recommends gcc-${{ env.gcc_version }} g++-${{ env.gcc_version }} + sudo update-alternatives --install /usr/bin/c++ c++ /usr/bin/g++-${{ env.gcc_version }} 1000 + sudo update-alternatives --install /usr/bin/cc cc /usr/bin/gcc-${{ env.gcc_version }} 1000 + + - uses: actions/checkout@v3 + with: + submodules: true + + - name: Configure Meson + run: | + if [ ${{ matrix.compiler }} = gcc ]; then pch=false; else pch=true; fi + CC=cc CXX=c++ CXX_LD=${{ matrix.linker }} meson setup build --buildtype=${{ matrix.type }} -Dpedantic=true -Dbuild_tests=true -Dbuild_examples=true -Db_pch=$pch + + - name: Build + run: meson compile -C build --jobs -1 + + - name: Test + run: meson test -C build --verbose + + windows: + strategy: + matrix: + type: + - "debug" + - "release" + + runs-on: windows-2022 + + defaults: + run: + shell: cmd + + steps: + - name: Install dependencies + run: | + python3 -m pip install -U pip==21.3.1 + pip3 install --no-cache-dir --upgrade meson ninja + + - uses: actions/checkout@v3 + with: + submodules: true + + - uses: ilammy/msvc-dev-cmd@v1 + + - name: Configure Meson + run: meson setup build --vsenv --buildtype=${{ matrix.type }} -Dpedantic=true -Dbuild_tests=true + + - name: Build + run: meson compile -C build --jobs -1 + + - name: Test + run: meson test -C build --verbose diff --git a/.github/workflows/gh-pages.yaml b/.github/workflows/gh-pages.yaml new file mode 100644 index 0000000..cfb84e0 --- /dev/null +++ b/.github/workflows/gh-pages.yaml @@ -0,0 +1,41 @@ +name: gh-pages + +on: + push: + branches: + - main + paths: + - "**.hpp" + - "**.dox" + - "**.md" + - "docs/**" + - "**/gh-pages.yaml" + workflow_dispatch: + +jobs: + gh-pages: + runs-on: ubuntu-latest + + defaults: + run: + shell: bash + + steps: + - uses: actions/checkout@v3 + + - name: Install dependencies + run: | + sudo apt -y update + sudo apt -y install --no-install-recommends git python3 python3-pip doxygen + sudo -H pip3 install --upgrade poxy + + - name: Generate docs + run: | + cd docs + poxy --verbose + + - name: Deploy + uses: peaceiris/actions-gh-pages@v3 + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + publish_dir: ./docs/html diff --git a/.gitignore b/.gitignore index 510c73d..08bc9e7 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,8 @@ +soagen_bug_report.zip +docs/html +docs/xml +subprojects/*/ + # Byte-compiled / optimized / DLL files __pycache__/ *.py[cod] diff --git a/.prettierrc.json b/.prettierrc.json new file mode 100644 index 0000000..47c4c47 --- /dev/null +++ b/.prettierrc.json @@ -0,0 +1,6 @@ +{ + "trailingComma": "es5", + "tabWidth": 4, + "endOfLine": "lf", + "printWidth" : 120 +} diff --git a/.style.yapf b/.style.yapf deleted file mode 100644 index 0c23b60..0000000 --- a/.style.yapf +++ /dev/null @@ -1,22 +0,0 @@ -[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 index 3944fd0..2b68e9b 100644 --- a/.vscode/c_cpp_properties.json +++ b/.vscode/c_cpp_properties.json @@ -4,7 +4,7 @@ "name": "Win32", "includePath": [ "${workspaceFolder}/**", - "${workspaceFolder}/soagen/data/hpp" + "${workspaceFolder}/src/soagen/hpp" ], "defines": [ "_DEBUG", diff --git a/.vscode/settings.json b/.vscode/settings.json index 9dc32df..1bb65d1 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -6,16 +6,10 @@ "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/CONTRIBUTING.md b/CONTRIBUTING.md index 0bf8ef8..8d1e8a5 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -9,9 +9,7 @@ For most situations the easiest way for you to contribute is to simply let me kn 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 @@ -23,20 +21,56 @@ as 'editable' from a clone of the repository: ```sh git clone cd soagen -pip install -r requirements.txt pip install -e . ``` -### Code style +
+ +### Python contributions + +#### 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 +If you want to be consistent, the codebase is configured for use with [black], 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. +#### Source files + +Python source files can be found in `src/soagen`. + +
+ +### C++ contributions + +#### Code style + +The codebase has a `.clang-format` config. So long as you make use of it, we're golden. + +#### Source files + +C++ source files can be found in `src/soagen/hpp`. + +#### Modifying `soagen.hpp` + +There are two versions of `soagen.hpp` in the codebase: + +- `src/soagen/hpp/single/soagen.hpp`:
+ This is the one used when a user invokes `--install`. It is preprocessed from other headers into one, + to simply the end-user experience. **Do not edit it directly - your changes will be lost!** + +- `src/soagen/hpp/soagen.hpp`:
+ This one is the entry-point for the preprocessor that generates the single-header version, and doesn't contain + any code. It's just a bunch of `#include` directives. There's nothing to edit! 😄️ + +The _actual_ way to contribute changes to `soagen.hpp` is: -

+1. Near the code you wish to change in `soagen.hpp` will be a banner indicating the `.hpp` file responsible for that + part of the file. Find it in `src/soagen/hpp` and edit it directly. +2. Regenerate the single-header version of `soagen.hpp` using the 'hidden' developer option `soagen --update` + (working directory does not matter, this should work from anywhere). +3. _Somewhat-optinal step:_ There's some tests set up in `tests/`. They use Catch2. Any pull-requests that also update + the tests are going to move much more quickly! [issues]: https://github.com/marzer/soagen/issues [gitter]: https://gitter.im/marzer/community -[yapf]: https://github.com/google/yapf +[black]: https://pypi.org/project/black/ diff --git a/README.md b/README.md index 2f3a11f..3251857 100644 --- a/README.md +++ b/README.md @@ -2,8 +2,8 @@ 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] +[![Sponsor](docs/images/badge-sponsor.svg)][sponsor] +[![Gitter](docs/images/badge-gitter.svg)][gitter] - [Installation](#installation) - [Usage](#usage) @@ -31,31 +31,44 @@ pip install soagen Soagen is a command-line application. ``` -usage: soagen [-h] [-v] [--version] [--werror | --no-werror] [--clang-format | --no-clang-format] [--bug-report] files [files ...] +usage: soagen [-h] [-v] [--version] [--install ] [--werror | --no-werror] + [--color | --no-color] [--clang-format | --no-clang-format] + [--doxygen | --no-doxygen] [--natvis | --no-natvis] [--bug-report] + [configs ...] ___ ___ __ _ __ _ ___ _ __ / __|/ _ \ / _` |/ _` |/ _ \ '_ \ \__ \ (_) | (_| | (_| | __/ | | | |___/\___/ \__,_|\__, |\___|_| |_| __/ | - |___/ v0.1.0 - github.com/marzer/soagen + |___/ v0.0.2 - marzer.github.io/soagen Struct-of-Arrays generator for C++ projects. positional arguments: - files + configs zero or more .toml files describing your structures-of-arrays + (wildcards are accepted, e.g. soa/*.toml) options: -h, --help show this help message and exit -v, --verbose enable very noisy diagnostic output --version print the version and exit + --install install soagen.hpp into a directory --werror, --no-werror treat warnings as errors (default: False) + --color, --no-color use colors in terminal output (default: True) + (the British spelling "colour" is also accepted) --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. + attempt to run clang-format on generated code (default: True) + --doxygen, --no-doxygen + include doxygen markup in the generated code (default: False) + --natvis, --no-natvis + generate .natvis files for Visual Studio (default: True) + --bug-report capture all inputs and outputs in a bug-report zip file ``` +

+ ## Config file options See the [Configuration options] wiki page. diff --git a/cpp.hint b/cpp.hint new file mode 100644 index 0000000..b84232c --- /dev/null +++ b/cpp.hint @@ -0,0 +1,22 @@ +#define SOAGEN_ALIGNED_COLUMN(...) +#define SOAGEN_ALWAYS_INLINE inline +#define SOAGEN_ATTR(...) +#define SOAGEN_COLUMN(...) +#define SOAGEN_CONST_GETTER +#define SOAGEN_CONST_INLINE_GETTER inline +#define SOAGEN_CONSTRAINED_TEMPLATE(condition, ...) template <__VA_ARGS__> +#define SOAGEN_CPP20_CONSTEXPR constexpr +#define SOAGEN_DECLSPEC(...) +#define SOAGEN_DOXYGEN_ONLY(...) +#define SOAGEN_EMPTY_BASES +#define SOAGEN_ENABLE_IF_T(T, ...) T +#define SOAGEN_HIDDEN_BASE(...) : __VA_ARGS__ +#define SOAGEN_HIDDEN_CONSTRAINT(condition, ...) template <__VA_ARGS__> +#define SOAGEN_HIDDEN_PARAM(...) , __VA_ARGS__ +#define SOAGEN_HIDDEN(...) __VA_ARGS__ +#define SOAGEN_IF_DOXYGEN(A, B) B +#define SOAGEN_INLINE_GETTER inline +#define SOAGEN_NODISCARD +#define SOAGEN_NODISCARD_CTOR +#define SOAGEN_PURE_GETTER +#define SOAGEN_PURE_INLINE_GETTER inline diff --git a/docs/images/author.jpg b/docs/images/author.jpg new file mode 100644 index 0000000..07a0548 Binary files /dev/null and b/docs/images/author.jpg differ diff --git a/docs/images/badge-gitter.svg b/docs/images/badge-gitter.svg new file mode 100644 index 0000000..08a4223 --- /dev/null +++ b/docs/images/badge-gitter.svg @@ -0,0 +1 @@ +chat: on gitterchaton gitter \ No newline at end of file diff --git a/docs/images/badge-license-MIT.svg b/docs/images/badge-license-MIT.svg new file mode 100644 index 0000000..52456a7 --- /dev/null +++ b/docs/images/badge-license-MIT.svg @@ -0,0 +1 @@ + licenseMIT \ No newline at end of file diff --git a/docs/images/badge-sponsor.svg b/docs/images/badge-sponsor.svg new file mode 100644 index 0000000..f3c196b --- /dev/null +++ b/docs/images/badge-sponsor.svg @@ -0,0 +1 @@ +sponsor: ❤sponsor \ No newline at end of file diff --git a/docs/pages.css b/docs/pages.css new file mode 100644 index 0000000..937accf --- /dev/null +++ b/docs/pages.css @@ -0,0 +1,33 @@ +.poxy-theme-light { + --table-odd-cells: rgba(0, 0, 0, 0.05); +} + +.poxy-theme-dark { + --table-odd-cells: rgba(255, 255, 255, 0.05); +} + +#intro_motivation th, +#intro_motivation td { + text-align: center; +} + +#intro_motivation table:first-of-type th:nth-child(odd) { + background-color: var(--table-odd-cells); +} + +#table_entities_layout_soa tbody td:nth-child(2n + 1) { + background-color: var(--table-odd-cells); +} + +#table_entities_layout tbody td:nth-child(n + 9), +#table_entities_layout tbody td:nth-child(-n + 4), +#table_employees_layout tbody td:nth-child(n + 9), +#table_employees_layout tbody td:nth-child(-n + 4) { + background-color: var(--table-odd-cells); +} + +article section .inset_table { + margin-top: 2rem !important; + margin-bottom: 2rem !important; + overflow-x: auto; +} diff --git a/docs/pages/intro.md b/docs/pages/intro.md new file mode 100644 index 0000000..307b0fa --- /dev/null +++ b/docs/pages/intro.md @@ -0,0 +1,1094 @@ +@mainpage soagen: A Structure-of-Arrays generator for C++ + +@tableofcontents + +@section intro_tldr TL;DR + +The leading section of this page is an overview of what [Structures-of-Arrays (SoA)](https://en.wikipedia.org/wiki/AoS_and_SoA) +are, what problems they solve, and the annoyances of working with them in C++. Following that is an overview of +**soagen** - a new Structure-Of-Arrays generator and library. + +@inline_success Skip to @ref intro_introducing_soagen if you already know all about SoA and want to go +straight to learning about **soagen** instead. + + + +@section intro_motivation Motivation + + + +@subsection intro_motivation_typical Typical data layouts (Array-of-Structures) + +Data records in a typical C++ program will be organized into `structs` and/or `classes`, one per 'unit' type, +and multiples of these will be stored in arrays. For example, a program for managing employee records +might contain something akin to this: + +```cpp +struct employee +{ + unsigned id; + std::string name; + std::tuple dob; + int salary; + // ... plus a bunch more +}; +``` + +And elsewhere in the program you'd almost certainly find this: + +```cpp +std::vector employees; +``` + +This paradigm is broadly called [Array-of-Structures](https://en.wikipedia.org/wiki/AoS_and_SoA) \(AoS\). +Stored this way, the all employee members are laid out sequentially in memory, so the `employees` array +would look like this: + +@parblock +\[parent_add_class inset_table\] + +\[parent_set_id table_employees_layout\] + + +
employees[0] employees[1] → +
`id` `name` `dob` `salary` + `id` `name` `dob` `salary` + → +
+@endparblock + +When iterating over the `std::vector` depicted above, the CPU's cache will be mostly (or entirely) filled by +only a single `employee` at all times. This is likely fine - most tasks pertaining to `employee` objects are going to +want to access multiple data members, so the linear layout is useful. Rare would be the situation where you'd need to loop +over the `employees` and fetch only a single field. + +What about scenarios where you _do_ want to only manipulate a single field from many objects, though? Let's say the +`std::vector` example above had 1,000,000 `employee` elements and you wanted to query the total labour cost of your +enormous company by summing up the `salary` member. Suddenly all those cache line misses start to add up and +your `calculate_labour_cost()` routine is now much slower than you'd like. + +@inline_remark Yes, yes, you wouldn't use C++ for this. It'd be in SQL or similar. This is an example. +Put down your pitchforks. + +Still, in the general case, the access patterns for an `employee` would benefit from the current layout +far more than any gains made by restructuring to solve this one specific problem (doing so is likely to pessimize the +rest of the program). Also consider that calls to `calculate_labour_cost()` would be infrequent +and the result could be cached, making this problem even less worth solving. + +Let's have a look at a situation where the problem very much _is_ worth solving. + + + +@subsection intro_motivation_soa Structure-of-Arrays + + + +@subsubsection intro_motivation_soa_naive A naïve implementation + +Consider a game engine: game worlds are populated by entities, those entities have various characteristics (position, +orientation, mesh, id, name, et cetera). Imagine we had those encapsulated by an `entity` struct: + +```cpp +struct entity +{ + unsigned id; + std::string name; + vec3 pos; + quaternion orient; + // ... and so on +}; +``` + +As before, lets have a look at what an array of these would look like in memory: + +```cpp +std::vector entities; +``` + +@parblock +\[parent_add_class inset_table\] + +\[parent_set_id table_entities_layout\] + + +
entities[0] entities[1] → +
`id` `name` `pos` `orient` + `id` `name` `pos` `orient` + → +
+@endparblock + +Game worlds feature many thousands of entities. Rendering these can be expensive, so great effort goes into ensuring +the engine does not render anything unnecessarily. One technique for eliminating entities from the render list is to +cull those that do not intersect with the camera's [view frustum](https://en.wikipedia.org/wiki/Viewing_frustum). This +necessitates iterating over all the entities in the game world at least once every frame (assisted by a bounding volume +hierarchy or other acceleration structure). Unlike the `employee` program above, taking constant cache misses during +this iteration by reading in entire `entity` structs is potentially devastating to our frame time! + +Ideally we want to _only_ read in the parts of the entities we care about (e.g. `entity::pos`), without dragging +anything else into the cache. + +Enter: [emoji sparkles] [Structure-of-Arrays](https://en.wikipedia.org/wiki/AoS_and_SoA). [emoji sparkles] + +If we restructured our entities into a series of parallel `std::vectors`, we could iterate over just the positions: + +```cpp +struct entities +{ + std::vector id; + std::vector name; + std::vector pos; + std::vector orient; + // ... and so on +}; +``` + +Now our data is conceptually more like a table, with columns for each characteristic (stored contiguously), +and rows for each individual element: + +@parblock +\[parent_add_class inset_table\] + +\[parent_set_id table_entities_layout_soa\] + + +
id name pos orient +
element 0 `id[0]` `name[0]` `pos[0]` `orient[0]` +
element 1 `id[1]` `name[1]` `pos[1]` `orient[1]` +
element 2 `id[2]` `name[2]` `pos[2]` `orient[2]` +
↓ +
+@endparblock + +We've lost the explicit `entity` class for representing one single game world entity, and have instead transitioned to +an implicit object model where an entity is described by all the elements sharing the same index in the parallel +`std::vectors`. As well as being faster for single-element iteration, this also has another neat side-effect: +no structs means no padding between struct members, so lower memory usage! + +[emoji warning] There are some big problems with this naïve SoA implementation, though. + + + +@subsubsection intro_motivation_soa_multiple_allocation Problem #1: Multiple allocations + +One heap allocation for each member `std::vector`. Sure we could come up with a custom `allocator` that works from the same +internal buffer, but that's not for the faint of heart. + +@inline_remark No doubt there's some solution for this in `std::pmr`. I'll leave that as an exercise for the reader. + +@subsubsection intro_motivation_soa_manual_sync Problem #2: Manual Synchronization + +Now that we have multiple arrays, we must ensure that they are all updated in unison whenever we insert, modify, +or delete elements. Forgetting to do this at any point will mean the parallel members are no longer in sync and lead to +disastrous and hilarious effects. + +@subsubsection intro_motivation_soa_iterators_weakly_typed Problem #3: Identities are weakly-typed + +We no longer have the benefit of the strongly-typed `std::vector::iterator` (or even just the ability to take +a pointer, `entity*`), so any time we need to store an association between a specific entity and some other thing, +it needs to be done via an index (e.g. `size_t`). This is much more error-prone; it's all-too-easy easy to +accidentally treat an index into one collection as an index into another. + +Using a 'strong type' library (like +[this one](https://github.com/rollbear/strong_type)) or using an `enum class` can help here, but that still means +you need `static_casts` (or some other conversion function) everywhere, which is _very annoying_. + +@subsubsection intro_motivation_soa_struct_mode_hard Problem #4: AoS-style access is cumbersome + +There will always be situations where you need to treat your SoA data as if it were AoS. Debug printing, for example. +Code that needs to work in AoS mode is now littered with `operator[]` invocations, making it uglier and more error prone. + +@subsubsection intro_motivation_soa_idiomatic Problem #5: Not idiomatic C++ + +AoS-style data means we can call `std::vector<>::push_back()` to add an entirely new self-contained record. We can call +`std::sort()` on the elements in that vector. We can get the `begin()` and `end()` iterators. And so on. These are idioms +all C++ programmers are innately familiar with. SoA-style data can make these tasks much harder, typically necessitating +a new set of internal idioms and reducing the understandability of the code. + +@subsubsection intro_motivation_soa_names Problem #6: Elegance or Names - pick one + +There are a number of solutions for the problems described above floating around the internet. All of them boil down to +one of two approaches: + +1. Most/all of the problems above are solved using a nice template syntax and some specialization tricks, but you lose + the names of members and instead need to fall-back to the incredibly unfrendly std::tuple-like `entities.get<0>()[N]`. +2. You get names, yay! But now your codebase is filled with macros, boo. + +Of the two options above, I generally prefer \#2, though having to update lists of template specialization macros whenever +you make changes to an SoA container is also a source of error. Without language-level reflection facilities, any solution +with names is going to have macros _somewhere_. + +@parblock + +\[parent_set_name aside\] + +\[parent_set_class m-note m-special\] + +We're done explaining AoS vs SoA, but if these concepts were new for you and you'd like to learn more about them before +you move on to read about **soagen**, here's some resources: + +- [AoS and SoA](https://en.wikipedia.org/wiki/AoS_and_SoA) - Wikipedia +- [Implementing a semi-automatic structure-of-arrays data container](https://blog.molecular-matters.com/2013/10/22/implementing-a-semi-automatic-structure-of-arrays-data-container/) - Molecular Matters +- [Memory Layout Transformations](https://www.intel.com/content/www/us/en/developer/articles/technical/memory-layout-transformations.html) - Intel +- [Structure of Arrays vs Array of Structures in CUDA](https://saturncloud.io/blog/structure-of-arrays-vs-array-of-structures-in-cuda/) - SaturnCloud + +@endparblock + + + +@section intro_introducing_soagen Introducing soagen + +I'd to present my solution to the problems of working with Structure-of-Arrays in C++: **soagen**. Soagen is fundamentally two things: + +1. [`soagen`], a command-line code utility for generating `std::vector`-like SoA containers, and +2. [`soagen.hpp`], a single-header backing library upon which the generated code depends. + +Typically you only need to use the command-line tool [`soagen`], and don't need to know anything about [`soagen.hpp`] +beyond "this needs to be on my include path somewhere" (since it's largely an implementation detail). +You may need to learn more about the backing library if you're doing more advanced stuff, but we'll cover that later. + +I'll go over how to use [`soagen`], and the code it generates, by demonstrating how to we'd use it to create a nice SoA +version of the `entity` class [described above](#intro_motivation_soa_naive). + +@inline_remark I stylize the name as 'soagen' (all lowercase), only capitalizing the S +when it's the first word in a sentence, but I don't feel strongly about it. Render it however you like, I'm not fussed. +SoAgen, Soagen, SOAgen, whatever. + +@inline_success If you have no interest in using a command-line tool to generate SoA classes and instead want to +build your own using [`soagen.hpp`] directly, skip to @ref intro_using_without_generator. The rest of the page is +probably worth a read just for context, though. + + + +@section intro_getting_started Getting started + +[`soagen`] is a command-line application written in Python Install it using `pip`: + +@m_class{m-console} + +```plaintext +> pip install soagen +``` + +@note `soagen` requires Python 3.9 or higher. + +After that, let's take a look at the help output: + +@m_class{m-console} + +```plaintext +> soagen --help + +usage: soagen [-h] [-v] [--version] [--install ] [--werror | --no-werror] + [--color | --no-color] [--clang-format | --no-clang-format] + [--doxygen | --no-doxygen] [--natvis | --no-natvis] [--bug-report] + [configs ...] + + ___ ___ __ _ __ _ ___ _ __ + / __|/ _ \ / _` |/ _` |/ _ \ '_ \ + \__ \ (_) | (_| | (_| | __/ | | | + |___/\___/ \__,_|\__, |\___|_| |_| + __/ | + |___/ v0.0.2 - marzer.github.io/soagen + +Struct-of-Arrays generator for C++ projects. + +positional arguments: + configs zero or more .toml files describing your structures-of-arrays + (wildcards are accepted, e.g. soa/*.toml) + +options: + -h, --help show this help message and exit + -v, --verbose enable very noisy diagnostic output + --version print the version and exit + --install install soagen.hpp into a directory + --werror, --no-werror + treat warnings as errors (default: False) + --color, --no-color use colors in terminal output (default: True) + (the British spelling "colour" is also accepted) + --clang-format, --no-clang-format + attempt to run clang-format on generated code (default: True) + --doxygen, --no-doxygen + include doxygen markup in the generated code (default: False) + --natvis, --no-natvis + generate .natvis files for Visual Studio (default: True) + --bug-report capture all inputs and outputs in a bug-report zip file +``` + +So we need to do two things: + +1. Tell `soagen` about the structs we want via a `.toml` file +2. Install [`soagen.hpp`] somewhere (e.g. `my_game/src`) + + + +@section intro_creating_first_class Creating your first SoA container class + +`soagen` creates the output `XXXX.hpp` next to each `XXXX.toml` input you pass in. Given a game engine project with a fairly standard file structure: + +```plaintext +docs/ +include/ + quaternion.hpp + vector.hpp + ... +src/ +README.md +... etc +``` + +...if we want [`entities.hpp`] to end up in `src/` because it's an implementation detail, then that's where the `soagen` +configuration file `entities.toml` should go, too. Let's create `src/entities.toml` and populate it thus: + +```toml +namespace = 'game' + +[structs.entities] +variables = [ + {name = 'id', type = 'unsigned' }, + {name = 'name', type = 'std::string', default = '""' }, + {name = 'pos', type = 'vec3', default = '{}' }, + {name = 'orient', type = 'quaternion', default = '{1, 0, 0, 0}' }, +] +``` + +@inline_remark There are many more options in the @ref schema but for now we'll just stick with the basics. + +Now run `soagen`: + +@m_class{m-console} + +```plaintext + +> soagen src/*.toml + +soagen v0.0.2 +Reading src/entities.toml +Running clang-format for src/entities.hpp +Writing src/entities.hpp +Writing src/entities.natvis +All done! +``` + +We now have [`src/entities.hpp`] containing the definition of `game::entities`, a std::vector-like SoA container class +([here's the documentation](classsoagen_1_1examples_1_1entities.html) if you'd like to see the API). + +@note Soagen has also generated a [`.natvis`] file for working with Visual Studio, though this isn't mandatory - it +can be disabled by passing `--no-natvis`. + +Our project now looks like this: + +```plaintext +docs/ +include/ + quaternion.hpp + vector.hpp +src/ + entities.hpp < + entities.natvis <----- new + entities.toml < +README.md +``` + + + +@section intro_installing_soagen_hpp Installing soagen.hpp + +Inspecting the contents of [`entities.hpp`], we see that right near the top it expects to be able to +`#include `. This means we need to 'install' it somewhere. The command-line utility is responsible for that, +too: + +@m_class{m-console} + +```plaintext +> soagen --install src + +soagen v0.0.2 +Copying soagen.hpp to src +All done! +``` + +Our project now looks like this: + +```plaintext +docs/ +include/ + quaternion.hpp + vector.hpp +src/ + entities.hpp + entities.natvis + entities.toml + soagen.hpp <----- new +README.md +``` + +You can also combine the source generation and install steps in the same command invocation: + +@m_class{m-console} + +```plaintext +> soagen src/*.toml --install src +``` + +This is the same as doing them both separately. + +@note [`soagen.hpp`] does not have to be installed inside your source tree, of course. You could install it wherever +you like. It does make sense to keep it side-by-side with your generated code, though, because the generated code is +always going make fairly exacting assumptions about the version of the header it was paired with. + + + +@section intro_configuring_includes Configuring #includes + +Taking another look at the contents of [`entities.hpp`], we see that `soagen` has detected the use of `std::string` +in the class interface and automatically added `#include ` for us. This will work for most standard library types +(please [file a bug report] if not!), but not for types specific to our codebase. This means that we'd need to add +an `#include` to make the `vec3` and `quaternion` types visible. Fortunately `soagen` knows how to do that. + +Assuming: + +- these math types live in `include/vector.hpp` and `include/quaternion.hpp`, and +- `include/` is configured as one our include paths in our project build system + +...then we need to add the following to our `entities.toml`: + +```toml +[hpp] +internal_includes = [ + 'vector.hpp', + 'quaternion.hpp' +] +``` + +After regenerating [`entities.hpp`] we'll see this: + +```cpp +#include "vector.hpp" +#include "quaternion.hpp" +``` + +Now the compiler will be happy and we can start writing some code :) + +@note `internal_includes` is, as the name suggests, for `#includes` that are internal to your project - they use the +double-quoted form of include directives (e.g. `#include "foo.hpp"`). There also exists `external_includes` for things +that are external to your project - these will use the angle-bracket form `#include `. + + + +@section intro_nomenclature A brief aside: terminology + +From here down there will be a slight change in terminology: + +@parblock + +\[parent_add_class inset_table\] + +\[parent_set_id table_terminology\] + + +
What I say What it means +
Table `soagen`-generated SoA container class +
Column A contiguous subarray of one data type inside a `soagen`-generated SoA container class +
Row A single 'element' in a `soagen`-generated SoA container class +
+@endparblock + + + +@section intro_adding_rows Adding rows + +Now then, lets do what we came here to do: stick entities in our new container! Tables have all the same insertion +operations as std::vector, only with multiple parameters: + +```cpp +// assuming you have #included "entities.hpp" somewhere: + +game::entities e; + +// push_back works as you'd expect +e.push_back(0, "foo", {0,0,0}, {1,0,0,0}); + +// it will also take advantage of the defaults +// we specified in the config, allowing us to elide +// some initializers: +e.push_back(1, "bar"); + +// insert has the same semantics too +e.insert(e.end(), 2, "qux", {1,2,3}); + +// ... but can also take a size_type as the position +// argument, not just iterators +e.insert(e.size(), 3, "kek"); + +for (auto&& row : e) + std::cout << row.id << ": " << row.name << "\n"; +``` + +@out +0: foo +1: bar +2: qux +3: kek +@eout + +We also have `emplace()` and `emplace_back()` just like std::vector, but there's a catch: the `std::vector` versions +are variadic to allow for constructing a single value with any number of constructor arguments. +Since we have multiple values per row, there's no way we can have a variadic `emplace()`/`emplace_back()` +that somehow deduces which arguments go to which values. We need to add an extra level of indirection for that: +the soagen::emplacer: + +```cpp +using soagen::emplacer; + +e.emplace_back(4, emplacer{ 10, 'A' }, {0,0,0}, {1,0,0,0}); + +// defaults also work with emplace and emplace_back() +e.emplace_back(5, emplacer{ 10, 'B' }); + +for (auto&& row : e) + std::cout << row.id << ": " << row.name << "\n"; +``` + +@out +0: foo +1: bar +2: qux +3: kek +4: AAAAAAAAAA +5: BBBBBBBBBB +@eout + + + +@section intro_removing_rows Removing rows + +Soagen tables support `pop_back()`, `erase()` and `resize()` just like std::vector, +and also [`unordered_erase()`] for fast erasure when order doesn't matter: + +```cpp + +// erase BBBBBBBBBB +e.pop_back(); + +// erase AAAAAAAAAA +e.erase(e.end() - 1); + +// erase kek (erase() also supports size_type) +e.erase(e.size() - 1); + +// erase foo quickly using swap-and-pop: +e.unordered_erase(e.begin()); + +for (auto&& row : e) + std::cout << row.id << ": " << row.name << "\n"; +``` + +@out +2: qux <---- this was moved here by unordered_erase() +1: bar +@eout + + + +@section intro_capacity Capacity + +Soagen tables have `capacity()`, `max_capacity()`, `reserve()`, and `shrink_to_fit()` as you'd expect: + +```cpp + +std::cout << "size: " << e.size() << "\n"; +std::cout << "capacity: " << e.capacity() << "\n\n"; + +e.reserve(100); +std::cout << "size: " << e.size() << "\n"; +std::cout << "capacity: " << e.capacity() << "\n\n"; + +e.shrink_to_fit(); +std::cout << "size: " << e.size() << "\n"; +std::cout << "capacity: " << e.capacity() << "\n\n"; + + +``` + +@out +size: 2 +capacity: 6 + +size: 2 +capacity: 100 + +size: 2 +capacity: 2 +@eout + + + +@section intro_columns Working with columns + +Recall that our table has four columns: + +- `id: unsigned` +- `name: std::string` +- `pos: vec3` +- `orient: quaternion` + +Since we don't have just one element type like a std::vector, we don't access these via `data()` - you use their names! +Each returns a pointer to their respective column's data: + +```cpp + +for (std::size_t i = 0; i < e.size(); i++) + std::cout << "id " << i << ": " << e.id()[i] << "\n"; + +for (std::size_t i = 0; i < e.size(); i++) + std::cout << "name " << i << ": " << e.name()[i] << "\n"; + +// ...and so on + +``` + +@out +id 0: 2 +id 1: 1 +name 0: qux +name 1: bar +@eout + + + +@section intro_rows Working with rows and iterators + +Soagen tables support treating rows as if they were regular AoS structs in their own right via #soagen::row. +This is what you get from `operator[]`, `at()`, and by dereferencing soagen::iterators. + +**Rows have reference semantics**; if we retrieve a row from a non-`const` lvalue reference to our `entities` table, we'll +get a row filled with named lvalue references to the members. For example: + +```cpp +// entities&, operator[] +auto row = e[0]; + +// fetches an instance of this: +struct row +{ + unsigned& id; + std::string& name; + vec3& pos; + quaternion& orient; +}; +``` + +Similarly, if we took a row from a `const`-qualified lvalue reference to our `entities`, +we'd get lvalue references to `const` members: + +```cpp +// const entities&, at(): +auto row = std::as_const(e).at(0); + +// fetches an instance of this: +struct row +{ + const unsigned& id; + const std::string& name; + const vec3& pos; + const quaternion& orient; +}; +``` + +Same applies for rvalues: + +```cpp +// entities&&, dereferencing an iterator: +auto row = *(std::move(e).begin()); + +// fetches an instance of this: +struct row +{ + unsigned&& id; + std::string&& name; + vec3&& pos; + quaternion&& orient; +}; +``` + +Note that **rows don't have to be all-or-nothing**; if you want a row view of just two of your members, you can call +[`row()`] to specify the columns you want: + +```cpp +// columns 0 and 1 of row 0 +auto row = e.row<0, 1>(0); + +// fetches an instance of this: +struct row +{ + unsigned& id; + std::string& name; +}; +``` + +This is true of iterators as well: + +```cpp +auto row = e.begin<0, 1>(0); +``` + +Any rows generated by dereferencing the iterator above will only have `id` and `name` members. + +You can even rearrange the order of the columns if you need to do some swizzling: + +```cpp +auto row = e.row<3,2,1>(0); + +// fetches an instance of this: +struct row +{ + quaternion& orient; // column 3 + vec3& pos; // column 2 + std::string& name; // column 1 +}; +``` + +Of course, using the raw column indices is quite susceptible to human error (particularly if you change the table +members later), so tables all have a `column_indices` member with named constants for each column: + +```cpp +auto row = e.begin(0); +``` + +No matter the column configuration, a #soagen::row will implement the std::tuple protocol so you can use it with +structured bindings and other fun template stuff: + +```cpp +auto r = e.row<3, 0>(); + +auto&& [orient, id] = r; + +auto&& member_0 = r.get<0>(); // orient +auto&& member_1 = r.get<1>(); // id + +auto&& column_0 = r.column<0>(); // id +// auto&& column_1 = r.column<1>(); // ERROR - r doesn't view column 1 +// auto&& column_2 = r.column<2>(); // ERROR - r doesn't view column 2 +auto&& column_3 = r.column<3>(); // orient +``` + +Finally, now that we understand rows, I can reveal that you can _also_ use them with `push_back()` and `insert()`: + +```cpp +e.push_back(e[0]); // push a copy of row[0] onto the end of the table +``` + +@see
    + +
  • soagen::iterator +
  • soagen::row
+ + + +@section intro_equality Equality + +If all the column types implement the named requirement [EqualityComparable] \(i.e. they have `operator==`\), then so too +do your tables: + +```cpp +game::entities e1; +game::entities e2; + +e1.push_back(0, "cat"); + +std::cout << (e1 == e2) << "\n"; + +e2.push_back(0, "cat"); + +std::cout << (e1 == e2) << "\n"; + +e1.push_back(1, "dog"); + +std::cout << (e1 == e2) << "\n"; + + +``` + +@out +false +true +false +@eout + +The same is true of rows; if all of the viewed members are [EqualityComparable], then so too are the rows: + +``` +std::cout << (e1[0] == e2[0]) << "\n"; +std::cout << (e1[0] == e1[1]) << "\n"; +``` + +@out +true +false +@eout + +@note Rows do not have to have come from a table that is entirely [EqualityComparable]; it only depends on whether or not +their viewed columns are. You can take a row view of only a few columns of a much larger table, and so long as the +viewed columns are [EqualityComparable], so too is the resulting #soagen::row type. + + + +@section intro_comparison Comparison + +Tables are comparable with operators `<`, `<=`, `>` and `>=` if all their column types implement the named +requirement [LessThanComparable] \(i.e. they have `operator<`\). Comparison is done row-wise, with row members +compared lexicographically: + +```cpp +game::entities e1; +game::entities e2; + +e1.push_back(0, "cat"); +e1.push_back(1, "dog"); + +e2.push_back(0, "cat"); +e2.push_back(1, "dog"); + +std::cout << (e1 < e2) << "\n"; +std::cout << (e1 <= e2) << "\n"; +std::cout << (e1 > e2) << "\n"; +std::cout << (e1 >= e2) << "\n"; + +e2[1].name = "bird"; + +std::cout << (e1 < e2) << "\n"; +std::cout << (e1 <= e2) << "\n"; +std::cout << (e1 > e2) << "\n"; +std::cout << (e1 >= e2) << "\n"; + +``` + +@out +true +true +false +false +@eout + +@note Just as with [EqualityComparable], rows are [LessThanComparable] if their viewed members are, too. +It does not depend on the source table. + + + +@section intro_custom_over_aligning_columns Over-aligning columns + +Owing to its very nature, SoA-structured data is often used in contexts where memory alignment is important. +One common example is SIMD; you may have a column of floats that need to be aligned on a 16, 32 or 64-byte boundary, +according to whatever the minimum instruction set is you're targeting. + +Soagen has support for this without you needing to come up with custom allocators or jump through any template hoops. +Let's re-visit our `entities.toml` file from earlier, only this time, we're going to over-align the `pos` column +by adding an `alignment`: + +```toml +namespace = 'game' + +[structs.entities] # | +variables = [ # | + {name = 'id', type = 'unsigned' }, # \|/ + {name = 'name', type = 'std::string', default = '""' }, # V + {name = 'pos', type = 'vec3', default = '{}', alignment = 32 }, + {name = 'orient', type = 'quaternion', default = '{1, 0, 0, 0}' }, +] +``` + +Re-generating the table now would see that the `entities::pos()` data pointer always returned a value that was aligned +on an (at least) 32-byte boundary. + +@note This information is also propagated through to the compiler via assume_aligned hints allllllll the way down. +Vectorizer go brrrrrr! + +It can also be the case that you need to advance through the table in 'aligned batches', where the start of each batch +for each column has the same alignment as the value you specified in the config. Soagen has you covered here, too: tables +have [`aligned_stride`] as a public constant: + +```cpp +constexpr auto stride = entities::aligned_stride; +for (auto ptr = e.pos(), end = ptr + e.size(); ptr < end; ptr += stride) +{ + // ... +} + +``` + +Note that `::aligned_stride` **is across _every_ column**; if you have multiple over-aligned columns, this value +will be the lowest-common-multiple of all of their `aligned_strides`, such that stepping through by that amount would +have them all correctly aligned. This value may be larger than you like; if you're really just interested in one column, +you can fetch the `aligned_stride` for it specifically using `column_traits`: + +```cpp + +constexpr auto stride = entities::column_traits<2>::aligned_stride; +for (auto ptr = e.pos(), end = ptr + e.size(); ptr < end; ptr += stride) +{ + // ... +} + +``` + +@see
    + +
  • soagen::table_traits::aligned_stride +
  • soagen::column_traits::aligned_stride
+ + + +@section intro_custom_byo_allocator Using custom allocators + +Soagen tables will use their own allocator by default: the #soagen::allocator. This is one of the mechanisms that allow +soagen to propagate the nice alignment hints all the way through to your user code. + +It is the default, but you're not stuck with it. You can change the allocator for any struct in your config like this: + +```toml +allocator = 'foo::allocator' # at root level to change it for all classes, or + +[structs.entity] +allocator = 'foo::fancy_allocator' # ...can also be overridden set per-struct +``` + +The only requirements of your allocator are: + +- It must properly implement the named requirement [Allocator], and +- Have a `value_type` of `char`, `unsigned char` or `std::byte`. + +@subsection intro_customizing_allocators Customizing allocators for soagen +Custom allocators may implement two soagen-specific extensions relating to memory alignment to assist the compiler +with generating performant code: + +@subsubsection intro_customizing_allocators_min_alignment Specifying a min_alignment +If you know that your allocator will never align allocations on a boundary smaller than a particular value (say, 64 +bytes), you can make soagen aware of this by adding a constexpr constant called `min_alignment`. Example: + +```cpp +struct my_allocator +{ + inline constexpr std::size_t min_alignment = 64; +} +``` + +Soagen will then propagate this information to the tables where it may be of use to the compiler. + +@subsubsection intro_customizing_allocators_aligned_allocate Alignment-aware allocate() +If your environment has some aligned-allocation facility you wish to make soagen aware of, you +can do so by providing an alignment-aware overload of `allocate()` taking a `std::align_val_t` +for alignment: + +```cpp +struct my_allocator +{ + value_type* allocate(std::size_t size, std::align_val_t alignment) + { + return my_aligned_alloc_function(size, static_cast(alignment)); + } +} +``` + +Soagen will choose this overload over any others if it is present. + +@see soagen::allocator + + + +@section intro_access_underlying_buffer Accessing the underlying buffer + +Soagen allocates one contiguous buffer for the entire table. If all the column types in your table are +[TriviallyCopyable] you'll be able to access the underlying buffer directly with `data()`, and determine it's size with +`allocation_size()`, allowing you to serialize/deserialize it directly, stream it, hash it, et cetera. + +@see
    + +
  • soagen::table::data() +
  • soagen::table::allocation_size()
+ + + +@section intro_using_without_generator Creating your own SoA types without the generator + +What if you don't want to use a command-line tool to generate code, and instead want to build your own SoA +types? That's totally fine! [`soagen.hpp`] was written with that use-case in mind. + +If you take a look at the source code for any `soagen`-generated table class you'll see that pretty much every function +call is a one-liner pass-through to the common underlying container type #soagen::table. The generated classes buy you +named members, and the ability to use rows and iterators, but even those could be adapted to custom types relatively +simply using the existing machinery. Crack open one of the generated .hpp files (e.g. [entities.hpp]) to see for yourself. + +The #soagen::table is expressed in terms of #soagen::table_traits, which is itself expressed in terms of +#soagen::column_traits. For example, to create a 'raw' verson of the entity table we've generated above: + +```cpp + +using raw_entities = soagen::table, + soagen::column_traits, + soagen::column_traits, + soagen::column_traits +>>; + +raw_entities e; + +e.emplace_back(/* ... */); // etc + +``` + +The interface of soagen::table is a little bit more 'unfurnished' than it's `soagen`-generated brethren, +but people who wish to build their own SoA types around it (or even just use it directly) are likely already advanced +users so I don't anticipate that being an issue :) + +@see
    + +
  • soagen::table +
  • soagen::table_traits +
  • soagen::column_traits
+ + + +@section intro_next_steps Next steps + +If all of this sounds like it would solve a problem for you, please consider giving soagen a try! I'm eager to see +how people get on with it. + +- A loose development roadmap can be found [here](https://github.com/marzer/soagen/issues/1) +- Bug reports and feature requests can be made here [here](https://github.com/marzer/soagen/issues) +- A contribution guide can be found [here](https://github.com/marzer/soagen/blob/main/CONTRIBUTING.md) + +Thanks! + + + +\[div class="m-block m-badge m-primary poxy-about-the-author"\] +\[img src="author.jpg" alt="The Author"\] +\[h3\]About the author\[/h3\] +\[p\] +I'm Mark. You might know me as the toml++ guy. +I write code. Some of it is alright. Almost all of it is C++. +\[span class="socials"\] +\[img src="poxy/poxy-icon-github.svg" class="poxy-icon"\] +\[img src="poxy/poxy-icon-twitter.svg" class="poxy-icon"\] +\[img src="poxy/poxy-icon-email.svg" class="poxy-icon"\] +\[/span] +\[/p\] +\[/div\] + +[`soagen`]: https://pypi.org/project/soagen +[`soagen.hpp`]: https://github.com/marzer/soagen/blob/main/src/soagen/hpp/single/soagen.hpp +[`entities.hpp`]: https://github.com/marzer/soagen/blob/main/examples/entities.hpp +[entities.hpp]: https://github.com/marzer/soagen/blob/main/examples/entities.hpp +[`src/entities.hpp`]: https://github.com/marzer/soagen/blob/main/examples/entities.hpp +[natvis]: https://learn.microsoft.com/en-us/visualstudio/debugger/create-custom-views-of-native-objects?view=vs-2022#BKMK_Using_Natvis_files +[.natvis]: https://learn.microsoft.com/en-us/visualstudio/debugger/create-custom-views-of-native-objects?view=vs-2022#BKMK_Using_Natvis_files +[`.natvis`]: https://learn.microsoft.com/en-us/visualstudio/debugger/create-custom-views-of-native-objects?view=vs-2022#BKMK_Using_Natvis_files +[file a bug report]: https://github.com/marzer/soagen/issues +[`unordered_erase()`]: classsoagen_1_1examples_1_1entities.html#a117807c0fbe9e2ed2fbe39c9193ff231 +[`row()`]: classsoagen_1_1examples_1_1entities.html#ac24830714a0cf3a0f677b77936a79e73 +[`aligned_stride`]: structsoagen_1_1table__traits.html#a7b18454ef28aa4279e1f1fc61bd15381 +[Allocator]: https://en.cppreference.com/w/cpp/named_req/Allocator +[EqualityComparable]: https://en.cppreference.com/w/cpp/named_req/EqualityComparable +[LessThanComparable]: https://en.cppreference.com/w/cpp/named_req/LessThanComparable +[TriviallyCopyable]: https://en.cppreference.com/w/cpp/named_req/TriviallyCopyable diff --git a/docs/pages/schema.md b/docs/pages/schema.md new file mode 100644 index 0000000..c5e190f --- /dev/null +++ b/docs/pages/schema.md @@ -0,0 +1,3 @@ +@page schema Config File Schema + +@tableofcontents diff --git a/docs/poxy.toml b/docs/poxy.toml new file mode 100644 index 0000000..7a97a97 --- /dev/null +++ b/docs/poxy.toml @@ -0,0 +1,91 @@ +# this is a config file for Poxy - a Doxygen + m.css front-end written in Python. +# https://github.com/marzer/poxy +# +# config reference: https://github.com/marzer/poxy/wiki/Configuration-options + +name = 'soagen' +author = 'Mark Gillard' +description = 'Structure-of-Arrays for C++' +cpp = 17 +github = 'marzer/soagen' +license = ['MIT', 'https://github.com/marzer/soagen/blob/master/LICENSE'] +twitter = 'marzer8789' +sponsor = 'https://github.com/sponsors/marzer' +show_includes = false +changelog = true +#logo = 'images/logo.svg' +#favicon = 'images/favicon.ico' +theme = 'light' +extra_files = [ + 'images/badge-gitter.svg', + 'images/badge-sponsor.svg', + 'images/author.jpg', +] +stylesheets = ['pages.css'] +navbar = ['pages', 'namespaces', 'classes'] + +[warnings] +enabled = true +treat_as_errors = false +undocumented = true + + +[sources] +paths = [ + '../src/soagen/hpp', + '../src/soagen/hpp/generated', + '../examples', + 'pages', +] +patterns = ['*.hpp', '*.dox', '*.md'] +strip_paths = ['../src/soagen/hpp', '../examples'] + + +# [examples] +# paths = 'snippets' +# patterns = ['*.hpp', '*.cpp', '*.dox', '*.md'] + + +[images] +paths = ['images'] + + +[code_blocks] +macros = ['SOAGEN_[A-Z0-9_]+?'] +types = [ + 'employee', + 'quaternion', + 'game::entities', + 'entities', + 'entities::column_indices', + 'emplacer', + 'game::raw_entities', + 'raw_entities', +] +namespaces = ['game'] + +[badges] +'1. Sponsor' = ['badge-sponsor.svg', 'https://github.com/sponsors/marzer'] +'2. Gitter' = ['badge-gitter.svg', 'https://gitter.im/marzer/tomlplusplus'] + +[autolinks] +'soagen::iterators?' = 'classsoagen_1_1iterator.html' +'soagen::rows?' = 'structsoagen_1_1row.html' +'(?:soagen::)emplacers?' = 'structsoagen_1_1emplacer.html' +'soagen::tables?' = 'classsoagen_1_1table.html' + +#[implementation_headers] +#'soagen.hpp' = [ +# 'allocator.hpp', +# 'column_traits.hpp', +# 'iterator.hpp', +# 'meta.hpp', +# 'mixins.hpp', +# 'row.hpp', +# 'table_traits.hpp', +# 'table.hpp', +# # 'generated/compressed_pair.hpp', +# 'generated/core.hpp', +# 'generated/functions.hpp', +# 'generated/preprocessor.hpp', +#] diff --git a/examples/entities.cpp b/examples/entities.cpp new file mode 100644 index 0000000..ae33fdb --- /dev/null +++ b/examples/entities.cpp @@ -0,0 +1,13 @@ +// 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 + +#include "entities.hpp" + +using namespace soagen::examples; + +int main() +{ + return 0; +} diff --git a/examples/entities.hpp b/examples/entities.hpp new file mode 100644 index 0000000..9865af0 --- /dev/null +++ b/examples/entities.hpp @@ -0,0 +1,1368 @@ +//---------------------------------------------------------------------------------------------------------------------- +// 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 +//---------------------------------------------------------------------------------------------------------------------- +// This file was generated by soagen v0.0.2 - do not modify it directly +// https://marzer.github.io/soagen +//---------------------------------------------------------------------------------------------------------------------- +#pragma once + +/// @file +/// @brief Contains the definition of soagen::examples::entities. +/// +/// @note The code and documentation in this file were generated by soagen - https://marzer.github.io/soagen + +#include +#if SOAGEN_VERSION_MAJOR != 0 || SOAGEN_VERSION_MINOR != 0 + #error soagen version mismatch - expected v0.0.X +#endif + +SOAGEN_DISABLE_WARNINGS; +#include +#if SOAGEN_HAS_EXCEPTIONS + #include +#endif +SOAGEN_ENABLE_WARNINGS; + +SOAGEN_PUSH_WARNINGS; +SOAGEN_DISABLE_SPAM_WARNINGS; +#if SOAGEN_CLANG >= 16 + #pragma clang diagnostic ignored "-Wunsafe-buffer-usage" +#endif +#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 + +//---------------------------------------------------------------------------------------------------------------------- +// doxygen safeguards +//---------------------------------------------------------------------------------------------------------------------- + +#if defined(DOXYGEN) || defined(__DOXYGEN) || defined(__DOXYGEN__) || defined(__doxygen__) || defined(__POXY__) \ + || defined(__poxy__) + + #ifndef SOAGEN_DOXYGEN + #define SOAGEN_DOXYGEN 1 + #endif + #ifndef SOAGEN_MAKE_NAME + #define SOAGEN_MAKE_NAME(...) static_assert(true) + #endif + #ifndef SOAGEN_MAKE_COL + #define SOAGEN_MAKE_COL(...) static_assert(true) + #endif + #ifndef SOAGEN_NODISCARD + #define SOAGEN_NODISCARD + #endif + #ifndef SOAGEN_NODISCARD_CTOR + #define SOAGEN_NODISCARD_CTOR + #endif + #ifndef SOAGEN_INLINE_GETTER + #define SOAGEN_INLINE_GETTER inline + #endif + #ifndef SOAGEN_PURE_GETTER + #define SOAGEN_PURE_GETTER + #endif + #ifndef SOAGEN_PURE_INLINE_GETTER + #define SOAGEN_PURE_INLINE_GETTER inline + #endif + #ifndef SOAGEN_ALWAYS_INLINE + #define SOAGEN_ALWAYS_INLINE inline + #endif + #ifndef SOAGEN_CPP20_CONSTEXPR + #define SOAGEN_CPP20_CONSTEXPR constexpr + #endif + #ifndef SOAGEN_HIDDEN + #define SOAGEN_HIDDEN(...) + #endif + #ifndef SOAGEN_HIDDEN_BASE + #define SOAGEN_HIDDEN_BASE(...) + #endif + #ifndef SOAGEN_HIDDEN_CONSTRAINT + #define SOAGEN_HIDDEN_CONSTRAINT(...) + #endif + #ifndef SOAGEN_HIDDEN_PARAM + #define SOAGEN_HIDDEN_PARAM(...) + #endif + #ifndef SOAGEN_ENABLE_IF_T + #define SOAGEN_ENABLE_IF_T(T, ...) T + #endif + #ifndef SOAGEN_ENABLE_IF + #define SOAGEN_ENABLE_IF(...) + #endif + #ifndef SOAGEN_REQUIRES + #define SOAGEN_REQUIRES(...) + #endif + #ifndef SOAGEN_EMPTY_BASES + #define SOAGEN_EMPTY_BASES + #endif + #ifndef SOAGEN_COLUMN + #define SOAGEN_COLUMN(...) + #endif + #ifndef SOAGEN_ALIGNED_COLUMN + #define SOAGEN_ALIGNED_COLUMN(...) + #endif + #if !defined(POXY_IMPLEMENTATION_DETAIL) && !(defined(__POXY__) || defined(__poxy__)) + #define POXY_IMPLEMENTATION_DETAIL(...) __VA_ARGS__ + #endif + +#endif // doxygen + +//---------------------------------------------------------------------------------------------------------------------- +// forward declarations + soagen internal boilerplate +//---------------------------------------------------------------------------------------------------------------------- + +/// @cond + +namespace soagen::examples +{ + class entities; +} + +namespace soagen +{ + template <> + inline constexpr bool is_soa = true; +} + +namespace soagen::detail +{ + // clang-format off + + #ifndef SOAGEN_NAME_id + #define SOAGEN_NAME_id + SOAGEN_MAKE_NAME(id); + #endif + + #ifndef SOAGEN_NAME_name + #define SOAGEN_NAME_name + SOAGEN_MAKE_NAME(name); + #endif + + #ifndef SOAGEN_NAME_orient + #define SOAGEN_NAME_orient + SOAGEN_MAKE_NAME(orient); + #endif + + #ifndef SOAGEN_NAME_pos + #define SOAGEN_NAME_pos + SOAGEN_MAKE_NAME(pos); + #endif + + // clang-format on + + template <> + struct table_traits_type_ + { + using type = table_traits< + /* id */ make_column, + /* name */ make_column, + /* pos */ make_column, 32>, + /* orient */ make_column>; + }; + + template <> + struct allocator_type_ + { + using type = soagen::allocator; + }; + + SOAGEN_MAKE_COL(soagen::examples::entities, 0, id); + SOAGEN_MAKE_COL(soagen::examples::entities, 1, name); + SOAGEN_MAKE_COL(soagen::examples::entities, 2, pos); + SOAGEN_MAKE_COL(soagen::examples::entities, 3, orient); + + template <> + struct table_type_ + { + using type = table, soagen::allocator>; + }; +} + +/// @endcond + +//---------------------------------------------------------------------------------------------------------------------- +// entities +//---------------------------------------------------------------------------------------------------------------------- + +namespace soagen::examples +{ + /// @brief entities + /// + /// @details + /// + /// @remark Models the Structure-of-arrays + /// equivalent of: @code{.cpp} + /// struct entities + /// { + /// unsigned id; + /// std::string name; + /// vec3 pos; + /// quaternion orient; + /// }; + /// @endcode + /// + /// + /// @note The code and documentation for this class were generated by soagen - https://marzer.github.io/soagen + class SOAGEN_EMPTY_BASES entities // + SOAGEN_HIDDEN_BASE(public soagen::mixins::resizable, + public soagen::mixins::equality_comparable, + public soagen::mixins::less_than_comparable, + public soagen::mixins::data_ptr, + public soagen::mixins::const_data_ptr, + public soagen::mixins::swappable) + { + public: + /// @brief The unsigned integer size type used by this class. + using size_type = std::size_t; + + /// @brief The signed integer difference type used by this class. + using difference_type = std::ptrdiff_t; + + /// @brief The allocator type used by this class. + using allocator_type = soagen::allocator_type; + + /// @brief This class's underlying soagen::table type. + using table_type = soagen::table_type; + + /// @brief The soagen::table_traits for the underlying table. + using table_traits = soagen::table_traits_type; + + /// @brief The number of columns in the table. + static constexpr size_t column_count = soagen::table_traits_type::column_count; + + /// @brief Gets the soagen::column_traits for a specific column of the table. + template + using column_traits = typename table_traits::template column; + + /// @brief Gets the type of a specific column in the table. + template + using column_type = typename column_traits::value_type; + + /// @brief Row iterators returned by iterator functions. + using iterator = soagen::iterator_type; + + /// @brief Row iterators returned by const-qualified iterator functions. + using const_iterator = soagen::iterator_type; + + /// @brief Row iterators returned by rvalue-qualified iterator functions. + using rvalue_iterator = soagen::iterator_type; + + /// @brief Regular (lvalue-qualified) row type used by this class. + using row_type = soagen::row_type; + + /// @brief Const row type used by this class. + using const_row_type = soagen::row_type; + + /// @brief Rvalue row type used by this class. + using rvalue_row_type = soagen::row_type; + + /// @brief The number of rows to advance to maintain the requested `alignment` for every column. + /// + /// @details The stride size you need to use when iterating through rows of this table such that + /// the starting element for each batch in each column would have the same memory alignment as the + /// value specified for the column-specific `alignment`. + /// + /// @note Typically you can ignore this; column elements are always aligned correctly according to their + /// type. This is for over-alignment scenarios where you need to do things in batches (e.g. SIMD). + static constexpr size_type aligned_stride = table_traits::aligned_stride; + +#if SOAGEN_DOXYGEN + /// @brief Named index constants for all of the columns in the table. + using column_indices = POXY_IMPLEMENTATION_DETAIL(struct dummy_t); +#else + struct column_indices + { + static constexpr size_type id = 0; + static constexpr size_type name = 1; + static constexpr size_type pos = 2; + static constexpr size_type orient = 3; + }; +#endif + + /// @brief Gets the name of the specified column as a string. + template + static constexpr auto& column_name = soagen::detail::col_name_::value; + + private: + /// @cond + + table_type table_; + + /// @endcond + + public: + /// @brief Default constructor. + SOAGEN_NODISCARD_CTOR + entities() = default; + + /// @brief Move constructor. + SOAGEN_NODISCARD_CTOR + entities(entities&&) = default; + + /// @brief Move-assignment operator. + entities& operator=(entities&&) = default; + + /// @brief Copy constructor. + SOAGEN_NODISCARD_CTOR + entities(const entities&) = default; + + /// @brief Copy-assignment operator. + entities& operator=(const entities&) = default; + + /// @brief Destructor. + ~entities() = default; + + /// @brief Constructs with the given allocator. + SOAGEN_NODISCARD_CTOR + constexpr explicit entities(const allocator_type& alloc) noexcept // + : table_{ alloc } + {} + + /// @brief Constructs with the given allocator. + SOAGEN_NODISCARD_CTOR + constexpr explicit entities(allocator_type&& alloc) noexcept // + : table_{ static_cast(alloc) } + {} + + /// @brief Returns the allocator being used by the table. + SOAGEN_INLINE_GETTER + SOAGEN_CPP20_CONSTEXPR + allocator_type get_allocator() const noexcept + { + return table_.get_allocator(); + } + + /// @name Underlying table access + /// @{ + + /// @brief Returns an lvalue reference to the underlying soagen::table. + SOAGEN_PURE_INLINE_GETTER + constexpr table_type& table() & noexcept + { + return table_; + } + + /// @brief Returns an rvalue reference to the underlying soagen::table. + SOAGEN_PURE_INLINE_GETTER + constexpr table_type&& table() && noexcept + { + return static_cast(table_); + } + + /// @brief Returns a const lvalue reference to the underlying soagen::table. + SOAGEN_PURE_INLINE_GETTER + constexpr const table_type& table() const& noexcept + { + return table_; + } + + /// @} + + /// @name Capacity + /// @{ + + /// @brief Returns true if the number of rows is zero. + SOAGEN_PURE_INLINE_GETTER + constexpr bool empty() const noexcept + { + return table_.empty(); + } + + /// @brief Returns the current number of rows. + SOAGEN_PURE_INLINE_GETTER + constexpr size_type size() const noexcept + { + return table_.size(); + } + + /// @brief Returns the maximum possible number of rows. + SOAGEN_PURE_INLINE_GETTER + constexpr size_type max_size() const noexcept + { + return table_.max_size(); + } + + /// @brief Returns the size of the current underlying buffer allocation in bytes. + SOAGEN_PURE_INLINE_GETTER + constexpr size_type allocation_size() const noexcept + { + return table_.allocation_size(); + } + + /// @brief Reserves storage for (at least) the given number of rows. + SOAGEN_ALWAYS_INLINE + SOAGEN_CPP20_CONSTEXPR + entities& reserve(size_type new_cap) // + noexcept(noexcept(std::declval().reserve(size_type{}))) + { + table_.reserve(new_cap); + return *this; + } + + /// @brief Returns the number of rows that can be held in currently allocated storage. + SOAGEN_PURE_INLINE_GETTER + constexpr size_type capacity() const noexcept + { + return table_.capacity(); + } + + /// @brief Frees unused capacity. + SOAGEN_ALWAYS_INLINE + SOAGEN_CPP20_CONSTEXPR + entities& shrink_to_fit() // + noexcept(noexcept(std::declval().shrink_to_fit())) + { + table_.shrink_to_fit(); + return *this; + } + + /// @} + + /// @name Modifiers + /// @{ + + /// @brief Removes all rows from table. + SOAGEN_ALWAYS_INLINE + SOAGEN_CPP20_CONSTEXPR + entities& clear() noexcept + { + table_.clear(); + return *this; + } + + /// @brief Erases the row at the given position. + /// + /// @availability This method is only available when all the column types are move-assignable. + SOAGEN_HIDDEN(template >) + SOAGEN_ALWAYS_INLINE + SOAGEN_CPP20_CONSTEXPR + SOAGEN_ENABLE_IF_T(entities&, sfinae) erase(size_type pos) // + noexcept(soagen::has_nothrow_erase_member) + { + table_.erase(pos); + return *this; + } + + /// @brief Erases the row at the given position without preserving order. + /// + /// @details This is much faster than #erase() because it uses the swap-and-pop idiom: + /// Instead of shifting all the higher rows downward, the last row is moved into the + /// position of the erased one and the size of the table is reduced by 1. + /// + /// @note If you are tracking row indices in some other place and need to maintain that invariant, + /// you can use the return value to update your data accordingly. + /// + /// @returns The position of the row that was moved into the erased row's position, if any. + /// + /// @availability This method is only available when all the column types are move-assignable. + SOAGEN_HIDDEN(template >) + SOAGEN_ALWAYS_INLINE + SOAGEN_CPP20_CONSTEXPR + SOAGEN_ENABLE_IF_T(soagen::optional, sfinae) unordered_erase(size_type pos) // + noexcept(soagen::has_nothrow_unordered_erase_member) + { + return table_.unordered_erase(pos); + } + + /// @brief Erases the row at the given iterator. + /// + /// @returns An iterator to the row immediately following the one which was removed, + /// or #end() if the one removed was the last row in the table. + /// + /// @availability This method is only available when all the column types are move-assignable. + SOAGEN_HIDDEN(template >) + SOAGEN_ALWAYS_INLINE + SOAGEN_CPP20_CONSTEXPR + SOAGEN_ENABLE_IF_T(iterator, sfinae) erase(iterator pos) // + noexcept(soagen::has_nothrow_erase_member) + { + table_.erase(static_cast(pos)); + return pos; + } + + /// @brief Erases the row at the given position without preserving order. + /// + /// @details This is much faster than #erase() because it uses the swap-and-pop idiom: + /// Instead of shifting all the higher rows downward, the last row is moved into the + /// position of the erased one and the size of the table is reduced by 1. + /// + /// @note If you are tracking row indices in some other place and need to maintain that invariant, + /// you can use the return value to update your data accordingly. + /// + /// @returns The position of the row that was moved into the erased row's position, if any. + /// + /// @availability This method is only available when all the column types are move-assignable. + SOAGEN_HIDDEN(template >) + SOAGEN_ALWAYS_INLINE + SOAGEN_CPP20_CONSTEXPR + SOAGEN_ENABLE_IF_T(soagen::optional, sfinae) unordered_erase(iterator pos) // + noexcept(soagen::has_nothrow_unordered_erase_member) + { + if (auto moved_pos = table_.unordered_erase(static_cast(pos)); moved_pos) + return iterator{ *this, static_cast(*moved_pos) }; + return {}; + } + + /// @brief Erases the row at the given iterator. + /// + /// @returns An iterator to the row immediately following the one which was removed, + /// or #cend() if the one removed was the last row in the table. + /// + /// @availability This method is only available when all the column types are move-assignable. + SOAGEN_HIDDEN(template >) + SOAGEN_ALWAYS_INLINE + SOAGEN_CPP20_CONSTEXPR + SOAGEN_ENABLE_IF_T(const_iterator, sfinae) erase(const_iterator pos) // + noexcept(soagen::has_nothrow_erase_member) + { + table_.erase(static_cast(pos)); + return pos; + } + + /// @brief Erases the row at the given position without preserving order. + /// + /// @details This is much faster than #erase() because it uses the swap-and-pop idiom: + /// Instead of shifting all the higher rows downward, the last row is moved into the + /// position of the erased one and the size of the table is reduced by 1. + /// + /// @note If you are tracking row indices in some other place and need to maintain that invariant, + /// you can use the return value to update your data accordingly. + /// + /// @returns The position of the row that was moved into the erased row's position, if any. + /// + /// @availability This method is only available when all the column types are move-assignable. + SOAGEN_HIDDEN(template >) + SOAGEN_ALWAYS_INLINE + SOAGEN_CPP20_CONSTEXPR + SOAGEN_ENABLE_IF_T(soagen::optional, sfinae) unordered_erase(const_iterator pos) // + noexcept(soagen::has_nothrow_unordered_erase_member) + { + if (auto moved_pos = table_.unordered_erase(static_cast(pos)); moved_pos) + return const_iterator{ *this, static_cast(*moved_pos) }; + return {}; + } + + /// @brief Removes the last row(s) from the table. + SOAGEN_ALWAYS_INLINE + SOAGEN_CPP20_CONSTEXPR + entities& pop_back(size_type num = 1) // + noexcept(noexcept(std::declval().pop_back(size_type{}))) + { + table_.pop_back(num); + return *this; + } + +#if SOAGEN_DOXYGEN + + /// @brief Resizes the table to the given number of rows. + /// + /// @availability This method is only available when all the column types are default-constructible. + entities& resize(size_type new_size) // + noexcept(soagen::has_nothrow_resize_member); + + /// @brief Swaps the contents of the table with another. + /// + /// @availability This method is only available when #allocator_type is swappable or non-propagating. + constexpr void swap(entities& other) // + noexcept(soagen::has_nothrow_swap_member); + +#endif + + /// @} + + /// @name Adding rows + /// @{ + + /// @brief Adds a new row at the end of the table. + SOAGEN_CPP20_CONSTEXPR + entities& push_back(column_traits<0>::param_type id, + column_traits<1>::param_type name = "", + column_traits<2>::param_type pos = {}, + column_traits<3>::param_type orient = { 1, 0, 0, 0 }) // + noexcept(table_traits::push_back_is_nothrow) + { + table_.emplace_back(static_cast::param_forward_type>(id), + static_cast::param_forward_type>(name), + static_cast::param_forward_type>(pos), + static_cast::param_forward_type>(orient)); + return *this; + } + + /// @brief Adds a new row at the end of the table (rvalue overload). + SOAGEN_HIDDEN(template ) + SOAGEN_CPP20_CONSTEXPR + entities& push_back(SOAGEN_ENABLE_IF_T(column_traits<0>::rvalue_type, sfinae) id, + column_traits<1>::rvalue_type name = "", + column_traits<2>::rvalue_type pos = {}, + column_traits<3>::rvalue_type orient = { 1, 0, 0, 0 }) // + noexcept(table_traits::rvalue_push_back_is_nothrow) + SOAGEN_REQUIRES(table_traits::rvalue_type_list_is_distinct) // + { + table_.emplace_back(static_cast::rvalue_forward_type>(id), + static_cast::rvalue_forward_type>(name), + static_cast::rvalue_forward_type>(pos), + static_cast::rvalue_forward_type>(orient)); + return *this; + } + + /// @brief Adds a new row at the end of the table. + template >)> + SOAGEN_CPP20_CONSTEXPR + entities& push_back(const soagen::row& row_) // + noexcept(table_traits::row_push_back_is_nothrow&>) + SOAGEN_REQUIRES(soagen::is_soa>) // + { + table_.emplace_back(row_); + return *this; + } + + /// @brief Constructs a new row directly in-place at the end of the table. + template ::default_emplace_type, + typename Pos = column_traits<2>::default_emplace_type, + typename Orient = column_traits<3>::default_emplace_type> + SOAGEN_CPP20_CONSTEXPR + entities& emplace_back(Id&& id, Name&& name = "", Pos&& pos = {}, Orient&& orient = { 1, 0, 0, 0 }) // + noexcept(table_traits::emplace_back_is_nothrow) + { + table_.emplace_back(static_cast(id), + static_cast(name), + static_cast(pos), + static_cast(orient)); + return *this; + } + + /// @} + + /// @name Inserting rows + /// @availability These overloads are only available when all the column types are move-constructible and move-assignable. + /// @{ + + /// @brief Inserts a new row at an arbitrary position in the table. + /// + /// @availability This overload is only available when all the column types are move-constructible and move-assignable. + SOAGEN_HIDDEN( + template + ) + SOAGEN_CPP20_CONSTEXPR + SOAGEN_ENABLE_IF_T(entities&, sfinae) insert(size_type index_, + column_traits<0>::param_type id, + column_traits<1>::param_type name = "", + column_traits<2>::param_type pos = {}, + column_traits<3>::param_type orient = { 1, 0, 0, 0 }) // + noexcept(table_traits::insert_is_nothrow) + SOAGEN_REQUIRES(table_traits::all_move_constructible&& table_traits::all_move_assignable) // + { + table_.emplace(index_, + static_cast::param_forward_type>(id), + static_cast::param_forward_type>(name), + static_cast::param_forward_type>(pos), + static_cast::param_forward_type>(orient)); + return *this; + } + + /// @brief Inserts a new row at an arbitrary position in the table. + /// + /// @availability This overload is only available when all the column types are move-constructible and move-assignable. + SOAGEN_HIDDEN( + template + ) + SOAGEN_CPP20_CONSTEXPR + SOAGEN_ENABLE_IF_T(iterator, sfinae) insert(iterator iter_, + column_traits<0>::param_type id, + column_traits<1>::param_type name = "", + column_traits<2>::param_type pos = {}, + column_traits<3>::param_type orient = { 1, 0, 0, 0 }) // + noexcept(table_traits::insert_is_nothrow) + SOAGEN_REQUIRES(table_traits::all_move_constructible&& table_traits::all_move_assignable) // + { + table_.emplace(static_cast(iter_), + static_cast::param_forward_type>(id), + static_cast::param_forward_type>(name), + static_cast::param_forward_type>(pos), + static_cast::param_forward_type>(orient)); + return iter_; + } + + /// @brief Inserts a new row at an arbitrary position in the table. + /// + /// @availability This overload is only available when all the column types are move-constructible and move-assignable. + SOAGEN_HIDDEN( + template + ) + SOAGEN_CPP20_CONSTEXPR + SOAGEN_ENABLE_IF_T(const_iterator, sfinae) insert(const_iterator iter_, + column_traits<0>::param_type id, + column_traits<1>::param_type name = "", + column_traits<2>::param_type pos = {}, + column_traits<3>::param_type orient = { 1, 0, 0, 0 }) // + noexcept(table_traits::insert_is_nothrow) + SOAGEN_REQUIRES(table_traits::all_move_constructible&& table_traits::all_move_assignable) // + { + table_.emplace(static_cast(iter_), + static_cast::param_forward_type>(id), + static_cast::param_forward_type>(name), + static_cast::param_forward_type>(pos), + static_cast::param_forward_type>(orient)); + return iter_; + } + + /// @brief Inserts a new row at an arbitrary position in the table (rvalue overload). + /// + /// @availability This overload is only available when all the column types are move-constructible and move-assignable. + SOAGEN_HIDDEN( + template + ) + SOAGEN_CPP20_CONSTEXPR + entities& insert(SOAGEN_ENABLE_IF_T(size_type, sfinae) index_, + column_traits<0>::rvalue_type id, + column_traits<1>::rvalue_type name = "", + column_traits<2>::rvalue_type pos = {}, + column_traits<3>::rvalue_type orient = { 1, 0, 0, 0 }) // + noexcept(table_traits::rvalue_insert_is_nothrow) + SOAGEN_REQUIRES(table_traits::rvalue_type_list_is_distinct&& table_traits::all_move_constructible&& + table_traits::all_move_assignable) // + { + table_.emplace(index_, + static_cast::rvalue_forward_type>(id), + static_cast::rvalue_forward_type>(name), + static_cast::rvalue_forward_type>(pos), + static_cast::rvalue_forward_type>(orient)); + return *this; + } + + /// @brief Inserts a new row at an arbitrary position in the table (rvalue overload). + /// + /// @availability This overload is only available when all the column types are move-constructible and move-assignable. + SOAGEN_HIDDEN( + template + ) + SOAGEN_CPP20_CONSTEXPR + iterator insert(SOAGEN_ENABLE_IF_T(iterator, sfinae) iter_, + column_traits<0>::rvalue_type id, + column_traits<1>::rvalue_type name = "", + column_traits<2>::rvalue_type pos = {}, + column_traits<3>::rvalue_type orient = { 1, 0, 0, 0 }) // + noexcept(table_traits::rvalue_insert_is_nothrow) + SOAGEN_REQUIRES(table_traits::rvalue_type_list_is_distinct&& table_traits::all_move_constructible&& + table_traits::all_move_assignable) // + { + table_.emplace(static_cast(iter_), + static_cast::rvalue_forward_type>(id), + static_cast::rvalue_forward_type>(name), + static_cast::rvalue_forward_type>(pos), + static_cast::rvalue_forward_type>(orient)); + return iter_; + } + + /// @brief Inserts a new row at an arbitrary position in the table (rvalue overload). + /// + /// @availability This overload is only available when all the column types are move-constructible and move-assignable. + SOAGEN_HIDDEN( + template + ) + SOAGEN_CPP20_CONSTEXPR + const_iterator insert(SOAGEN_ENABLE_IF_T(const_iterator, sfinae) iter_, + column_traits<0>::rvalue_type id, + column_traits<1>::rvalue_type name = "", + column_traits<2>::rvalue_type pos = {}, + column_traits<3>::rvalue_type orient = { 1, 0, 0, 0 }) // + noexcept(table_traits::rvalue_insert_is_nothrow) + SOAGEN_REQUIRES(table_traits::rvalue_type_list_is_distinct&& table_traits::all_move_constructible&& + table_traits::all_move_assignable) // + { + table_.emplace(static_cast(iter_), + static_cast::rvalue_forward_type>(id), + static_cast::rvalue_forward_type>(name), + static_cast::rvalue_forward_type>(pos), + static_cast::rvalue_forward_type>(orient)); + return iter_; + } + + /// @brief Inserts a new row at an arbitrary position in the table. + /// + /// @availability This overload is only available when all the column types are move-constructible and move-assignable. + template > + && table_traits::all_move_constructible + && table_traits::all_move_assignable))> + SOAGEN_CPP20_CONSTEXPR + entities& insert(size_type index_, const soagen::row& row_) // + noexcept(table_traits::row_insert_is_nothrow&>) + SOAGEN_REQUIRES(soagen::is_soa>&& table_traits::all_move_constructible&& + table_traits::all_move_assignable) // + { + table_.emplace(index_, row_); + return *this; + } + + /// @brief Inserts a new row at an arbitrary position in the table. + /// + /// @availability This overload is only available when all the column types are move-constructible and move-assignable. + template > + && table_traits::all_move_constructible + && table_traits::all_move_assignable))> + SOAGEN_CPP20_CONSTEXPR + iterator insert(iterator iter_, const soagen::row& row_) // + noexcept(table_traits::row_insert_is_nothrow&>) + SOAGEN_REQUIRES(soagen::is_soa>&& table_traits::all_move_constructible&& + table_traits::all_move_assignable) // + { + table_.emplace(static_cast(iter_), row_); + return iter_; + } + + /// @brief Inserts a new row at an arbitrary position in the table. + /// + /// @availability This overload is only available when all the column types are move-constructible and move-assignable. + template > + && table_traits::all_move_constructible + && table_traits::all_move_assignable))> + SOAGEN_CPP20_CONSTEXPR + const_iterator insert(const_iterator iter_, const soagen::row& row_) // + noexcept(table_traits::row_insert_is_nothrow&>) + SOAGEN_REQUIRES(soagen::is_soa>&& table_traits::all_move_constructible&& + table_traits::all_move_assignable) // + { + table_.emplace(static_cast(iter_), row_); + return iter_; + } + + /// @brief Constructs a new row directly in-place at an arbitrary position in the table. + /// + /// @availability This overload is only available when all the column types are move-constructible and move-assignable. + template ::default_emplace_type, + typename Pos = column_traits<2>::default_emplace_type, + typename Orient = column_traits<3>::default_emplace_type SOAGEN_HIDDEN_PARAM( + bool sfinae = (table_traits::all_move_constructible && table_traits::all_move_assignable))> + SOAGEN_CPP20_CONSTEXPR + SOAGEN_ENABLE_IF_T(entities&, sfinae) emplace(size_type index_, + Id&& id, + Name&& name = "", + Pos&& pos = {}, + Orient&& orient = { 1, 0, 0, 0 }) // + noexcept(table_traits::emplace_is_nothrow) + SOAGEN_REQUIRES(table_traits::all_move_constructible&& table_traits::all_move_assignable) // + { + table_.emplace(index_, + static_cast(id), + static_cast(name), + static_cast(pos), + static_cast(orient)); + return *this; + } + + /// @brief Constructs a new row directly in-place at an arbitrary position in the table. + /// + /// @availability This overload is only available when all the column types are move-constructible and move-assignable. + template ::default_emplace_type, + typename Pos = column_traits<2>::default_emplace_type, + typename Orient = column_traits<3>::default_emplace_type SOAGEN_HIDDEN_PARAM( + bool sfinae = (table_traits::all_move_constructible && table_traits::all_move_assignable))> + SOAGEN_CPP20_CONSTEXPR + SOAGEN_ENABLE_IF_T(iterator, sfinae) emplace(iterator iter_, + Id&& id, + Name&& name = "", + Pos&& pos = {}, + Orient&& orient = { 1, 0, 0, 0 }) // + noexcept(table_traits::emplace_is_nothrow) + SOAGEN_REQUIRES(table_traits::all_move_constructible&& table_traits::all_move_assignable) // + { + table_.emplace(static_cast(iter_), + static_cast(id), + static_cast(name), + static_cast(pos), + static_cast(orient)); + return iter_; + } + + /// @brief Constructs a new row directly in-place at an arbitrary position in the table. + /// + /// @availability This overload is only available when all the column types are move-constructible and move-assignable. + template ::default_emplace_type, + typename Pos = column_traits<2>::default_emplace_type, + typename Orient = column_traits<3>::default_emplace_type SOAGEN_HIDDEN_PARAM( + bool sfinae = (table_traits::all_move_constructible && table_traits::all_move_assignable))> + SOAGEN_CPP20_CONSTEXPR + SOAGEN_ENABLE_IF_T(const_iterator, sfinae) emplace(const_iterator iter_, + Id&& id, + Name&& name = "", + Pos&& pos = {}, + Orient&& orient = { 1, 0, 0, 0 }) // + noexcept(table_traits::emplace_is_nothrow) + SOAGEN_REQUIRES(table_traits::all_move_constructible&& table_traits::all_move_assignable) // + { + table_.emplace(static_cast(iter_), + static_cast(id), + static_cast(name), + static_cast(pos), + static_cast(orient)); + return iter_; + } + + /// @} + + /// @name Equality + /// @availability These operators are only available when all the column types are equality-comparable. + /// @{ + +#if SOAGEN_DOXYGEN + + /// @brief Returns true if all of the elements in two tables are equal. + friend constexpr bool operator==(const entities& lhs, const entities& rhs) // + noexcept(soagen::is_nothrow_equality_comparable); + + /// @brief Returns true if not all of the elements in two tables are equal. + friend constexpr bool operator!=(const entities& lhs, const entities& rhs) // + noexcept(soagen::is_nothrow_equality_comparable); + +#endif + + /// @} + + /// @name Comparison + /// @availability These operators are only available when all the column types are less-than-comparable. + /// @{ + +#if SOAGEN_DOXYGEN + + /// @brief Returns true if the LHS table is ordered lexicographically less-than the RHS table. + friend constexpr bool operator<(const entities& lhs, const entities& rhs) // + noexcept(soagen::is_nothrow_less_than_comparable); + + /// @brief Returns true if the LHS table is ordered lexicographically less-than-or-equal-to the RHS table. + friend constexpr bool operator<=(const entities& lhs, const entities& rhs) // + noexcept(soagen::is_nothrow_less_than_comparable); + + /// @brief Returns true if the LHS table is ordered lexicographically greater-than the RHS table. + friend constexpr bool operator>(const entities& lhs, const entities& rhs) // + noexcept(soagen::is_nothrow_less_than_comparable); + + /// @brief Returns true if the LHS table is ordered lexicographically greater-than-or-equal-to the RHS table. + friend constexpr bool operator>=(const entities& lhs, const entities& rhs) // + noexcept(soagen::is_nothrow_less_than_comparable); + +#endif + + /// @} + + /// @name Column access + /// @{ + +#if SOAGEN_DOXYGEN + + /// @brief Returns a pointer to the raw byte backing array. + /// + /// @availability This method is only available when all the column types are trivially-copyable. + constexpr std::byte* data() // + noexcept(soagen::has_nothrow_data_member); + + /// @brief Returns a pointer to the raw byte backing array. + /// + /// @availability This method is only available when all the column types are trivially-copyable. + constexpr const std::byte* const data() // + noexcept(soagen::has_nothrow_data_member); + +#endif + + /// @brief Returns a pointer to the elements of a specific column. + template + SOAGEN_ALIGNED_COLUMN(Column) + constexpr column_type* column() noexcept + { + static_assert(Column < table_traits::column_count, "column index out of range"); + + return soagen::assume_aligned< + soagen::detail::actual_column_alignment>( + table_.template column()); + } + + /// @brief Returns a pointer to the elements of a specific column. + template + SOAGEN_ALIGNED_COLUMN(Column) + constexpr std::add_const_t>* column() const noexcept + { + static_assert(Column < table_traits::column_count, "column index out of range"); + + return soagen::assume_aligned< + soagen::detail::actual_column_alignment>( + table_.template column()); + } + + /// @brief Returns a pointer to the elements in column [0]: id. + SOAGEN_ALIGNED_COLUMN(0) + constexpr unsigned* id() noexcept + { + return column<0>(); + } + + /// @brief Returns a pointer to the elements in column [0]: id. + SOAGEN_ALIGNED_COLUMN(0) + constexpr const unsigned* id() const noexcept + { + return column<0>(); + } + + /// @brief Returns a pointer to the elements in column [1]: name. + SOAGEN_ALIGNED_COLUMN(1) + constexpr std::string* name() noexcept + { + return column<1>(); + } + + /// @brief Returns a pointer to the elements in column [1]: name. + SOAGEN_ALIGNED_COLUMN(1) + constexpr const std::string* name() const noexcept + { + return column<1>(); + } + + /// @brief Returns a pointer to the elements in column [2]: pos. + SOAGEN_ALIGNED_COLUMN(2) + constexpr vec3* pos() noexcept + { + return column<2>(); + } + + /// @brief Returns a pointer to the elements in column [2]: pos. + SOAGEN_ALIGNED_COLUMN(2) + constexpr const vec3* pos() const noexcept + { + return column<2>(); + } + + /// @brief Returns a pointer to the elements in column [3]: orient. + SOAGEN_ALIGNED_COLUMN(3) + constexpr quaternion* orient() noexcept + { + return column<3>(); + } + + /// @brief Returns a pointer to the elements in column [3]: orient. + SOAGEN_ALIGNED_COLUMN(3) + constexpr const quaternion* orient() const noexcept + { + return column<3>(); + } + + /// @brief Invokes a function once for each column data pointer. + /// + /// @tparam Func A callable type compatible with one of the following signatures:
+ /// - `void(auto*, size_type)` + /// - `void(size_type, auto*)` + /// - `void(auto*)` + /// + /// @param func The callable to invoke. + template + constexpr void for_each_column(Func&& func) // + noexcept(table_traits::for_each_column_ptr_nothrow_invocable) + { + soagen::invoke_with_optarg(static_cast(func), this->template column<0>(), size_type{ 0 }); + soagen::invoke_with_optarg(static_cast(func), this->template column<1>(), size_type{ 1 }); + soagen::invoke_with_optarg(static_cast(func), this->template column<2>(), size_type{ 2 }); + soagen::invoke_with_optarg(static_cast(func), this->template column<3>(), size_type{ 3 }); + } + + /// @brief Invokes a function once for each column data pointer. + /// + /// @tparam Func A callable type compatible with one of the following signatures:
+ /// - `void(auto*, size_type)` + /// - `void(size_type, auto*)` + /// - `void(auto*)` + /// + /// @param func The callable to invoke. + template + constexpr void for_each_column(Func&& func) const // + noexcept(table_traits::for_each_column_ptr_nothrow_invocable) + { + soagen::invoke_with_optarg(static_cast(func), this->template column<0>(), size_type{ 0 }); + soagen::invoke_with_optarg(static_cast(func), this->template column<1>(), size_type{ 1 }); + soagen::invoke_with_optarg(static_cast(func), this->template column<2>(), size_type{ 2 }); + soagen::invoke_with_optarg(static_cast(func), this->template column<3>(), size_type{ 3 }); + } + + /// @} + + /// @name Row access + /// @{ + + /// @brief Returns the row at the given index. + /// + /// @tparam Columns Indices of the columns to include in the row. Leave the list empty for all columns. + template + SOAGEN_PURE_GETTER + SOAGEN_CPP20_CONSTEXPR + soagen::row_type row(size_type index) & noexcept + { + if constexpr (sizeof...(Columns)) + { + return { { this->template column()[index] }... }; + } + else + { + return (*this).template row<0, 1, 2, 3>(index); + } + } + + /// @brief Returns the row at the given index. + SOAGEN_PURE_INLINE_GETTER + SOAGEN_CPP20_CONSTEXPR + row_type operator[](size_type index) & noexcept + { + return (*this).row(index); + } + + /// @brief Returns the row at the given index. + /// + /// @throws std::out_of_range + SOAGEN_PURE_GETTER + SOAGEN_CPP20_CONSTEXPR + row_type at(size_type index) & + { +#if SOAGEN_HAS_EXCEPTIONS + if (index >= size()) + throw std::out_of_range{ "bad element access" }; +#endif + return (*this).row(index); + } + + /// @brief Returns the very first row in the table. + SOAGEN_PURE_INLINE_GETTER + SOAGEN_CPP20_CONSTEXPR + row_type front() & noexcept + { + return (*this).row(0u); + } + + /// @brief Returns the very last row in the table. + SOAGEN_PURE_INLINE_GETTER + SOAGEN_CPP20_CONSTEXPR + row_type back() & noexcept + { + return (*this).row(size() - 1u); + } + + /// @brief Returns the row at the given index. + /// + /// @tparam Columns Indices of the columns to include in the row. Leave the list empty for all columns. + template + SOAGEN_PURE_GETTER + SOAGEN_CPP20_CONSTEXPR + soagen::row_type row(size_type index) && noexcept + { + if constexpr (sizeof...(Columns)) + { + return { { std::move(this->template column()[index]) }... }; + } + else + { + return std::move(*this).template row<0, 1, 2, 3>(index); + } + } + + /// @brief Returns the row at the given index. + SOAGEN_PURE_INLINE_GETTER + SOAGEN_CPP20_CONSTEXPR + rvalue_row_type operator[](size_type index) && noexcept + { + return std::move(*this).row(index); + } + + /// @brief Returns the row at the given index. + /// + /// @throws std::out_of_range + SOAGEN_PURE_GETTER + SOAGEN_CPP20_CONSTEXPR + rvalue_row_type at(size_type index) && + { +#if SOAGEN_HAS_EXCEPTIONS + if (index >= size()) + throw std::out_of_range{ "bad element access" }; +#endif + return std::move(*this).row(index); + } + + /// @brief Returns the very first row in the table. + SOAGEN_PURE_INLINE_GETTER + SOAGEN_CPP20_CONSTEXPR + rvalue_row_type front() && noexcept + { + return std::move(*this).row(0u); + } + + /// @brief Returns the very last row in the table. + SOAGEN_PURE_INLINE_GETTER + SOAGEN_CPP20_CONSTEXPR + rvalue_row_type back() && noexcept + { + return std::move(*this).row(size() - 1u); + } + + /// @brief Returns the row at the given index. + /// + /// @tparam Columns Indices of the columns to include in the row. Leave the list empty for all columns. + template + SOAGEN_PURE_GETTER + SOAGEN_CPP20_CONSTEXPR + soagen::row_type row(size_type index) const& noexcept + { + if constexpr (sizeof...(Columns)) + { + return { { this->template column()[index] }... }; + } + else + { + return (*this).template row<0, 1, 2, 3>(index); + } + } + + /// @brief Returns the row at the given index. + SOAGEN_PURE_INLINE_GETTER + SOAGEN_CPP20_CONSTEXPR + const_row_type operator[](size_type index) const& noexcept + { + return (*this).row(index); + } + + /// @brief Returns the row at the given index. + /// + /// @throws std::out_of_range + SOAGEN_PURE_GETTER + SOAGEN_CPP20_CONSTEXPR + const_row_type at(size_type index) const& + { +#if SOAGEN_HAS_EXCEPTIONS + if (index >= size()) + throw std::out_of_range{ "bad element access" }; +#endif + return (*this).row(index); + } + + /// @brief Returns the very first row in the table. + SOAGEN_PURE_INLINE_GETTER + SOAGEN_CPP20_CONSTEXPR + const_row_type front() const& noexcept + { + return (*this).row(0u); + } + + /// @brief Returns the very last row in the table. + SOAGEN_PURE_INLINE_GETTER + SOAGEN_CPP20_CONSTEXPR + const_row_type back() const& noexcept + { + return (*this).row(size() - 1u); + } + + /// @} + + /// @name Iterators + /// @{ + + /// @brief Returns an iterator to the first row in the table. + template + SOAGEN_PURE_INLINE_GETTER + constexpr soagen::iterator_type begin() & noexcept + { + return { *this, 0 }; + } + + /// @brief Returns an iterator to one-past-the-last row in the table. + template + SOAGEN_PURE_INLINE_GETTER + constexpr soagen::iterator_type end() & noexcept + { + return { *this, static_cast(size()) }; + } + + /// @brief Returns an iterator to the first row in the table. + template + SOAGEN_PURE_INLINE_GETTER + constexpr soagen::iterator_type begin() && noexcept + { + return { std::move(*this), 0 }; + } + + /// @brief Returns an iterator to one-past-the-last row in the table. + template + SOAGEN_PURE_INLINE_GETTER + constexpr soagen::iterator_type end() && noexcept + { + return { std::move(*this), static_cast(size()) }; + } + + /// @brief Returns an iterator to the first row in the table. + template + SOAGEN_PURE_INLINE_GETTER + constexpr soagen::iterator_type begin() const& noexcept + { + return { *this, 0 }; + } + + /// @brief Returns an iterator to one-past-the-last row in the table. + template + SOAGEN_PURE_INLINE_GETTER + constexpr soagen::iterator_type end() const& noexcept + { + return { *this, static_cast(size()) }; + } + + /// @brief Returns an iterator to the first row in the table. + template + SOAGEN_PURE_INLINE_GETTER + constexpr soagen::iterator_type cbegin() const& noexcept + { + return { *this, 0 }; + } + + /// @brief Returns an iterator to one-past-the-last row in the table. + template + SOAGEN_PURE_INLINE_GETTER + constexpr soagen::iterator_type cend() const& noexcept + { + return { *this, static_cast(size()) }; + } + + /// @} + }; + + /// @brief Swaps the contents of two instances of #soagen::examples::entities. + /// + /// @availability This overload is only available when #soagen::examples::entities::allocator_type + /// is swappable or non-propagating. + SOAGEN_HIDDEN_CONSTRAINT(sfinae, auto sfinae = soagen::has_swap_member) + SOAGEN_ALWAYS_INLINE + constexpr void swap(entities& lhs, entities& rhs) // + noexcept(soagen::has_nothrow_swap_member) + { + lhs.swap(rhs); + } + + /// @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/entities.natvis b/examples/entities.natvis new file mode 100644 index 0000000..d9ad51d --- /dev/null +++ b/examples/entities.natvis @@ -0,0 +1,118 @@ + + + + + + + + + + + + + + + + + + + + + {{ size={size()} }} + + + size() + capacity() + size_bytes() + + + {{ {*(get_0())}, {*(get_0() + 1)}, {*(get_0() + 2)}, ... }} + {{ {*(get_0())}, {*(get_0() + 1)}, {*(get_0() + 2)} }} + {{ {*(get_0())}, {*(get_0() + 1)} }} + {{ {*(get_0())} }} + + + + size() + get_0() + + + + + + {{ {*(get_1())}, {*(get_1() + 1)}, {*(get_1() + 2)}, ... }} + {{ {*(get_1())}, {*(get_1() + 1)}, {*(get_1() + 2)} }} + {{ {*(get_1())}, {*(get_1() + 1)} }} + {{ {*(get_1())} }} + + + + size() + get_1() + + + + + + {{ {*(get_2())}, {*(get_2() + 1)}, {*(get_2() + 2)}, ... }} + {{ {*(get_2())}, {*(get_2() + 1)}, {*(get_2() + 2)} }} + {{ {*(get_2())}, {*(get_2() + 1)} }} + {{ {*(get_2())} }} + + + + size() + get_2() + + + + + + {{ {*(get_3())}, {*(get_3() + 1)}, {*(get_3() + 2)}, ... }} + {{ {*(get_3())}, {*(get_3() + 1)}, {*(get_3() + 2)} }} + {{ {*(get_3())}, {*(get_3() + 1)} }} + {{ {*(get_3())} }} + + + + size() + get_3() + + + + + + + + + + + + + + {{ {id}, {name}, {pos}, {orient} }} + + id + name + pos + orient + + + diff --git a/examples/entities.toml b/examples/entities.toml new file mode 100644 index 0000000..e5efa52 --- /dev/null +++ b/examples/entities.toml @@ -0,0 +1,17 @@ +namespace = 'soagen::examples' + +[hpp] +banner = ''' +// 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 +''' + +[structs.entities] +variables = [ + {name = 'id', type = 'unsigned' }, + {name = 'name', type = 'std::string', default = '""'}, + {name = 'pos', type = 'vec3', default = '{}', alignment = 32}, + {name = 'orient', type = 'quaternion', default = '{1, 0, 0, 0}' }, +] diff --git a/examples/meson.build b/examples/meson.build index 7211828..bc7d6ba 100644 --- a/examples/meson.build +++ b/examples/meson.build @@ -3,24 +3,45 @@ # See https://github.com/marzer/soagen/blob/master/LICENSE for the full license text. # SPDX-License-Identifier: MIT +example_names = [ + 'shapes' +] + example_args = [] example_args += global_args -examples = [ - 'particles' -] +example_link_args = [] +example_link_args += global_link_args + +example_overrides = [] +example_overrides += global_overrides example_executables = [] -foreach example : examples +foreach name : example_names + + example_cpp_files = [] + example_extra_files = [ soagen_hpp, cpp_hint ] + foreach suffix : ['.hpp','.cpp','.toml','.natvis'] + if fs.is_file(name + suffix) + if suffix == '.cpp' + example_cpp_files += files(name + suffix) + else + example_extra_files += files(name + suffix) + endif + endif + endforeach + example_executables += [[ - example, + name, executable( - example, - [ example + '.cpp' ], + name, + example_cpp_files, dependencies: soagen_dep, cpp_args: example_args, - link_args: global_link_args, - override_options: global_overrides + link_args: example_link_args, + override_options: example_overrides, + extra_files: example_extra_files ) ]] + endforeach diff --git a/examples/particles.cpp b/examples/particles.cpp deleted file mode 100644 index 29ec21e..0000000 --- a/examples/particles.cpp +++ /dev/null @@ -1,17 +0,0 @@ -#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 deleted file mode 100644 index 7a74ff0..0000000 --- a/examples/particles.hpp +++ /dev/null @@ -1,225 +0,0 @@ -//---------------------------------------------------------------------------------------------------------------------- -// 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 deleted file mode 100644 index 598e2e7..0000000 --- a/examples/particles.toml +++ /dev/null @@ -1,13 +0,0 @@ -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/examples/shapes.cpp b/examples/shapes.cpp new file mode 100644 index 0000000..3225732 --- /dev/null +++ b/examples/shapes.cpp @@ -0,0 +1,32 @@ +// 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 + +#include "shapes.hpp" + +using namespace soagen::examples; + +int main() +{ + spheres p; + spheres p2; + + p.reserve(10); + + p = std::move(p2); + p.swap(p2); + p2 = p; + + p.pop_back(); + p.resize(10); + + p2.push_back(1.0f, 2.0f, 3.0f); + p2.insert(0, 4.0f, 5.0f, 6.0f); + p2.emplace_back(7.0f, 8.0f, 9.0f); + p2.emplace(0, 10.0f, 11.0f, 12.0f); + + [[maybe_unused]] auto back = p2.back(); + + return 0; +} diff --git a/examples/shapes.hpp b/examples/shapes.hpp new file mode 100644 index 0000000..17d643c --- /dev/null +++ b/examples/shapes.hpp @@ -0,0 +1,2831 @@ +//---------------------------------------------------------------------------------------------------------------------- +// 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 +//---------------------------------------------------------------------------------------------------------------------- +// This file was generated by soagen v0.0.2 - do not modify it directly +// https://marzer.github.io/soagen +//---------------------------------------------------------------------------------------------------------------------- +#pragma once + +/// @file +/// @brief Contains the definitions of soagen::examples::boxes, soagen::examples::spheres. +/// +/// @note The code and documentation in this file were generated by soagen - https://marzer.github.io/soagen + +#include +#if SOAGEN_VERSION_MAJOR != 0 || SOAGEN_VERSION_MINOR != 0 + #error soagen version mismatch - expected v0.0.X +#endif + +SOAGEN_DISABLE_WARNINGS; +#if SOAGEN_HAS_EXCEPTIONS + #include +#endif +SOAGEN_ENABLE_WARNINGS; + +SOAGEN_PUSH_WARNINGS; +SOAGEN_DISABLE_SPAM_WARNINGS; +#if SOAGEN_CLANG >= 16 + #pragma clang diagnostic ignored "-Wunsafe-buffer-usage" +#endif +#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 + +//---------------------------------------------------------------------------------------------------------------------- +// doxygen safeguards +//---------------------------------------------------------------------------------------------------------------------- + +#if defined(DOXYGEN) || defined(__DOXYGEN) || defined(__DOXYGEN__) || defined(__doxygen__) || defined(__POXY__) \ + || defined(__poxy__) + + #ifndef SOAGEN_DOXYGEN + #define SOAGEN_DOXYGEN 1 + #endif + #ifndef SOAGEN_MAKE_NAME + #define SOAGEN_MAKE_NAME(...) static_assert(true) + #endif + #ifndef SOAGEN_MAKE_COL + #define SOAGEN_MAKE_COL(...) static_assert(true) + #endif + #ifndef SOAGEN_NODISCARD + #define SOAGEN_NODISCARD + #endif + #ifndef SOAGEN_NODISCARD_CTOR + #define SOAGEN_NODISCARD_CTOR + #endif + #ifndef SOAGEN_INLINE_GETTER + #define SOAGEN_INLINE_GETTER inline + #endif + #ifndef SOAGEN_PURE_GETTER + #define SOAGEN_PURE_GETTER + #endif + #ifndef SOAGEN_PURE_INLINE_GETTER + #define SOAGEN_PURE_INLINE_GETTER inline + #endif + #ifndef SOAGEN_ALWAYS_INLINE + #define SOAGEN_ALWAYS_INLINE inline + #endif + #ifndef SOAGEN_CPP20_CONSTEXPR + #define SOAGEN_CPP20_CONSTEXPR constexpr + #endif + #ifndef SOAGEN_HIDDEN + #define SOAGEN_HIDDEN(...) + #endif + #ifndef SOAGEN_HIDDEN_BASE + #define SOAGEN_HIDDEN_BASE(...) + #endif + #ifndef SOAGEN_HIDDEN_CONSTRAINT + #define SOAGEN_HIDDEN_CONSTRAINT(...) + #endif + #ifndef SOAGEN_HIDDEN_PARAM + #define SOAGEN_HIDDEN_PARAM(...) + #endif + #ifndef SOAGEN_ENABLE_IF_T + #define SOAGEN_ENABLE_IF_T(T, ...) T + #endif + #ifndef SOAGEN_ENABLE_IF + #define SOAGEN_ENABLE_IF(...) + #endif + #ifndef SOAGEN_REQUIRES + #define SOAGEN_REQUIRES(...) + #endif + #ifndef SOAGEN_EMPTY_BASES + #define SOAGEN_EMPTY_BASES + #endif + #ifndef SOAGEN_COLUMN + #define SOAGEN_COLUMN(...) + #endif + #ifndef SOAGEN_ALIGNED_COLUMN + #define SOAGEN_ALIGNED_COLUMN(...) + #endif + #if !defined(POXY_IMPLEMENTATION_DETAIL) && !(defined(__POXY__) || defined(__poxy__)) + #define POXY_IMPLEMENTATION_DETAIL(...) __VA_ARGS__ + #endif + +#endif // doxygen + +//---------------------------------------------------------------------------------------------------------------------- +// forward declarations + soagen internal boilerplate +//---------------------------------------------------------------------------------------------------------------------- + +/// @cond + +namespace soagen::examples +{ + class boxes; + class spheres; +} + +namespace soagen +{ + template <> + inline constexpr bool is_soa = true; + + template <> + inline constexpr bool is_soa = true; +} + +namespace soagen::detail +{ + // clang-format off + + #ifndef SOAGEN_NAME_center_x + #define SOAGEN_NAME_center_x + SOAGEN_MAKE_NAME(center_x); + #endif + + #ifndef SOAGEN_NAME_center_y + #define SOAGEN_NAME_center_y + SOAGEN_MAKE_NAME(center_y); + #endif + + #ifndef SOAGEN_NAME_center_z + #define SOAGEN_NAME_center_z + SOAGEN_MAKE_NAME(center_z); + #endif + + #ifndef SOAGEN_NAME_extents_x + #define SOAGEN_NAME_extents_x + SOAGEN_MAKE_NAME(extents_x); + #endif + + #ifndef SOAGEN_NAME_extents_y + #define SOAGEN_NAME_extents_y + SOAGEN_MAKE_NAME(extents_y); + #endif + + #ifndef SOAGEN_NAME_extents_z + #define SOAGEN_NAME_extents_z + SOAGEN_MAKE_NAME(extents_z); + #endif + + #ifndef SOAGEN_NAME_mass + #define SOAGEN_NAME_mass + SOAGEN_MAKE_NAME(mass); + #endif + + #ifndef SOAGEN_NAME_radius + #define SOAGEN_NAME_radius + SOAGEN_MAKE_NAME(radius); + #endif + + // clang-format on + + template <> + struct table_traits_type_ + { + using type = table_traits< + /* center_x */ make_column, 32>, + /* center_y */ make_column, 32>, + /* center_z */ make_column, 32>, + /* extents_x */ make_column, 32>, + /* extents_y */ make_column, 32>, + /* extents_z */ make_column, 32>, + /* mass */ make_column>; + }; + + template <> + struct allocator_type_ + { + using type = soagen::allocator; + }; + + SOAGEN_MAKE_COL(soagen::examples::boxes, 0, center_x); + SOAGEN_MAKE_COL(soagen::examples::boxes, 1, center_y); + SOAGEN_MAKE_COL(soagen::examples::boxes, 2, center_z); + SOAGEN_MAKE_COL(soagen::examples::boxes, 3, extents_x); + SOAGEN_MAKE_COL(soagen::examples::boxes, 4, extents_y); + SOAGEN_MAKE_COL(soagen::examples::boxes, 5, extents_z); + SOAGEN_MAKE_COL(soagen::examples::boxes, 6, mass); + + template <> + struct table_type_ + { + using type = table, soagen::allocator>; + }; + + template <> + struct table_traits_type_ + { + using type = table_traits< + /* center_x */ make_column, 32>, + /* center_y */ make_column, 32>, + /* center_z */ make_column, 32>, + /* radius */ make_column, 32>, + /* mass */ make_column>; + }; + + template <> + struct allocator_type_ + { + using type = soagen::allocator; + }; + + SOAGEN_MAKE_COL(soagen::examples::spheres, 0, center_x); + SOAGEN_MAKE_COL(soagen::examples::spheres, 1, center_y); + SOAGEN_MAKE_COL(soagen::examples::spheres, 2, center_z); + SOAGEN_MAKE_COL(soagen::examples::spheres, 3, radius); + SOAGEN_MAKE_COL(soagen::examples::spheres, 4, mass); + + template <> + struct table_type_ + { + using type = table, soagen::allocator>; + }; +} + +/// @endcond + +//---------------------------------------------------------------------------------------------------------------------- +// header +//---------------------------------------------------------------------------------------------------------------------- + +/// @brief Types generated by soagen for example purposes. +/// @attention Nothing in this namespace is required to use soagen! +/// This documentation is to demonstrate what soagen is capable of generating, and is just for exposition. +namespace soagen::examples +{ +} + +//---------------------------------------------------------------------------------------------------------------------- +// boxes +//---------------------------------------------------------------------------------------------------------------------- + +namespace soagen::examples +{ + /// @brief A SIMD-friendly AABB container. + /// + /// @details + /// + /// @remark Models the Structure-of-arrays + /// equivalent of: @code{.cpp} + /// struct boxes + /// { + /// float center_x; + /// float center_y; + /// float center_z; + /// float extents_x; + /// float extents_y; + /// float extents_z; + /// float mass; + /// }; + /// @endcode + /// + /// + /// @note The code and documentation for this class were generated by soagen - https://marzer.github.io/soagen + class SOAGEN_EMPTY_BASES boxes // + SOAGEN_HIDDEN_BASE(public soagen::mixins::resizable, + public soagen::mixins::equality_comparable, + public soagen::mixins::less_than_comparable, + public soagen::mixins::data_ptr, + public soagen::mixins::const_data_ptr, + public soagen::mixins::swappable) + { + public: + /// @brief The unsigned integer size type used by this class. + using size_type = std::size_t; + + /// @brief The signed integer difference type used by this class. + using difference_type = std::ptrdiff_t; + + /// @brief The allocator type used by this class. + using allocator_type = soagen::allocator_type; + + /// @brief This class's underlying soagen::table type. + using table_type = soagen::table_type; + + /// @brief The soagen::table_traits for the underlying table. + using table_traits = soagen::table_traits_type; + + /// @brief The number of columns in the table. + static constexpr size_t column_count = soagen::table_traits_type::column_count; + + /// @brief Gets the soagen::column_traits for a specific column of the table. + template + using column_traits = typename table_traits::template column; + + /// @brief Gets the type of a specific column in the table. + template + using column_type = typename column_traits::value_type; + + /// @brief Row iterators returned by iterator functions. + using iterator = soagen::iterator_type; + + /// @brief Row iterators returned by const-qualified iterator functions. + using const_iterator = soagen::iterator_type; + + /// @brief Row iterators returned by rvalue-qualified iterator functions. + using rvalue_iterator = soagen::iterator_type; + + /// @brief Regular (lvalue-qualified) row type used by this class. + using row_type = soagen::row_type; + + /// @brief Const row type used by this class. + using const_row_type = soagen::row_type; + + /// @brief Rvalue row type used by this class. + using rvalue_row_type = soagen::row_type; + + /// @brief The number of rows to advance to maintain the requested `alignment` for every column. + /// + /// @details The stride size you need to use when iterating through rows of this table such that + /// the starting element for each batch in each column would have the same memory alignment as the + /// value specified for the column-specific `alignment`. + /// + /// @note Typically you can ignore this; column elements are always aligned correctly according to their + /// type. This is for over-alignment scenarios where you need to do things in batches (e.g. SIMD). + static constexpr size_type aligned_stride = table_traits::aligned_stride; + +#if SOAGEN_DOXYGEN + /// @brief Named index constants for all of the columns in the table. + using column_indices = POXY_IMPLEMENTATION_DETAIL(struct dummy_t); +#else + struct column_indices + { + static constexpr size_type center_x = 0; + static constexpr size_type center_y = 1; + static constexpr size_type center_z = 2; + static constexpr size_type extents_x = 3; + static constexpr size_type extents_y = 4; + static constexpr size_type extents_z = 5; + static constexpr size_type mass = 6; + }; +#endif + + /// @brief Gets the name of the specified column as a string. + template + static constexpr auto& column_name = soagen::detail::col_name_::value; + + static constexpr float default_mass = 2.0; + + private: + /// @cond + + table_type table_; + + /// @endcond + + public: + /// @brief Default constructor. + SOAGEN_NODISCARD_CTOR + boxes() = default; + + /// @brief Move constructor. + SOAGEN_NODISCARD_CTOR + boxes(boxes&&) = default; + + /// @brief Move-assignment operator. + boxes& operator=(boxes&&) = default; + + /// @brief Copy constructor. + SOAGEN_NODISCARD_CTOR + boxes(const boxes&) = default; + + /// @brief Copy-assignment operator. + boxes& operator=(const boxes&) = default; + + /// @brief Destructor. + ~boxes() = default; + + /// @brief Constructs with the given allocator. + SOAGEN_NODISCARD_CTOR + constexpr explicit boxes(const allocator_type& alloc) noexcept // + : table_{ alloc } + {} + + /// @brief Constructs with the given allocator. + SOAGEN_NODISCARD_CTOR + constexpr explicit boxes(allocator_type&& alloc) noexcept // + : table_{ static_cast(alloc) } + {} + + /// @brief Returns the allocator being used by the table. + SOAGEN_INLINE_GETTER + SOAGEN_CPP20_CONSTEXPR + allocator_type get_allocator() const noexcept + { + return table_.get_allocator(); + } + + /// @name Underlying table access + /// @{ + + /// @brief Returns an lvalue reference to the underlying soagen::table. + SOAGEN_PURE_INLINE_GETTER + constexpr table_type& table() & noexcept + { + return table_; + } + + /// @brief Returns an rvalue reference to the underlying soagen::table. + SOAGEN_PURE_INLINE_GETTER + constexpr table_type&& table() && noexcept + { + return static_cast(table_); + } + + /// @brief Returns a const lvalue reference to the underlying soagen::table. + SOAGEN_PURE_INLINE_GETTER + constexpr const table_type& table() const& noexcept + { + return table_; + } + + /// @} + + /// @name Capacity + /// @{ + + /// @brief Returns true if the number of rows is zero. + SOAGEN_PURE_INLINE_GETTER + constexpr bool empty() const noexcept + { + return table_.empty(); + } + + /// @brief Returns the current number of rows. + SOAGEN_PURE_INLINE_GETTER + constexpr size_type size() const noexcept + { + return table_.size(); + } + + /// @brief Returns the maximum possible number of rows. + SOAGEN_PURE_INLINE_GETTER + constexpr size_type max_size() const noexcept + { + return table_.max_size(); + } + + /// @brief Returns the size of the current underlying buffer allocation in bytes. + SOAGEN_PURE_INLINE_GETTER + constexpr size_type allocation_size() const noexcept + { + return table_.allocation_size(); + } + + /// @brief Reserves storage for (at least) the given number of rows. + SOAGEN_ALWAYS_INLINE + SOAGEN_CPP20_CONSTEXPR + boxes& reserve(size_type new_cap) // + noexcept(noexcept(std::declval().reserve(size_type{}))) + { + table_.reserve(new_cap); + return *this; + } + + /// @brief Returns the number of rows that can be held in currently allocated storage. + SOAGEN_PURE_INLINE_GETTER + constexpr size_type capacity() const noexcept + { + return table_.capacity(); + } + + /// @brief Frees unused capacity. + SOAGEN_ALWAYS_INLINE + SOAGEN_CPP20_CONSTEXPR + boxes& shrink_to_fit() // + noexcept(noexcept(std::declval().shrink_to_fit())) + { + table_.shrink_to_fit(); + return *this; + } + + /// @} + + /// @name Modifiers + /// @{ + + /// @brief Removes all rows from table. + SOAGEN_ALWAYS_INLINE + SOAGEN_CPP20_CONSTEXPR + boxes& clear() noexcept + { + table_.clear(); + return *this; + } + + /// @brief Erases the row at the given position. + /// + /// @availability This method is only available when all the column types are move-assignable. + SOAGEN_HIDDEN(template >) + SOAGEN_ALWAYS_INLINE + SOAGEN_CPP20_CONSTEXPR + SOAGEN_ENABLE_IF_T(boxes&, sfinae) erase(size_type pos) // + noexcept(soagen::has_nothrow_erase_member) + { + table_.erase(pos); + return *this; + } + + /// @brief Erases the row at the given position without preserving order. + /// + /// @details This is much faster than #erase() because it uses the swap-and-pop idiom: + /// Instead of shifting all the higher rows downward, the last row is moved into the + /// position of the erased one and the size of the table is reduced by 1. + /// + /// @note If you are tracking row indices in some other place and need to maintain that invariant, + /// you can use the return value to update your data accordingly. + /// + /// @returns The position of the row that was moved into the erased row's position, if any. + /// + /// @availability This method is only available when all the column types are move-assignable. + SOAGEN_HIDDEN(template >) + SOAGEN_ALWAYS_INLINE + SOAGEN_CPP20_CONSTEXPR + SOAGEN_ENABLE_IF_T(soagen::optional, sfinae) unordered_erase(size_type pos) // + noexcept(soagen::has_nothrow_unordered_erase_member) + { + return table_.unordered_erase(pos); + } + + /// @brief Erases the row at the given iterator. + /// + /// @returns An iterator to the row immediately following the one which was removed, + /// or #end() if the one removed was the last row in the table. + /// + /// @availability This method is only available when all the column types are move-assignable. + SOAGEN_HIDDEN(template >) + SOAGEN_ALWAYS_INLINE + SOAGEN_CPP20_CONSTEXPR + SOAGEN_ENABLE_IF_T(iterator, sfinae) erase(iterator pos) // + noexcept(soagen::has_nothrow_erase_member) + { + table_.erase(static_cast(pos)); + return pos; + } + + /// @brief Erases the row at the given position without preserving order. + /// + /// @details This is much faster than #erase() because it uses the swap-and-pop idiom: + /// Instead of shifting all the higher rows downward, the last row is moved into the + /// position of the erased one and the size of the table is reduced by 1. + /// + /// @note If you are tracking row indices in some other place and need to maintain that invariant, + /// you can use the return value to update your data accordingly. + /// + /// @returns The position of the row that was moved into the erased row's position, if any. + /// + /// @availability This method is only available when all the column types are move-assignable. + SOAGEN_HIDDEN(template >) + SOAGEN_ALWAYS_INLINE + SOAGEN_CPP20_CONSTEXPR + SOAGEN_ENABLE_IF_T(soagen::optional, sfinae) unordered_erase(iterator pos) // + noexcept(soagen::has_nothrow_unordered_erase_member) + { + if (auto moved_pos = table_.unordered_erase(static_cast(pos)); moved_pos) + return iterator{ *this, static_cast(*moved_pos) }; + return {}; + } + + /// @brief Erases the row at the given iterator. + /// + /// @returns An iterator to the row immediately following the one which was removed, + /// or #cend() if the one removed was the last row in the table. + /// + /// @availability This method is only available when all the column types are move-assignable. + SOAGEN_HIDDEN(template >) + SOAGEN_ALWAYS_INLINE + SOAGEN_CPP20_CONSTEXPR + SOAGEN_ENABLE_IF_T(const_iterator, sfinae) erase(const_iterator pos) // + noexcept(soagen::has_nothrow_erase_member) + { + table_.erase(static_cast(pos)); + return pos; + } + + /// @brief Erases the row at the given position without preserving order. + /// + /// @details This is much faster than #erase() because it uses the swap-and-pop idiom: + /// Instead of shifting all the higher rows downward, the last row is moved into the + /// position of the erased one and the size of the table is reduced by 1. + /// + /// @note If you are tracking row indices in some other place and need to maintain that invariant, + /// you can use the return value to update your data accordingly. + /// + /// @returns The position of the row that was moved into the erased row's position, if any. + /// + /// @availability This method is only available when all the column types are move-assignable. + SOAGEN_HIDDEN(template >) + SOAGEN_ALWAYS_INLINE + SOAGEN_CPP20_CONSTEXPR + SOAGEN_ENABLE_IF_T(soagen::optional, sfinae) unordered_erase(const_iterator pos) // + noexcept(soagen::has_nothrow_unordered_erase_member) + { + if (auto moved_pos = table_.unordered_erase(static_cast(pos)); moved_pos) + return const_iterator{ *this, static_cast(*moved_pos) }; + return {}; + } + + /// @brief Removes the last row(s) from the table. + SOAGEN_ALWAYS_INLINE + SOAGEN_CPP20_CONSTEXPR + boxes& pop_back(size_type num = 1) // + noexcept(noexcept(std::declval().pop_back(size_type{}))) + { + table_.pop_back(num); + return *this; + } + +#if SOAGEN_DOXYGEN + + /// @brief Resizes the table to the given number of rows. + /// + /// @availability This method is only available when all the column types are default-constructible. + boxes& resize(size_type new_size) // + noexcept(soagen::has_nothrow_resize_member); + + /// @brief Swaps the contents of the table with another. + /// + /// @availability This method is only available when #allocator_type is swappable or non-propagating. + constexpr void swap(boxes& other) // + noexcept(soagen::has_nothrow_swap_member); + +#endif + + /// @} + + /// @name Adding rows + /// @{ + + /// @brief Adds a new row at the end of the table. + SOAGEN_CPP20_CONSTEXPR + boxes& push_back(column_traits<0>::param_type center_x, + column_traits<1>::param_type center_y, + column_traits<2>::param_type center_z, + column_traits<3>::param_type extents_x = 0.5, + column_traits<4>::param_type extents_y = 0.5, + column_traits<5>::param_type extents_z = 0.5, + column_traits<6>::param_type mass = default_mass) // + noexcept(table_traits::push_back_is_nothrow) + { + table_.emplace_back(static_cast::param_forward_type>(center_x), + static_cast::param_forward_type>(center_y), + static_cast::param_forward_type>(center_z), + static_cast::param_forward_type>(extents_x), + static_cast::param_forward_type>(extents_y), + static_cast::param_forward_type>(extents_z), + static_cast::param_forward_type>(mass)); + return *this; + } + + /// @brief Adds a new row at the end of the table (rvalue overload). + SOAGEN_HIDDEN(template ) + SOAGEN_CPP20_CONSTEXPR + boxes& push_back(SOAGEN_ENABLE_IF_T(column_traits<0>::rvalue_type, sfinae) center_x, + column_traits<1>::rvalue_type center_y, + column_traits<2>::rvalue_type center_z, + column_traits<3>::rvalue_type extents_x = 0.5, + column_traits<4>::rvalue_type extents_y = 0.5, + column_traits<5>::rvalue_type extents_z = 0.5, + column_traits<6>::rvalue_type mass = default_mass) // + noexcept(table_traits::rvalue_push_back_is_nothrow) + SOAGEN_REQUIRES(table_traits::rvalue_type_list_is_distinct) // + { + table_.emplace_back(static_cast::rvalue_forward_type>(center_x), + static_cast::rvalue_forward_type>(center_y), + static_cast::rvalue_forward_type>(center_z), + static_cast::rvalue_forward_type>(extents_x), + static_cast::rvalue_forward_type>(extents_y), + static_cast::rvalue_forward_type>(extents_z), + static_cast::rvalue_forward_type>(mass)); + return *this; + } + + /// @brief Adds a new row at the end of the table. + template >)> + SOAGEN_CPP20_CONSTEXPR + boxes& push_back(const soagen::row& row_) // + noexcept(table_traits::row_push_back_is_nothrow&>) + SOAGEN_REQUIRES(soagen::is_soa>) // + { + table_.emplace_back(row_); + return *this; + } + + /// @brief Constructs a new row directly in-place at the end of the table. + template ::default_emplace_type, + typename ExtentsY = column_traits<4>::default_emplace_type, + typename ExtentsZ = column_traits<5>::default_emplace_type, + typename Mass = column_traits<6>::default_emplace_type> + SOAGEN_CPP20_CONSTEXPR + boxes& emplace_back(CenterX&& center_x, + CenterY&& center_y, + CenterZ&& center_z, + ExtentsX&& extents_x = 0.5, + ExtentsY&& extents_y = 0.5, + ExtentsZ&& extents_z = 0.5, + Mass&& mass = default_mass) // + noexcept(table_traits::emplace_back_is_nothrow) + { + table_.emplace_back(static_cast(center_x), + static_cast(center_y), + static_cast(center_z), + static_cast(extents_x), + static_cast(extents_y), + static_cast(extents_z), + static_cast(mass)); + return *this; + } + + /// @} + + /// @name Inserting rows + /// @availability These overloads are only available when all the column types are move-constructible and move-assignable. + /// @{ + + /// @brief Inserts a new row at an arbitrary position in the table. + /// + /// @availability This overload is only available when all the column types are move-constructible and move-assignable. + SOAGEN_HIDDEN( + template + ) + SOAGEN_CPP20_CONSTEXPR + SOAGEN_ENABLE_IF_T(boxes&, sfinae) insert(size_type index_, + column_traits<0>::param_type center_x, + column_traits<1>::param_type center_y, + column_traits<2>::param_type center_z, + column_traits<3>::param_type extents_x = 0.5, + column_traits<4>::param_type extents_y = 0.5, + column_traits<5>::param_type extents_z = 0.5, + column_traits<6>::param_type mass = default_mass) // + noexcept(table_traits::insert_is_nothrow) + SOAGEN_REQUIRES(table_traits::all_move_constructible&& table_traits::all_move_assignable) // + { + table_.emplace(index_, + static_cast::param_forward_type>(center_x), + static_cast::param_forward_type>(center_y), + static_cast::param_forward_type>(center_z), + static_cast::param_forward_type>(extents_x), + static_cast::param_forward_type>(extents_y), + static_cast::param_forward_type>(extents_z), + static_cast::param_forward_type>(mass)); + return *this; + } + + /// @brief Inserts a new row at an arbitrary position in the table. + /// + /// @availability This overload is only available when all the column types are move-constructible and move-assignable. + SOAGEN_HIDDEN( + template + ) + SOAGEN_CPP20_CONSTEXPR + SOAGEN_ENABLE_IF_T(iterator, sfinae) insert(iterator iter_, + column_traits<0>::param_type center_x, + column_traits<1>::param_type center_y, + column_traits<2>::param_type center_z, + column_traits<3>::param_type extents_x = 0.5, + column_traits<4>::param_type extents_y = 0.5, + column_traits<5>::param_type extents_z = 0.5, + column_traits<6>::param_type mass = default_mass) // + noexcept(table_traits::insert_is_nothrow) + SOAGEN_REQUIRES(table_traits::all_move_constructible&& table_traits::all_move_assignable) // + { + table_.emplace(static_cast(iter_), + static_cast::param_forward_type>(center_x), + static_cast::param_forward_type>(center_y), + static_cast::param_forward_type>(center_z), + static_cast::param_forward_type>(extents_x), + static_cast::param_forward_type>(extents_y), + static_cast::param_forward_type>(extents_z), + static_cast::param_forward_type>(mass)); + return iter_; + } + + /// @brief Inserts a new row at an arbitrary position in the table. + /// + /// @availability This overload is only available when all the column types are move-constructible and move-assignable. + SOAGEN_HIDDEN( + template + ) + SOAGEN_CPP20_CONSTEXPR + SOAGEN_ENABLE_IF_T(const_iterator, sfinae) insert(const_iterator iter_, + column_traits<0>::param_type center_x, + column_traits<1>::param_type center_y, + column_traits<2>::param_type center_z, + column_traits<3>::param_type extents_x = 0.5, + column_traits<4>::param_type extents_y = 0.5, + column_traits<5>::param_type extents_z = 0.5, + column_traits<6>::param_type mass = default_mass) // + noexcept(table_traits::insert_is_nothrow) + SOAGEN_REQUIRES(table_traits::all_move_constructible&& table_traits::all_move_assignable) // + { + table_.emplace(static_cast(iter_), + static_cast::param_forward_type>(center_x), + static_cast::param_forward_type>(center_y), + static_cast::param_forward_type>(center_z), + static_cast::param_forward_type>(extents_x), + static_cast::param_forward_type>(extents_y), + static_cast::param_forward_type>(extents_z), + static_cast::param_forward_type>(mass)); + return iter_; + } + + /// @brief Inserts a new row at an arbitrary position in the table (rvalue overload). + /// + /// @availability This overload is only available when all the column types are move-constructible and move-assignable. + SOAGEN_HIDDEN( + template + ) + SOAGEN_CPP20_CONSTEXPR + boxes& insert(SOAGEN_ENABLE_IF_T(size_type, sfinae) index_, + column_traits<0>::rvalue_type center_x, + column_traits<1>::rvalue_type center_y, + column_traits<2>::rvalue_type center_z, + column_traits<3>::rvalue_type extents_x = 0.5, + column_traits<4>::rvalue_type extents_y = 0.5, + column_traits<5>::rvalue_type extents_z = 0.5, + column_traits<6>::rvalue_type mass = default_mass) // + noexcept(table_traits::rvalue_insert_is_nothrow) + SOAGEN_REQUIRES(table_traits::rvalue_type_list_is_distinct&& table_traits::all_move_constructible&& + table_traits::all_move_assignable) // + { + table_.emplace(index_, + static_cast::rvalue_forward_type>(center_x), + static_cast::rvalue_forward_type>(center_y), + static_cast::rvalue_forward_type>(center_z), + static_cast::rvalue_forward_type>(extents_x), + static_cast::rvalue_forward_type>(extents_y), + static_cast::rvalue_forward_type>(extents_z), + static_cast::rvalue_forward_type>(mass)); + return *this; + } + + /// @brief Inserts a new row at an arbitrary position in the table (rvalue overload). + /// + /// @availability This overload is only available when all the column types are move-constructible and move-assignable. + SOAGEN_HIDDEN( + template + ) + SOAGEN_CPP20_CONSTEXPR + iterator insert(SOAGEN_ENABLE_IF_T(iterator, sfinae) iter_, + column_traits<0>::rvalue_type center_x, + column_traits<1>::rvalue_type center_y, + column_traits<2>::rvalue_type center_z, + column_traits<3>::rvalue_type extents_x = 0.5, + column_traits<4>::rvalue_type extents_y = 0.5, + column_traits<5>::rvalue_type extents_z = 0.5, + column_traits<6>::rvalue_type mass = default_mass) // + noexcept(table_traits::rvalue_insert_is_nothrow) + SOAGEN_REQUIRES(table_traits::rvalue_type_list_is_distinct&& table_traits::all_move_constructible&& + table_traits::all_move_assignable) // + { + table_.emplace(static_cast(iter_), + static_cast::rvalue_forward_type>(center_x), + static_cast::rvalue_forward_type>(center_y), + static_cast::rvalue_forward_type>(center_z), + static_cast::rvalue_forward_type>(extents_x), + static_cast::rvalue_forward_type>(extents_y), + static_cast::rvalue_forward_type>(extents_z), + static_cast::rvalue_forward_type>(mass)); + return iter_; + } + + /// @brief Inserts a new row at an arbitrary position in the table (rvalue overload). + /// + /// @availability This overload is only available when all the column types are move-constructible and move-assignable. + SOAGEN_HIDDEN( + template + ) + SOAGEN_CPP20_CONSTEXPR + const_iterator insert(SOAGEN_ENABLE_IF_T(const_iterator, sfinae) iter_, + column_traits<0>::rvalue_type center_x, + column_traits<1>::rvalue_type center_y, + column_traits<2>::rvalue_type center_z, + column_traits<3>::rvalue_type extents_x = 0.5, + column_traits<4>::rvalue_type extents_y = 0.5, + column_traits<5>::rvalue_type extents_z = 0.5, + column_traits<6>::rvalue_type mass = default_mass) // + noexcept(table_traits::rvalue_insert_is_nothrow) + SOAGEN_REQUIRES(table_traits::rvalue_type_list_is_distinct&& table_traits::all_move_constructible&& + table_traits::all_move_assignable) // + { + table_.emplace(static_cast(iter_), + static_cast::rvalue_forward_type>(center_x), + static_cast::rvalue_forward_type>(center_y), + static_cast::rvalue_forward_type>(center_z), + static_cast::rvalue_forward_type>(extents_x), + static_cast::rvalue_forward_type>(extents_y), + static_cast::rvalue_forward_type>(extents_z), + static_cast::rvalue_forward_type>(mass)); + return iter_; + } + + /// @brief Inserts a new row at an arbitrary position in the table. + /// + /// @availability This overload is only available when all the column types are move-constructible and move-assignable. + template > + && table_traits::all_move_constructible + && table_traits::all_move_assignable))> + SOAGEN_CPP20_CONSTEXPR + boxes& insert(size_type index_, const soagen::row& row_) // + noexcept(table_traits::row_insert_is_nothrow&>) + SOAGEN_REQUIRES(soagen::is_soa>&& table_traits::all_move_constructible&& + table_traits::all_move_assignable) // + { + table_.emplace(index_, row_); + return *this; + } + + /// @brief Inserts a new row at an arbitrary position in the table. + /// + /// @availability This overload is only available when all the column types are move-constructible and move-assignable. + template > + && table_traits::all_move_constructible + && table_traits::all_move_assignable))> + SOAGEN_CPP20_CONSTEXPR + iterator insert(iterator iter_, const soagen::row& row_) // + noexcept(table_traits::row_insert_is_nothrow&>) + SOAGEN_REQUIRES(soagen::is_soa>&& table_traits::all_move_constructible&& + table_traits::all_move_assignable) // + { + table_.emplace(static_cast(iter_), row_); + return iter_; + } + + /// @brief Inserts a new row at an arbitrary position in the table. + /// + /// @availability This overload is only available when all the column types are move-constructible and move-assignable. + template > + && table_traits::all_move_constructible + && table_traits::all_move_assignable))> + SOAGEN_CPP20_CONSTEXPR + const_iterator insert(const_iterator iter_, const soagen::row& row_) // + noexcept(table_traits::row_insert_is_nothrow&>) + SOAGEN_REQUIRES(soagen::is_soa>&& table_traits::all_move_constructible&& + table_traits::all_move_assignable) // + { + table_.emplace(static_cast(iter_), row_); + return iter_; + } + + /// @brief Constructs a new row directly in-place at an arbitrary position in the table. + /// + /// @availability This overload is only available when all the column types are move-constructible and move-assignable. + template ::default_emplace_type, + typename ExtentsY = column_traits<4>::default_emplace_type, + typename ExtentsZ = column_traits<5>::default_emplace_type, + typename Mass = column_traits<6>::default_emplace_type SOAGEN_HIDDEN_PARAM( + bool sfinae = (table_traits::all_move_constructible && table_traits::all_move_assignable))> + SOAGEN_CPP20_CONSTEXPR + SOAGEN_ENABLE_IF_T(boxes&, sfinae) emplace(size_type index_, + CenterX&& center_x, + CenterY&& center_y, + CenterZ&& center_z, + ExtentsX&& extents_x = 0.5, + ExtentsY&& extents_y = 0.5, + ExtentsZ&& extents_z = 0.5, + Mass&& mass = default_mass) // + noexcept(table_traits::emplace_is_nothrow) + SOAGEN_REQUIRES(table_traits::all_move_constructible&& table_traits::all_move_assignable) // + { + table_.emplace(index_, + static_cast(center_x), + static_cast(center_y), + static_cast(center_z), + static_cast(extents_x), + static_cast(extents_y), + static_cast(extents_z), + static_cast(mass)); + return *this; + } + + /// @brief Constructs a new row directly in-place at an arbitrary position in the table. + /// + /// @availability This overload is only available when all the column types are move-constructible and move-assignable. + template ::default_emplace_type, + typename ExtentsY = column_traits<4>::default_emplace_type, + typename ExtentsZ = column_traits<5>::default_emplace_type, + typename Mass = column_traits<6>::default_emplace_type SOAGEN_HIDDEN_PARAM( + bool sfinae = (table_traits::all_move_constructible && table_traits::all_move_assignable))> + SOAGEN_CPP20_CONSTEXPR + SOAGEN_ENABLE_IF_T(iterator, sfinae) emplace(iterator iter_, + CenterX&& center_x, + CenterY&& center_y, + CenterZ&& center_z, + ExtentsX&& extents_x = 0.5, + ExtentsY&& extents_y = 0.5, + ExtentsZ&& extents_z = 0.5, + Mass&& mass = default_mass) // + noexcept(table_traits::emplace_is_nothrow) + SOAGEN_REQUIRES(table_traits::all_move_constructible&& table_traits::all_move_assignable) // + { + table_.emplace(static_cast(iter_), + static_cast(center_x), + static_cast(center_y), + static_cast(center_z), + static_cast(extents_x), + static_cast(extents_y), + static_cast(extents_z), + static_cast(mass)); + return iter_; + } + + /// @brief Constructs a new row directly in-place at an arbitrary position in the table. + /// + /// @availability This overload is only available when all the column types are move-constructible and move-assignable. + template ::default_emplace_type, + typename ExtentsY = column_traits<4>::default_emplace_type, + typename ExtentsZ = column_traits<5>::default_emplace_type, + typename Mass = column_traits<6>::default_emplace_type SOAGEN_HIDDEN_PARAM( + bool sfinae = (table_traits::all_move_constructible && table_traits::all_move_assignable))> + SOAGEN_CPP20_CONSTEXPR + SOAGEN_ENABLE_IF_T(const_iterator, sfinae) emplace(const_iterator iter_, + CenterX&& center_x, + CenterY&& center_y, + CenterZ&& center_z, + ExtentsX&& extents_x = 0.5, + ExtentsY&& extents_y = 0.5, + ExtentsZ&& extents_z = 0.5, + Mass&& mass = default_mass) // + noexcept(table_traits::emplace_is_nothrow) + SOAGEN_REQUIRES(table_traits::all_move_constructible&& table_traits::all_move_assignable) // + { + table_.emplace(static_cast(iter_), + static_cast(center_x), + static_cast(center_y), + static_cast(center_z), + static_cast(extents_x), + static_cast(extents_y), + static_cast(extents_z), + static_cast(mass)); + return iter_; + } + + /// @} + + /// @name Equality + /// @availability These operators are only available when all the column types are equality-comparable. + /// @{ + +#if SOAGEN_DOXYGEN + + /// @brief Returns true if all of the elements in two tables are equal. + friend constexpr bool operator==(const boxes& lhs, const boxes& rhs) // + noexcept(soagen::is_nothrow_equality_comparable); + + /// @brief Returns true if not all of the elements in two tables are equal. + friend constexpr bool operator!=(const boxes& lhs, const boxes& rhs) // + noexcept(soagen::is_nothrow_equality_comparable); + +#endif + + /// @} + + /// @name Comparison + /// @availability These operators are only available when all the column types are less-than-comparable. + /// @{ + +#if SOAGEN_DOXYGEN + + /// @brief Returns true if the LHS table is ordered lexicographically less-than the RHS table. + friend constexpr bool operator<(const boxes& lhs, const boxes& rhs) // + noexcept(soagen::is_nothrow_less_than_comparable); + + /// @brief Returns true if the LHS table is ordered lexicographically less-than-or-equal-to the RHS table. + friend constexpr bool operator<=(const boxes& lhs, const boxes& rhs) // + noexcept(soagen::is_nothrow_less_than_comparable); + + /// @brief Returns true if the LHS table is ordered lexicographically greater-than the RHS table. + friend constexpr bool operator>(const boxes& lhs, const boxes& rhs) // + noexcept(soagen::is_nothrow_less_than_comparable); + + /// @brief Returns true if the LHS table is ordered lexicographically greater-than-or-equal-to the RHS table. + friend constexpr bool operator>=(const boxes& lhs, const boxes& rhs) // + noexcept(soagen::is_nothrow_less_than_comparable); + +#endif + + /// @} + + /// @name Column access + /// @{ + +#if SOAGEN_DOXYGEN + + /// @brief Returns a pointer to the raw byte backing array. + /// + /// @availability This method is only available when all the column types are trivially-copyable. + constexpr std::byte* data() // + noexcept(soagen::has_nothrow_data_member); + + /// @brief Returns a pointer to the raw byte backing array. + /// + /// @availability This method is only available when all the column types are trivially-copyable. + constexpr const std::byte* const data() // + noexcept(soagen::has_nothrow_data_member); + +#endif + + /// @brief Returns a pointer to the elements of a specific column. + template + SOAGEN_ALIGNED_COLUMN(Column) + constexpr column_type* column() noexcept + { + static_assert(Column < table_traits::column_count, "column index out of range"); + + return soagen::assume_aligned< + soagen::detail::actual_column_alignment>( + table_.template column()); + } + + /// @brief Returns a pointer to the elements of a specific column. + template + SOAGEN_ALIGNED_COLUMN(Column) + constexpr std::add_const_t>* column() const noexcept + { + static_assert(Column < table_traits::column_count, "column index out of range"); + + return soagen::assume_aligned< + soagen::detail::actual_column_alignment>( + table_.template column()); + } + + /// @brief Returns a pointer to the elements in column [0]: center_x. + SOAGEN_ALIGNED_COLUMN(0) + constexpr float* center_x() noexcept + { + return column<0>(); + } + + /// @brief Returns a pointer to the elements in column [0]: center_x. + SOAGEN_ALIGNED_COLUMN(0) + constexpr const float* center_x() const noexcept + { + return column<0>(); + } + + /// @brief Returns a pointer to the elements in column [1]: center_y. + SOAGEN_ALIGNED_COLUMN(1) + constexpr float* center_y() noexcept + { + return column<1>(); + } + + /// @brief Returns a pointer to the elements in column [1]: center_y. + SOAGEN_ALIGNED_COLUMN(1) + constexpr const float* center_y() const noexcept + { + return column<1>(); + } + + /// @brief Returns a pointer to the elements in column [2]: center_z. + SOAGEN_ALIGNED_COLUMN(2) + constexpr float* center_z() noexcept + { + return column<2>(); + } + + /// @brief Returns a pointer to the elements in column [2]: center_z. + SOAGEN_ALIGNED_COLUMN(2) + constexpr const float* center_z() const noexcept + { + return column<2>(); + } + + /// @brief Returns a pointer to the elements in column [3]: extents_x. + SOAGEN_ALIGNED_COLUMN(3) + constexpr float* extents_x() noexcept + { + return column<3>(); + } + + /// @brief Returns a pointer to the elements in column [3]: extents_x. + SOAGEN_ALIGNED_COLUMN(3) + constexpr const float* extents_x() const noexcept + { + return column<3>(); + } + + /// @brief Returns a pointer to the elements in column [4]: extents_y. + SOAGEN_ALIGNED_COLUMN(4) + constexpr float* extents_y() noexcept + { + return column<4>(); + } + + /// @brief Returns a pointer to the elements in column [4]: extents_y. + SOAGEN_ALIGNED_COLUMN(4) + constexpr const float* extents_y() const noexcept + { + return column<4>(); + } + + /// @brief Returns a pointer to the elements in column [5]: extents_z. + SOAGEN_ALIGNED_COLUMN(5) + constexpr float* extents_z() noexcept + { + return column<5>(); + } + + /// @brief Returns a pointer to the elements in column [5]: extents_z. + SOAGEN_ALIGNED_COLUMN(5) + constexpr const float* extents_z() const noexcept + { + return column<5>(); + } + + /// @brief Returns a pointer to the elements in column [6]: mass. + SOAGEN_ALIGNED_COLUMN(6) + constexpr float* mass() noexcept + { + return column<6>(); + } + + /// @brief Returns a pointer to the elements in column [6]: mass. + SOAGEN_ALIGNED_COLUMN(6) + constexpr const float* mass() const noexcept + { + return column<6>(); + } + + /// @brief Invokes a function once for each column data pointer. + /// + /// @tparam Func A callable type compatible with one of the following signatures:
+ /// - `void(auto*, size_type)` + /// - `void(size_type, auto*)` + /// - `void(auto*)` + /// + /// @param func The callable to invoke. + template + constexpr void for_each_column(Func&& func) // + noexcept(table_traits::for_each_column_ptr_nothrow_invocable) + { + soagen::invoke_with_optarg(static_cast(func), this->template column<0>(), size_type{ 0 }); + soagen::invoke_with_optarg(static_cast(func), this->template column<1>(), size_type{ 1 }); + soagen::invoke_with_optarg(static_cast(func), this->template column<2>(), size_type{ 2 }); + soagen::invoke_with_optarg(static_cast(func), this->template column<3>(), size_type{ 3 }); + soagen::invoke_with_optarg(static_cast(func), this->template column<4>(), size_type{ 4 }); + soagen::invoke_with_optarg(static_cast(func), this->template column<5>(), size_type{ 5 }); + soagen::invoke_with_optarg(static_cast(func), this->template column<6>(), size_type{ 6 }); + } + + /// @brief Invokes a function once for each column data pointer. + /// + /// @tparam Func A callable type compatible with one of the following signatures:
+ /// - `void(auto*, size_type)` + /// - `void(size_type, auto*)` + /// - `void(auto*)` + /// + /// @param func The callable to invoke. + template + constexpr void for_each_column(Func&& func) const // + noexcept(table_traits::for_each_column_ptr_nothrow_invocable) + { + soagen::invoke_with_optarg(static_cast(func), this->template column<0>(), size_type{ 0 }); + soagen::invoke_with_optarg(static_cast(func), this->template column<1>(), size_type{ 1 }); + soagen::invoke_with_optarg(static_cast(func), this->template column<2>(), size_type{ 2 }); + soagen::invoke_with_optarg(static_cast(func), this->template column<3>(), size_type{ 3 }); + soagen::invoke_with_optarg(static_cast(func), this->template column<4>(), size_type{ 4 }); + soagen::invoke_with_optarg(static_cast(func), this->template column<5>(), size_type{ 5 }); + soagen::invoke_with_optarg(static_cast(func), this->template column<6>(), size_type{ 6 }); + } + + /// @} + + /// @name Row access + /// @{ + + /// @brief Returns the row at the given index. + /// + /// @tparam Columns Indices of the columns to include in the row. Leave the list empty for all columns. + template + SOAGEN_PURE_GETTER + SOAGEN_CPP20_CONSTEXPR + soagen::row_type row(size_type index) & noexcept + { + if constexpr (sizeof...(Columns)) + { + return { { this->template column()[index] }... }; + } + else + { + return (*this).template row<0, 1, 2, 3, 4, 5, 6>(index); + } + } + + /// @brief Returns the row at the given index. + SOAGEN_PURE_INLINE_GETTER + SOAGEN_CPP20_CONSTEXPR + row_type operator[](size_type index) & noexcept + { + return (*this).row(index); + } + + /// @brief Returns the row at the given index. + /// + /// @throws std::out_of_range + SOAGEN_PURE_GETTER + SOAGEN_CPP20_CONSTEXPR + row_type at(size_type index) & + { +#if SOAGEN_HAS_EXCEPTIONS + if (index >= size()) + throw std::out_of_range{ "bad element access" }; +#endif + return (*this).row(index); + } + + /// @brief Returns the very first row in the table. + SOAGEN_PURE_INLINE_GETTER + SOAGEN_CPP20_CONSTEXPR + row_type front() & noexcept + { + return (*this).row(0u); + } + + /// @brief Returns the very last row in the table. + SOAGEN_PURE_INLINE_GETTER + SOAGEN_CPP20_CONSTEXPR + row_type back() & noexcept + { + return (*this).row(size() - 1u); + } + + /// @brief Returns the row at the given index. + /// + /// @tparam Columns Indices of the columns to include in the row. Leave the list empty for all columns. + template + SOAGEN_PURE_GETTER + SOAGEN_CPP20_CONSTEXPR + soagen::row_type row(size_type index) && noexcept + { + if constexpr (sizeof...(Columns)) + { + return { { std::move(this->template column()[index]) }... }; + } + else + { + return std::move(*this).template row<0, 1, 2, 3, 4, 5, 6>(index); + } + } + + /// @brief Returns the row at the given index. + SOAGEN_PURE_INLINE_GETTER + SOAGEN_CPP20_CONSTEXPR + rvalue_row_type operator[](size_type index) && noexcept + { + return std::move(*this).row(index); + } + + /// @brief Returns the row at the given index. + /// + /// @throws std::out_of_range + SOAGEN_PURE_GETTER + SOAGEN_CPP20_CONSTEXPR + rvalue_row_type at(size_type index) && + { +#if SOAGEN_HAS_EXCEPTIONS + if (index >= size()) + throw std::out_of_range{ "bad element access" }; +#endif + return std::move(*this).row(index); + } + + /// @brief Returns the very first row in the table. + SOAGEN_PURE_INLINE_GETTER + SOAGEN_CPP20_CONSTEXPR + rvalue_row_type front() && noexcept + { + return std::move(*this).row(0u); + } + + /// @brief Returns the very last row in the table. + SOAGEN_PURE_INLINE_GETTER + SOAGEN_CPP20_CONSTEXPR + rvalue_row_type back() && noexcept + { + return std::move(*this).row(size() - 1u); + } + + /// @brief Returns the row at the given index. + /// + /// @tparam Columns Indices of the columns to include in the row. Leave the list empty for all columns. + template + SOAGEN_PURE_GETTER + SOAGEN_CPP20_CONSTEXPR + soagen::row_type row(size_type index) const& noexcept + { + if constexpr (sizeof...(Columns)) + { + return { { this->template column()[index] }... }; + } + else + { + return (*this).template row<0, 1, 2, 3, 4, 5, 6>(index); + } + } + + /// @brief Returns the row at the given index. + SOAGEN_PURE_INLINE_GETTER + SOAGEN_CPP20_CONSTEXPR + const_row_type operator[](size_type index) const& noexcept + { + return (*this).row(index); + } + + /// @brief Returns the row at the given index. + /// + /// @throws std::out_of_range + SOAGEN_PURE_GETTER + SOAGEN_CPP20_CONSTEXPR + const_row_type at(size_type index) const& + { +#if SOAGEN_HAS_EXCEPTIONS + if (index >= size()) + throw std::out_of_range{ "bad element access" }; +#endif + return (*this).row(index); + } + + /// @brief Returns the very first row in the table. + SOAGEN_PURE_INLINE_GETTER + SOAGEN_CPP20_CONSTEXPR + const_row_type front() const& noexcept + { + return (*this).row(0u); + } + + /// @brief Returns the very last row in the table. + SOAGEN_PURE_INLINE_GETTER + SOAGEN_CPP20_CONSTEXPR + const_row_type back() const& noexcept + { + return (*this).row(size() - 1u); + } + + /// @} + + /// @name Iterators + /// @{ + + /// @brief Returns an iterator to the first row in the table. + template + SOAGEN_PURE_INLINE_GETTER + constexpr soagen::iterator_type begin() & noexcept + { + return { *this, 0 }; + } + + /// @brief Returns an iterator to one-past-the-last row in the table. + template + SOAGEN_PURE_INLINE_GETTER + constexpr soagen::iterator_type end() & noexcept + { + return { *this, static_cast(size()) }; + } + + /// @brief Returns an iterator to the first row in the table. + template + SOAGEN_PURE_INLINE_GETTER + constexpr soagen::iterator_type begin() && noexcept + { + return { std::move(*this), 0 }; + } + + /// @brief Returns an iterator to one-past-the-last row in the table. + template + SOAGEN_PURE_INLINE_GETTER + constexpr soagen::iterator_type end() && noexcept + { + return { std::move(*this), static_cast(size()) }; + } + + /// @brief Returns an iterator to the first row in the table. + template + SOAGEN_PURE_INLINE_GETTER + constexpr soagen::iterator_type begin() const& noexcept + { + return { *this, 0 }; + } + + /// @brief Returns an iterator to one-past-the-last row in the table. + template + SOAGEN_PURE_INLINE_GETTER + constexpr soagen::iterator_type end() const& noexcept + { + return { *this, static_cast(size()) }; + } + + /// @brief Returns an iterator to the first row in the table. + template + SOAGEN_PURE_INLINE_GETTER + constexpr soagen::iterator_type cbegin() const& noexcept + { + return { *this, 0 }; + } + + /// @brief Returns an iterator to one-past-the-last row in the table. + template + SOAGEN_PURE_INLINE_GETTER + constexpr soagen::iterator_type cend() const& noexcept + { + return { *this, static_cast(size()) }; + } + + /// @} + }; + + /// @brief Swaps the contents of two instances of #soagen::examples::boxes. + /// + /// @availability This overload is only available when #soagen::examples::boxes::allocator_type + /// is swappable or non-propagating. + SOAGEN_HIDDEN_CONSTRAINT(sfinae, auto sfinae = soagen::has_swap_member) + SOAGEN_ALWAYS_INLINE + constexpr void swap(boxes& lhs, boxes& rhs) // + noexcept(soagen::has_nothrow_swap_member) + { + lhs.swap(rhs); + } + + /// @cond + + /// @endcond +} + +//---------------------------------------------------------------------------------------------------------------------- +// spheres +//---------------------------------------------------------------------------------------------------------------------- + +namespace soagen::examples +{ + /// @brief A SIMD-friendly bounding sphere container. + /// + /// @details + /// + /// @remark Models the Structure-of-arrays + /// equivalent of: @code{.cpp} + /// struct spheres + /// { + /// float center_x; + /// float center_y; + /// float center_z; + /// float radius; + /// float mass; + /// }; + /// @endcode + /// + /// + /// @note The code and documentation for this class were generated by soagen - https://marzer.github.io/soagen + class SOAGEN_EMPTY_BASES spheres // + SOAGEN_HIDDEN_BASE(public soagen::mixins::resizable, + public soagen::mixins::equality_comparable, + public soagen::mixins::less_than_comparable, + public soagen::mixins::data_ptr, + public soagen::mixins::const_data_ptr, + public soagen::mixins::swappable) + { + public: + /// @brief The unsigned integer size type used by this class. + using size_type = std::size_t; + + /// @brief The signed integer difference type used by this class. + using difference_type = std::ptrdiff_t; + + /// @brief The allocator type used by this class. + using allocator_type = soagen::allocator_type; + + /// @brief This class's underlying soagen::table type. + using table_type = soagen::table_type; + + /// @brief The soagen::table_traits for the underlying table. + using table_traits = soagen::table_traits_type; + + /// @brief The number of columns in the table. + static constexpr size_t column_count = soagen::table_traits_type::column_count; + + /// @brief Gets the soagen::column_traits for a specific column of the table. + template + using column_traits = typename table_traits::template column; + + /// @brief Gets the type of a specific column in the table. + template + using column_type = typename column_traits::value_type; + + /// @brief Row iterators returned by iterator functions. + using iterator = soagen::iterator_type; + + /// @brief Row iterators returned by const-qualified iterator functions. + using const_iterator = soagen::iterator_type; + + /// @brief Row iterators returned by rvalue-qualified iterator functions. + using rvalue_iterator = soagen::iterator_type; + + /// @brief Regular (lvalue-qualified) row type used by this class. + using row_type = soagen::row_type; + + /// @brief Const row type used by this class. + using const_row_type = soagen::row_type; + + /// @brief Rvalue row type used by this class. + using rvalue_row_type = soagen::row_type; + + /// @brief The number of rows to advance to maintain the requested `alignment` for every column. + /// + /// @details The stride size you need to use when iterating through rows of this table such that + /// the starting element for each batch in each column would have the same memory alignment as the + /// value specified for the column-specific `alignment`. + /// + /// @note Typically you can ignore this; column elements are always aligned correctly according to their + /// type. This is for over-alignment scenarios where you need to do things in batches (e.g. SIMD). + static constexpr size_type aligned_stride = table_traits::aligned_stride; + +#if SOAGEN_DOXYGEN + /// @brief Named index constants for all of the columns in the table. + using column_indices = POXY_IMPLEMENTATION_DETAIL(struct dummy_t); +#else + struct column_indices + { + static constexpr size_type center_x = 0; + static constexpr size_type center_y = 1; + static constexpr size_type center_z = 2; + static constexpr size_type radius = 3; + static constexpr size_type mass = 4; + }; +#endif + + /// @brief Gets the name of the specified column as a string. + template + static constexpr auto& column_name = soagen::detail::col_name_::value; + + static constexpr float default_mass = 1.0; + + private: + /// @cond + + table_type table_; + + /// @endcond + + public: + /// @brief Default constructor. + SOAGEN_NODISCARD_CTOR + spheres() = default; + + /// @brief Move constructor. + SOAGEN_NODISCARD_CTOR + spheres(spheres&&) = default; + + /// @brief Move-assignment operator. + spheres& operator=(spheres&&) = default; + + /// @brief Copy constructor. + SOAGEN_NODISCARD_CTOR + spheres(const spheres&) = default; + + /// @brief Copy-assignment operator. + spheres& operator=(const spheres&) = default; + + /// @brief Destructor. + ~spheres() = default; + + /// @brief Constructs with the given allocator. + SOAGEN_NODISCARD_CTOR + constexpr explicit spheres(const allocator_type& alloc) noexcept // + : table_{ alloc } + {} + + /// @brief Constructs with the given allocator. + SOAGEN_NODISCARD_CTOR + constexpr explicit spheres(allocator_type&& alloc) noexcept // + : table_{ static_cast(alloc) } + {} + + /// @brief Returns the allocator being used by the table. + SOAGEN_INLINE_GETTER + SOAGEN_CPP20_CONSTEXPR + allocator_type get_allocator() const noexcept + { + return table_.get_allocator(); + } + + /// @name Underlying table access + /// @{ + + /// @brief Returns an lvalue reference to the underlying soagen::table. + SOAGEN_PURE_INLINE_GETTER + constexpr table_type& table() & noexcept + { + return table_; + } + + /// @brief Returns an rvalue reference to the underlying soagen::table. + SOAGEN_PURE_INLINE_GETTER + constexpr table_type&& table() && noexcept + { + return static_cast(table_); + } + + /// @brief Returns a const lvalue reference to the underlying soagen::table. + SOAGEN_PURE_INLINE_GETTER + constexpr const table_type& table() const& noexcept + { + return table_; + } + + /// @} + + /// @name Capacity + /// @{ + + /// @brief Returns true if the number of rows is zero. + SOAGEN_PURE_INLINE_GETTER + constexpr bool empty() const noexcept + { + return table_.empty(); + } + + /// @brief Returns the current number of rows. + SOAGEN_PURE_INLINE_GETTER + constexpr size_type size() const noexcept + { + return table_.size(); + } + + /// @brief Returns the maximum possible number of rows. + SOAGEN_PURE_INLINE_GETTER + constexpr size_type max_size() const noexcept + { + return table_.max_size(); + } + + /// @brief Returns the size of the current underlying buffer allocation in bytes. + SOAGEN_PURE_INLINE_GETTER + constexpr size_type allocation_size() const noexcept + { + return table_.allocation_size(); + } + + /// @brief Reserves storage for (at least) the given number of rows. + SOAGEN_ALWAYS_INLINE + SOAGEN_CPP20_CONSTEXPR + spheres& reserve(size_type new_cap) // + noexcept(noexcept(std::declval().reserve(size_type{}))) + { + table_.reserve(new_cap); + return *this; + } + + /// @brief Returns the number of rows that can be held in currently allocated storage. + SOAGEN_PURE_INLINE_GETTER + constexpr size_type capacity() const noexcept + { + return table_.capacity(); + } + + /// @brief Frees unused capacity. + SOAGEN_ALWAYS_INLINE + SOAGEN_CPP20_CONSTEXPR + spheres& shrink_to_fit() // + noexcept(noexcept(std::declval().shrink_to_fit())) + { + table_.shrink_to_fit(); + return *this; + } + + /// @} + + /// @name Modifiers + /// @{ + + /// @brief Removes all rows from table. + SOAGEN_ALWAYS_INLINE + SOAGEN_CPP20_CONSTEXPR + spheres& clear() noexcept + { + table_.clear(); + return *this; + } + + /// @brief Erases the row at the given position. + /// + /// @availability This method is only available when all the column types are move-assignable. + SOAGEN_HIDDEN(template >) + SOAGEN_ALWAYS_INLINE + SOAGEN_CPP20_CONSTEXPR + SOAGEN_ENABLE_IF_T(spheres&, sfinae) erase(size_type pos) // + noexcept(soagen::has_nothrow_erase_member) + { + table_.erase(pos); + return *this; + } + + /// @brief Erases the row at the given position without preserving order. + /// + /// @details This is much faster than #erase() because it uses the swap-and-pop idiom: + /// Instead of shifting all the higher rows downward, the last row is moved into the + /// position of the erased one and the size of the table is reduced by 1. + /// + /// @note If you are tracking row indices in some other place and need to maintain that invariant, + /// you can use the return value to update your data accordingly. + /// + /// @returns The position of the row that was moved into the erased row's position, if any. + /// + /// @availability This method is only available when all the column types are move-assignable. + SOAGEN_HIDDEN(template >) + SOAGEN_ALWAYS_INLINE + SOAGEN_CPP20_CONSTEXPR + SOAGEN_ENABLE_IF_T(soagen::optional, sfinae) unordered_erase(size_type pos) // + noexcept(soagen::has_nothrow_unordered_erase_member) + { + return table_.unordered_erase(pos); + } + + /// @brief Erases the row at the given iterator. + /// + /// @returns An iterator to the row immediately following the one which was removed, + /// or #end() if the one removed was the last row in the table. + /// + /// @availability This method is only available when all the column types are move-assignable. + SOAGEN_HIDDEN(template >) + SOAGEN_ALWAYS_INLINE + SOAGEN_CPP20_CONSTEXPR + SOAGEN_ENABLE_IF_T(iterator, sfinae) erase(iterator pos) // + noexcept(soagen::has_nothrow_erase_member) + { + table_.erase(static_cast(pos)); + return pos; + } + + /// @brief Erases the row at the given position without preserving order. + /// + /// @details This is much faster than #erase() because it uses the swap-and-pop idiom: + /// Instead of shifting all the higher rows downward, the last row is moved into the + /// position of the erased one and the size of the table is reduced by 1. + /// + /// @note If you are tracking row indices in some other place and need to maintain that invariant, + /// you can use the return value to update your data accordingly. + /// + /// @returns The position of the row that was moved into the erased row's position, if any. + /// + /// @availability This method is only available when all the column types are move-assignable. + SOAGEN_HIDDEN(template >) + SOAGEN_ALWAYS_INLINE + SOAGEN_CPP20_CONSTEXPR + SOAGEN_ENABLE_IF_T(soagen::optional, sfinae) unordered_erase(iterator pos) // + noexcept(soagen::has_nothrow_unordered_erase_member) + { + if (auto moved_pos = table_.unordered_erase(static_cast(pos)); moved_pos) + return iterator{ *this, static_cast(*moved_pos) }; + return {}; + } + + /// @brief Erases the row at the given iterator. + /// + /// @returns An iterator to the row immediately following the one which was removed, + /// or #cend() if the one removed was the last row in the table. + /// + /// @availability This method is only available when all the column types are move-assignable. + SOAGEN_HIDDEN(template >) + SOAGEN_ALWAYS_INLINE + SOAGEN_CPP20_CONSTEXPR + SOAGEN_ENABLE_IF_T(const_iterator, sfinae) erase(const_iterator pos) // + noexcept(soagen::has_nothrow_erase_member) + { + table_.erase(static_cast(pos)); + return pos; + } + + /// @brief Erases the row at the given position without preserving order. + /// + /// @details This is much faster than #erase() because it uses the swap-and-pop idiom: + /// Instead of shifting all the higher rows downward, the last row is moved into the + /// position of the erased one and the size of the table is reduced by 1. + /// + /// @note If you are tracking row indices in some other place and need to maintain that invariant, + /// you can use the return value to update your data accordingly. + /// + /// @returns The position of the row that was moved into the erased row's position, if any. + /// + /// @availability This method is only available when all the column types are move-assignable. + SOAGEN_HIDDEN(template >) + SOAGEN_ALWAYS_INLINE + SOAGEN_CPP20_CONSTEXPR + SOAGEN_ENABLE_IF_T(soagen::optional, sfinae) unordered_erase(const_iterator pos) // + noexcept(soagen::has_nothrow_unordered_erase_member) + { + if (auto moved_pos = table_.unordered_erase(static_cast(pos)); moved_pos) + return const_iterator{ *this, static_cast(*moved_pos) }; + return {}; + } + + /// @brief Removes the last row(s) from the table. + SOAGEN_ALWAYS_INLINE + SOAGEN_CPP20_CONSTEXPR + spheres& pop_back(size_type num = 1) // + noexcept(noexcept(std::declval().pop_back(size_type{}))) + { + table_.pop_back(num); + return *this; + } + +#if SOAGEN_DOXYGEN + + /// @brief Resizes the table to the given number of rows. + /// + /// @availability This method is only available when all the column types are default-constructible. + spheres& resize(size_type new_size) // + noexcept(soagen::has_nothrow_resize_member); + + /// @brief Swaps the contents of the table with another. + /// + /// @availability This method is only available when #allocator_type is swappable or non-propagating. + constexpr void swap(spheres& other) // + noexcept(soagen::has_nothrow_swap_member); + +#endif + + /// @} + + /// @name Adding rows + /// @{ + + /// @brief Adds a new row at the end of the table. + SOAGEN_CPP20_CONSTEXPR + spheres& push_back(column_traits<0>::param_type center_x, + column_traits<1>::param_type center_y, + column_traits<2>::param_type center_z, + column_traits<3>::param_type radius = 0.5, + column_traits<4>::param_type mass = default_mass) // + noexcept(table_traits::push_back_is_nothrow) + { + table_.emplace_back(static_cast::param_forward_type>(center_x), + static_cast::param_forward_type>(center_y), + static_cast::param_forward_type>(center_z), + static_cast::param_forward_type>(radius), + static_cast::param_forward_type>(mass)); + return *this; + } + + /// @brief Adds a new row at the end of the table (rvalue overload). + SOAGEN_HIDDEN(template ) + SOAGEN_CPP20_CONSTEXPR + spheres& push_back(SOAGEN_ENABLE_IF_T(column_traits<0>::rvalue_type, sfinae) center_x, + column_traits<1>::rvalue_type center_y, + column_traits<2>::rvalue_type center_z, + column_traits<3>::rvalue_type radius = 0.5, + column_traits<4>::rvalue_type mass = default_mass) // + noexcept(table_traits::rvalue_push_back_is_nothrow) + SOAGEN_REQUIRES(table_traits::rvalue_type_list_is_distinct) // + { + table_.emplace_back(static_cast::rvalue_forward_type>(center_x), + static_cast::rvalue_forward_type>(center_y), + static_cast::rvalue_forward_type>(center_z), + static_cast::rvalue_forward_type>(radius), + static_cast::rvalue_forward_type>(mass)); + return *this; + } + + /// @brief Adds a new row at the end of the table. + template >)> + SOAGEN_CPP20_CONSTEXPR + spheres& push_back(const soagen::row& row_) // + noexcept(table_traits::row_push_back_is_nothrow&>) + SOAGEN_REQUIRES(soagen::is_soa>) // + { + table_.emplace_back(row_); + return *this; + } + + /// @brief Constructs a new row directly in-place at the end of the table. + template ::default_emplace_type, + typename Mass = column_traits<4>::default_emplace_type> + SOAGEN_CPP20_CONSTEXPR + spheres& emplace_back(CenterX&& center_x, + CenterY&& center_y, + CenterZ&& center_z, + Radius&& radius = 0.5, + Mass&& mass = default_mass) // + noexcept( + table_traits::emplace_back_is_nothrow) + { + table_.emplace_back(static_cast(center_x), + static_cast(center_y), + static_cast(center_z), + static_cast(radius), + static_cast(mass)); + return *this; + } + + /// @} + + /// @name Inserting rows + /// @availability These overloads are only available when all the column types are move-constructible and move-assignable. + /// @{ + + /// @brief Inserts a new row at an arbitrary position in the table. + /// + /// @availability This overload is only available when all the column types are move-constructible and move-assignable. + SOAGEN_HIDDEN( + template + ) + SOAGEN_CPP20_CONSTEXPR + SOAGEN_ENABLE_IF_T(spheres&, sfinae) insert(size_type index_, + column_traits<0>::param_type center_x, + column_traits<1>::param_type center_y, + column_traits<2>::param_type center_z, + column_traits<3>::param_type radius = 0.5, + column_traits<4>::param_type mass = default_mass) // + noexcept(table_traits::insert_is_nothrow) + SOAGEN_REQUIRES(table_traits::all_move_constructible&& table_traits::all_move_assignable) // + { + table_.emplace(index_, + static_cast::param_forward_type>(center_x), + static_cast::param_forward_type>(center_y), + static_cast::param_forward_type>(center_z), + static_cast::param_forward_type>(radius), + static_cast::param_forward_type>(mass)); + return *this; + } + + /// @brief Inserts a new row at an arbitrary position in the table. + /// + /// @availability This overload is only available when all the column types are move-constructible and move-assignable. + SOAGEN_HIDDEN( + template + ) + SOAGEN_CPP20_CONSTEXPR + SOAGEN_ENABLE_IF_T(iterator, sfinae) insert(iterator iter_, + column_traits<0>::param_type center_x, + column_traits<1>::param_type center_y, + column_traits<2>::param_type center_z, + column_traits<3>::param_type radius = 0.5, + column_traits<4>::param_type mass = default_mass) // + noexcept(table_traits::insert_is_nothrow) + SOAGEN_REQUIRES(table_traits::all_move_constructible&& table_traits::all_move_assignable) // + { + table_.emplace(static_cast(iter_), + static_cast::param_forward_type>(center_x), + static_cast::param_forward_type>(center_y), + static_cast::param_forward_type>(center_z), + static_cast::param_forward_type>(radius), + static_cast::param_forward_type>(mass)); + return iter_; + } + + /// @brief Inserts a new row at an arbitrary position in the table. + /// + /// @availability This overload is only available when all the column types are move-constructible and move-assignable. + SOAGEN_HIDDEN( + template + ) + SOAGEN_CPP20_CONSTEXPR + SOAGEN_ENABLE_IF_T(const_iterator, sfinae) insert(const_iterator iter_, + column_traits<0>::param_type center_x, + column_traits<1>::param_type center_y, + column_traits<2>::param_type center_z, + column_traits<3>::param_type radius = 0.5, + column_traits<4>::param_type mass = default_mass) // + noexcept(table_traits::insert_is_nothrow) + SOAGEN_REQUIRES(table_traits::all_move_constructible&& table_traits::all_move_assignable) // + { + table_.emplace(static_cast(iter_), + static_cast::param_forward_type>(center_x), + static_cast::param_forward_type>(center_y), + static_cast::param_forward_type>(center_z), + static_cast::param_forward_type>(radius), + static_cast::param_forward_type>(mass)); + return iter_; + } + + /// @brief Inserts a new row at an arbitrary position in the table (rvalue overload). + /// + /// @availability This overload is only available when all the column types are move-constructible and move-assignable. + SOAGEN_HIDDEN( + template + ) + SOAGEN_CPP20_CONSTEXPR + spheres& insert(SOAGEN_ENABLE_IF_T(size_type, sfinae) index_, + column_traits<0>::rvalue_type center_x, + column_traits<1>::rvalue_type center_y, + column_traits<2>::rvalue_type center_z, + column_traits<3>::rvalue_type radius = 0.5, + column_traits<4>::rvalue_type mass = default_mass) // + noexcept(table_traits::rvalue_insert_is_nothrow) + SOAGEN_REQUIRES(table_traits::rvalue_type_list_is_distinct&& table_traits::all_move_constructible&& + table_traits::all_move_assignable) // + { + table_.emplace(index_, + static_cast::rvalue_forward_type>(center_x), + static_cast::rvalue_forward_type>(center_y), + static_cast::rvalue_forward_type>(center_z), + static_cast::rvalue_forward_type>(radius), + static_cast::rvalue_forward_type>(mass)); + return *this; + } + + /// @brief Inserts a new row at an arbitrary position in the table (rvalue overload). + /// + /// @availability This overload is only available when all the column types are move-constructible and move-assignable. + SOAGEN_HIDDEN( + template + ) + SOAGEN_CPP20_CONSTEXPR + iterator insert(SOAGEN_ENABLE_IF_T(iterator, sfinae) iter_, + column_traits<0>::rvalue_type center_x, + column_traits<1>::rvalue_type center_y, + column_traits<2>::rvalue_type center_z, + column_traits<3>::rvalue_type radius = 0.5, + column_traits<4>::rvalue_type mass = default_mass) // + noexcept(table_traits::rvalue_insert_is_nothrow) + SOAGEN_REQUIRES(table_traits::rvalue_type_list_is_distinct&& table_traits::all_move_constructible&& + table_traits::all_move_assignable) // + { + table_.emplace(static_cast(iter_), + static_cast::rvalue_forward_type>(center_x), + static_cast::rvalue_forward_type>(center_y), + static_cast::rvalue_forward_type>(center_z), + static_cast::rvalue_forward_type>(radius), + static_cast::rvalue_forward_type>(mass)); + return iter_; + } + + /// @brief Inserts a new row at an arbitrary position in the table (rvalue overload). + /// + /// @availability This overload is only available when all the column types are move-constructible and move-assignable. + SOAGEN_HIDDEN( + template + ) + SOAGEN_CPP20_CONSTEXPR + const_iterator insert(SOAGEN_ENABLE_IF_T(const_iterator, sfinae) iter_, + column_traits<0>::rvalue_type center_x, + column_traits<1>::rvalue_type center_y, + column_traits<2>::rvalue_type center_z, + column_traits<3>::rvalue_type radius = 0.5, + column_traits<4>::rvalue_type mass = default_mass) // + noexcept(table_traits::rvalue_insert_is_nothrow) + SOAGEN_REQUIRES(table_traits::rvalue_type_list_is_distinct&& table_traits::all_move_constructible&& + table_traits::all_move_assignable) // + { + table_.emplace(static_cast(iter_), + static_cast::rvalue_forward_type>(center_x), + static_cast::rvalue_forward_type>(center_y), + static_cast::rvalue_forward_type>(center_z), + static_cast::rvalue_forward_type>(radius), + static_cast::rvalue_forward_type>(mass)); + return iter_; + } + + /// @brief Inserts a new row at an arbitrary position in the table. + /// + /// @availability This overload is only available when all the column types are move-constructible and move-assignable. + template > + && table_traits::all_move_constructible + && table_traits::all_move_assignable))> + SOAGEN_CPP20_CONSTEXPR + spheres& insert(size_type index_, const soagen::row& row_) // + noexcept(table_traits::row_insert_is_nothrow&>) + SOAGEN_REQUIRES(soagen::is_soa>&& table_traits::all_move_constructible&& + table_traits::all_move_assignable) // + { + table_.emplace(index_, row_); + return *this; + } + + /// @brief Inserts a new row at an arbitrary position in the table. + /// + /// @availability This overload is only available when all the column types are move-constructible and move-assignable. + template > + && table_traits::all_move_constructible + && table_traits::all_move_assignable))> + SOAGEN_CPP20_CONSTEXPR + iterator insert(iterator iter_, const soagen::row& row_) // + noexcept(table_traits::row_insert_is_nothrow&>) + SOAGEN_REQUIRES(soagen::is_soa>&& table_traits::all_move_constructible&& + table_traits::all_move_assignable) // + { + table_.emplace(static_cast(iter_), row_); + return iter_; + } + + /// @brief Inserts a new row at an arbitrary position in the table. + /// + /// @availability This overload is only available when all the column types are move-constructible and move-assignable. + template > + && table_traits::all_move_constructible + && table_traits::all_move_assignable))> + SOAGEN_CPP20_CONSTEXPR + const_iterator insert(const_iterator iter_, const soagen::row& row_) // + noexcept(table_traits::row_insert_is_nothrow&>) + SOAGEN_REQUIRES(soagen::is_soa>&& table_traits::all_move_constructible&& + table_traits::all_move_assignable) // + { + table_.emplace(static_cast(iter_), row_); + return iter_; + } + + /// @brief Constructs a new row directly in-place at an arbitrary position in the table. + /// + /// @availability This overload is only available when all the column types are move-constructible and move-assignable. + template ::default_emplace_type, + typename Mass = column_traits<4>::default_emplace_type SOAGEN_HIDDEN_PARAM( + bool sfinae = (table_traits::all_move_constructible && table_traits::all_move_assignable))> + SOAGEN_CPP20_CONSTEXPR + SOAGEN_ENABLE_IF_T(spheres&, sfinae) emplace(size_type index_, + CenterX&& center_x, + CenterY&& center_y, + CenterZ&& center_z, + Radius&& radius = 0.5, + Mass&& mass = default_mass) // + noexcept(table_traits::emplace_is_nothrow) + SOAGEN_REQUIRES(table_traits::all_move_constructible&& table_traits::all_move_assignable) // + { + table_.emplace(index_, + static_cast(center_x), + static_cast(center_y), + static_cast(center_z), + static_cast(radius), + static_cast(mass)); + return *this; + } + + /// @brief Constructs a new row directly in-place at an arbitrary position in the table. + /// + /// @availability This overload is only available when all the column types are move-constructible and move-assignable. + template ::default_emplace_type, + typename Mass = column_traits<4>::default_emplace_type SOAGEN_HIDDEN_PARAM( + bool sfinae = (table_traits::all_move_constructible && table_traits::all_move_assignable))> + SOAGEN_CPP20_CONSTEXPR + SOAGEN_ENABLE_IF_T(iterator, sfinae) emplace(iterator iter_, + CenterX&& center_x, + CenterY&& center_y, + CenterZ&& center_z, + Radius&& radius = 0.5, + Mass&& mass = default_mass) // + noexcept(table_traits::emplace_is_nothrow) + SOAGEN_REQUIRES(table_traits::all_move_constructible&& table_traits::all_move_assignable) // + { + table_.emplace(static_cast(iter_), + static_cast(center_x), + static_cast(center_y), + static_cast(center_z), + static_cast(radius), + static_cast(mass)); + return iter_; + } + + /// @brief Constructs a new row directly in-place at an arbitrary position in the table. + /// + /// @availability This overload is only available when all the column types are move-constructible and move-assignable. + template ::default_emplace_type, + typename Mass = column_traits<4>::default_emplace_type SOAGEN_HIDDEN_PARAM( + bool sfinae = (table_traits::all_move_constructible && table_traits::all_move_assignable))> + SOAGEN_CPP20_CONSTEXPR + SOAGEN_ENABLE_IF_T(const_iterator, sfinae) emplace(const_iterator iter_, + CenterX&& center_x, + CenterY&& center_y, + CenterZ&& center_z, + Radius&& radius = 0.5, + Mass&& mass = default_mass) // + noexcept(table_traits::emplace_is_nothrow) + SOAGEN_REQUIRES(table_traits::all_move_constructible&& table_traits::all_move_assignable) // + { + table_.emplace(static_cast(iter_), + static_cast(center_x), + static_cast(center_y), + static_cast(center_z), + static_cast(radius), + static_cast(mass)); + return iter_; + } + + /// @} + + /// @name Equality + /// @availability These operators are only available when all the column types are equality-comparable. + /// @{ + +#if SOAGEN_DOXYGEN + + /// @brief Returns true if all of the elements in two tables are equal. + friend constexpr bool operator==(const spheres& lhs, const spheres& rhs) // + noexcept(soagen::is_nothrow_equality_comparable); + + /// @brief Returns true if not all of the elements in two tables are equal. + friend constexpr bool operator!=(const spheres& lhs, const spheres& rhs) // + noexcept(soagen::is_nothrow_equality_comparable); + +#endif + + /// @} + + /// @name Comparison + /// @availability These operators are only available when all the column types are less-than-comparable. + /// @{ + +#if SOAGEN_DOXYGEN + + /// @brief Returns true if the LHS table is ordered lexicographically less-than the RHS table. + friend constexpr bool operator<(const spheres& lhs, const spheres& rhs) // + noexcept(soagen::is_nothrow_less_than_comparable); + + /// @brief Returns true if the LHS table is ordered lexicographically less-than-or-equal-to the RHS table. + friend constexpr bool operator<=(const spheres& lhs, const spheres& rhs) // + noexcept(soagen::is_nothrow_less_than_comparable); + + /// @brief Returns true if the LHS table is ordered lexicographically greater-than the RHS table. + friend constexpr bool operator>(const spheres& lhs, const spheres& rhs) // + noexcept(soagen::is_nothrow_less_than_comparable); + + /// @brief Returns true if the LHS table is ordered lexicographically greater-than-or-equal-to the RHS table. + friend constexpr bool operator>=(const spheres& lhs, const spheres& rhs) // + noexcept(soagen::is_nothrow_less_than_comparable); + +#endif + + /// @} + + /// @name Column access + /// @{ + +#if SOAGEN_DOXYGEN + + /// @brief Returns a pointer to the raw byte backing array. + /// + /// @availability This method is only available when all the column types are trivially-copyable. + constexpr std::byte* data() // + noexcept(soagen::has_nothrow_data_member); + + /// @brief Returns a pointer to the raw byte backing array. + /// + /// @availability This method is only available when all the column types are trivially-copyable. + constexpr const std::byte* const data() // + noexcept(soagen::has_nothrow_data_member); + +#endif + + /// @brief Returns a pointer to the elements of a specific column. + template + SOAGEN_ALIGNED_COLUMN(Column) + constexpr column_type* column() noexcept + { + static_assert(Column < table_traits::column_count, "column index out of range"); + + return soagen::assume_aligned< + soagen::detail::actual_column_alignment>( + table_.template column()); + } + + /// @brief Returns a pointer to the elements of a specific column. + template + SOAGEN_ALIGNED_COLUMN(Column) + constexpr std::add_const_t>* column() const noexcept + { + static_assert(Column < table_traits::column_count, "column index out of range"); + + return soagen::assume_aligned< + soagen::detail::actual_column_alignment>( + table_.template column()); + } + + /// @brief Returns a pointer to the elements in column [0]: center_x. + SOAGEN_ALIGNED_COLUMN(0) + constexpr float* center_x() noexcept + { + return column<0>(); + } + + /// @brief Returns a pointer to the elements in column [0]: center_x. + SOAGEN_ALIGNED_COLUMN(0) + constexpr const float* center_x() const noexcept + { + return column<0>(); + } + + /// @brief Returns a pointer to the elements in column [1]: center_y. + SOAGEN_ALIGNED_COLUMN(1) + constexpr float* center_y() noexcept + { + return column<1>(); + } + + /// @brief Returns a pointer to the elements in column [1]: center_y. + SOAGEN_ALIGNED_COLUMN(1) + constexpr const float* center_y() const noexcept + { + return column<1>(); + } + + /// @brief Returns a pointer to the elements in column [2]: center_z. + SOAGEN_ALIGNED_COLUMN(2) + constexpr float* center_z() noexcept + { + return column<2>(); + } + + /// @brief Returns a pointer to the elements in column [2]: center_z. + SOAGEN_ALIGNED_COLUMN(2) + constexpr const float* center_z() const noexcept + { + return column<2>(); + } + + /// @brief Returns a pointer to the elements in column [3]: radius. + SOAGEN_ALIGNED_COLUMN(3) + constexpr float* radius() noexcept + { + return column<3>(); + } + + /// @brief Returns a pointer to the elements in column [3]: radius. + SOAGEN_ALIGNED_COLUMN(3) + constexpr const float* radius() const noexcept + { + return column<3>(); + } + + /// @brief Returns a pointer to the elements in column [4]: mass. + SOAGEN_ALIGNED_COLUMN(4) + constexpr float* mass() noexcept + { + return column<4>(); + } + + /// @brief Returns a pointer to the elements in column [4]: mass. + SOAGEN_ALIGNED_COLUMN(4) + constexpr const float* mass() const noexcept + { + return column<4>(); + } + + /// @brief Invokes a function once for each column data pointer. + /// + /// @tparam Func A callable type compatible with one of the following signatures:
+ /// - `void(auto*, size_type)` + /// - `void(size_type, auto*)` + /// - `void(auto*)` + /// + /// @param func The callable to invoke. + template + constexpr void for_each_column(Func&& func) // + noexcept(table_traits::for_each_column_ptr_nothrow_invocable) + { + soagen::invoke_with_optarg(static_cast(func), this->template column<0>(), size_type{ 0 }); + soagen::invoke_with_optarg(static_cast(func), this->template column<1>(), size_type{ 1 }); + soagen::invoke_with_optarg(static_cast(func), this->template column<2>(), size_type{ 2 }); + soagen::invoke_with_optarg(static_cast(func), this->template column<3>(), size_type{ 3 }); + soagen::invoke_with_optarg(static_cast(func), this->template column<4>(), size_type{ 4 }); + } + + /// @brief Invokes a function once for each column data pointer. + /// + /// @tparam Func A callable type compatible with one of the following signatures:
+ /// - `void(auto*, size_type)` + /// - `void(size_type, auto*)` + /// - `void(auto*)` + /// + /// @param func The callable to invoke. + template + constexpr void for_each_column(Func&& func) const // + noexcept(table_traits::for_each_column_ptr_nothrow_invocable) + { + soagen::invoke_with_optarg(static_cast(func), this->template column<0>(), size_type{ 0 }); + soagen::invoke_with_optarg(static_cast(func), this->template column<1>(), size_type{ 1 }); + soagen::invoke_with_optarg(static_cast(func), this->template column<2>(), size_type{ 2 }); + soagen::invoke_with_optarg(static_cast(func), this->template column<3>(), size_type{ 3 }); + soagen::invoke_with_optarg(static_cast(func), this->template column<4>(), size_type{ 4 }); + } + + /// @} + + /// @name Row access + /// @{ + + /// @brief Returns the row at the given index. + /// + /// @tparam Columns Indices of the columns to include in the row. Leave the list empty for all columns. + template + SOAGEN_PURE_GETTER + SOAGEN_CPP20_CONSTEXPR + soagen::row_type row(size_type index) & noexcept + { + if constexpr (sizeof...(Columns)) + { + return { { this->template column()[index] }... }; + } + else + { + return (*this).template row<0, 1, 2, 3, 4>(index); + } + } + + /// @brief Returns the row at the given index. + SOAGEN_PURE_INLINE_GETTER + SOAGEN_CPP20_CONSTEXPR + row_type operator[](size_type index) & noexcept + { + return (*this).row(index); + } + + /// @brief Returns the row at the given index. + /// + /// @throws std::out_of_range + SOAGEN_PURE_GETTER + SOAGEN_CPP20_CONSTEXPR + row_type at(size_type index) & + { +#if SOAGEN_HAS_EXCEPTIONS + if (index >= size()) + throw std::out_of_range{ "bad element access" }; +#endif + return (*this).row(index); + } + + /// @brief Returns the very first row in the table. + SOAGEN_PURE_INLINE_GETTER + SOAGEN_CPP20_CONSTEXPR + row_type front() & noexcept + { + return (*this).row(0u); + } + + /// @brief Returns the very last row in the table. + SOAGEN_PURE_INLINE_GETTER + SOAGEN_CPP20_CONSTEXPR + row_type back() & noexcept + { + return (*this).row(size() - 1u); + } + + /// @brief Returns the row at the given index. + /// + /// @tparam Columns Indices of the columns to include in the row. Leave the list empty for all columns. + template + SOAGEN_PURE_GETTER + SOAGEN_CPP20_CONSTEXPR + soagen::row_type row(size_type index) && noexcept + { + if constexpr (sizeof...(Columns)) + { + return { { std::move(this->template column()[index]) }... }; + } + else + { + return std::move(*this).template row<0, 1, 2, 3, 4>(index); + } + } + + /// @brief Returns the row at the given index. + SOAGEN_PURE_INLINE_GETTER + SOAGEN_CPP20_CONSTEXPR + rvalue_row_type operator[](size_type index) && noexcept + { + return std::move(*this).row(index); + } + + /// @brief Returns the row at the given index. + /// + /// @throws std::out_of_range + SOAGEN_PURE_GETTER + SOAGEN_CPP20_CONSTEXPR + rvalue_row_type at(size_type index) && + { +#if SOAGEN_HAS_EXCEPTIONS + if (index >= size()) + throw std::out_of_range{ "bad element access" }; +#endif + return std::move(*this).row(index); + } + + /// @brief Returns the very first row in the table. + SOAGEN_PURE_INLINE_GETTER + SOAGEN_CPP20_CONSTEXPR + rvalue_row_type front() && noexcept + { + return std::move(*this).row(0u); + } + + /// @brief Returns the very last row in the table. + SOAGEN_PURE_INLINE_GETTER + SOAGEN_CPP20_CONSTEXPR + rvalue_row_type back() && noexcept + { + return std::move(*this).row(size() - 1u); + } + + /// @brief Returns the row at the given index. + /// + /// @tparam Columns Indices of the columns to include in the row. Leave the list empty for all columns. + template + SOAGEN_PURE_GETTER + SOAGEN_CPP20_CONSTEXPR + soagen::row_type row(size_type index) const& noexcept + { + if constexpr (sizeof...(Columns)) + { + return { { this->template column()[index] }... }; + } + else + { + return (*this).template row<0, 1, 2, 3, 4>(index); + } + } + + /// @brief Returns the row at the given index. + SOAGEN_PURE_INLINE_GETTER + SOAGEN_CPP20_CONSTEXPR + const_row_type operator[](size_type index) const& noexcept + { + return (*this).row(index); + } + + /// @brief Returns the row at the given index. + /// + /// @throws std::out_of_range + SOAGEN_PURE_GETTER + SOAGEN_CPP20_CONSTEXPR + const_row_type at(size_type index) const& + { +#if SOAGEN_HAS_EXCEPTIONS + if (index >= size()) + throw std::out_of_range{ "bad element access" }; +#endif + return (*this).row(index); + } + + /// @brief Returns the very first row in the table. + SOAGEN_PURE_INLINE_GETTER + SOAGEN_CPP20_CONSTEXPR + const_row_type front() const& noexcept + { + return (*this).row(0u); + } + + /// @brief Returns the very last row in the table. + SOAGEN_PURE_INLINE_GETTER + SOAGEN_CPP20_CONSTEXPR + const_row_type back() const& noexcept + { + return (*this).row(size() - 1u); + } + + /// @} + + /// @name Iterators + /// @{ + + /// @brief Returns an iterator to the first row in the table. + template + SOAGEN_PURE_INLINE_GETTER + constexpr soagen::iterator_type begin() & noexcept + { + return { *this, 0 }; + } + + /// @brief Returns an iterator to one-past-the-last row in the table. + template + SOAGEN_PURE_INLINE_GETTER + constexpr soagen::iterator_type end() & noexcept + { + return { *this, static_cast(size()) }; + } + + /// @brief Returns an iterator to the first row in the table. + template + SOAGEN_PURE_INLINE_GETTER + constexpr soagen::iterator_type begin() && noexcept + { + return { std::move(*this), 0 }; + } + + /// @brief Returns an iterator to one-past-the-last row in the table. + template + SOAGEN_PURE_INLINE_GETTER + constexpr soagen::iterator_type end() && noexcept + { + return { std::move(*this), static_cast(size()) }; + } + + /// @brief Returns an iterator to the first row in the table. + template + SOAGEN_PURE_INLINE_GETTER + constexpr soagen::iterator_type begin() const& noexcept + { + return { *this, 0 }; + } + + /// @brief Returns an iterator to one-past-the-last row in the table. + template + SOAGEN_PURE_INLINE_GETTER + constexpr soagen::iterator_type end() const& noexcept + { + return { *this, static_cast(size()) }; + } + + /// @brief Returns an iterator to the first row in the table. + template + SOAGEN_PURE_INLINE_GETTER + constexpr soagen::iterator_type cbegin() const& noexcept + { + return { *this, 0 }; + } + + /// @brief Returns an iterator to one-past-the-last row in the table. + template + SOAGEN_PURE_INLINE_GETTER + constexpr soagen::iterator_type cend() const& noexcept + { + return { *this, static_cast(size()) }; + } + + /// @} + }; + + /// @brief Swaps the contents of two instances of #soagen::examples::spheres. + /// + /// @availability This overload is only available when #soagen::examples::spheres::allocator_type + /// is swappable or non-propagating. + SOAGEN_HIDDEN_CONSTRAINT(sfinae, auto sfinae = soagen::has_swap_member) + SOAGEN_ALWAYS_INLINE + constexpr void swap(spheres& lhs, spheres& rhs) // + noexcept(soagen::has_nothrow_swap_member) + { + lhs.swap(rhs); + } + + /// @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/shapes.natvis b/examples/shapes.natvis new file mode 100644 index 0000000..36605f7 --- /dev/null +++ b/examples/shapes.natvis @@ -0,0 +1,309 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + {{ size={size()} }} + + + size() + capacity() + size_bytes() + + + {{ {*(get_0())}, {*(get_0() + 1)}, {*(get_0() + 2)}, ... }} + {{ {*(get_0())}, {*(get_0() + 1)}, {*(get_0() + 2)} }} + {{ {*(get_0())}, {*(get_0() + 1)} }} + {{ {*(get_0())} }} + + + + size() + get_0() + + + + + + {{ {*(get_1())}, {*(get_1() + 1)}, {*(get_1() + 2)}, ... }} + {{ {*(get_1())}, {*(get_1() + 1)}, {*(get_1() + 2)} }} + {{ {*(get_1())}, {*(get_1() + 1)} }} + {{ {*(get_1())} }} + + + + size() + get_1() + + + + + + {{ {*(get_2())}, {*(get_2() + 1)}, {*(get_2() + 2)}, ... }} + {{ {*(get_2())}, {*(get_2() + 1)}, {*(get_2() + 2)} }} + {{ {*(get_2())}, {*(get_2() + 1)} }} + {{ {*(get_2())} }} + + + + size() + get_2() + + + + + + {{ {*(get_3())}, {*(get_3() + 1)}, {*(get_3() + 2)}, ... }} + {{ {*(get_3())}, {*(get_3() + 1)}, {*(get_3() + 2)} }} + {{ {*(get_3())}, {*(get_3() + 1)} }} + {{ {*(get_3())} }} + + + + size() + get_3() + + + + + + {{ {*(get_4())}, {*(get_4() + 1)}, {*(get_4() + 2)}, ... }} + {{ {*(get_4())}, {*(get_4() + 1)}, {*(get_4() + 2)} }} + {{ {*(get_4())}, {*(get_4() + 1)} }} + {{ {*(get_4())} }} + + + + size() + get_4() + + + + + + {{ {*(get_5())}, {*(get_5() + 1)}, {*(get_5() + 2)}, ... }} + {{ {*(get_5())}, {*(get_5() + 1)}, {*(get_5() + 2)} }} + {{ {*(get_5())}, {*(get_5() + 1)} }} + {{ {*(get_5())} }} + + + + size() + get_5() + + + + + + {{ {*(get_6())}, {*(get_6() + 1)}, {*(get_6() + 2)}, ... }} + {{ {*(get_6())}, {*(get_6() + 1)}, {*(get_6() + 2)} }} + {{ {*(get_6())}, {*(get_6() + 1)} }} + {{ {*(get_6())} }} + + + + size() + get_6() + + + + + + + + + + + + + + {{ {center_x}, {center_y}, {center_z}, {extents_x}, {extents_y}, {extents_z}, {mass} }} + + center_x + center_y + center_z + extents_x + extents_y + extents_z + mass + + + + + + + + + + + + + + + + + + + + + + {{ size={size()} }} + + + size() + capacity() + size_bytes() + + + {{ {*(get_0())}, {*(get_0() + 1)}, {*(get_0() + 2)}, ... }} + {{ {*(get_0())}, {*(get_0() + 1)}, {*(get_0() + 2)} }} + {{ {*(get_0())}, {*(get_0() + 1)} }} + {{ {*(get_0())} }} + + + + size() + get_0() + + + + + + {{ {*(get_1())}, {*(get_1() + 1)}, {*(get_1() + 2)}, ... }} + {{ {*(get_1())}, {*(get_1() + 1)}, {*(get_1() + 2)} }} + {{ {*(get_1())}, {*(get_1() + 1)} }} + {{ {*(get_1())} }} + + + + size() + get_1() + + + + + + {{ {*(get_2())}, {*(get_2() + 1)}, {*(get_2() + 2)}, ... }} + {{ {*(get_2())}, {*(get_2() + 1)}, {*(get_2() + 2)} }} + {{ {*(get_2())}, {*(get_2() + 1)} }} + {{ {*(get_2())} }} + + + + size() + get_2() + + + + + + {{ {*(get_3())}, {*(get_3() + 1)}, {*(get_3() + 2)}, ... }} + {{ {*(get_3())}, {*(get_3() + 1)}, {*(get_3() + 2)} }} + {{ {*(get_3())}, {*(get_3() + 1)} }} + {{ {*(get_3())} }} + + + + size() + get_3() + + + + + + {{ {*(get_4())}, {*(get_4() + 1)}, {*(get_4() + 2)}, ... }} + {{ {*(get_4())}, {*(get_4() + 1)}, {*(get_4() + 2)} }} + {{ {*(get_4())}, {*(get_4() + 1)} }} + {{ {*(get_4())} }} + + + + size() + get_4() + + + + + + + + + + + + + + {{ {center_x}, {center_y}, {center_z}, {radius}, {mass} }} + + center_x + center_y + center_z + radius + mass + + + diff --git a/examples/shapes.toml b/examples/shapes.toml new file mode 100644 index 0000000..eaaa8b2 --- /dev/null +++ b/examples/shapes.toml @@ -0,0 +1,43 @@ +namespace = 'soagen::examples' + +[hpp] +banner = ''' +// 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 +''' +header = ''' +/// @brief Types generated by soagen for example purposes. +/// @attention Nothing in this namespace is required to use soagen! +/// This documentation is to demonstrate what soagen is capable of generating, and is just for exposition. +namespace {% namespace %}{} +''' + +[structs.spheres] +brief = 'A SIMD-friendly bounding sphere container.' +static_variables = [ + { name = 'default_mass', type = 'float', value = 1.0 } +] +variables = [ + { name = 'center_x', type = 'float', alignment = 32 }, + { name = 'center_y', type = 'float', alignment = 32 }, + { name = 'center_z', type = 'float', alignment = 32 }, + { name = 'radius', type = 'float', alignment = 32, default = 0.5 }, + { name = 'mass', type = 'float', default = '{% struct::scope %}default_mass' }, +] + +[structs.boxes] +brief = 'A SIMD-friendly AABB container.' +static_variables = [ + { name = 'default_mass', type = 'float', value = 2.0 } +] +variables = [ + { name = 'center_x', type = 'float', alignment = 32 }, + { name = 'center_y', type = 'float', alignment = 32 }, + { name = 'center_z', type = 'float', alignment = 32 }, + { name = 'extents_x', type = 'float', alignment = 32, default = 0.5 }, + { name = 'extents_y', type = 'float', alignment = 32, default = 0.5 }, + { name = 'extents_z', type = 'float', alignment = 32, default = 0.5 }, + { name = 'mass', type = 'float', default = '{% struct::scope %}default_mass' }, +] diff --git a/meson.build b/meson.build index eac9143..60a8fd7 100644 --- a/meson.build +++ b/meson.build @@ -6,7 +6,7 @@ project( 'soagen', 'cpp', - version : '1.0.0', + version : '0.0.2', meson_version : '>=0.60.0', license : 'MIT', default_options : [ 'cpp_std=c++17', 'b_ndebug=if-release', 'buildtype=release' ] @@ -26,7 +26,7 @@ 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_pedantic = get_option('pedantic') is_subproject = meson.is_subproject() cpp = meson.get_compiler('cpp') @@ -41,6 +41,8 @@ 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 +fs = import('fs') + #----------------------------------------------------------------------------------------------------------------------- # global_args # @@ -56,6 +58,7 @@ global_args = cpp.get_supported_arguments( '-Wno-reserved-macro-identifier', '-Wno-init-list-lifetime', '-fchar8_t', + '-g', # msvc '/bigobj', '/Gy', # function-level linking @@ -67,7 +70,9 @@ global_args = cpp.get_supported_arguments( '/Zc:__cplusplus', '/Zc:inline', '/Zc:externConstexpr', - '/Zc:preprocessor' + '/Zc:preprocessor', + '/Zi', + '/ZH:SHA_256' ) if has_exceptions global_args += cpp.get_supported_arguments('/Zc:throwingNew', '-D_HAS_EXCEPTIONS=1') @@ -134,7 +139,10 @@ global_args += cpp.get_supported_arguments( #----------------------------------------------------------------------------------------------------------------------- global_link_args = [] - +global_link_args += cpp.get_supported_link_arguments( + # msvc + '/DEBUG:FULL', +) if is_release global_link_args += cpp.get_supported_link_arguments( # msvc @@ -157,18 +165,30 @@ if is_pedantic 'werror=true', ] endif +#if is_clang +# global_overrides += ['b_sanitize=address', 'b_lundef=false'] +#endif + +subproject_overrides = [ 'werror=false', 'warning_level=1', 'default_library=static' ] #----------------------------------------------------------------------------------------------------------------------- # subdirectories + dependencies #----------------------------------------------------------------------------------------------------------------------- -subdir('soagen') +cpp_hint = files('cpp.hint') + +subdir('src') 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') +if not meson.is_subproject() + if get_option('build_tests') + subdir('tests') + endif + if get_option('build_examples') + subdir('examples') + endif endif diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..eb2c68e --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,78 @@ +# 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 + +# windows: +# python -m build && twine upload dist/* && rmdir /S /Q dist + +[build-system] +requires = ['setuptools', 'wheel'] +build-backend = 'setuptools.build_meta' + +[project] +name = 'soagen' +requires-python = '>=3.9' +description = 'Struct-of-Arrays generator for C++ projects.' +authors = [{ name = "Mark Gillard", email = "mark.gillard@outlook.com.au" }] +license = { text = 'MIT' } +keywords = [ + 'c++', + 'soa', + 'struct-of-arrays', + 'structure-of-arrays', + 'struct of arrays', + 'structure of arrays', + 'parallel-arrays', + 'parallel arrays', + 'std::vector', +] +classifiers = [ + 'Development Status :: 3 - Alpha', + 'License :: OSI Approved :: MIT License', + 'Programming Language :: C++', + 'Topic :: Software Development :: Code Generators', + 'Topic :: Utilities', +] +dependencies = [ + 'misk >= 0.7.0', + 'tomli', + 'schema != 0.7.5', + 'colorama', + 'trieregex', +] +dynamic = ['version', 'readme'] + +[project.scripts] +soagen = 'soagen:main' + +[project.urls] +Source = 'https://github.com/marzer/soagen' +Tracker = 'https://github.com/marzer/soagen/issues' +Funding = 'https://github.com/sponsors/marzer' + +[tool.setuptools] +zip-safe = true + +[tool.setuptools.dynamic] +version = { file = 'src/soagen/version.txt' } +readme = { file = [ + 'README.md', + 'CHANGELOG.md', +], content-type = 'text/markdown' } + +[tool.setuptools.packages.find] +where = ["src"] +namespaces = true + +[tool.setuptools.package-data] +"*" = ['*.txt', '*.hpp', '.clang-format'] + +[tool.setuptools.exclude-package-data] +"*" = ['meson.build', '.git*'] + +[tool.black] +line-length = 120 +target-version = ['py39'] +skip-string-normalization = true +skip-magic-trailing-comma = true diff --git a/requirements.txt b/requirements.txt deleted file mode 100644 index e8b4e29..0000000 --- a/requirements.txt +++ /dev/null @@ -1,5 +0,0 @@ -misk>=0.7.0 -tomli -schema!=0.7.5 -colorama -trieregex diff --git a/run_yapf.bat b/run_yapf.bat deleted file mode 100644 index a080c58..0000000 --- a/run_yapf.bat +++ /dev/null @@ -1,47 +0,0 @@ -@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 deleted file mode 100644 index 3b3325b..0000000 --- a/setup.py +++ /dev/null @@ -1,89 +0,0 @@ -#!/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 index 8a0e40e..0794761 100644 --- a/soagen.code-workspace +++ b/soagen.code-workspace @@ -33,7 +33,61 @@ "iosfwd": "cpp", "memory": "cpp", "tuple": "cpp", - "typeinfo": "cpp" + "typeinfo": "cpp", + "vector": "cpp", + "map": "cpp", + "set": "cpp", + "xtree": "cpp", + "optional": "cpp", + "span": "cpp", + "stdexcept": "cpp", + "xstring": "cpp", + "algorithm": "cpp", + "array": "cpp", + "cctype": "cpp", + "charconv": "cpp", + "chrono": "cpp", + "clocale": "cpp", + "cmath": "cpp", + "condition_variable": "cpp", + "deque": "cpp", + "format": "cpp", + "forward_list": "cpp", + "fstream": "cpp", + "functional": "cpp", + "future": "cpp", + "iomanip": "cpp", + "ios": "cpp", + "iostream": "cpp", + "istream": "cpp", + "iterator": "cpp", + "list": "cpp", + "locale": "cpp", + "mutex": "cpp", + "ostream": "cpp", + "random": "cpp", + "ranges": "cpp", + "ratio": "cpp", + "regex": "cpp", + "sstream": "cpp", + "stack": "cpp", + "stop_token": "cpp", + "streambuf": "cpp", + "string": "cpp", + "system_error": "cpp", + "thread": "cpp", + "unordered_map": "cpp", + "variant": "cpp", + "xfacet": "cpp", + "xhash": "cpp", + "xiosbase": "cpp", + "xlocale": "cpp", + "xlocbuf": "cpp", + "xlocinfo": "cpp", + "xlocmes": "cpp", + "xlocmon": "cpp", + "xlocnum": "cpp", + "xloctime": "cpp" }, "C_Cpp.default.compileCommands": "builddir\\compile_commands.json", "todo-tree.general.tags": [ @@ -49,6 +103,14 @@ "[ ]", "[x]", "bug:" - ] + ], + "[python]": { + "editor.defaultFormatter": "ms-python.black-formatter" + }, + "python.formatting.provider": "none", + "doxdocgen.c.firstLine": "///", + "doxdocgen.c.lastLine": "///", + "doxdocgen.c.commentPrefix": "///", + "doxdocgen.c.triggerSequence": "///" } } diff --git a/soagen/column.py b/soagen/column.py deleted file mode 100644 index 542a5d9..0000000 --- a/soagen/column.py +++ /dev/null @@ -1,146 +0,0 @@ -#!/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 deleted file mode 100644 index 294cf98..0000000 --- a/soagen/config.py +++ /dev/null @@ -1,155 +0,0 @@ -#!/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 deleted file mode 100644 index 02a6a2e..0000000 --- a/soagen/configurable.py +++ /dev/null @@ -1,43 +0,0 @@ -#!/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/hpp/column_traits.hpp b/soagen/data/hpp/column_traits.hpp deleted file mode 100644 index cf4683a..0000000 --- a/soagen/data/hpp/column_traits.hpp +++ /dev/null @@ -1,473 +0,0 @@ -//# 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/soagen.hpp b/soagen/data/hpp/soagen.hpp deleted file mode 100644 index 5f742b0..0000000 --- a/soagen/data/hpp/soagen.hpp +++ /dev/null @@ -1,4233 +0,0 @@ -//---------------------------------------------------------------------------------------------------------------------- -// -// soagen.hpp -// https://github.com/marzer/soagen -// SPDX-License-Identifier: MIT -// -//---------------------------------------------------------------------------------------------------------------------- -// !!!!! THIS FILE WAS ASSEMBLED FROM MULTIPLE HEADER FILES BY A SCRIPT - PLEASE DON'T EDIT IT DIRECTLY !!!!! -//---------------------------------------------------------------------------------------------------------------------- -// -// 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. -// -//---------------------------------------------------------------------------------------------------------------------- -#ifndef SOAGEN_HPP -#define SOAGEN_HPP - -//******** preprocessor.hpp ****************************************************************************************** - -#ifndef SOAGEN_CPP - #ifdef _MSVC_LANG - #if _MSVC_LANG > __cplusplus - #define SOAGEN_CPP _MSVC_LANG - #endif - #endif - #ifndef SOAGEN_CPP - #define SOAGEN_CPP __cplusplus - #endif - #if SOAGEN_CPP >= 202600L - #undef SOAGEN_CPP - #define SOAGEN_CPP 26 - #elif SOAGEN_CPP >= 202300L - #undef SOAGEN_CPP - #define SOAGEN_CPP 23 - #elif SOAGEN_CPP >= 202002L - #undef SOAGEN_CPP - #define SOAGEN_CPP 20 - #elif SOAGEN_CPP >= 201703L - #undef SOAGEN_CPP - #define SOAGEN_CPP 17 - #elif SOAGEN_CPP >= 201402L - #undef SOAGEN_CPP - #define SOAGEN_CPP 14 - #elif SOAGEN_CPP >= 201103L - #undef SOAGEN_CPP - #define SOAGEN_CPP 11 - #else - #undef SOAGEN_CPP - #define SOAGEN_CPP 0 - #endif -#endif - -#ifndef SOAGEN_MAKE_VERSION - #define SOAGEN_MAKE_VERSION(major, minor, patch) (((major)*10000) + ((minor)*100) + ((patch))) -#endif - -#ifndef SOAGEN_INTELLISENSE - #ifdef __INTELLISENSE__ - #define SOAGEN_INTELLISENSE 1 - #else - #define SOAGEN_INTELLISENSE 0 - #endif -#endif - -#ifndef SOAGEN_DOXYGEN - #if defined(DOXYGEN) || defined(__DOXYGEN) || defined(__DOXYGEN__) || defined(__doxygen__) || defined(__POXY__) \ - || defined(__poxy__) - #define SOAGEN_DOXYGEN 1 - #else - #define SOAGEN_DOXYGEN 0 - #endif -#endif - -#ifndef SOAGEN_CLANG - #ifdef __clang__ - #define SOAGEN_CLANG __clang_major__ - #else - #define SOAGEN_CLANG 0 - #endif - - // special handling for apple clang; see: - // - https://github.com/marzer/tomlplusplus/issues/189 - // - https://en.wikipedia.org/wiki/Xcode - // - - // https://stackoverflow.com/questions/19387043/how-can-i-reliably-detect-the-version-of-clang-at-preprocessing-time - #if SOAGEN_CLANG && defined(__apple_build_version__) - #undef SOAGEN_CLANG - #define SOAGEN_CLANG_VERSION SOAGEN_MAKE_VERSION(__clang_major__, __clang_minor__, __clang_patchlevel__) - #if SOAGEN_CLANG_VERSION >= SOAGEN_MAKE_VERSION(15, 0, 0) - #define SOAGEN_CLANG 16 - #elif SOAGEN_CLANG_VERSION >= SOAGEN_MAKE_VERSION(14, 3, 0) - #define SOAGEN_CLANG 15 - #elif SOAGEN_CLANG_VERSION >= SOAGEN_MAKE_VERSION(14, 0, 0) - #define SOAGEN_CLANG 14 - #elif SOAGEN_CLANG_VERSION >= SOAGEN_MAKE_VERSION(13, 1, 6) - #define SOAGEN_CLANG 13 - #elif SOAGEN_CLANG_VERSION >= SOAGEN_MAKE_VERSION(13, 0, 0) - #define SOAGEN_CLANG 12 - #elif SOAGEN_CLANG_VERSION >= SOAGEN_MAKE_VERSION(12, 0, 5) - #define SOAGEN_CLANG 11 - #elif SOAGEN_CLANG_VERSION >= SOAGEN_MAKE_VERSION(12, 0, 0) - #define SOAGEN_CLANG 10 - #elif SOAGEN_CLANG_VERSION >= SOAGEN_MAKE_VERSION(11, 0, 3) - #define SOAGEN_CLANG 9 - #elif SOAGEN_CLANG_VERSION >= SOAGEN_MAKE_VERSION(11, 0, 0) - #define SOAGEN_CLANG 8 - #elif SOAGEN_CLANG_VERSION >= SOAGEN_MAKE_VERSION(10, 0, 1) - #define SOAGEN_CLANG 7 - #else - #define SOAGEN_CLANG 6 // not strictly correct but doesn't matter below this - #endif - #undef SOAGEN_CLANG_VERSION - #endif -#endif - -#ifndef SOAGEN_ICC - #ifdef __INTEL_COMPILER - #define SOAGEN_ICC __INTEL_COMPILER - #ifdef __ICL - #define SOAGEN_ICC_CL SOAGEN_ICC - #else - #define SOAGEN_ICC_CL 0 - #endif - #else - #define SOAGEN_ICC 0 - #define SOAGEN_ICC_CL 0 - #endif -#endif - -#ifndef SOAGEN_MSVC_LIKE - #ifdef _MSC_VER - #define SOAGEN_MSVC_LIKE _MSC_VER - #else - #define SOAGEN_MSVC_LIKE 0 - #endif -#endif - -#ifndef SOAGEN_MSVC - #if SOAGEN_MSVC_LIKE && !SOAGEN_CLANG && !SOAGEN_ICC - #define SOAGEN_MSVC SOAGEN_MSVC_LIKE - #else - #define SOAGEN_MSVC 0 - #endif -#endif - -#ifndef SOAGEN_GCC_LIKE - #ifdef __GNUC__ - #define SOAGEN_GCC_LIKE __GNUC__ - #else - #define SOAGEN_GCC_LIKE 0 - #endif -#endif - -#ifndef SOAGEN_GCC - #if SOAGEN_GCC_LIKE && !SOAGEN_CLANG && !SOAGEN_ICC - #define SOAGEN_GCC SOAGEN_GCC_LIKE - #else - #define SOAGEN_GCC 0 - #endif -#endif - -#ifndef SOAGEN_CUDA - #if defined(__CUDACC__) || defined(__CUDA_ARCH__) || defined(__CUDA_LIBDEVICE__) - #define SOAGEN_CUDA 1 - #else - #define SOAGEN_CUDA 0 - #endif -#endif - -#ifndef SOAGEN_ARCH_ITANIUM - #if defined(__ia64__) || defined(__ia64) || defined(_IA64) || defined(__IA64__) || defined(_M_IA64) - #define SOAGEN_ARCH_ITANIUM 1 - #define SOAGEN_ARCH_BITNESS 64 - #else - #define SOAGEN_ARCH_ITANIUM 0 - #endif -#endif - -#ifndef SOAGEN_ARCH_AMD64 - #if defined(__amd64__) || defined(__amd64) || defined(__x86_64__) || defined(__x86_64) || defined(_M_AMD64) - #define SOAGEN_ARCH_AMD64 1 - #define SOAGEN_ARCH_BITNESS 64 - #else - #define SOAGEN_ARCH_AMD64 0 - #endif -#endif - -#ifndef SOAGEN_ARCH_X86 - #if defined(__i386__) || defined(_M_IX86) - #define SOAGEN_ARCH_X86 1 - #define SOAGEN_ARCH_BITNESS 32 - #else - #define SOAGEN_ARCH_X86 0 - #endif -#endif - -#ifndef SOAGEN_ARCH_ARM - #if defined(__aarch64__) || defined(__ARM_ARCH_ISA_A64) || defined(_M_ARM64) || defined(__ARM_64BIT_STATE) \ - || defined(_M_ARM64EC) - #define SOAGEN_ARCH_ARM32 0 - #define SOAGEN_ARCH_ARM64 1 - #define SOAGEN_ARCH_ARM 1 - #define SOAGEN_ARCH_BITNESS 64 - #elif defined(__arm__) || defined(_M_ARM) || defined(__ARM_32BIT_STATE) - #define SOAGEN_ARCH_ARM32 1 - #define SOAGEN_ARCH_ARM64 0 - #define SOAGEN_ARCH_ARM 1 - #define SOAGEN_ARCH_BITNESS 32 - #else - #define SOAGEN_ARCH_ARM32 0 - #define SOAGEN_ARCH_ARM64 0 - #define SOAGEN_ARCH_ARM 0 - #endif -#endif - -#ifndef SOAGEN_ARCH_BITNESS - #define SOAGEN_ARCH_BITNESS 0 -#endif - -#ifndef SOAGEN_ARCH_X64 - #if SOAGEN_ARCH_BITNESS == 64 - #define SOAGEN_ARCH_X64 1 - #else - #define SOAGEN_ARCH_X64 0 - #endif -#endif - -#ifdef _WIN32 - #define SOAGEN_WINDOWS 1 -#else - #define SOAGEN_WINDOWS 0 -#endif -#ifdef __unix__ - #define SOAGEN_UNIX 1 -#else - #define SOAGEN_UNIX 0 -#endif -#ifdef __linux__ - #define SOAGEN_LINUX 1 -#else - #define SOAGEN_LINUX 0 -#endif - -#ifndef SOAGEN_HAS_INCLUDE - #ifdef __has_include - #define SOAGEN_HAS_INCLUDE(header) __has_include(header) - #else - #define SOAGEN_HAS_INCLUDE(header) 0 - #endif -#endif - -#if SOAGEN_CPP >= 20 && SOAGEN_HAS_INCLUDE() - #include -#endif - -#ifndef SOAGEN_HAS_BUILTIN - #ifdef __has_builtin - #define SOAGEN_HAS_BUILTIN(name) __has_builtin(name) - #else - #define SOAGEN_HAS_BUILTIN(name) 0 - #endif -#endif - -#ifndef SOAGEN_HAS_ATTR - #ifdef __has_attribute - #define SOAGEN_HAS_ATTR(attr) __has_attribute(attr) - #else - #define SOAGEN_HAS_ATTR(attr) 0 - #endif -#endif - -#ifndef SOAGEN_HAS_CPP_ATTR - #ifdef __has_cpp_attribute - #define SOAGEN_HAS_CPP_ATTR(attr) __has_cpp_attribute(attr) - #else - #define SOAGEN_HAS_CPP_ATTR(attr) 0 - #endif -#endif - -#ifndef SOAGEN_ATTR - #if SOAGEN_CLANG || SOAGEN_GCC_LIKE - #define SOAGEN_ATTR(...) __attribute__((__VA_ARGS__)) - #else - #define SOAGEN_ATTR(...) - #endif -#endif - -#ifndef SOAGEN_DECLSPEC - #if SOAGEN_MSVC_LIKE - #define SOAGEN_DECLSPEC(...) __declspec(__VA_ARGS__) - #else - #define SOAGEN_DECLSPEC(...) - #endif -#endif - -#ifndef SOAGEN_ALWAYS_INLINE - #if SOAGEN_MSVC_LIKE - #define SOAGEN_ALWAYS_INLINE __forceinline - #elif SOAGEN_CLANG || SOAGEN_GCC_LIKE || SOAGEN_HAS_ATTR(__always_inline__) - #define SOAGEN_ALWAYS_INLINE \ - SOAGEN_ATTR(__always_inline__) \ - inline - #else - #define SOAGEN_ALWAYS_INLINE inline - #endif -#endif - -#ifndef SOAGEN_NEVER_INLINE - #if SOAGEN_MSVC_LIKE - #define SOAGEN_NEVER_INLINE SOAGEN_DECLSPEC(noinline) - #elif SOAGEN_CUDA // https://gitlab.gnome.org/GNOME/glib/-/issues/2555 - #define SOAGEN_NEVER_INLINE SOAGEN_ATTR(noinline) - #else - #if SOAGEN_GCC || SOAGEN_CLANG || SOAGEN_HAS_ATTR(__noinline__) - #define SOAGEN_NEVER_INLINE SOAGEN_ATTR(__noinline__) - #endif - #endif - #ifndef SOAGEN_NEVER_INLINE - #define SOAGEN_NEVER_INLINE - #endif -#endif - -#ifndef SOAGEN_EMPTY_BASES - #if SOAGEN_MSVC_LIKE - #define SOAGEN_EMPTY_BASES SOAGEN_DECLSPEC(empty_bases) - #else - #define SOAGEN_EMPTY_BASES - #endif -#endif - -#ifndef SOAGEN_LIKELY - #if SOAGEN_CPP >= 20 && SOAGEN_HAS_CPP_ATTR(likely) >= 201803 - #define SOAGEN_LIKELY(...) (__VA_ARGS__) [[likely]] - #elif SOAGEN_CLANG || SOAGEN_GCC_LIKE || SOAGEN_HAS_BUILTIN(__builtin_expect) - #define SOAGEN_LIKELY(...) (__builtin_expect(!!(__VA_ARGS__), 1)) - #else - #define SOAGEN_LIKELY(...) (__VA_ARGS__) - #endif -#endif - -#ifndef SOAGEN_UNLIKELY - #if SOAGEN_CPP >= 20 && SOAGEN_HAS_CPP_ATTR(unlikely) >= 201803 - #define SOAGEN_UNLIKELY(...) (__VA_ARGS__) [[unlikely]] - #elif SOAGEN_CLANG || SOAGEN_GCC_LIKE || SOAGEN_HAS_BUILTIN(__builtin_expect) - #define SOAGEN_UNLIKELY(...) (__builtin_expect(!!(__VA_ARGS__), 0)) - #else - #define SOAGEN_UNLIKELY(...) (__VA_ARGS__) - #endif -#endif - -#ifndef SOAGEN_NODISCARD - #if SOAGEN_HAS_CPP_ATTR(nodiscard) >= 201603 - #define SOAGEN_NODISCARD [[nodiscard]] - #define SOAGEN_NODISCARD_CLASS [[nodiscard]] - #elif SOAGEN_CLANG || SOAGEN_GCC_LIKE || SOAGEN_HAS_ATTR(__warn_unused_result__) - #define SOAGEN_NODISCARD SOAGEN_ATTR(__warn_unused_result__) - #else - #define SOAGEN_NODISCARD - #endif - #ifndef SOAGEN_NODISCARD_CLASS - #define SOAGEN_NODISCARD_CLASS - #endif - #if SOAGEN_HAS_CPP_ATTR(nodiscard) >= 201907 - #define SOAGEN_NODISCARD_CTOR [[nodiscard]] - #else - #define SOAGEN_NODISCARD_CTOR - #endif -#endif - -// clang-format off -#ifndef SOAGEN_PURE_GETTER - #define SOAGEN_INLINE_GETTER SOAGEN_NODISCARD SOAGEN_ALWAYS_INLINE - #ifdef NDEBUG - #define SOAGEN_PURE SOAGEN_DECLSPEC(noalias) SOAGEN_ATTR(pure) - #define SOAGEN_CONST SOAGEN_DECLSPEC(noalias) SOAGEN_ATTR(const) - #define SOAGEN_PURE_GETTER SOAGEN_NODISCARD SOAGEN_PURE - #define SOAGEN_CONST_GETTER SOAGEN_NODISCARD SOAGEN_CONST - #define SOAGEN_PURE_INLINE_GETTER SOAGEN_NODISCARD SOAGEN_ALWAYS_INLINE SOAGEN_PURE - #define SOAGEN_CONST_INLINE_GETTER SOAGEN_NODISCARD SOAGEN_ALWAYS_INLINE SOAGEN_CONST - #else - #define SOAGEN_PURE - #define SOAGEN_CONST - #define SOAGEN_PURE_GETTER SOAGEN_NODISCARD - #define SOAGEN_CONST_GETTER SOAGEN_NODISCARD - #define SOAGEN_PURE_INLINE_GETTER SOAGEN_NODISCARD SOAGEN_ALWAYS_INLINE - #define SOAGEN_CONST_INLINE_GETTER SOAGEN_NODISCARD SOAGEN_ALWAYS_INLINE - #endif -#endif -// clang-format on - -#ifndef SOAGEN_NO_UNIQUE_ADDRESS - #if SOAGEN_MSVC >= 1929 - #define SOAGEN_NO_UNIQUE_ADDRESS [[msvc::no_unique_address]] - #elif SOAGEN_CPP >= 20 && SOAGEN_HAS_CPP_ATTR(no_unique_address) >= 201803 - #define SOAGEN_NO_UNIQUE_ADDRESS [[no_unique_address]] - #else - #define SOAGEN_NO_UNIQUE_ADDRESS - #endif -#endif - -#ifndef SOAGEN_COLUMN - #define SOAGEN_COLUMN(I) \ - SOAGEN_PURE_INLINE_GETTER \ - SOAGEN_ATTR(returns_nonnull) -#endif - -#ifndef SOAGEN_ALIGNED_COLUMN - #define SOAGEN_ALIGNED_COLUMN(I) \ - SOAGEN_COLUMN(I) \ - SOAGEN_ATTR(assume_aligned(soagen::detail::actual_column_alignment)) -#endif - -#if defined(__cpp_constexpr) && __cpp_constexpr >= 202002L - #define SOAGEN_CPP20_CONSTEXPR constexpr -#else - #define SOAGEN_CPP20_CONSTEXPR -#endif - -#ifndef SOAGEN_CONCEPTS - #if defined(__cpp_concepts) && __cpp_concepts >= 201907 - #define SOAGEN_CONCEPTS 1 - #else - #define SOAGEN_CONCEPTS 0 - #endif -#endif - -#ifndef SOAGEN_REQUIRES - #if !SOAGEN_DOXYGEN && SOAGEN_CONCEPTS - #define SOAGEN_REQUIRES(...) requires(__VA_ARGS__) - #else - #define SOAGEN_REQUIRES(...) - #endif -#endif - -#ifndef SOAGEN_STD_CONCEPT - #if !SOAGEN_DOXYGEN && defined(__cpp_lib_concepts) && __cpp_lib_concepts >= 202002 - #define SOAGEN_STD_CONCEPT(...) __VA_ARGS__ - #else - #define SOAGEN_STD_CONCEPT(...) true - #endif -#endif - -#ifndef SOAGEN_ENABLE_IF - #if !SOAGEN_DOXYGEN - #define SOAGEN_ENABLE_IF(...) , typename std::enable_if::type = 0 - #else - #define SOAGEN_ENABLE_IF(...) - #endif -#endif - -#ifndef SOAGEN_CONSTRAINED_TEMPLATE - #if !SOAGEN_DOXYGEN - #define SOAGEN_CONSTRAINED_TEMPLATE(condition, ...) \ - template <__VA_ARGS__ SOAGEN_ENABLE_IF(!!(condition))> \ - SOAGEN_REQUIRES(!!(condition)) - #else - #define SOAGEN_CONSTRAINED_TEMPLATE(condition, ...) template <__VA_ARGS__> - #endif -#endif - -#ifndef SOAGEN_HIDDEN_CONSTRAINT - #if !SOAGEN_DOXYGEN - #define SOAGEN_HIDDEN_CONSTRAINT(condition, ...) SOAGEN_CONSTRAINED_TEMPLATE(condition, __VA_ARGS__) - #else - #define SOAGEN_HIDDEN_CONSTRAINT(condition, ...) - #endif -#endif - -#ifndef SOAGEN_CONSTRAINED_COLUMN - #define SOAGEN_CONSTRAINED_COLUMN(I, ...) \ - SOAGEN_CONSTRAINED_TEMPLATE(sfinae_col_idx == (I) && (__VA_ARGS__), size_t sfinae_col_idx = (I)) -#endif -#ifndef SOAGEN_CONSTRAINED_COLUMN - #define SOAGEN_CONSTRAINED_COLUMN(I, ...) -#endif - -#ifndef SOAGEN_PUSH_WARNINGS - #if SOAGEN_CLANG - #define SOAGEN_PUSH_WARNINGS \ - _Pragma("clang diagnostic push") \ - static_assert(true) - #elif SOAGEN_MSVC || SOAGEN_ICC - #define SOAGEN_PUSH_WARNINGS \ - __pragma(warning(push)) \ - static_assert(true) - #elif SOAGEN_GCC - #define SOAGEN_PUSH_WARNINGS \ - _Pragma("GCC diagnostic push") \ - static_assert(true) - #else - #define SOAGEN_PUSH_WARNINGS static_assert(true) - #endif -#endif - -#ifndef SOAGEN_POP_WARNINGS - #if SOAGEN_CLANG - #define SOAGEN_POP_WARNINGS \ - _Pragma("clang diagnostic pop") \ - static_assert(true) - #elif SOAGEN_MSVC || SOAGEN_ICC - #define SOAGEN_POP_WARNINGS \ - __pragma(warning(pop)) \ - static_assert(true) - #elif SOAGEN_GCC - #define SOAGEN_POP_WARNINGS \ - _Pragma("GCC diagnostic pop") \ - static_assert(true) - #else - #define SOAGEN_POP_WARNINGS static_assert(true) - #endif -#endif - -#ifndef SOAGEN_DISABLE_SWITCH_WARNINGS - #if SOAGEN_CLANG - #define SOAGEN_DISABLE_SWITCH_WARNINGS \ - _Pragma("clang diagnostic ignored \"-Wswitch\"") \ - _Pragma("clang diagnostic ignored \"-Wcovered-switch-default\"") \ - static_assert(true) - #elif SOAGEN_MSVC - #define SOAGEN_DISABLE_SWITCH_WARNINGS \ - __pragma(warning(disable : 4061)) \ - __pragma(warning(disable : 4062)) \ - __pragma(warning(disable : 4063)) \ - __pragma(warning(disable : 4468)) /* 'fallthrough': attribute must be followed by a case label */ \ - __pragma(warning(disable : 5262)) /* implicit through */ \ - __pragma(warning(disable : 26819)) /* cg: unannotated fallthrough */ \ - static_assert(true) - #elif SOAGEN_GCC - #define SOAGEN_DISABLE_SWITCH_WARNINGS \ - _Pragma("GCC diagnostic ignored \"-Wswitch\"") \ - _Pragma("GCC diagnostic ignored \"-Wswitch-enum\"") \ - _Pragma("GCC diagnostic ignored \"-Wswitch-default\"") \ - static_assert(true) - #else - #define SOAGEN_DISABLE_SWITCH_WARNINGS static_assert(true) - #endif -#endif - -#ifndef SOAGEN_DISABLE_LIFETIME_WARNINGS - #if SOAGEN_CLANG - #define SOAGEN_DISABLE_LIFETIME_WARNINGS \ - _Pragma("clang diagnostic ignored \"-Wmissing-field-initializers\"") \ - _Pragma("clang diagnostic ignored \"-Wglobal-constructors\"") \ - _Pragma("clang diagnostic ignored \"-Wexit-time-destructors\"") \ - static_assert(true) - #elif SOAGEN_GCC - #if SOAGEN_GCC >= 8 - #define SOAGEN_DISABLE_LIFETIME_WARNINGS_GCC_8 \ - _Pragma("GCC diagnostic ignored \"-Wclass-memaccess\"") \ - static_assert(true) - #else - #define SOAGEN_DISABLE_LIFETIME_WARNINGS_GCC_8 static_assert(true) - #endif - #define SOAGEN_DISABLE_LIFETIME_WARNINGS \ - _Pragma("GCC diagnostic ignored \"-Wmissing-field-initializers\"") \ - _Pragma("GCC diagnostic ignored \"-Wmaybe-uninitialized\"") \ - _Pragma("GCC diagnostic ignored \"-Wuninitialized\"") \ - SOAGEN_DISABLE_LIFETIME_WARNINGS_GCC_8; \ - static_assert(true) - #else - #define SOAGEN_DISABLE_LIFETIME_WARNINGS static_assert(true) - #endif -#endif - -#ifndef SOAGEN_DISABLE_ARITHMETIC_WARNINGS - #if SOAGEN_CLANG - #if SOAGEN_CLANG >= 10 - #define SOAGEN_DISABLE_ARITHMETIC_WARNINGS_CLANG_10 \ - _Pragma("clang diagnostic ignored \"-Wimplicit-int-float-conversion\"") \ - static_assert(true) - #else - #define SOAGEN_DISABLE_ARITHMETIC_WARNINGS_CLANG_10 static_assert(true) - #endif - #define SOAGEN_DISABLE_ARITHMETIC_WARNINGS \ - _Pragma("clang diagnostic ignored \"-Wfloat-equal\"") \ - _Pragma("clang diagnostic ignored \"-Wdouble-promotion\"") \ - _Pragma("clang diagnostic ignored \"-Wchar-subscripts\"") \ - _Pragma("clang diagnostic ignored \"-Wshift-sign-overflow\"") \ - SOAGEN_DISABLE_ARITHMETIC_WARNINGS_CLANG_10; \ - static_assert(true) - #elif SOAGEN_MSVC - #define SOAGEN_DISABLE_ARITHMETIC_WARNINGS \ - __pragma(warning(disable : 4365)) /* argument signed/unsigned mismatch */ \ - __pragma(warning(disable : 4738)) /* storing 32-bit float result in memory */ \ - __pragma(warning(disable : 5219)) /* implicit conversion from integral to float */ \ - static_assert(true) - #elif SOAGEN_GCC - #define SOAGEN_DISABLE_ARITHMETIC_WARNINGS \ - _Pragma("GCC diagnostic ignored \"-Wfloat-equal\"") \ - _Pragma("GCC diagnostic ignored \"-Wsign-conversion\"") \ - _Pragma("GCC diagnostic ignored \"-Wchar-subscripts\"") \ - static_assert(true) - #else - #define SOAGEN_DISABLE_ARITHMETIC_WARNINGS static_assert(true) - #endif -#endif - -#ifndef SOAGEN_DISABLE_SHADOW_WARNINGS - #if SOAGEN_CLANG - #define SOAGEN_DISABLE_SHADOW_WARNINGS \ - _Pragma("clang diagnostic ignored \"-Wshadow\"") \ - _Pragma("clang diagnostic ignored \"-Wshadow-field\"") \ - static_assert(true) - #elif SOAGEN_MSVC - #define SOAGEN_DISABLE_SHADOW_WARNINGS \ - __pragma(warning(disable : 4458)) \ - static_assert(true) - #elif SOAGEN_GCC - #define SOAGEN_DISABLE_SHADOW_WARNINGS \ - _Pragma("GCC diagnostic ignored \"-Wshadow\"") \ - static_assert(true) - #else - #define SOAGEN_DISABLE_SHADOW_WARNINGS static_assert(true) - #endif -#endif - -#ifndef SOAGEN_DISABLE_SPAM_WARNINGS - #if SOAGEN_CLANG - #if SOAGEN_CLANG >= 8 - #define SOAGEN_DISABLE_SPAM_WARNINGS_CLANG_8 \ - _Pragma("clang diagnostic ignored \"-Wdefaulted-function-deleted\"") \ - static_assert(true) - #else - #define SOAGEN_DISABLE_SPAM_WARNINGS_CLANG_8 static_assert(true) - #endif - #if SOAGEN_CLANG >= 9 - #define SOAGEN_DISABLE_SPAM_WARNINGS_CLANG_9 \ - _Pragma("clang diagnostic ignored \"-Wctad-maybe-unsupported\"") \ - static_assert(true) - #else - #define SOAGEN_DISABLE_SPAM_WARNINGS_CLANG_9 static_assert(true) - #endif - #if SOAGEN_CLANG >= 13 - #define SOAGEN_DISABLE_SPAM_WARNINGS_CLANG_13 \ - _Pragma("clang diagnostic ignored \"-Wc++20-compat\"") \ - static_assert(true) - #else - #define SOAGEN_DISABLE_SPAM_WARNINGS_CLANG_13 static_assert(true) - #endif - #define SOAGEN_DISABLE_SPAM_WARNINGS \ - _Pragma("clang diagnostic ignored \"-Wc++98-compat-pedantic\"") \ - _Pragma("clang diagnostic ignored \"-Wc++98-compat\"") \ - _Pragma("clang diagnostic ignored \"-Wweak-vtables\"") \ - _Pragma("clang diagnostic ignored \"-Wdouble-promotion\"") \ - _Pragma("clang diagnostic ignored \"-Wweak-template-vtables\"") \ - _Pragma("clang diagnostic ignored \"-Wpadded\"") \ - _Pragma("clang diagnostic ignored \"-Wc++2a-compat\"") \ - _Pragma("clang diagnostic ignored \"-Wtautological-pointer-compare\"") \ - _Pragma("clang diagnostic ignored \"-Wmissing-field-initializers\"") \ - _Pragma("clang diagnostic ignored \"-Wpacked\"") \ - _Pragma("clang diagnostic ignored \"-Wdisabled-macro-expansion\"") \ - _Pragma("clang diagnostic ignored \"-Wused-but-marked-unused\"") \ - _Pragma("clang diagnostic ignored \"-Wcovered-switch-default\"") \ - _Pragma("clang diagnostic ignored \"-Wtautological-pointer-compare\"") \ - SOAGEN_DISABLE_SPAM_WARNINGS_CLANG_8; \ - SOAGEN_DISABLE_SPAM_WARNINGS_CLANG_9; \ - SOAGEN_DISABLE_SPAM_WARNINGS_CLANG_13; \ - static_assert(true) - #elif SOAGEN_MSVC - #define SOAGEN_DISABLE_SPAM_WARNINGS \ - __pragma(warning(disable : 4127)) /* conditional expr is constant */ \ - __pragma(warning(disable : 4324)) /* structure was padded due to alignment specifier */ \ - __pragma(warning(disable : 4348)) \ - __pragma(warning(disable : 4464)) /* relative include path contains '..' */ \ - __pragma(warning(disable : 4505)) /* unreferenced local function removed */ \ - __pragma(warning(disable : 4514)) /* unreferenced inline function has been removed */ \ - __pragma(warning(disable : 4582)) /* constructor is not implicitly called */ \ - __pragma(warning(disable : 4619)) /* there is no warning number 'XXXX' */ \ - __pragma(warning(disable : 4623)) /* default constructor was implicitly defined as deleted */ \ - __pragma(warning(disable : 4625)) /* copy constructor was implicitly defined as deleted */ \ - __pragma(warning(disable : 4626)) /* assignment operator was implicitly defined as deleted */ \ - __pragma(warning(disable : 4686)) /* possible change in behavior, change in UDT return calling convention \ - * \ \ \ - * \ \ \ - * \ \ - */ \ - __pragma(warning(disable : 4710)) /* function not inlined */ \ - __pragma(warning(disable : 4711)) /* function selected for automatic expansion */ \ - __pragma(warning(disable : 4820)) /* N bytes padding added */ \ - __pragma(warning(disable : 4866)) /* compiler may not enforce left-to-right evaluation order for call */ \ - __pragma(warning(disable : 4946)) /* reinterpret_cast used between related classes */ \ - __pragma(warning(disable : 5026)) /* move constructor was implicitly defined as deleted */ \ - __pragma(warning(disable : 5027)) /* move assignment operator was implicitly defined as deleted */ \ - __pragma(warning(disable : 5039)) /* potentially throwing function passed to 'extern "C"' function */ \ - __pragma(warning(disable : 5045)) /* Compiler will insert Spectre mitigation */ \ - __pragma(warning(disable : 5246)) /* initialization of a subobject should be wrapped in braces */ \ - __pragma(warning(disable : 5264)) /* const variable is not used (false-positive) */ \ - __pragma(warning(disable : 26490)) /* cg: dont use reinterpret_cast */ \ - __pragma(warning(disable : 26812)) /* cg: Prefer 'enum class' over 'enum' */ \ - __pragma(warning(disable : 4848)) /* msvc::no_unique_address in C++17 is a vendor extension */ \ - static_assert(true) - #elif SOAGEN_ICC - #define SOAGEN_DISABLE_SPAM_WARNINGS \ - __pragma(warning(disable : 82)) /* storage class is not first */ \ - __pragma(warning(disable : 111)) /* statement unreachable (false-positive) */ \ - __pragma(warning(disable : 869)) /* unreferenced parameter */ \ - __pragma(warning(disable : 1011)) /* missing return (false-positive) */ \ - __pragma(warning(disable : 2261)) /* assume expr side-effects discarded */ \ - static_assert(true) - #elif SOAGEN_GCC - #if SOAGEN_GCC >= 9 - #define SOAGEN_DISABLE_SPAM_WARNINGS_GCC_9 \ - _Pragma("GCC diagnostic ignored \"-Wattributes\"") \ - static_assert(true) - #else - #define SOAGEN_DISABLE_SPAM_WARNINGS_GCC_9 static_assert(true) - #endif - #if SOAGEN_GCC >= 12 - #define SOAGEN_DISABLE_SPAM_WARNINGS_GCC_12 \ - _Pragma("GCC diagnostic ignored \"-Winterference-size\"") \ - static_assert(true) - #else - #define SOAGEN_DISABLE_SPAM_WARNINGS_GCC_12 static_assert(true) - #endif - #define SOAGEN_DISABLE_SPAM_WARNINGS \ - _Pragma("GCC diagnostic ignored \"-Wpadded\"") \ - _Pragma("GCC diagnostic ignored \"-Wcast-align\"") \ - _Pragma("GCC diagnostic ignored \"-Wcomment\"") \ - _Pragma("GCC diagnostic ignored \"-Wsubobject-linkage\"") \ - _Pragma("GCC diagnostic ignored \"-Wuseless-cast\"") \ - _Pragma("GCC diagnostic ignored \"-Wmissing-field-initializers\"") \ - _Pragma("GCC diagnostic ignored \"-Wmaybe-uninitialized\"") \ - _Pragma("GCC diagnostic ignored \"-Wtype-limits\"") \ - SOAGEN_DISABLE_SPAM_WARNINGS_GCC_9; \ - SOAGEN_DISABLE_SPAM_WARNINGS_GCC_12; \ - static_assert(true) - #else - #define SOAGEN_DISABLE_SPAM_WARNINGS static_assert(true) - #endif -#endif - -#ifndef SOAGEN_DISABLE_DEPRECATION_WARNINGS - #if SOAGEN_MSVC - #define SOAGEN_DISABLE_DEPRECATION_WARNINGS \ - __pragma(warning(disable : 4996)) \ - static_assert(true) - #else - #define SOAGEN_DISABLE_DEPRECATION_WARNINGS static_assert(true) - #endif -#endif - -#ifndef SOAGEN_DISABLE_CODE_ANALYSIS_WARNINGS - #if SOAGEN_MSVC - #if SOAGEN_HAS_INCLUDE() - #pragma warning(push, 0) - #include - #pragma warning(pop) - #define SOAGEN_DISABLE_CODE_ANALYSIS_WARNINGS \ - __pragma(warning(disable : ALL_CODE_ANALYSIS_WARNINGS)) \ - static_assert(true) - #else - #define SOAGEN_DISABLE_CODE_ANALYSIS_WARNINGS static_assert(true) - #endif - #else - #define SOAGEN_DISABLE_CODE_ANALYSIS_WARNINGS static_assert(true) - #endif -#endif - -#ifndef SOAGEN_DISABLE_SUGGESTION_WARNINGS - #if SOAGEN_GCC - #define SOAGEN_DISABLE_SUGGESTION_WARNINGS \ - _Pragma("GCC diagnostic ignored \"-Wsuggest-attribute=const\"") \ - _Pragma("GCC diagnostic ignored \"-Wsuggest-attribute=pure\"") \ - static_assert(true) - #else - #define SOAGEN_DISABLE_SUGGESTION_WARNINGS static_assert(true) - #endif -#endif - -// backcompat -#ifndef SOAGEN_DISABLE_SUGGEST_WARNINGS - #define SOAGEN_DISABLE_SUGGEST_WARNINGS SOAGEN_DISABLE_SUGGESTION_WARNINGS -#endif - -#ifndef SOAGEN_DISABLE_WARNINGS - #if SOAGEN_CLANG - #define SOAGEN_DISABLE_WARNINGS \ - SOAGEN_PUSH_WARNINGS; \ - _Pragma("clang diagnostic ignored \"-Weverything\"") \ - static_assert(true, "") - #elif SOAGEN_MSVC - #define SOAGEN_DISABLE_WARNINGS \ - __pragma(warning(push, 0)) \ - __pragma(warning(disable : 4348)) \ - __pragma(warning(disable : 4668)) \ - __pragma(warning(disable : 5105)) \ - SOAGEN_DISABLE_CODE_ANALYSIS_WARNINGS; \ - SOAGEN_DISABLE_SWITCH_WARNINGS; \ - SOAGEN_DISABLE_SHADOW_WARNINGS; \ - SOAGEN_DISABLE_DEPRECATION_WARNINGS; \ - SOAGEN_DISABLE_SPAM_WARNINGS; \ - SOAGEN_DISABLE_ARITHMETIC_WARNINGS; \ - static_assert(true) - #elif SOAGEN_ICC - #define SOAGEN_DISABLE_WARNINGS \ - __pragma(warning(push, 0)) \ - static_assert(true) - #elif SOAGEN_GCC - #define SOAGEN_DISABLE_WARNINGS \ - SOAGEN_PUSH_WARNINGS; \ - _Pragma("GCC diagnostic ignored \"-Wall\"") \ - _Pragma("GCC diagnostic ignored \"-Wextra\"") \ - _Pragma("GCC diagnostic ignored \"-Wpedantic\"") \ - SOAGEN_DISABLE_SWITCH_WARNINGS; \ - SOAGEN_DISABLE_LIFETIME_WARNINGS; \ - SOAGEN_DISABLE_ARITHMETIC_WARNINGS; \ - SOAGEN_DISABLE_SHADOW_WARNINGS; \ - SOAGEN_DISABLE_SUGGEST_WARNINGS; \ - SOAGEN_DISABLE_SPAM_WARNINGS; \ - static_assert(true) - #else - #define SOAGEN_DISABLE_WARNINGS static_assert(true) - #endif -#endif - -#ifndef SOAGEN_ENABLE_WARNINGS - #if SOAGEN_CLANG || SOAGEN_MSVC || SOAGEN_ICC || SOAGEN_GCC - #define SOAGEN_ENABLE_WARNINGS \ - SOAGEN_POP_WARNINGS; \ - static_assert(true) - #else - #define SOAGEN_ENABLE_WARNINGS static_assert(true) - #endif -#endif - -#ifndef SOAGEN_HAS_CONSTEVAL - #if defined(__cpp_consteval) && __cpp_consteval >= 201811 && (!SOAGEN_MSVC || SOAGEN_MSVC >= 1934) \ - && (!SOAGEN_CLANG || SOAGEN_CLANG >= 15) - #define SOAGEN_HAS_CONSTEVAL 1 - #else - #ifdef SOAGEN_DOXYGEN - #define SOAGEN_HAS_CONSTEVAL SOAGEN_DOXYGEN - #else - #define SOAGEN_HAS_CONSTEVAL 0 - #endif - #endif -#endif - -#ifndef SOAGEN_CONSTEVAL - #if SOAGEN_HAS_CONSTEVAL - #define SOAGEN_CONSTEVAL consteval - #else - #define SOAGEN_CONSTEVAL constexpr - #endif -#endif - -#ifndef SOAGEN_HAS_IF_CONSTEVAL - #if defined(__cpp_if_consteval) && __cpp_if_consteval >= 202106 - #define SOAGEN_HAS_IF_CONSTEVAL 1 - #else - #define SOAGEN_HAS_IF_CONSTEVAL 0 - #endif -#endif - -#ifndef SOAGEN_IF_CONSTEVAL - #if SOAGEN_HAS_IF_CONSTEVAL - #define SOAGEN_IF_CONSTEVAL if consteval - #define SOAGEN_IF_RUNTIME if !consteval - #else - #define SOAGEN_IF_CONSTEVAL if (::soagen::is_constant_evaluated()) - #define SOAGEN_IF_RUNTIME if (!::soagen::is_constant_evaluated()) - #endif -#endif - -#ifndef SOAGEN_ASSERT - #ifdef NDEBUG - #define SOAGEN_ASSERT(cond) static_cast(0) - #else - #ifndef assert -SOAGEN_DISABLE_WARNINGS; - #include -SOAGEN_ENABLE_WARNINGS; - #endif - #define SOAGEN_ASSERT(cond) assert(cond) - #endif -#endif -#ifdef NDEBUG - // ensure any overrides respect NDEBUG - #undef SOAGEN_ASSERT - #define SOAGEN_ASSERT(cond) static_cast(0) -#endif - -#ifndef SOAGEN_ASSUME - #if SOAGEN_MSVC_LIKE - #define SOAGEN_ASSUME(expr) __assume(expr) - #elif SOAGEN_ICC || SOAGEN_CLANG || SOAGEN_HAS_BUILTIN(__builtin_assume) - #define SOAGEN_ASSUME(expr) __builtin_assume(expr) - #elif SOAGEN_HAS_CPP_ATTR(assume) >= 202207 - #define SOAGEN_ASSUME(expr) [[assume(expr)]] - #elif SOAGEN_HAS_ATTR(__assume__) - #define SOAGEN_ASSUME(expr) __attribute__((__assume__(expr))) - #else - #define SOAGEN_ASSUME(expr) static_cast(0) - #endif -#endif - -#ifndef SOAGEN_CONSTEXPR_SAFE_ASSERT - #ifdef NDEBUG - #define SOAGEN_CONSTEXPR_SAFE_ASSERT(cond) static_cast(0) - #else - #define SOAGEN_CONSTEXPR_SAFE_ASSERT(cond) \ - do \ - { \ - if constexpr (SOAGEN_HAS_IF_CONSTEVAL || ::soagen::build::supports_is_constant_evaluated) \ - { \ - SOAGEN_IF_RUNTIME \ - { \ - SOAGEN_ASSERT(cond); \ - } \ - } \ - } \ - while (false) - #endif -#endif - -#ifndef NDEBUG - #undef SOAGEN_ASSUME - #define SOAGEN_ASSUME(cond) SOAGEN_CONSTEXPR_SAFE_ASSERT(cond) -#endif - -#ifndef SOAGEN_ALWAYS_OPTIMIZE - #define SOAGEN_ALWAYS_OPTIMIZE 1 -#endif - -#ifndef SOAGEN_DELETE_COPY - #define SOAGEN_DELETE_COPY(T) \ - T(const T&) = delete; \ - T& operator=(const T&) = delete -#endif - -#ifndef SOAGEN_DELETE_MOVE - #define SOAGEN_DELETE_MOVE(T) \ - T(T&&) = delete; \ - T& operator=(T&&) = delete -#endif - -#ifndef SOAGEN_DEFAULT_COPY - #define SOAGEN_DEFAULT_COPY(T) \ - T(const T&) = default; \ - T& operator=(const T&) = default -#endif - -#ifndef SOAGEN_DEFAULT_MOVE - #define SOAGEN_DEFAULT_MOVE(T) \ - T(T&&) = default; \ - T& operator=(T&&) = default -#endif - -#ifndef SOAGEN_DEFAULT_RULE_OF_FOUR - #define SOAGEN_DEFAULT_RULE_OF_FOUR(T) \ - SOAGEN_DEFAULT_COPY(T); \ - SOAGEN_DEFAULT_MOVE(T) -#endif - -#ifndef SOAGEN_DEFAULT_RULE_OF_FIVE - #define SOAGEN_DEFAULT_RULE_OF_FIVE(T) \ - T() = default; \ - SOAGEN_DEFAULT_RULE_OF_FOUR(T) -#endif - -//******** core.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; - -SOAGEN_PUSH_WARNINGS; -SOAGEN_DISABLE_SPAM_WARNINGS; - -#if SOAGEN_MSVC_LIKE - #pragma push_macro("min") - #pragma push_macro("max") - #undef min - #undef max -#endif - -#if SOAGEN_ALWAYS_OPTIMIZE - #if SOAGEN_MSVC - #pragma inline_recursion(on) - #pragma optimize("gt", on) - #pragma runtime_checks("", off) - #pragma strict_gs_check(push, off) - #elif SOAGEN_GCC - #pragma GCC push_options - #pragma GCC optimize("O2") - #endif -#endif - -#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