From d78d7c84b5560aa8a6cc887258d21649adbc71b4 Mon Sep 17 00:00:00 2001 From: Mark Gillard Date: Tue, 8 Aug 2023 21:55:42 +0300 Subject: [PATCH] Added rows and iterators to `soagen::table` also: - added `table::for_each_column()` - refactoring --- CHANGELOG.md | 6 + docs/pages/intro.md | 42 +- examples/entities.hpp | 87 +- examples/entities.natvis | 2 +- examples/shapes.hpp | 162 ++- examples/shapes.natvis | 2 +- src/soagen/header_file.py | 4 - src/soagen/hpp/column_traits.hpp | 49 +- src/soagen/hpp/core.hpp | 136 +- src/soagen/hpp/emplacer.hpp | 24 +- src/soagen/hpp/generated/version.hpp | 4 +- src/soagen/hpp/iterator.hpp | 99 +- src/soagen/hpp/row.hpp | 92 +- src/soagen/hpp/single/soagen.hpp | 1957 +++++++++++++++----------- src/soagen/hpp/table.hpp | 548 +++++++- src/soagen/hpp/table_traits.hpp | 84 +- src/soagen/main.py | 1 - src/soagen/struct.py | 46 +- src/soagen/version.txt | 2 +- tests/employees.cpp | 122 +- tests/employees.hpp | 87 +- tests/employees.natvis | 2 +- 22 files changed, 2247 insertions(+), 1311 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e78e0fe..e218051 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +## v0.5.0 + +- Added rows to `soagen::table` +- Added iterators to `soagen::table` +- Added `soagen::table::for_each_column()` + ## v0.4.0 - Fixed `soagen::is_table<>` diff --git a/docs/pages/intro.md b/docs/pages/intro.md index b1e3a16..bcf401f 100644 --- a/docs/pages/intro.md +++ b/docs/pages/intro.md @@ -243,6 +243,9 @@ I'd to present my solution to the problems of working with Structure-of-Arrays i 1. [`soagen`], a command-line code utility for generating bespoke `std::vector`-like SoA containers, and 2. [`soagen.hpp`], a single-header backing library upon which the generated code depends. +@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. + 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. @@ -254,10 +257,6 @@ version of the `entity` class [described above](#intro_motivation_soa_naive). 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 @@ -294,7 +293,7 @@ usage: soagen [-h] [-v] [--version] [--install ] [--werror | --no-werror] \__ \ (_) | (_| | (_| | __/ | | | |___/\___/ \__,_|\__, |\___|_| |_| __/ | - |___/ v0.4.0 - marzer.github.io/soagen + |___/ v0.5.0 - marzer.github.io/soagen Struct-of-Arrays generator for C++ projects. @@ -367,7 +366,7 @@ Now run `soagen`: > soagen src/*.toml -soagen v0.4.0 +soagen v0.5.0 Reading src/entities.toml Running clang-format for src/entities.hpp Writing src/entities.hpp @@ -408,7 +407,7 @@ too: ```plaintext > soagen --install src -soagen v0.4.0 +soagen v0.5.0 Copying soagen.hpp to src All done! ``` @@ -1082,25 +1081,24 @@ default_constructible = 'auto' # let GENERATED_BODY create the default ctor @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, default arguments to `push_back()` etc., and the ability to use rows and iterators, but most of 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. +types? That's totally fine! [`soagen.hpp`] was written with that use-case in mind. The generated classes buy you +named members and default arguments to `push_back()` and friends, and are simply thin wrappers around the #soagen::table. -To use a #soagen::table directly, you need to express it 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: +To use a #soagen::table directly, you need to express it terms of #soagen::table_traits, +which is itself expressed in terms of the individual columns. For example, to create a 'raw' verson of the entity +table we've generated above: ```cpp -using entities = soagen::table, - soagen::column_traits, - soagen::column_traits, - soagen::column_traits ->>; +using entities = soagen::table< + soagen::table_traits< + unsigned, + std::string, + soagen::column_traits, // over-aligned + quaternion + >, + soagen::allocator +>; entities e; diff --git a/examples/entities.hpp b/examples/entities.hpp index 5528da1..5bfeda9 100644 --- a/examples/entities.hpp +++ b/examples/entities.hpp @@ -4,7 +4,7 @@ // 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.4.0 - do not modify it directly +// This file was generated by soagen v0.5.0 - do not modify it directly // https://marzer.github.io/soagen //---------------------------------------------------------------------------------------------------------------------- #pragma once @@ -15,15 +15,12 @@ /// @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 < 4 - #error soagen version mismatch - expected v0.4.X +#if SOAGEN_VERSION_MAJOR != 0 || SOAGEN_VERSION_MINOR < 5 + #error soagen version mismatch - expected v0.5.X #endif SOAGEN_DISABLE_WARNINGS; #include -#if SOAGEN_HAS_EXCEPTIONS - #include -#endif SOAGEN_ENABLE_WARNINGS; SOAGEN_PUSH_WARNINGS; @@ -124,12 +121,6 @@ namespace soagen::examples class entities; } -namespace soagen -{ - template <> - inline constexpr bool is_soa = true; -} - namespace soagen::detail { #ifndef SOAGEN_NAME_id @@ -168,6 +159,10 @@ namespace soagen::detail using type = soagen::allocator; }; + template <> + struct is_soa_ : std::true_type + {}; + SOAGEN_MAKE_COLUMN(soagen::examples::entities, 0, id); SOAGEN_MAKE_COLUMN(soagen::examples::entities, 1, name); SOAGEN_MAKE_COLUMN(soagen::examples::entities, 2, pos); @@ -232,7 +227,7 @@ namespace soagen::examples using table_traits = soagen::table_traits_type; /// @brief The number of columns in the table. - static constexpr size_type column_count = soagen::table_traits_type::column_count; + static constexpr size_type column_count = table_traits::column_count; /// @brief Gets the soagen::column_traits for a specific column of the table. template @@ -243,19 +238,19 @@ namespace soagen::examples using column_type = typename column_traits(Column)>::value_type; /// @brief Row iterators returned by iterator functions. - using iterator = soagen::iterator_type; + using iterator = soagen::iterator_type; /// @brief Row iterators returned by const-qualified iterator functions. - using const_iterator = soagen::iterator_type; + 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; + using row_type = soagen::row_type; /// @brief Const row type used by this class. - using const_row_type = soagen::row_type; + using const_row_type = soagen::row_type; /// @brief Rvalue row type used by this class. using rvalue_row_type = soagen::row_type; @@ -508,7 +503,7 @@ namespace soagen::examples 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 iterator{ table_, static_cast(*moved_pos) }; return {}; } @@ -547,7 +542,7 @@ namespace soagen::examples 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 const_iterator{ table_, static_cast(*moved_pos) }; return {}; } @@ -568,10 +563,10 @@ namespace soagen::examples SOAGEN_ALWAYS_INLINE SOAGEN_CPP20_CONSTEXPR entities& swap_columns() // - noexcept(noexcept( - std::declval().template swap_columns(A), static_cast(B)>())) + noexcept(noexcept(std::declval() + .template swap_columns(A), static_cast(B)>())) { - table_.template swap_columns(A), static_cast(B)>(); + table_.template swap_columns(A), static_cast(B)>(); return *this; } @@ -1134,7 +1129,7 @@ namespace soagen::examples template SOAGEN_PURE_GETTER SOAGEN_CPP20_CONSTEXPR - soagen::row_type row(size_type index) & noexcept + soagen::row_type row(size_type index) & noexcept { if constexpr (sizeof...(Columns)) { @@ -1246,7 +1241,7 @@ namespace soagen::examples template SOAGEN_PURE_GETTER SOAGEN_CPP20_CONSTEXPR - soagen::row_type row(size_type index) const& noexcept + soagen::row_type row(size_type index) const& noexcept { if constexpr (sizeof...(Columns)) { @@ -1302,67 +1297,67 @@ namespace soagen::examples /// @{ /// @brief Returns an iterator to the first row in the table. - template + template SOAGEN_PURE_INLINE_GETTER - constexpr soagen::iterator_type begin() & noexcept + constexpr soagen::iterator_type begin() & noexcept { - return { *this, 0 }; + return { table_, 0 }; } /// @brief Returns an iterator to one-past-the-last row in the table. - template + template SOAGEN_PURE_INLINE_GETTER - constexpr soagen::iterator_type end() & noexcept + constexpr soagen::iterator_type end() & noexcept { - return { *this, static_cast(size()) }; + return { table_, static_cast(size()) }; } /// @brief Returns an iterator to the first row in the table. - template + template SOAGEN_PURE_INLINE_GETTER constexpr soagen::iterator_type begin() && noexcept { - return { std::move(*this), 0 }; + return { std::move(table_), 0 }; } /// @brief Returns an iterator to one-past-the-last row in the table. - template + template SOAGEN_PURE_INLINE_GETTER constexpr soagen::iterator_type end() && noexcept { - return { std::move(*this), static_cast(size()) }; + return { std::move(table_), static_cast(size()) }; } /// @brief Returns an iterator to the first row in the table. - template + template SOAGEN_PURE_INLINE_GETTER - constexpr soagen::iterator_type begin() const& noexcept + constexpr soagen::iterator_type begin() const& noexcept { - return { *this, 0 }; + return { table_, 0 }; } /// @brief Returns an iterator to one-past-the-last row in the table. - template + template SOAGEN_PURE_INLINE_GETTER - constexpr soagen::iterator_type end() const& noexcept + constexpr soagen::iterator_type end() const& noexcept { - return { *this, static_cast(size()) }; + return { table_, static_cast(size()) }; } /// @brief Returns an iterator to the first row in the table. - template + template SOAGEN_PURE_INLINE_GETTER - constexpr soagen::iterator_type cbegin() const& noexcept + constexpr soagen::iterator_type cbegin() const noexcept { - return { *this, 0 }; + return { table_, 0 }; } /// @brief Returns an iterator to one-past-the-last row in the table. - template + template SOAGEN_PURE_INLINE_GETTER - constexpr soagen::iterator_type cend() const& noexcept + constexpr soagen::iterator_type cend() const noexcept { - return { *this, static_cast(size()) }; + return { table_, static_cast(size()) }; } /// @} diff --git a/examples/entities.natvis b/examples/entities.natvis index f0de668..e3be978 100644 --- a/examples/entities.natvis +++ b/examples/entities.natvis @@ -1,6 +1,6 @@ diff --git a/examples/shapes.hpp b/examples/shapes.hpp index 8046353..803ad23 100644 --- a/examples/shapes.hpp +++ b/examples/shapes.hpp @@ -4,7 +4,7 @@ // 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.4.0 - do not modify it directly +// This file was generated by soagen v0.5.0 - do not modify it directly // https://marzer.github.io/soagen //---------------------------------------------------------------------------------------------------------------------- #pragma once @@ -15,14 +15,11 @@ /// @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 < 4 - #error soagen version mismatch - expected v0.4.X +#if SOAGEN_VERSION_MAJOR != 0 || SOAGEN_VERSION_MINOR < 5 + #error soagen version mismatch - expected v0.5.X #endif SOAGEN_DISABLE_WARNINGS; -#if SOAGEN_HAS_EXCEPTIONS - #include -#endif SOAGEN_ENABLE_WARNINGS; SOAGEN_PUSH_WARNINGS; @@ -124,15 +121,6 @@ namespace soagen::examples class spheres; } -namespace soagen -{ - template <> - inline constexpr bool is_soa = true; - - template <> - inline constexpr bool is_soa = true; -} - namespace soagen::detail { #ifndef SOAGEN_NAME_center_x @@ -194,6 +182,10 @@ namespace soagen::detail using type = soagen::allocator; }; + template <> + struct is_soa_ : std::true_type + {}; + SOAGEN_MAKE_COLUMN(soagen::examples::boxes, 0, center_x); SOAGEN_MAKE_COLUMN(soagen::examples::boxes, 1, center_y); SOAGEN_MAKE_COLUMN(soagen::examples::boxes, 2, center_z); @@ -225,6 +217,10 @@ namespace soagen::detail using type = soagen::allocator; }; + template <> + struct is_soa_ : std::true_type + {}; + SOAGEN_MAKE_COLUMN(soagen::examples::spheres, 0, center_x); SOAGEN_MAKE_COLUMN(soagen::examples::spheres, 1, center_y); SOAGEN_MAKE_COLUMN(soagen::examples::spheres, 2, center_z); @@ -304,7 +300,7 @@ namespace soagen::examples using table_traits = soagen::table_traits_type; /// @brief The number of columns in the table. - static constexpr size_type column_count = soagen::table_traits_type::column_count; + static constexpr size_type column_count = table_traits::column_count; /// @brief Gets the soagen::column_traits for a specific column of the table. template @@ -315,19 +311,19 @@ namespace soagen::examples using column_type = typename column_traits(Column)>::value_type; /// @brief Row iterators returned by iterator functions. - using iterator = soagen::iterator_type; + using iterator = soagen::iterator_type; /// @brief Row iterators returned by const-qualified iterator functions. - using const_iterator = soagen::iterator_type; + 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; + using row_type = soagen::row_type; /// @brief Const row type used by this class. - using const_row_type = soagen::row_type; + using const_row_type = soagen::row_type; /// @brief Rvalue row type used by this class. using rvalue_row_type = soagen::row_type; @@ -584,7 +580,7 @@ namespace soagen::examples 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 iterator{ table_, static_cast(*moved_pos) }; return {}; } @@ -623,7 +619,7 @@ namespace soagen::examples 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 const_iterator{ table_, static_cast(*moved_pos) }; return {}; } @@ -644,10 +640,10 @@ namespace soagen::examples SOAGEN_ALWAYS_INLINE SOAGEN_CPP20_CONSTEXPR boxes& swap_columns() // - noexcept(noexcept( - std::declval().template swap_columns(A), static_cast(B)>())) + noexcept(noexcept(std::declval() + .template swap_columns(A), static_cast(B)>())) { - table_.template swap_columns(A), static_cast(B)>(); + table_.template swap_columns(A), static_cast(B)>(); return *this; } @@ -1425,7 +1421,7 @@ namespace soagen::examples template SOAGEN_PURE_GETTER SOAGEN_CPP20_CONSTEXPR - soagen::row_type row(size_type index) & noexcept + soagen::row_type row(size_type index) & noexcept { if constexpr (sizeof...(Columns)) { @@ -1537,7 +1533,7 @@ namespace soagen::examples template SOAGEN_PURE_GETTER SOAGEN_CPP20_CONSTEXPR - soagen::row_type row(size_type index) const& noexcept + soagen::row_type row(size_type index) const& noexcept { if constexpr (sizeof...(Columns)) { @@ -1593,67 +1589,67 @@ namespace soagen::examples /// @{ /// @brief Returns an iterator to the first row in the table. - template + template SOAGEN_PURE_INLINE_GETTER - constexpr soagen::iterator_type begin() & noexcept + constexpr soagen::iterator_type begin() & noexcept { - return { *this, 0 }; + return { table_, 0 }; } /// @brief Returns an iterator to one-past-the-last row in the table. - template + template SOAGEN_PURE_INLINE_GETTER - constexpr soagen::iterator_type end() & noexcept + constexpr soagen::iterator_type end() & noexcept { - return { *this, static_cast(size()) }; + return { table_, static_cast(size()) }; } /// @brief Returns an iterator to the first row in the table. - template + template SOAGEN_PURE_INLINE_GETTER constexpr soagen::iterator_type begin() && noexcept { - return { std::move(*this), 0 }; + return { std::move(table_), 0 }; } /// @brief Returns an iterator to one-past-the-last row in the table. - template + template SOAGEN_PURE_INLINE_GETTER constexpr soagen::iterator_type end() && noexcept { - return { std::move(*this), static_cast(size()) }; + return { std::move(table_), static_cast(size()) }; } /// @brief Returns an iterator to the first row in the table. - template + template SOAGEN_PURE_INLINE_GETTER - constexpr soagen::iterator_type begin() const& noexcept + constexpr soagen::iterator_type begin() const& noexcept { - return { *this, 0 }; + return { table_, 0 }; } /// @brief Returns an iterator to one-past-the-last row in the table. - template + template SOAGEN_PURE_INLINE_GETTER - constexpr soagen::iterator_type end() const& noexcept + constexpr soagen::iterator_type end() const& noexcept { - return { *this, static_cast(size()) }; + return { table_, static_cast(size()) }; } /// @brief Returns an iterator to the first row in the table. - template + template SOAGEN_PURE_INLINE_GETTER - constexpr soagen::iterator_type cbegin() const& noexcept + constexpr soagen::iterator_type cbegin() const noexcept { - return { *this, 0 }; + return { table_, 0 }; } /// @brief Returns an iterator to one-past-the-last row in the table. - template + template SOAGEN_PURE_INLINE_GETTER - constexpr soagen::iterator_type cend() const& noexcept + constexpr soagen::iterator_type cend() const noexcept { - return { *this, static_cast(size()) }; + return { table_, static_cast(size()) }; } /// @} @@ -1725,7 +1721,7 @@ namespace soagen::examples using table_traits = soagen::table_traits_type; /// @brief The number of columns in the table. - static constexpr size_type column_count = soagen::table_traits_type::column_count; + static constexpr size_type column_count = table_traits::column_count; /// @brief Gets the soagen::column_traits for a specific column of the table. template @@ -1736,19 +1732,19 @@ namespace soagen::examples using column_type = typename column_traits(Column)>::value_type; /// @brief Row iterators returned by iterator functions. - using iterator = soagen::iterator_type; + using iterator = soagen::iterator_type; /// @brief Row iterators returned by const-qualified iterator functions. - using const_iterator = soagen::iterator_type; + 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; + using row_type = soagen::row_type; /// @brief Const row type used by this class. - using const_row_type = soagen::row_type; + using const_row_type = soagen::row_type; /// @brief Rvalue row type used by this class. using rvalue_row_type = soagen::row_type; @@ -2004,7 +2000,7 @@ namespace soagen::examples 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 iterator{ table_, static_cast(*moved_pos) }; return {}; } @@ -2043,7 +2039,7 @@ namespace soagen::examples 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 const_iterator{ table_, static_cast(*moved_pos) }; return {}; } @@ -2064,10 +2060,10 @@ namespace soagen::examples SOAGEN_ALWAYS_INLINE SOAGEN_CPP20_CONSTEXPR spheres& swap_columns() // - noexcept(noexcept( - std::declval().template swap_columns(A), static_cast(B)>())) + noexcept(noexcept(std::declval() + .template swap_columns(A), static_cast(B)>())) { - table_.template swap_columns(A), static_cast(B)>(); + table_.template swap_columns(A), static_cast(B)>(); return *this; } @@ -2686,7 +2682,7 @@ namespace soagen::examples template SOAGEN_PURE_GETTER SOAGEN_CPP20_CONSTEXPR - soagen::row_type row(size_type index) & noexcept + soagen::row_type row(size_type index) & noexcept { if constexpr (sizeof...(Columns)) { @@ -2798,7 +2794,7 @@ namespace soagen::examples template SOAGEN_PURE_GETTER SOAGEN_CPP20_CONSTEXPR - soagen::row_type row(size_type index) const& noexcept + soagen::row_type row(size_type index) const& noexcept { if constexpr (sizeof...(Columns)) { @@ -2854,67 +2850,67 @@ namespace soagen::examples /// @{ /// @brief Returns an iterator to the first row in the table. - template + template SOAGEN_PURE_INLINE_GETTER - constexpr soagen::iterator_type begin() & noexcept + constexpr soagen::iterator_type begin() & noexcept { - return { *this, 0 }; + return { table_, 0 }; } /// @brief Returns an iterator to one-past-the-last row in the table. - template + template SOAGEN_PURE_INLINE_GETTER - constexpr soagen::iterator_type end() & noexcept + constexpr soagen::iterator_type end() & noexcept { - return { *this, static_cast(size()) }; + return { table_, static_cast(size()) }; } /// @brief Returns an iterator to the first row in the table. - template + template SOAGEN_PURE_INLINE_GETTER constexpr soagen::iterator_type begin() && noexcept { - return { std::move(*this), 0 }; + return { std::move(table_), 0 }; } /// @brief Returns an iterator to one-past-the-last row in the table. - template + template SOAGEN_PURE_INLINE_GETTER constexpr soagen::iterator_type end() && noexcept { - return { std::move(*this), static_cast(size()) }; + return { std::move(table_), static_cast(size()) }; } /// @brief Returns an iterator to the first row in the table. - template + template SOAGEN_PURE_INLINE_GETTER - constexpr soagen::iterator_type begin() const& noexcept + constexpr soagen::iterator_type begin() const& noexcept { - return { *this, 0 }; + return { table_, 0 }; } /// @brief Returns an iterator to one-past-the-last row in the table. - template + template SOAGEN_PURE_INLINE_GETTER - constexpr soagen::iterator_type end() const& noexcept + constexpr soagen::iterator_type end() const& noexcept { - return { *this, static_cast(size()) }; + return { table_, static_cast(size()) }; } /// @brief Returns an iterator to the first row in the table. - template + template SOAGEN_PURE_INLINE_GETTER - constexpr soagen::iterator_type cbegin() const& noexcept + constexpr soagen::iterator_type cbegin() const noexcept { - return { *this, 0 }; + return { table_, 0 }; } /// @brief Returns an iterator to one-past-the-last row in the table. - template + template SOAGEN_PURE_INLINE_GETTER - constexpr soagen::iterator_type cend() const& noexcept + constexpr soagen::iterator_type cend() const noexcept { - return { *this, static_cast(size()) }; + return { table_, static_cast(size()) }; } /// @} diff --git a/examples/shapes.natvis b/examples/shapes.natvis index 418fd20..f6dd6e2 100644 --- a/examples/shapes.natvis +++ b/examples/shapes.natvis @@ -1,6 +1,6 @@ diff --git a/src/soagen/header_file.py b/src/soagen/header_file.py index 1b540ff..39b78b0 100644 --- a/src/soagen/header_file.py +++ b/src/soagen/header_file.py @@ -238,10 +238,6 @@ def write(self, o: Writer): for struct in self.structs: struct.write_forward_declarations(o) o() - with Namespace(o, 'soagen'): - for struct in self.structs: - struct.write_soagen_specializations(o) - o() with Namespace(o, 'soagen::detail'): names = set() for struct in self.structs: diff --git a/src/soagen/hpp/column_traits.hpp b/src/soagen/hpp/column_traits.hpp index 2ccfdfa..2d58997 100644 --- a/src/soagen/hpp/column_traits.hpp +++ b/src/soagen/hpp/column_traits.hpp @@ -946,21 +946,24 @@ namespace soagen using default_emplace_type = make_cref; }; - /// @brief True if `T` is an instance of #soagen::column_traits. - template - inline constexpr bool is_column_traits = POXY_IMPLEMENTATION_DETAIL(false); /// @cond - template - inline constexpr bool is_column_traits> = true; - template - inline constexpr bool is_column_traits> = true; - template - inline constexpr bool is_column_traits = is_column_traits; - template - inline constexpr bool is_column_traits = is_column_traits; - template - inline constexpr bool is_column_traits = is_column_traits; + namespace detail + { + template + struct is_column_traits_ : std::false_type + {}; + template + struct is_column_traits_> : std::true_type + {}; + template + struct is_column_traits_> : std::true_type + {}; + } /// @endcond + /// @brief True if `T` is a #soagen::column_traits. + template + inline constexpr bool is_column_traits = + POXY_IMPLEMENTATION_DETAIL(detail::is_column_traits_>::value); } /// @cond @@ -968,7 +971,6 @@ namespace soagen::detail { template struct to_base_traits_; - template struct to_base_traits_> { @@ -976,9 +978,26 @@ namespace soagen::detail static_assert(std::is_base_of_v>); }; - template using to_base_traits = typename to_base_traits_::type; + + template + struct as_column_ + { + using type = column_traits; + }; + template + struct as_column_> + { + using type = column_traits; + }; + template + struct as_column_> + { + using type = detail::column_traits_base; + }; + template + using as_column = typename as_column_::type; } /// @endcond diff --git a/src/soagen/hpp/core.hpp b/src/soagen/hpp/core.hpp index 1f08569..8748288 100644 --- a/src/soagen/hpp/core.hpp +++ b/src/soagen/hpp/core.hpp @@ -18,6 +18,9 @@ SOAGEN_DISABLE_WARNINGS; #include #include #include +#if SOAGEN_HAS_EXCEPTIONS + #include +#endif #ifndef SOAGEN_COLUMN_SPAN_TYPE #if SOAGEN_CPP >= 20 && SOAGEN_HAS_INCLUDE() @@ -53,17 +56,17 @@ SOAGEN_ENABLE_WARNINGS; static constexpr const char value[] = #Name; \ }; \ \ - template typename Transformation = soagen::identity_type> \ + template \ struct named_ref_##Name \ { \ - Transformation Name; \ + T Name; \ \ protected: \ SOAGEN_PURE_INLINE_GETTER \ constexpr decltype(auto) get_ref() const noexcept \ { \ - if constexpr (std::is_reference_v>) \ - return static_cast>(Name); \ + if constexpr (std::is_reference_v) \ + return static_cast(Name); \ else \ return Name; \ } \ @@ -77,7 +80,7 @@ SOAGEN_ENABLE_WARNINGS; {}; \ \ template <> \ - struct column_ref \ + struct column_ref \ : named_ref_##Name(Column)>>> \ {}; \ \ @@ -87,7 +90,7 @@ SOAGEN_ENABLE_WARNINGS; {}; \ \ template <> \ - struct column_ref \ + struct column_ref \ : named_ref_##Name(Column)>>>> \ {}; \ @@ -108,6 +111,20 @@ namespace soagen using std::uintptr_t; using std::nullptr_t; + /// @cond + // forward declarations + template + struct emplacer; + template + struct row; + template + class iterator; + template + struct table_traits; + template + class table; + /// @endcond + /// @brief Equivalent to C+20's std::remove_cvref_t. template using remove_cvref = std::remove_cv_t>; @@ -120,15 +137,6 @@ namespace soagen template using coerce_ref = std::conditional_t, T, std::add_lvalue_reference_t>; - /// @brief The identity type transformation. - template - using identity_type = T; - - /// @brief The 'identity' base for CRTP scenarios (adds nothing to the interface). - template - struct SOAGEN_EMPTY_BASES identity_base - {}; - /// @brief True if `T` is `const` or `volatile` qualified. template inline constexpr bool is_cv = !std::is_same_v, T>; @@ -157,17 +165,42 @@ namespace soagen template inline constexpr bool any_same_value = ((Value == Values) || ...); - /// @brief True if `T` is a soagen-generated SoA table type. - template - inline constexpr bool is_soa = POXY_IMPLEMENTATION_DETAIL(false); // specialized in generated code /// @cond + namespace detail + { + template + struct is_table_ : std::false_type + {}; + template + struct is_table_> : std::true_type + {}; + } + /// @endcond + /// @brief True if `T` is a #soagen::table. template - inline constexpr bool is_soa = is_soa; - template - inline constexpr bool is_soa = is_soa; + inline constexpr bool is_table = POXY_IMPLEMENTATION_DETAIL(detail::is_table_>::value); + + /// @cond + namespace detail + { + template + struct is_soa_ : is_table_ // specialized in the generated code + {}; + } + /// @endcond + /// @brief True if `T` is a #soagen::table or a soagen-generated SoA class. template - inline constexpr bool is_soa = is_soa; + inline constexpr bool is_soa = POXY_IMPLEMENTATION_DETAIL(detail::is_soa_>::value); + /// @brief Conditionally adds `const` to a type. + template + using conditionally_add_const = std::conditional_t, T>; + + /// @brief Conditionally adds `volatile` to a type. + template + using conditionally_add_volatile = std::conditional_t, T>; + + /// @cond namespace detail { template @@ -176,6 +209,48 @@ namespace soagen std::conjunction, std::is_trivially_constructible, std::is_trivially_destructible>>; + + template + struct copy_cvref_ + { + using type = T; + }; + template + struct copy_cvref_ + { + using type = std::add_lvalue_reference_t::type>; + }; + template + struct copy_cvref_ + { + using type = std::add_rvalue_reference_t::type>; + }; + template + struct copy_cvref_ + { + using type = std::add_const_t::type>; + }; + template + struct copy_cvref_ + { + using type = std::add_volatile_t::type>; + }; + template + struct copy_cvref_ + { + using type = std::add_cv_t::type>; + }; + + template + struct remove_lvalue_ref_ + { + using type = T; + }; + template + struct remove_lvalue_ref_ + { + using type = T; + }; } /// @endcond @@ -184,6 +259,14 @@ namespace soagen inline constexpr bool is_implicit_lifetime_type = POXY_IMPLEMENTATION_DETAIL(detail::is_implicit_lifetime_type_::value); + /// @brief Copies the cvref-qualifiers from one type onto another, replacing the existing ones. + template + using copy_cvref = POXY_IMPLEMENTATION_DETAIL(typename detail::copy_cvref_, CopyFrom>::type); + + /// @brief Removes lvalue reference qualifiers. Rvalues references are preserved as-is. + template + using remove_lvalue_ref = POXY_IMPLEMENTATION_DETAIL(typename detail::remove_lvalue_ref_::type); + /// @brief Alias for `std::integral_constant` template using index_constant = std::integral_constant(Value)>; @@ -460,7 +543,7 @@ namespace soagen /// @brief Gets the underlying #soagen::table of an SoA type. template - using table_type = POXY_IMPLEMENTATION_DETAIL(typename detail::table_type_>::type); + using table_type = POXY_IMPLEMENTATION_DETAIL(typename detail::table_type_>::type); /// @brief True if two types have the same underlying #soagen::table type. template @@ -480,8 +563,7 @@ namespace soagen /// @brief Gets the #soagen::table_traits for the underlying #soagen::table of an SoA type. template - using table_traits_type = - POXY_IMPLEMENTATION_DETAIL(typename detail::table_traits_type_>::type); + using table_traits_type = POXY_IMPLEMENTATION_DETAIL(typename detail::table_traits_type_>::type); /// @cond namespace detail @@ -496,12 +578,12 @@ namespace soagen /// @brief Gets the allocator being used by the #soagen::table of an SoA type. template - using allocator_type = POXY_IMPLEMENTATION_DETAIL(typename detail::allocator_type_>::type); + using allocator_type = POXY_IMPLEMENTATION_DETAIL(typename detail::allocator_type_>::type); /// @brief Gets the #soagen::column_traits::value_type for the selected column of an SoA type. template using value_type = POXY_IMPLEMENTATION_DETAIL( - typename table_traits_type::template column(Column)>::value_type); + typename table_traits_type>::template column(Column)>::value_type); /// @cond namespace detail diff --git a/src/soagen/hpp/emplacer.hpp b/src/soagen/hpp/emplacer.hpp index f3f4efb..7127812 100644 --- a/src/soagen/hpp/emplacer.hpp +++ b/src/soagen/hpp/emplacer.hpp @@ -35,20 +35,20 @@ namespace soagen emplacer(Args&&...) -> emplacer; /// @endcond - /// @brief True if `T` is an instance of #soagen::emplacer. - template - inline constexpr bool is_emplacer = POXY_IMPLEMENTATION_DETAIL(false); - /// @cond - template - inline constexpr bool is_emplacer> = true; - template - inline constexpr bool is_emplacer = is_emplacer; - template - inline constexpr bool is_emplacer = is_emplacer; - template - inline constexpr bool is_emplacer = is_emplacer; + namespace detail + { + template + struct is_emplacer_ : std::false_type + {}; + template + struct is_emplacer_> : std::true_type + {}; + } /// @endcond + template + /// @brief True if `T` is an instance of #soagen::emplacer. + inline constexpr bool is_emplacer = POXY_IMPLEMENTATION_DETAIL(detail::is_emplacer_>::value); } /// @cond diff --git a/src/soagen/hpp/generated/version.hpp b/src/soagen/hpp/generated/version.hpp index 1fa99ed..3404cee 100644 --- a/src/soagen/hpp/generated/version.hpp +++ b/src/soagen/hpp/generated/version.hpp @@ -5,6 +5,6 @@ #pragma once #define SOAGEN_VERSION_MAJOR 0 -#define SOAGEN_VERSION_MINOR 4 +#define SOAGEN_VERSION_MINOR 5 #define SOAGEN_VERSION_PATCH 0 -#define SOAGEN_VERSION_STRING "0.4.0" +#define SOAGEN_VERSION_STRING "0.5.0" diff --git a/src/soagen/hpp/iterator.hpp b/src/soagen/hpp/iterator.hpp index 25a4d1b..c2652e2 100644 --- a/src/soagen/hpp/iterator.hpp +++ b/src/soagen/hpp/iterator.hpp @@ -27,7 +27,7 @@ namespace soagen template struct iterator_storage { - std::add_const_t>* table; + remove_cvref* table; typename remove_cvref
::difference_type offset; }; } @@ -39,26 +39,24 @@ namespace soagen struct SOAGEN_EMPTY_BASES iterator_base {}; - /// @brief RandomAccessIterator for soagen-generated table types. + /// @brief RandomAccessIterator for table types. template class SOAGEN_EMPTY_BASES iterator /// - SOAGEN_HIDDEN_BASE(protected detail::iterator_storage>, - public iterator_base>) + SOAGEN_HIDDEN_BASE(protected detail::iterator_storage>>, + public iterator_base, Columns...>>) { - static_assert(std::is_empty_v>>, + static_assert(std::is_empty_v, Columns...>>>, "iterator_base specializations may not have data members"); - static_assert(std::is_trivial_v>>, + static_assert(std::is_trivial_v, Columns...>>>, "iterator_base specializations must be trivial"); public: - /// @brief Base SoA table type for this iterator. - using table_type = remove_cvref
; - static_assert(is_soa, "soagen iterators are for use with soagen-generated SoA table types."); + /// @brief Base soagen::table type for this iterator. + using table_type = soagen::table_type>; + static_assert(is_table, "soagen iterators are for use with soagen SoA types."); /// @brief Cvref-qualified version of #table_type. - using table_ref = Table; - static_assert(std::is_reference_v, - "Table must be a reference so row members can derive their reference category"); + using table_ref = coerce_ref>; using size_type = typename table_type::size_type; @@ -66,7 +64,7 @@ namespace soagen using difference_type = typename table_type::difference_type; /// @brief The row type dereferenced by this iterator. - using row_type = row; + using row_type = soagen::row_type, Columns...>; /// @brief Alias for #row_type. using value_type = row_type; @@ -84,8 +82,7 @@ namespace soagen private: /// @cond - using base = detail::iterator_storage>; - using table_ptr = std::add_pointer_t>; + using base = detail::iterator_storage>>; template friend class soagen::iterator; @@ -105,7 +102,7 @@ namespace soagen /// @brief Constructs an iterator to some part of a table. SOAGEN_NODISCARD_CTOR constexpr iterator(table_ref tbl, difference_type pos) noexcept // - : base{ &tbl, pos } + : base{ const_cast(&tbl), pos } {} /// @name Incrementing @@ -193,6 +190,21 @@ namespace soagen /// @name Dereferencing /// @{ + private: + /// @cond + template + using cv_value_type = + conditionally_add_volatile, Column>, + std::is_const_v>>, + std::is_volatile_v>>; + + template + using cv_value_ref = std::conditional_t, + std::add_rvalue_reference_t>, + std::add_lvalue_reference_t>>; + /// @endcond + + public: /// @brief Returns the row the iterator refers to. SOAGEN_PURE_GETTER constexpr reference operator*() const noexcept @@ -200,8 +212,8 @@ namespace soagen SOAGEN_ASSUME(!!base::table); SOAGEN_ASSUME(base::offset >= 0); - return static_cast(*const_cast(base::table)) - .template row(static_cast(base::offset)); + return row_type{ { static_cast>( + base::table->template column()[base::offset]) }... }; } /// @brief Returns the row the iterator refers to. @@ -218,8 +230,8 @@ namespace soagen SOAGEN_ASSUME(!!base::table); SOAGEN_ASSUME(base::offset + offset >= 0); - return static_cast(*const_cast(base::table)) - .template row(static_cast(base::offset + offset)); + return row_type{ { static_cast>( + base::table->template column()[base::offset + offset]) }... }; } /// @} @@ -304,8 +316,8 @@ namespace soagen /// /// @attention There are no conversions provided which offer the equivalent of a `const_cast`-by-proxy. /// This is by design. - SOAGEN_CONSTRAINED_TEMPLATE((detail::implicit_conversion_ok - && !detail::explicit_conversion_ok), + SOAGEN_CONSTRAINED_TEMPLATE((detail::implicit_conversion_ok, coerce_ref> + && !detail::explicit_conversion_ok, coerce_ref>), typename T, size_t... Cols) SOAGEN_PURE_INLINE_GETTER @@ -316,8 +328,8 @@ namespace soagen /// @cond - SOAGEN_CONSTRAINED_TEMPLATE((!detail::implicit_conversion_ok - && detail::explicit_conversion_ok), + SOAGEN_CONSTRAINED_TEMPLATE((!detail::implicit_conversion_ok, coerce_ref> + && detail::explicit_conversion_ok, coerce_ref>), typename T, size_t... Cols) SOAGEN_PURE_INLINE_GETTER @@ -354,20 +366,6 @@ namespace soagen return it + n; } - /// @brief True if `T` is an instance of #soagen::iterator. - template - inline constexpr bool is_iterator = POXY_IMPLEMENTATION_DETAIL(false); - /// @cond - template - inline constexpr bool is_iterator> = true; - template - inline constexpr bool is_iterator = is_row; - template - inline constexpr bool is_iterator = is_row; - template - inline constexpr bool is_iterator = is_row; - /// @endcond - /// @cond namespace detail { @@ -387,19 +385,36 @@ namespace soagen template struct iterator_type_> { - using type = iterator; + using type = iterator, Columns...>; }; template struct iterator_type_> - : iterator_type_>::column_count>> + : iterator_type_, + std::make_index_sequence>::column_count>> {}; } /// @endcond /// @brief The #soagen::iterator for a given SoA type and (some subset of) its columns. - template + template using iterator_type = POXY_IMPLEMENTATION_DETAIL( - typename detail::iterator_type_, std::index_sequence>::type); + typename detail::iterator_type_, + std::index_sequence(Columns)...>>::type); + + /// @cond + namespace detail + { + template + struct is_iterator_ : std::false_type + {}; + template + struct is_iterator_> : std::true_type + {}; + } + /// @endcond + /// @brief True if `T` is a #soagen::iterator. + template + inline constexpr bool is_iterator = POXY_IMPLEMENTATION_DETAIL(detail::is_iterator_>::value); } #include "header_end.hpp" diff --git a/src/soagen/hpp/row.hpp b/src/soagen/hpp/row.hpp index f27bb76..8e611e8 100644 --- a/src/soagen/hpp/row.hpp +++ b/src/soagen/hpp/row.hpp @@ -10,9 +10,6 @@ namespace soagen { /// @cond - template - struct row; - namespace detail { // general rules for allowing implicit conversions: @@ -70,7 +67,7 @@ namespace soagen template inline constexpr bool row_implicit_conversion_ok, // row> = - implicit_conversion_ok + implicit_conversion_ok, coerce_ref> && column_conversion_ok, std::index_sequence>; // row explicit conversions: @@ -81,7 +78,7 @@ namespace soagen template inline constexpr bool row_explicit_conversion_ok, // row> = - explicit_conversion_ok + explicit_conversion_ok, coerce_ref> && column_conversion_ok, std::index_sequence>; } /// @endcond @@ -95,13 +92,13 @@ namespace soagen /// @brief A proxy type for treating (some subset of) an SoA row as if it were a regular AoS struct. template struct SOAGEN_EMPTY_BASES row // - SOAGEN_HIDDEN_BASE(public detail::column_ref..., public row_base>) + SOAGEN_HIDDEN_BASE(public detail::column_ref, Columns>..., + public row_base, Columns...>>) { - static_assert(std::is_reference_v
, - "Table must be a reference so row members can derive their reference category"); - static_assert(std::is_empty_v>>, + static_assert(std::is_empty_v, Columns...>>>, "row_base specializations may not have data members"); - static_assert(std::is_trivial_v>>, "row_base specializations must be trivial"); + static_assert(std::is_trivial_v, Columns...>>>, + "row_base specializations must be trivial"); // columns: @@ -109,10 +106,10 @@ namespace soagen SOAGEN_PURE_INLINE_GETTER constexpr decltype(auto) column() const noexcept { - static_assert(static_cast(Column) < table_traits_type>::column_count, + static_assert(static_cast(Column) < table_traits_type
::column_count, "column index out of range"); - return detail::column_ref(Column)>::get_ref(); + return detail::column_ref, static_cast(Column)>::get_ref(); } // tuple protocol: @@ -123,7 +120,7 @@ namespace soagen { static_assert(Member < sizeof...(Columns), "member index out of range"); - return type_at_index...>::get_ref(); + return type_at_index, Columns>...>::get_ref(); } /// @name Equality @@ -131,24 +128,22 @@ namespace soagen /// @{ /// @brief Returns true if all of the elements in two rows are equal. - SOAGEN_CONSTRAINED_TEMPLATE((same_table_type - && table_traits_type>::all_equality_comparable), + SOAGEN_CONSTRAINED_TEMPLATE((same_table_type && table_traits_type
::all_equality_comparable), typename T) SOAGEN_NODISCARD friend constexpr bool operator==(const row& lhs, const row& rhs) // - noexcept(table_traits_type>::all_nothrow_equality_comparable) + noexcept(table_traits_type
::all_nothrow_equality_comparable) { return ((lhs.template column() == rhs.template column()) && ...); } /// @brief Returns true if not all of the elements in two rows are equal. - SOAGEN_CONSTRAINED_TEMPLATE((same_table_type - && table_traits_type>::all_equality_comparable), + SOAGEN_CONSTRAINED_TEMPLATE((same_table_type && table_traits_type
::all_equality_comparable), typename T) SOAGEN_NODISCARD SOAGEN_ALWAYS_INLINE friend constexpr bool operator!=(const row& lhs, const row& rhs) // - noexcept(table_traits_type>::all_nothrow_equality_comparable) + noexcept(table_traits_type
::all_nothrow_equality_comparable) { return !(lhs == rhs); } @@ -164,7 +159,7 @@ namespace soagen template SOAGEN_NODISCARD static constexpr int row_compare_impl(const row& lhs, const row& rhs) // - noexcept(table_traits_type>::all_nothrow_less_than_comparable) + noexcept(table_traits_type
::all_nothrow_less_than_comparable) { if (lhs.template get() < rhs.template get()) return -1; @@ -181,45 +176,41 @@ namespace soagen public: /// @brief Returns true if the RHS row is ordered lexicographically less-than the RHS row. - SOAGEN_CONSTRAINED_TEMPLATE((same_table_type - && table_traits_type>::all_less_than_comparable), + SOAGEN_CONSTRAINED_TEMPLATE((same_table_type && table_traits_type
::all_less_than_comparable), typename T) SOAGEN_NODISCARD friend constexpr bool operator<(const row& lhs, const row& rhs) // - noexcept(table_traits_type>::all_nothrow_less_than_comparable) + noexcept(table_traits_type
::all_nothrow_less_than_comparable) { return row_compare_impl<0>(lhs, rhs) < 0; } /// @brief Returns true if the RHS row is ordered lexicographically less-than-or-equal-to the RHS row. - SOAGEN_CONSTRAINED_TEMPLATE((same_table_type - && table_traits_type>::all_less_than_comparable), + SOAGEN_CONSTRAINED_TEMPLATE((same_table_type && table_traits_type
::all_less_than_comparable), typename T) SOAGEN_NODISCARD friend constexpr bool operator<=(const row& lhs, const row& rhs) // - noexcept(table_traits_type>::all_nothrow_less_than_comparable) + noexcept(table_traits_type
::all_nothrow_less_than_comparable) { return row_compare_impl<0>(lhs, rhs) <= 0; } /// @brief Returns true if the RHS row is ordered lexicographically greater-than the RHS row. - SOAGEN_CONSTRAINED_TEMPLATE((same_table_type - && table_traits_type>::all_less_than_comparable), + SOAGEN_CONSTRAINED_TEMPLATE((same_table_type && table_traits_type
::all_less_than_comparable), typename T) SOAGEN_NODISCARD friend constexpr bool operator>(const row& lhs, const row& rhs) // - noexcept(table_traits_type>::all_nothrow_less_than_comparable) + noexcept(table_traits_type
::all_nothrow_less_than_comparable) { return row_compare_impl<0>(lhs, rhs) > 0; } /// @brief Returns true if the RHS row is ordered lexicographically greater-than-or-equal-to the RHS row. - SOAGEN_CONSTRAINED_TEMPLATE((same_table_type - && table_traits_type>::all_less_than_comparable), + SOAGEN_CONSTRAINED_TEMPLATE((same_table_type && table_traits_type
::all_less_than_comparable), typename T) SOAGEN_NODISCARD friend constexpr bool operator>=(const row& lhs, const row& rhs) // - noexcept(table_traits_type>::all_nothrow_less_than_comparable) + noexcept(table_traits_type
::all_nothrow_less_than_comparable) { return row_compare_impl<0>(lhs, rhs) >= 0; } @@ -276,20 +267,6 @@ namespace soagen /// @} }; - /// @brief True if `T` is an instance of #soagen::row. - template - inline constexpr bool is_row = POXY_IMPLEMENTATION_DETAIL(false); - /// @cond - template - inline constexpr bool is_row> = true; - template - inline constexpr bool is_row = is_row; - template - inline constexpr bool is_row = is_row; - template - inline constexpr bool is_row = is_row; - /// @endcond - /// @cond namespace detail { @@ -309,11 +286,12 @@ namespace soagen template struct row_type_> { - using type = row; + using type = row, Columns...>; }; template struct row_type_> - : row_type_>::column_count>> + : row_type_, + std::make_index_sequence>::column_count>> {}; } /// @endcond @@ -321,7 +299,23 @@ namespace soagen /// @brief The #soagen::row for a given SoA type and (some subset of) its columns. template using row_type = POXY_IMPLEMENTATION_DETAIL( - typename detail::row_type_, std::index_sequence(Columns)...>>::type); + typename detail::row_type_, + std::index_sequence(Columns)...>>::type); + + /// @cond + namespace detail + { + template + struct is_row_ : std::false_type + {}; + template + struct is_row_> : std::true_type + {}; + } + /// @endcond + /// @brief True if `T` is a #soagen::row. + template + inline constexpr bool is_row = POXY_IMPLEMENTATION_DETAIL(detail::is_row_>::value); } /// @cond diff --git a/src/soagen/hpp/single/soagen.hpp b/src/soagen/hpp/single/soagen.hpp index e8f92ca..b086bfa 100644 --- a/src/soagen/hpp/single/soagen.hpp +++ b/src/soagen/hpp/single/soagen.hpp @@ -1,6 +1,6 @@ //---------------------------------------------------------------------------------------------------------------------- // -// soagen.hpp v0.4.0 +// soagen.hpp v0.5.0 // https://github.com/marzer/soagen // SPDX-License-Identifier: MIT // @@ -32,9 +32,9 @@ //******** generated/version.hpp ************************************************************************************* #define SOAGEN_VERSION_MAJOR 0 -#define SOAGEN_VERSION_MINOR 4 +#define SOAGEN_VERSION_MINOR 5 #define SOAGEN_VERSION_PATCH 0 -#define SOAGEN_VERSION_STRING "0.4.0" +#define SOAGEN_VERSION_STRING "0.5.0" //******** generated/preprocessor.hpp ******************************************************************************** @@ -1037,6 +1037,9 @@ SOAGEN_DISABLE_WARNINGS; #include #include #include +#if SOAGEN_HAS_EXCEPTIONS + #include +#endif #ifndef SOAGEN_COLUMN_SPAN_TYPE #if SOAGEN_CPP >= 20 && SOAGEN_HAS_INCLUDE() @@ -1093,17 +1096,17 @@ SOAGEN_DISABLE_SPAM_WARNINGS; static constexpr const char value[] = #Name; \ }; \ \ - template typename Transformation = soagen::identity_type> \ + template \ struct named_ref_##Name \ { \ - Transformation Name; \ + T Name; \ \ protected: \ SOAGEN_PURE_INLINE_GETTER \ constexpr decltype(auto) get_ref() const noexcept \ { \ - if constexpr (std::is_reference_v>) \ - return static_cast>(Name); \ + if constexpr (std::is_reference_v) \ + return static_cast(Name); \ else \ return Name; \ } \ @@ -1117,7 +1120,7 @@ SOAGEN_DISABLE_SPAM_WARNINGS; {}; \ \ template <> \ - struct column_ref \ + struct column_ref \ : named_ref_##Name(Column)>>> \ {}; \ \ @@ -1127,7 +1130,7 @@ SOAGEN_DISABLE_SPAM_WARNINGS; {}; \ \ template <> \ - struct column_ref \ + struct column_ref \ : named_ref_##Name(Column)>>>> \ {}; \ @@ -1147,6 +1150,18 @@ namespace soagen using std::uintptr_t; using std::nullptr_t; + // forward declarations + template + struct emplacer; + template + struct row; + template + class iterator; + template + struct table_traits; + template + class table; + template using remove_cvref = std::remove_cv_t>; @@ -1156,13 +1171,6 @@ namespace soagen template using coerce_ref = std::conditional_t, T, std::add_lvalue_reference_t>; - template - using identity_type = T; - - template - struct SOAGEN_EMPTY_BASES identity_base - {}; - template inline constexpr bool is_cv = !std::is_same_v, T>; @@ -1184,15 +1192,34 @@ namespace soagen template inline constexpr bool any_same_value = ((Value == Values) || ...); - template - inline constexpr bool is_soa = POXY_IMPLEMENTATION_DETAIL(false); // specialized in generated code + namespace detail + { + template + struct is_table_ : std::false_type + {}; + template + struct is_table_> : std::true_type + {}; + } template - inline constexpr bool is_soa = is_soa; - template - inline constexpr bool is_soa = is_soa; + inline constexpr bool is_table = POXY_IMPLEMENTATION_DETAIL(detail::is_table_>::value); + + namespace detail + { + template + struct is_soa_ : is_table_ // specialized in the generated code + {}; + } + template - inline constexpr bool is_soa = is_soa; + inline constexpr bool is_soa = POXY_IMPLEMENTATION_DETAIL(detail::is_soa_>::value); + + template + using conditionally_add_const = std::conditional_t, T>; + + template + using conditionally_add_volatile = std::conditional_t, T>; namespace detail { @@ -1202,12 +1229,60 @@ namespace soagen std::conjunction, std::is_trivially_constructible, std::is_trivially_destructible>>; + + template + struct copy_cvref_ + { + using type = T; + }; + template + struct copy_cvref_ + { + using type = std::add_lvalue_reference_t::type>; + }; + template + struct copy_cvref_ + { + using type = std::add_rvalue_reference_t::type>; + }; + template + struct copy_cvref_ + { + using type = std::add_const_t::type>; + }; + template + struct copy_cvref_ + { + using type = std::add_volatile_t::type>; + }; + template + struct copy_cvref_ + { + using type = std::add_cv_t::type>; + }; + + template + struct remove_lvalue_ref_ + { + using type = T; + }; + template + struct remove_lvalue_ref_ + { + using type = T; + }; } template inline constexpr bool is_implicit_lifetime_type = POXY_IMPLEMENTATION_DETAIL(detail::is_implicit_lifetime_type_::value); + template + using copy_cvref = POXY_IMPLEMENTATION_DETAIL(typename detail::copy_cvref_, CopyFrom>::type); + + template + using remove_lvalue_ref = POXY_IMPLEMENTATION_DETAIL(typename detail::remove_lvalue_ref_::type); + template using index_constant = std::integral_constant(Value)>; @@ -1444,7 +1519,7 @@ namespace soagen } template - using table_type = POXY_IMPLEMENTATION_DETAIL(typename detail::table_type_>::type); + using table_type = POXY_IMPLEMENTATION_DETAIL(typename detail::table_type_>::type); template inline constexpr bool same_table_type = @@ -1460,8 +1535,7 @@ namespace soagen } template - using table_traits_type = - POXY_IMPLEMENTATION_DETAIL(typename detail::table_traits_type_>::type); + using table_traits_type = POXY_IMPLEMENTATION_DETAIL(typename detail::table_traits_type_>::type); namespace detail { @@ -1473,11 +1547,11 @@ namespace soagen } template - using allocator_type = POXY_IMPLEMENTATION_DETAIL(typename detail::allocator_type_>::type); + using allocator_type = POXY_IMPLEMENTATION_DETAIL(typename detail::allocator_type_>::type); template using value_type = POXY_IMPLEMENTATION_DETAIL( - typename table_traits_type::template column(Column)>::value_type); + typename table_traits_type>::template column(Column)>::value_type); namespace detail { @@ -1631,9 +1705,6 @@ namespace soagen namespace soagen { - template - struct row; - namespace detail { // general rules for allowing implicit conversions: @@ -1688,7 +1759,7 @@ namespace soagen template inline constexpr bool row_implicit_conversion_ok, // row> = - implicit_conversion_ok + implicit_conversion_ok, coerce_ref> && column_conversion_ok, std::index_sequence>; // row explicit conversions: @@ -1698,7 +1769,7 @@ namespace soagen template inline constexpr bool row_explicit_conversion_ok, // row> = - explicit_conversion_ok + explicit_conversion_ok, coerce_ref> && column_conversion_ok, std::index_sequence>; } @@ -1708,23 +1779,23 @@ namespace soagen template struct SOAGEN_EMPTY_BASES row // - SOAGEN_HIDDEN_BASE(public detail::column_ref..., public row_base>) + SOAGEN_HIDDEN_BASE(public detail::column_ref, Columns>..., + public row_base, Columns...>>) { - static_assert(std::is_reference_v
, - "Table must be a reference so row members can derive their reference category"); - static_assert(std::is_empty_v>>, + static_assert(std::is_empty_v, Columns...>>>, "row_base specializations may not have data members"); - static_assert(std::is_trivial_v>>, "row_base specializations must be trivial"); + static_assert(std::is_trivial_v, Columns...>>>, + "row_base specializations must be trivial"); // columns: template SOAGEN_PURE_INLINE_GETTER constexpr decltype(auto) column() const noexcept { - static_assert(static_cast(Column) < table_traits_type>::column_count, + static_assert(static_cast(Column) < table_traits_type
::column_count, "column index out of range"); - return detail::column_ref(Column)>::get_ref(); + return detail::column_ref, static_cast(Column)>::get_ref(); } // tuple protocol: @@ -1734,26 +1805,24 @@ namespace soagen { static_assert(Member < sizeof...(Columns), "member index out of range"); - return type_at_index...>::get_ref(); + return type_at_index, Columns>...>::get_ref(); } - SOAGEN_CONSTRAINED_TEMPLATE((same_table_type - && table_traits_type>::all_equality_comparable), + SOAGEN_CONSTRAINED_TEMPLATE((same_table_type && table_traits_type
::all_equality_comparable), typename T) SOAGEN_NODISCARD friend constexpr bool operator==(const row& lhs, const row& rhs) // - noexcept(table_traits_type>::all_nothrow_equality_comparable) + noexcept(table_traits_type
::all_nothrow_equality_comparable) { return ((lhs.template column() == rhs.template column()) && ...); } - SOAGEN_CONSTRAINED_TEMPLATE((same_table_type - && table_traits_type>::all_equality_comparable), + SOAGEN_CONSTRAINED_TEMPLATE((same_table_type && table_traits_type
::all_equality_comparable), typename T) SOAGEN_NODISCARD SOAGEN_ALWAYS_INLINE friend constexpr bool operator!=(const row& lhs, const row& rhs) // - noexcept(table_traits_type>::all_nothrow_equality_comparable) + noexcept(table_traits_type
::all_nothrow_equality_comparable) { return !(lhs == rhs); } @@ -1762,7 +1831,7 @@ namespace soagen template SOAGEN_NODISCARD static constexpr int row_compare_impl(const row& lhs, const row& rhs) // - noexcept(table_traits_type>::all_nothrow_less_than_comparable) + noexcept(table_traits_type
::all_nothrow_less_than_comparable) { if (lhs.template get() < rhs.template get()) return -1; @@ -1777,42 +1846,38 @@ namespace soagen } public: - SOAGEN_CONSTRAINED_TEMPLATE((same_table_type - && table_traits_type>::all_less_than_comparable), + SOAGEN_CONSTRAINED_TEMPLATE((same_table_type && table_traits_type
::all_less_than_comparable), typename T) SOAGEN_NODISCARD friend constexpr bool operator<(const row& lhs, const row& rhs) // - noexcept(table_traits_type>::all_nothrow_less_than_comparable) + noexcept(table_traits_type
::all_nothrow_less_than_comparable) { return row_compare_impl<0>(lhs, rhs) < 0; } - SOAGEN_CONSTRAINED_TEMPLATE((same_table_type - && table_traits_type>::all_less_than_comparable), + SOAGEN_CONSTRAINED_TEMPLATE((same_table_type && table_traits_type
::all_less_than_comparable), typename T) SOAGEN_NODISCARD friend constexpr bool operator<=(const row& lhs, const row& rhs) // - noexcept(table_traits_type>::all_nothrow_less_than_comparable) + noexcept(table_traits_type
::all_nothrow_less_than_comparable) { return row_compare_impl<0>(lhs, rhs) <= 0; } - SOAGEN_CONSTRAINED_TEMPLATE((same_table_type - && table_traits_type>::all_less_than_comparable), + SOAGEN_CONSTRAINED_TEMPLATE((same_table_type && table_traits_type
::all_less_than_comparable), typename T) SOAGEN_NODISCARD friend constexpr bool operator>(const row& lhs, const row& rhs) // - noexcept(table_traits_type>::all_nothrow_less_than_comparable) + noexcept(table_traits_type
::all_nothrow_less_than_comparable) { return row_compare_impl<0>(lhs, rhs) > 0; } - SOAGEN_CONSTRAINED_TEMPLATE((same_table_type - && table_traits_type>::all_less_than_comparable), + SOAGEN_CONSTRAINED_TEMPLATE((same_table_type && table_traits_type
::all_less_than_comparable), typename T) SOAGEN_NODISCARD friend constexpr bool operator>=(const row& lhs, const row& rhs) // - noexcept(table_traits_type>::all_nothrow_less_than_comparable) + noexcept(table_traits_type
::all_nothrow_less_than_comparable) { return row_compare_impl<0>(lhs, rhs) >= 0; } @@ -1840,18 +1905,6 @@ namespace soagen } }; - template - inline constexpr bool is_row = POXY_IMPLEMENTATION_DETAIL(false); - - template - inline constexpr bool is_row> = true; - template - inline constexpr bool is_row = is_row; - template - inline constexpr bool is_row = is_row; - template - inline constexpr bool is_row = is_row; - namespace detail { template @@ -1870,17 +1923,32 @@ namespace soagen template struct row_type_> { - using type = row; + using type = row, Columns...>; }; template struct row_type_> - : row_type_>::column_count>> + : row_type_, + std::make_index_sequence>::column_count>> {}; } template using row_type = POXY_IMPLEMENTATION_DETAIL( - typename detail::row_type_, std::index_sequence(Columns)...>>::type); + typename detail::row_type_, + std::index_sequence(Columns)...>>::type); + + namespace detail + { + template + struct is_row_ : std::false_type + {}; + template + struct is_row_> : std::true_type + {}; + } + + template + inline constexpr bool is_row = POXY_IMPLEMENTATION_DETAIL(detail::is_row_>::value); } namespace std @@ -2557,17 +2625,19 @@ namespace soagen template emplacer(Args&&...) -> emplacer; - template - inline constexpr bool is_emplacer = POXY_IMPLEMENTATION_DETAIL(false); + namespace detail + { + template + struct is_emplacer_ : std::false_type + {}; + template + struct is_emplacer_> : std::true_type + {}; + } - template - inline constexpr bool is_emplacer> = true; - template - inline constexpr bool is_emplacer = is_emplacer; template - inline constexpr bool is_emplacer = is_emplacer; - template - inline constexpr bool is_emplacer = is_emplacer; + + inline constexpr bool is_emplacer = POXY_IMPLEMENTATION_DETAIL(detail::is_emplacer_>::value); } namespace std @@ -3577,26 +3647,28 @@ namespace soagen using default_emplace_type = make_cref; }; - template - inline constexpr bool is_column_traits = POXY_IMPLEMENTATION_DETAIL(false); + namespace detail + { + template + struct is_column_traits_ : std::false_type + {}; + template + struct is_column_traits_> : std::true_type + {}; + template + struct is_column_traits_> : std::true_type + {}; + } - template - inline constexpr bool is_column_traits> = true; - template - inline constexpr bool is_column_traits> = true; - template - inline constexpr bool is_column_traits = is_column_traits; template - inline constexpr bool is_column_traits = is_column_traits; - template - inline constexpr bool is_column_traits = is_column_traits; + inline constexpr bool is_column_traits = + POXY_IMPLEMENTATION_DETAIL(detail::is_column_traits_>::value); } namespace soagen::detail { template struct to_base_traits_; - template struct to_base_traits_> { @@ -3604,9 +3676,26 @@ namespace soagen::detail static_assert(std::is_base_of_v>); }; - template using to_base_traits = typename to_base_traits_::type; + + template + struct as_column_ + { + using type = column_traits; + }; + template + struct as_column_> + { + using type = column_traits; + }; + template + struct as_column_> + { + using type = detail::column_traits_base; + }; + template + using as_column = typename as_column_::type; } #undef soagen_aligned_storage @@ -3699,6 +3788,8 @@ namespace soagen::detail template struct table_traits_base, Columns...> { + static_assert((... && is_column_traits), "columns must be instances of soagen::column_traits"); + static_assert(std::is_same_v, std::make_index_sequence>, "index sequence must match columns"); @@ -4432,22 +4523,24 @@ namespace soagen::detail //--- swap columns --------------------------------------------------------------------------------------------- - template + template static constexpr bool can_swap_columns = - A == B || (std::is_same_v, storage_type> && column::is_swappable); + static_cast(A) == static_cast(B) + || (std::is_same_v, storage_type> && column::is_swappable); - template + template static constexpr bool can_nothrow_swap_columns = - A == B || (std::is_same_v, storage_type> && column::is_nothrow_swappable); + static_cast(A) == static_cast(B) + || (std::is_same_v, storage_type> && column::is_nothrow_swappable); - SOAGEN_HIDDEN_CONSTRAINT((can_swap_columns), size_t A, size_t B) + SOAGEN_HIDDEN_CONSTRAINT((can_swap_columns), auto A, auto B) SOAGEN_CPP20_CONSTEXPR static void swap_columns([[maybe_unused]] column_pointers& columns, [[maybe_unused]] size_t start, [[maybe_unused]] size_t count) // noexcept(can_nothrow_swap_columns) { - if constexpr (A != B) + if constexpr (static_cast(A) != static_cast(B)) { static_assert(std::is_same_v, storage_type>); static_assert(column::is_swappable); @@ -4508,6 +4601,8 @@ namespace soagen::detail struct table_traits_base_specialized, Columns...> // : public table_traits_base, to_base_traits...> { + static_assert((... && is_column_traits), "columns must be instances of soagen::column_traits"); + template static constexpr bool for_each_column_invocable = (is_invocable_with_optional_index struct SOAGEN_EMPTY_BASES table_traits // - SOAGEN_HIDDEN_BASE( - public detail::table_traits_base_specialized, Columns...>) + SOAGEN_HIDDEN_BASE(public detail::table_traits_base_specialized, + detail::as_column...>) { static constexpr size_t column_count = sizeof...(Columns); static_assert(column_count, "tables must have at least one column"); - static_assert((... && is_column_traits), "columns must be instances of soagen::column_traits"); - static constexpr size_t aligned_stride = lcm(size_t{ 1 }, Columns::aligned_stride...); + static constexpr size_t aligned_stride = lcm(size_t{ 1 }, detail::as_column::aligned_stride...); // columns // (note that these hide the base class typedefs - this is intentional) template - using column = type_at_index(Index), Columns...>; + using column = type_at_index(Index), detail::as_column...>; template - using column_from_ic = type_at_index(IndexConstant::value), Columns...>; + using column_from_ic = type_at_index(IndexConstant::value), detail::as_column...>; - static constexpr size_t column_alignments[column_count] = { Columns::alignment... }; + static constexpr size_t column_alignments[column_count] = { detail::as_column::alignment... }; - static constexpr size_t largest_alignment = max(size_t{ 1 }, Columns::alignment...); + static constexpr size_t largest_alignment = max(size_t{ 1 }, detail::as_column::alignment...); - static constexpr bool rvalue_type_list_is_distinct = POXY_IMPLEMENTATION_DETAIL( - !(std::is_same_v && ...)); + static constexpr bool rvalue_type_list_is_distinct = + POXY_IMPLEMENTATION_DETAIL(!(std::is_same_v::param_type, + typename detail::as_column::rvalue_type> + && ...)); template static constexpr bool emplace_back_is_nothrow = @@ -4563,11 +4659,11 @@ namespace soagen template static constexpr bool push_back_is_nothrow = - emplace_back_is_nothrow; + emplace_back_is_nothrow::param_forward_type...>; template static constexpr bool rvalue_push_back_is_nothrow = - emplace_back_is_nothrow; + emplace_back_is_nothrow::rvalue_forward_type...>; template static constexpr bool row_push_back_is_nothrow = emplace_back_is_nothrow; @@ -4579,29 +4675,32 @@ namespace soagen template static constexpr bool insert_is_nothrow = - emplace_is_nothrow; + emplace_is_nothrow::param_forward_type...>; template static constexpr bool rvalue_insert_is_nothrow = - emplace_is_nothrow; + emplace_is_nothrow::rvalue_forward_type...>; template static constexpr bool row_insert_is_nothrow = emplace_is_nothrow; }; - template - inline constexpr bool is_table_traits = POXY_IMPLEMENTATION_DETAIL(false); + namespace detail + { + template + struct is_table_traits_ : std::false_type + {}; + template + struct is_table_traits_> : std::true_type + {}; + template + struct is_table_traits_> : std::true_type + {}; + } - template - inline constexpr bool is_table_traits> = true; - template - inline constexpr bool is_table_traits> = true; template - inline constexpr bool is_table_traits = is_table_traits; - template - inline constexpr bool is_table_traits = is_table_traits; - template - inline constexpr bool is_table_traits = is_table_traits; + inline constexpr bool is_table_traits = + POXY_IMPLEMENTATION_DETAIL(detail::is_table_traits_>::value); } namespace soagen::detail @@ -4609,7 +4708,7 @@ namespace soagen::detail template struct to_base_traits_> { - using type = table_traits_base...>; + using type = table_traits_base>...>; static_assert(std::is_base_of_v>); }; @@ -4621,275 +4720,585 @@ namespace soagen::detail }; } -//******** table.hpp ************************************************************************************************* +//******** iterator.hpp ********************************************************************************************** -namespace soagen::detail +namespace soagen { - SOAGEN_CONSTRAINED_TEMPLATE(is_unsigned, typename T) - SOAGEN_NODISCARD - constexpr bool add_without_overflowing(T lhs, T rhs, T& result) noexcept + namespace detail { - if (lhs > static_cast(-1) - rhs) - return false; + template + struct arrow_proxy + { + mutable T value; - result = lhs + rhs; - return true; - }; + SOAGEN_PURE_INLINE_GETTER + constexpr T* operator->() const noexcept + { + return &value; + } + }; - inline static constexpr size_t min_actual_column_alignment = - max(size_t{ __STDCPP_DEFAULT_NEW_ALIGNMENT__ }, alignof(std::max_align_t), size_t{ 16 }); + template + struct iterator_storage + { + remove_cvref
* table; + typename remove_cvref
::difference_type offset; + }; + } - // trait for determining the _actual_ alignment of a table column, taking the allocator and - // table-allocation semantics into account (since the full allocation for a table always has - // alignment == table_traits::largest_alignment). - // - // note that this has absolutely nothing to do with the aligned_stride; that is still calculated - // according to the user's specified alignment requirements. this trait is _only_ used - // to help the compiler via assume_aligned. - template - inline constexpr size_t actual_column_alignment = - max(Traits::template column::alignment, min_actual_column_alignment); + template + struct SOAGEN_EMPTY_BASES iterator_base + {}; - template - inline constexpr size_t actual_column_alignment = - max(Traits::template column<0>::alignment, - allocator_traits::min_alignment, - Traits::largest_alignment, - min_actual_column_alignment); + template + class SOAGEN_EMPTY_BASES iterator + SOAGEN_HIDDEN_BASE(protected detail::iterator_storage>>, + public iterator_base, Columns...>>) + { + static_assert(std::is_empty_v, Columns...>>>, + "iterator_base specializations may not have data members"); + static_assert(std::is_trivial_v, Columns...>>>, + "iterator_base specializations must be trivial"); - //------------------------------------------------------------------------------------------------------------------ - // generic allocation class for tracking the column pointers and the actual size in bytes - //------------------------------------------------------------------------------------------------------------------ + public: + using table_type = soagen::table_type>; + static_assert(is_table, "soagen iterators are for use with soagen SoA types."); - template - struct table_allocation - { - using column_pointers = std::byte* [ColumnCount]; - using const_column_pointers = std::byte* const[ColumnCount]; + using table_ref = coerce_ref>; - column_pointers columns; - size_t size_in_bytes; + using size_type = typename table_type::size_type; - SOAGEN_PURE_INLINE_GETTER - explicit constexpr operator bool() const noexcept - { - return !!columns[0]; - } - }; + using difference_type = typename table_type::difference_type; - //------------------------------------------------------------------------------------------------------------------ - // base class for handling most allocation-related boilerplate - //------------------------------------------------------------------------------------------------------------------ + using row_type = soagen::row_type, Columns...>; - template - class table_storage - { - static_assert(ColumnCount, "tables must have at least one column"); - static_assert(!is_cvref, "allocators may not be cvref-qualified"); + using value_type = row_type; - public: - using size_type = size_t; - using difference_type = ptrdiff_t; - using allocator_type = Allocator; + using reference = row_type; - protected: - using allocation = table_allocation; + using iterator_category = std::random_access_iterator_tag; - allocation alloc_ = {}; - size_t count_ = {}; - compressed_pair capacity_; +#if SOAGEN_CPP <= 17 + using pointer = void; +#endif + + private: + using base = detail::iterator_storage>>; + + template + friend class soagen::iterator; + + SOAGEN_NODISCARD_CTOR + constexpr iterator(base b) noexcept // + : base{ b } + {} public: SOAGEN_NODISCARD_CTOR - explicit constexpr table_storage(const Allocator& alloc) noexcept // - : capacity_{ size_t{}, alloc } - { - static_assert(std::is_nothrow_copy_constructible_v, - "allocators must be nothrow copy-constructible"); - } + constexpr iterator() noexcept = default; SOAGEN_NODISCARD_CTOR - explicit constexpr table_storage(Allocator&& alloc) noexcept // - : capacity_{ size_t{}, static_cast(alloc) } + constexpr iterator(table_ref tbl, difference_type pos) noexcept // + : base{ const_cast(&tbl), pos } + {} + + friend constexpr iterator& operator++(iterator& it) noexcept // pre { - static_assert(std::is_nothrow_move_constructible_v, - "allocators must be nothrow move-constructible"); + ++it.offset; + return it; } - SOAGEN_NODISCARD_CTOR - constexpr table_storage(table_storage&& other) noexcept // - : alloc_{ std::exchange(other.alloc_, allocation{}) }, - count_{ std::exchange(other.count_, size_t{}) }, - capacity_{ std::exchange(other.capacity_.first(), size_t{}), static_cast(other.allocator()) } + friend constexpr iterator operator++(iterator& it, int) noexcept // post { - static_assert(std::is_nothrow_move_constructible_v, - "allocators must be nothrow move-constructible"); + iterator pre = it; + ++it.offset; + return pre; } - // conditionally-implemented in specialized child classes: - table_storage() = delete; - table_storage& operator=(table_storage&&) = delete; - table_storage(const table_storage&) = delete; - table_storage& operator=(const table_storage&) = delete; - - ~table_storage() noexcept + friend constexpr iterator& operator+=(iterator& it, difference_type n) noexcept { - static_assert(std::is_nothrow_destructible_v, "allocators must be nothrow destructible"); - - // element destructors are run in a more specialized child class + it.offset += n; + return it; + } - if (alloc_) - deallocate(alloc_); + SOAGEN_PURE_GETTER + friend constexpr iterator operator+(const iterator& it, difference_type n) noexcept + { + auto it2 = it; + it2 += n; + return it2; } - SOAGEN_PURE_INLINE_GETTER - constexpr size_t size() const noexcept + friend constexpr iterator& operator--(iterator& it) noexcept // pre { - return count_; + --it.offset; + return it; } - SOAGEN_PURE_INLINE_GETTER - constexpr bool empty() const noexcept + friend constexpr iterator operator--(iterator& it, int) noexcept // post { - return !count_; + iterator pre = it; + --it.offset; + return pre; } - SOAGEN_PURE_INLINE_GETTER - constexpr size_t capacity() const noexcept + friend constexpr iterator& operator-=(iterator& it, difference_type n) noexcept { - return capacity_.first(); + return it += (-n); } SOAGEN_PURE_INLINE_GETTER - constexpr size_t allocation_size() const noexcept + friend constexpr iterator operator-(const iterator& it, difference_type n) noexcept { - return alloc_.size_in_bytes; + return it + (-n); } - SOAGEN_INLINE_GETTER - SOAGEN_CPP20_CONSTEXPR - Allocator get_allocator() const noexcept + SOAGEN_CONSTRAINED_TEMPLATE((same_table_type), typename T, size_t... Cols) + SOAGEN_PURE_GETTER + constexpr difference_type operator-(const iterator& rhs) const noexcept { - return capacity_.second(); + return base::offset - rhs.offset; } - protected: - SOAGEN_INLINE_GETTER - constexpr Allocator& allocator() noexcept + private: + template + using cv_value_type = + conditionally_add_volatile, Column>, + std::is_const_v>>, + std::is_volatile_v>>; + + template + using cv_value_ref = std::conditional_t, + std::add_rvalue_reference_t>, + std::add_lvalue_reference_t>>; + + public: + SOAGEN_PURE_GETTER + constexpr reference operator*() const noexcept { - return capacity_.second(); + SOAGEN_ASSUME(!!base::table); + SOAGEN_ASSUME(base::offset >= 0); + + return row_type{ { static_cast>( + base::table->template column()[base::offset]) }... }; } - SOAGEN_INLINE_GETTER - constexpr const Allocator& allocator() const noexcept + SOAGEN_PURE_INLINE_GETTER + constexpr detail::arrow_proxy operator->() const noexcept { - return capacity_.second(); + return { *(*this) }; } - static constexpr bool allocate_is_nothrow = - noexcept(allocator_traits::allocate(std::declval(), size_t{}, std::align_val_t{})); + SOAGEN_PURE_GETTER + constexpr reference operator[](difference_type offset) const noexcept + { + SOAGEN_ASSUME(!!base::table); + SOAGEN_ASSUME(base::offset + offset >= 0); - // guard against allocators with incorrect pointer typedefs where possible - using allocator_pointer_type = std::remove_reference_t< - decltype(allocator_traits::allocate(std::declval(), size_t{}, std::align_val_t{}))>; - static_assert(std::is_pointer_v); + return row_type{ { static_cast>( + base::table->template column()[base::offset + offset]) }... }; + } - SOAGEN_NODISCARD - constexpr allocation allocate(size_t n_bytes, size_t alignment) noexcept(allocate_is_nothrow) + SOAGEN_CONSTRAINED_TEMPLATE((same_table_type), typename T, size_t... Cols) + SOAGEN_PURE_GETTER + constexpr bool operator==(const iterator& rhs) const noexcept { - SOAGEN_ASSUME(n_bytes); - SOAGEN_ASSUME((static_cast(alignment) & (static_cast(alignment) - 1u)) == 0u); - - const auto ptr = soagen::assume_aligned::min_alignment>( - allocator_traits::allocate( - allocator(), - n_bytes, - std::align_val_t{ max(alignment, allocator_traits::min_alignment) })); + return base::table == rhs.table && base::offset == rhs.offset; + } - SOAGEN_ASSUME(ptr != nullptr); - std::memset(ptr, 0, n_bytes); + SOAGEN_CONSTRAINED_TEMPLATE((same_table_type), typename T, size_t... Cols) + SOAGEN_PURE_INLINE_GETTER + friend constexpr bool operator!=(const iterator& lhs, const iterator& rhs) noexcept + { + return !(lhs == rhs); + } - if constexpr (std::is_same_v) - return { { ptr }, n_bytes }; - else - return { { reinterpret_cast(ptr) }, n_bytes }; + SOAGEN_CONSTRAINED_TEMPLATE((same_table_type), typename T, size_t... Cols) + SOAGEN_PURE_INLINE_GETTER + constexpr bool operator<(const iterator& rhs) const noexcept + { + return base::offset < rhs.offset; } - constexpr void deallocate(const allocation& al) noexcept + SOAGEN_CONSTRAINED_TEMPLATE((same_table_type), typename T, size_t... Cols) + SOAGEN_PURE_INLINE_GETTER + constexpr bool operator<=(const iterator& rhs) const noexcept { - SOAGEN_ASSUME(al.columns[0]); - SOAGEN_ASSUME(al.size_in_bytes); + return base::offset <= rhs.offset; + } - if constexpr (std::is_same_v) - allocator_traits::deallocate(allocator(), al.columns[0], al.size_in_bytes); - else - allocator_traits::deallocate(allocator(), - reinterpret_cast(al.columns[0]), - al.size_in_bytes); + SOAGEN_CONSTRAINED_TEMPLATE((same_table_type), typename T, size_t... Cols) + SOAGEN_PURE_INLINE_GETTER + constexpr bool operator>(const iterator& rhs) const noexcept + { + return base::offset > rhs.offset; } - }; - //------------------------------------------------------------------------------------------------------------------ - // specialization: default-constructibility - //------------------------------------------------------------------------------------------------------------------ + SOAGEN_CONSTRAINED_TEMPLATE((same_table_type), typename T, size_t... Cols) + SOAGEN_PURE_INLINE_GETTER + constexpr bool operator>=(const iterator& rhs) const noexcept + { + return base::offset >= rhs.offset; + } -#undef SOAGEN_BASE_NAME -#define SOAGEN_BASE_NAME table_storage -#undef SOAGEN_BASE_TYPE -#define SOAGEN_BASE_TYPE SOAGEN_BASE_NAME + SOAGEN_CONSTRAINED_TEMPLATE((detail::implicit_conversion_ok, coerce_ref> + && !detail::explicit_conversion_ok, coerce_ref>), + typename T, + size_t... Cols) + SOAGEN_PURE_INLINE_GETTER + constexpr operator iterator() const noexcept + { + return iterator{ static_cast(*this) }; + } - template > - class SOAGEN_EMPTY_BASES table_default_construct // - : public SOAGEN_BASE_TYPE - { - public: - using SOAGEN_BASE_TYPE::SOAGEN_BASE_NAME; + SOAGEN_CONSTRAINED_TEMPLATE((!detail::implicit_conversion_ok, coerce_ref> + && detail::explicit_conversion_ok, coerce_ref>), + typename T, + size_t... Cols) + SOAGEN_PURE_INLINE_GETTER + explicit constexpr operator iterator() const noexcept + { + return iterator{ static_cast(*this) }; + } - SOAGEN_DEFAULT_RULE_OF_FOUR(table_default_construct); + SOAGEN_PURE_INLINE_GETTER + explicit constexpr operator difference_type() const noexcept + { + return base::offset; + } - SOAGEN_NODISCARD_CTOR - constexpr table_default_construct() noexcept // - : SOAGEN_BASE_TYPE{ Allocator{} } + SOAGEN_PURE_INLINE_GETTER + explicit constexpr operator size_type() const noexcept { - static_assert(std::is_nothrow_default_constructible_v, - "allocators must be nothrow default-constructible, or not default-constructible at all"); + SOAGEN_ASSUME(base::offset >= 0); + + return static_cast(base::offset); } }; - template - class SOAGEN_EMPTY_BASES table_default_construct // - : public SOAGEN_BASE_TYPE + template + SOAGEN_PURE_INLINE_GETTER + constexpr iterator operator+(typename iterator::difference_type n, + const iterator& it) noexcept { - public: - using SOAGEN_BASE_TYPE::SOAGEN_BASE_NAME; + return it + n; + } - SOAGEN_DEFAULT_RULE_OF_FIVE(table_default_construct); - }; + namespace detail + { + template + struct table_type_> + { + using type = remove_cvref
; + }; + template + struct table_traits_type_> + { + using type = table_traits_type>; + }; - //------------------------------------------------------------------------------------------------------------------ - // specialization: move-assignment (un-typed base parts only) - //------------------------------------------------------------------------------------------------------------------ + template + struct iterator_type_; + template + struct iterator_type_> + { + using type = iterator, Columns...>; + }; + template + struct iterator_type_> + : iterator_type_, + std::make_index_sequence>::column_count>> + {}; + } -#undef SOAGEN_BASE_NAME -#define SOAGEN_BASE_NAME table_default_construct + template + using iterator_type = POXY_IMPLEMENTATION_DETAIL( + typename detail::iterator_type_, + std::index_sequence(Columns)...>>::type); - template ::propagate_on_container_move_assignment::value - || std::is_move_assignable_v> - class SOAGEN_EMPTY_BASES table_move_assignable_base // - : public SOAGEN_BASE_TYPE + namespace detail { - private: - using base = SOAGEN_BASE_TYPE; + template + struct is_iterator_ : std::false_type + {}; + template + struct is_iterator_> : std::true_type + {}; + } - protected: - using allocation = table_allocation; + template + inline constexpr bool is_iterator = POXY_IMPLEMENTATION_DETAIL(detail::is_iterator_>::value); +} - constexpr bool move_from_by_taking_ownership(table_move_assignable_base&& rhs) noexcept - { +//******** table.hpp ************************************************************************************************* + +namespace soagen::detail +{ + SOAGEN_CONSTRAINED_TEMPLATE(is_unsigned, typename T) + SOAGEN_NODISCARD + constexpr bool add_without_overflowing(T lhs, T rhs, T& result) noexcept + { + if (lhs > static_cast(-1) - rhs) + return false; + + result = lhs + rhs; + return true; + }; + + inline static constexpr size_t min_actual_column_alignment = + max(size_t{ __STDCPP_DEFAULT_NEW_ALIGNMENT__ }, alignof(std::max_align_t), size_t{ 16 }); + + // trait for determining the _actual_ alignment of a table column, taking the allocator and + // table-allocation semantics into account (since the full allocation for a table always has + // alignment == table_traits::largest_alignment). + // + // note that this has absolutely nothing to do with the aligned_stride; that is still calculated + // according to the user's specified alignment requirements. this trait is _only_ used + // to help the compiler via assume_aligned. + template + inline constexpr size_t actual_column_alignment = + max(Traits::template column::alignment, min_actual_column_alignment); + + template + inline constexpr size_t actual_column_alignment = + max(Traits::template column<0>::alignment, + allocator_traits::min_alignment, + Traits::largest_alignment, + min_actual_column_alignment); + + //------------------------------------------------------------------------------------------------------------------ + // generic allocation class for tracking the column pointers and the actual size in bytes + //------------------------------------------------------------------------------------------------------------------ + + template + struct table_allocation + { + using column_pointers = std::byte* [ColumnCount]; + using const_column_pointers = std::byte* const[ColumnCount]; + + column_pointers columns; + size_t size_in_bytes; + + SOAGEN_PURE_INLINE_GETTER + explicit constexpr operator bool() const noexcept + { + return !!columns[0]; + } + }; + + //------------------------------------------------------------------------------------------------------------------ + // base class for handling most allocation-related boilerplate + //------------------------------------------------------------------------------------------------------------------ + + template + class table_storage + { + static_assert(ColumnCount, "tables must have at least one column"); + static_assert(!is_cvref, "allocators may not be cvref-qualified"); + + public: + using size_type = size_t; + using difference_type = ptrdiff_t; + using allocator_type = Allocator; + + protected: + using allocation = table_allocation; + + allocation alloc_ = {}; + size_t count_ = {}; + compressed_pair capacity_; + + public: + SOAGEN_NODISCARD_CTOR + explicit constexpr table_storage(const Allocator& alloc) noexcept // + : capacity_{ size_t{}, alloc } + { + static_assert(std::is_nothrow_copy_constructible_v, + "allocators must be nothrow copy-constructible"); + } + + SOAGEN_NODISCARD_CTOR + explicit constexpr table_storage(Allocator&& alloc) noexcept // + : capacity_{ size_t{}, static_cast(alloc) } + { + static_assert(std::is_nothrow_move_constructible_v, + "allocators must be nothrow move-constructible"); + } + + SOAGEN_NODISCARD_CTOR + constexpr table_storage(table_storage&& other) noexcept // + : alloc_{ std::exchange(other.alloc_, allocation{}) }, + count_{ std::exchange(other.count_, size_t{}) }, + capacity_{ std::exchange(other.capacity_.first(), size_t{}), static_cast(other.allocator()) } + { + static_assert(std::is_nothrow_move_constructible_v, + "allocators must be nothrow move-constructible"); + } + + // conditionally-implemented in specialized child classes: + table_storage() = delete; + table_storage& operator=(table_storage&&) = delete; + table_storage(const table_storage&) = delete; + table_storage& operator=(const table_storage&) = delete; + + ~table_storage() noexcept + { + static_assert(std::is_nothrow_destructible_v, "allocators must be nothrow destructible"); + + // element destructors are run in a more specialized child class + + if (alloc_) + deallocate(alloc_); + } + + SOAGEN_PURE_INLINE_GETTER + constexpr size_t size() const noexcept + { + return count_; + } + + SOAGEN_PURE_INLINE_GETTER + constexpr bool empty() const noexcept + { + return !count_; + } + + SOAGEN_PURE_INLINE_GETTER + constexpr size_t capacity() const noexcept + { + return capacity_.first(); + } + + SOAGEN_PURE_INLINE_GETTER + constexpr size_t allocation_size() const noexcept + { + return alloc_.size_in_bytes; + } + + SOAGEN_INLINE_GETTER + SOAGEN_CPP20_CONSTEXPR + Allocator get_allocator() const noexcept + { + return capacity_.second(); + } + + protected: + SOAGEN_INLINE_GETTER + constexpr Allocator& allocator() noexcept + { + return capacity_.second(); + } + + SOAGEN_INLINE_GETTER + constexpr const Allocator& allocator() const noexcept + { + return capacity_.second(); + } + + static constexpr bool allocate_is_nothrow = + noexcept(allocator_traits::allocate(std::declval(), size_t{}, std::align_val_t{})); + + // guard against allocators with incorrect pointer typedefs where possible + using allocator_pointer_type = std::remove_reference_t< + decltype(allocator_traits::allocate(std::declval(), size_t{}, std::align_val_t{}))>; + static_assert(std::is_pointer_v); + + SOAGEN_NODISCARD + constexpr allocation allocate(size_t n_bytes, size_t alignment) noexcept(allocate_is_nothrow) + { + SOAGEN_ASSUME(n_bytes); + SOAGEN_ASSUME((static_cast(alignment) & (static_cast(alignment) - 1u)) == 0u); + + const auto ptr = soagen::assume_aligned::min_alignment>( + allocator_traits::allocate( + allocator(), + n_bytes, + std::align_val_t{ max(alignment, allocator_traits::min_alignment) })); + + SOAGEN_ASSUME(ptr != nullptr); + std::memset(ptr, 0, n_bytes); + + if constexpr (std::is_same_v) + return { { ptr }, n_bytes }; + else + return { { reinterpret_cast(ptr) }, n_bytes }; + } + + constexpr void deallocate(const allocation& al) noexcept + { + SOAGEN_ASSUME(al.columns[0]); + SOAGEN_ASSUME(al.size_in_bytes); + + if constexpr (std::is_same_v) + allocator_traits::deallocate(allocator(), al.columns[0], al.size_in_bytes); + else + allocator_traits::deallocate(allocator(), + reinterpret_cast(al.columns[0]), + al.size_in_bytes); + } + }; + + //------------------------------------------------------------------------------------------------------------------ + // specialization: default-constructibility + //------------------------------------------------------------------------------------------------------------------ + +#undef SOAGEN_BASE_NAME +#define SOAGEN_BASE_NAME table_storage +#undef SOAGEN_BASE_TYPE +#define SOAGEN_BASE_TYPE SOAGEN_BASE_NAME + + template > + class SOAGEN_EMPTY_BASES table_default_construct // + : public SOAGEN_BASE_TYPE + { + public: + using SOAGEN_BASE_TYPE::SOAGEN_BASE_NAME; + + SOAGEN_DEFAULT_RULE_OF_FOUR(table_default_construct); + + SOAGEN_NODISCARD_CTOR + constexpr table_default_construct() noexcept // + : SOAGEN_BASE_TYPE{ Allocator{} } + { + static_assert(std::is_nothrow_default_constructible_v, + "allocators must be nothrow default-constructible, or not default-constructible at all"); + } + }; + + template + class SOAGEN_EMPTY_BASES table_default_construct // + : public SOAGEN_BASE_TYPE + { + public: + using SOAGEN_BASE_TYPE::SOAGEN_BASE_NAME; + + SOAGEN_DEFAULT_RULE_OF_FIVE(table_default_construct); + }; + + //------------------------------------------------------------------------------------------------------------------ + // specialization: move-assignment (un-typed base parts only) + //------------------------------------------------------------------------------------------------------------------ + +#undef SOAGEN_BASE_NAME +#define SOAGEN_BASE_NAME table_default_construct + + template ::propagate_on_container_move_assignment::value + || std::is_move_assignable_v> + class SOAGEN_EMPTY_BASES table_move_assignable_base // + : public SOAGEN_BASE_TYPE + { + private: + using base = SOAGEN_BASE_TYPE; + + protected: + using allocation = table_allocation; + + constexpr bool move_from_by_taking_ownership(table_move_assignable_base&& rhs) noexcept + { SOAGEN_ASSUME(&rhs != this); const auto take_ownership = [&]() noexcept @@ -5411,14 +5820,14 @@ namespace soagen::detail && actual_column_alignment == actual_column_alignment); public: - template + template SOAGEN_CPP20_CONSTEXPR void swap_columns() // - noexcept(can_nothrow_swap_columns) + noexcept(can_nothrow_swap_columns(A), static_cast(B)>) { - static_assert(can_swap_columns); + static_assert(can_swap_columns(A), static_cast(B)>); - if constexpr (A != B) + if constexpr (static_cast(A) != static_cast(B)) { using storage_a = typename Traits::template storage_type; using storage_b = typename Traits::template storage_type; @@ -5426,15 +5835,17 @@ namespace soagen::detail // if they have the same base alignment, we can just swap the two pointers // rather than having to do an element-wise swap - if constexpr (actual_column_alignment - == actual_column_alignment) + if constexpr (actual_column_alignment(A)> + == actual_column_alignment(B)>) { - std::swap(base::alloc_.columns[A], base::alloc_.columns[B]); + std::swap(base::alloc_.columns[static_cast(A)], + base::alloc_.columns[static_cast(B)]); } - else { - Traits::template swap_columns(base::alloc_.columns, {}, base::count_); + Traits::template swap_columns(A), static_cast(B)>(base::alloc_.columns, + {}, + base::count_); } } } @@ -6022,12 +6433,247 @@ namespace soagen::detail SOAGEN_DEFAULT_RULE_OF_FIVE(table_data_ptr); }; -} + + //------------------------------------------------------------------------------------------------------------------ + // specialization: functions that require the column indices + //------------------------------------------------------------------------------------------------------------------ #undef SOAGEN_BASE_NAME #define SOAGEN_BASE_NAME table_data_ptr + + template + class table_row_and_column_funcs; + + template + class table_row_and_column_funcs> // + : public SOAGEN_BASE_TYPE + { + static_assert(std::is_same_v, std::make_index_sequence>); + + private: + using base = SOAGEN_BASE_TYPE; + + public: + using SOAGEN_BASE_TYPE::SOAGEN_BASE_NAME; + + SOAGEN_DEFAULT_RULE_OF_FIVE(table_row_and_column_funcs); + + using size_type = std::size_t; + using difference_type = std::ptrdiff_t; + using allocator_type = Allocator; + + using table_type = table; + using table_traits = Traits; + template + using column_traits = typename table_traits::template column(Column)>; + template + using column_type = typename column_traits(Column)>::value_type; + + using iterator = soagen::iterator_type; + using const_iterator = soagen::iterator_type; + using rvalue_iterator = soagen::iterator_type; + + using row_type = soagen::row_type; + using const_row_type = soagen::row_type; + using rvalue_row_type = soagen::row_type; + + template + SOAGEN_ALIGNED_COLUMN(Column) + constexpr column_type* column() noexcept + { + static_assert(static_cast(Column) < table_traits::column_count, "column index out of range"); + + using column = column_traits(Column)>; + using storage_type = typename column::storage_type; + using value_type = typename column::value_type; + + SOAGEN_CPP23_STATIC_CONSTEXPR size_t align = + detail::actual_column_alignment(Column)>; + + if constexpr (std::is_pointer_v) + { + static_assert(std::is_same_v); + + return soagen::assume_aligned( + SOAGEN_LAUNDER(reinterpret_cast(base::alloc_.columns[static_cast(Column)]))); + } + else + { + static_assert(std::is_same_v>); + + return soagen::assume_aligned(column::ptr(base::alloc_.columns[static_cast(Column)])); + } + } + + template + SOAGEN_ALIGNED_COLUMN(Column) + constexpr std::add_const_t>* column() const noexcept + { + return const_cast(*this).template column(Column)>(); + } + + template + SOAGEN_PURE_GETTER + SOAGEN_CPP20_CONSTEXPR + soagen::row_type row(size_type index) & noexcept + { + if constexpr (sizeof...(Cols)) + { + return { { this->template column(Cols)>()[index] }... }; + } + else + { + return { { this->template column(Columns)>()[index] }... }; + } + } + + template + SOAGEN_PURE_GETTER + SOAGEN_CPP20_CONSTEXPR + soagen::row_type row(size_type index) && noexcept + { + if constexpr (sizeof...(Cols)) + { + return { { std::move(this->template column(Cols)>()[index]) }... }; + } + else + { + return { { std::move(this->template column(Columns)>()[index]) }... }; + } + } + + template + SOAGEN_PURE_GETTER + SOAGEN_CPP20_CONSTEXPR + soagen::row_type row(size_type index) const& noexcept + { + if constexpr (sizeof...(Cols)) + { + return { { this->template column(Cols)>()[index] }... }; + } + else + { + return { { this->template column(Columns)>()[index] }... }; + } + } + + SOAGEN_PURE_INLINE_GETTER + SOAGEN_CPP20_CONSTEXPR + row_type operator[](size_type index) & noexcept + { + return (*this).row(index); + } + + SOAGEN_PURE_INLINE_GETTER + SOAGEN_CPP20_CONSTEXPR + rvalue_row_type operator[](size_type index) && noexcept + { + return std::move(*this).row(index); + } + + SOAGEN_PURE_INLINE_GETTER + SOAGEN_CPP20_CONSTEXPR + const_row_type operator[](size_type index) const& noexcept + { + return (*this).row(index); + } + + SOAGEN_PURE_GETTER + SOAGEN_CPP20_CONSTEXPR + row_type at(size_type index) & + { +#if SOAGEN_HAS_EXCEPTIONS + if (index >= base::size()) + throw std::out_of_range{ "bad element access" }; +#endif + return (*this).row(index); + } + + SOAGEN_PURE_GETTER + SOAGEN_CPP20_CONSTEXPR + rvalue_row_type at(size_type index) && + { +#if SOAGEN_HAS_EXCEPTIONS + if (index >= base::size()) + throw std::out_of_range{ "bad element access" }; +#endif + return std::move(*this).row(index); + } + + SOAGEN_PURE_GETTER + SOAGEN_CPP20_CONSTEXPR + const_row_type at(size_type index) const& + { +#if SOAGEN_HAS_EXCEPTIONS + if (index >= base::size()) + throw std::out_of_range{ "bad element access" }; +#endif + return (*this).row(index); + } + + SOAGEN_PURE_INLINE_GETTER + SOAGEN_CPP20_CONSTEXPR + row_type front() & noexcept + { + return (*this).row(0u); + } + + SOAGEN_PURE_INLINE_GETTER + SOAGEN_CPP20_CONSTEXPR + row_type back() & noexcept + { + return (*this).row(base::size() - 1u); + } + + SOAGEN_PURE_INLINE_GETTER + SOAGEN_CPP20_CONSTEXPR + rvalue_row_type front() && noexcept + { + return std::move(*this).row(0u); + } + + SOAGEN_PURE_INLINE_GETTER + SOAGEN_CPP20_CONSTEXPR + rvalue_row_type back() && noexcept + { + return std::move(*this).row(base::size() - 1u); + } + + SOAGEN_PURE_INLINE_GETTER + SOAGEN_CPP20_CONSTEXPR + const_row_type front() const& noexcept + { + return (*this).row(0u); + } + + SOAGEN_PURE_INLINE_GETTER + SOAGEN_CPP20_CONSTEXPR + const_row_type back() const& noexcept + { + return (*this).row(base::size() - 1u); + } + + template + constexpr void for_each_column(Func&& func) // + noexcept(table_traits::template for_each_column_nothrow_invocable) + { + (invoke_with_optional_index(static_cast(func), this->template column()), ...); + } + + template + constexpr void for_each_column(Func&& func) const // + noexcept(table_traits::template for_each_column_nothrow_invocable) + { + (invoke_with_optional_index(static_cast(func), this->template column()), ...); + } + }; +} + +#undef SOAGEN_BASE_NAME +#define SOAGEN_BASE_NAME table_row_and_column_funcs #undef SOAGEN_BASE_TYPE -#define SOAGEN_BASE_TYPE detail::SOAGEN_BASE_NAME +#define SOAGEN_BASE_TYPE \ + detail::SOAGEN_BASE_NAME::column_count>> namespace soagen { @@ -6053,19 +6699,34 @@ namespace soagen using base = SOAGEN_BASE_TYPE; public: - using size_type = size_t; - using difference_type = ptrdiff_t; + using size_type = std::size_t; - using table_traits = Traits; + using difference_type = std::ptrdiff_t; using allocator_type = Allocator; + using table_traits = Traits; + + static constexpr size_type column_count = table_traits::column_count; + template using column_traits = typename table_traits::template column(Column)>; template using column_type = typename column_traits(Column)>::value_type; + using iterator = soagen::iterator_type
; + + using const_iterator = soagen::iterator_type; + + using rvalue_iterator = soagen::iterator_type; + + using row_type = soagen::row_type
; + + using const_row_type = soagen::row_type; + + using rvalue_row_type = soagen::row_type; + static constexpr size_t aligned_stride = Traits::aligned_stride; SOAGEN_NODISCARD_CTOR @@ -6085,54 +6746,63 @@ namespace soagen using SOAGEN_BASE_TYPE::SOAGEN_BASE_NAME; - template - SOAGEN_ALIGNED_COLUMN(Column) - constexpr column_type* column() noexcept + template + SOAGEN_PURE_INLINE_GETTER + constexpr soagen::iterator_type begin() & noexcept { - static_assert(static_cast(Column) < table_traits::column_count, "column index out of range"); + return { *this, 0 }; + } - using column = column_traits(Column)>; - using storage_type = typename column::storage_type; - using value_type = typename column::value_type; + template + SOAGEN_PURE_INLINE_GETTER + constexpr soagen::iterator_type end() & noexcept + { + return { *this, static_cast(base::size()) }; + } - SOAGEN_CPP23_STATIC_CONSTEXPR size_t align = - detail::actual_column_alignment(Column)>; + template + SOAGEN_PURE_INLINE_GETTER + constexpr soagen::iterator_type begin() && noexcept + { + return { static_cast(*this), 0 }; + } - if constexpr (std::is_pointer_v) - { - static_assert(std::is_same_v); + template + SOAGEN_PURE_INLINE_GETTER + constexpr soagen::iterator_type end() && noexcept + { + return { static_cast(*this), static_cast(base::size()) }; + } - return soagen::assume_aligned( - SOAGEN_LAUNDER(reinterpret_cast(base::alloc_.columns[static_cast(Column)]))); - } - else - { - static_assert(std::is_same_v>); + template + SOAGEN_PURE_INLINE_GETTER + constexpr soagen::iterator_type begin() const& noexcept + { + return { *this, 0 }; + } - return soagen::assume_aligned(column::ptr(base::alloc_.columns[static_cast(Column)])); - } + template + SOAGEN_PURE_INLINE_GETTER + constexpr soagen::iterator_type end() const& noexcept + { + return { *this, static_cast(base::size()) }; } - template - SOAGEN_ALIGNED_COLUMN(Column) - constexpr std::add_const_t>* column() const noexcept + template + SOAGEN_PURE_INLINE_GETTER + constexpr soagen::iterator_type cbegin() const noexcept + { + return { *this, 0 }; + } + + template + SOAGEN_PURE_INLINE_GETTER + constexpr soagen::iterator_type cend() const noexcept { - return const_cast(*this).template column(Column)>(); + return { *this, static_cast(base::size()) }; } }; - template - inline constexpr bool is_table = POXY_IMPLEMENTATION_DETAIL(false); - - template - inline constexpr bool is_table> = true; - template - inline constexpr bool is_table = is_table; - template - inline constexpr bool is_table = is_table; - template - inline constexpr bool is_table = is_table; - SOAGEN_CONSTRAINED_TEMPLATE((has_swap_member>), typename... Args) SOAGEN_ALWAYS_INLINE constexpr void swap(table& lhs, table& rhs) // @@ -6150,17 +6820,29 @@ namespace soagen::detail using type = table; }; - template typename Transformation = soagen::identity_type> + template + struct table_traits_type_> + { + using type = Traits; + }; + + template + struct allocator_type_> + { + using type = Allocator; + }; + + template struct unnamed_ref { protected: - Transformation val_; + T val_; SOAGEN_PURE_INLINE_GETTER constexpr decltype(auto) get_ref() const noexcept { - if constexpr (std::is_reference_v>) - return static_cast>(val_); + if constexpr (std::is_reference_v) + return static_cast(val_); else return val_; } @@ -6176,7 +6858,7 @@ namespace soagen::detail }; template - struct column_ref&, Column> + struct column_ref, Column> : unnamed_ref, Column>>> {}; @@ -6186,477 +6868,180 @@ namespace soagen::detail {}; template - struct column_ref&, Column> + struct column_ref, Column> : unnamed_ref, Column>>>> {}; template struct column_ref&&, Column> : unnamed_ref, Column>>>> - {} -} - -#undef SOAGEN_BASE_NAME -#undef SOAGEN_BASE_TYPE - -//******** mixins.hpp ************************************************************************************************ - -namespace soagen::mixins -{ - //--- resize() ----------------------------------------------------------------------------------------------------- - - template >> - struct SOAGEN_EMPTY_BASES resizable - { - using table_type = soagen::table_type; - using size_type = typename table_type::size_type; - - SOAGEN_ALWAYS_INLINE - SOAGEN_CPP20_CONSTEXPR - Derived& resize(size_type new_size) // - noexcept(soagen::has_nothrow_resize_member) - { - static_cast(*this).table().resize(new_size); - return static_cast(*this); - } - }; - - template - struct SOAGEN_EMPTY_BASES resizable - {}; - - //--- swap() ------------------------------------------------------------------------------------------------------- - - template >> - struct SOAGEN_EMPTY_BASES swappable - { - using table_type = soagen::table_type; - - SOAGEN_ALWAYS_INLINE - SOAGEN_CPP20_CONSTEXPR - void swap(Derived& other) // - noexcept(soagen::has_nothrow_swap_member) - { - return static_cast(*this).table().swap(other.table()); - } - }; - - template - struct SOAGEN_EMPTY_BASES swappable - {}; - - //--- equality-comparable------------------------------------------------------------------------------------------- - - template >> - struct SOAGEN_EMPTY_BASES equality_comparable - { - using table_type = soagen::table_type; - - SOAGEN_NODISCARD - SOAGEN_ALWAYS_INLINE - friend constexpr bool operator==(const Derived& lhs, const Derived& rhs) // - noexcept(soagen::is_nothrow_equality_comparable) - { - return lhs.table() == rhs.table(); - } - - SOAGEN_NODISCARD - SOAGEN_ALWAYS_INLINE - friend constexpr bool operator!=(const Derived& lhs, const Derived& rhs) // - noexcept(soagen::is_nothrow_equality_comparable) - { - return lhs.table() != rhs.table(); - } - }; - - template - struct SOAGEN_EMPTY_BASES equality_comparable - {}; - - //--- less-than-comparable------------------------------------------------------------------------------------------- - - template >> - struct SOAGEN_EMPTY_BASES less_than_comparable - { - using table_type = soagen::table_type; - - SOAGEN_NODISCARD - SOAGEN_ALWAYS_INLINE - friend constexpr bool operator<(const Derived& lhs, const Derived& rhs) // - noexcept(soagen::is_nothrow_less_than_comparable) - { - return lhs.table() < rhs.table(); - } - - SOAGEN_NODISCARD - SOAGEN_ALWAYS_INLINE - friend constexpr bool operator<=(const Derived& lhs, const Derived& rhs) // - noexcept(soagen::is_nothrow_less_than_comparable) - { - return lhs.table() <= rhs.table(); - } - - SOAGEN_NODISCARD - SOAGEN_ALWAYS_INLINE - friend constexpr bool operator>(const Derived& lhs, const Derived& rhs) // - noexcept(soagen::is_nothrow_less_than_comparable) - { - return lhs.table() > rhs.table(); - } - - SOAGEN_NODISCARD - SOAGEN_ALWAYS_INLINE - friend constexpr bool operator>=(const Derived& lhs, const Derived& rhs) // - noexcept(soagen::is_nothrow_less_than_comparable) - { - return lhs.table() >= rhs.table(); - } - }; - - template - struct SOAGEN_EMPTY_BASES less_than_comparable - {}; - - //--- data() ------------------------------------------------------------------------------------------------------- - - template >> - struct SOAGEN_EMPTY_BASES data_ptr - { - using table_type = soagen::table_type; - using table_traits = soagen::table_traits_type; - using allocator_type = soagen::allocator_type; - - SOAGEN_ALIGNED_COLUMN(0) - constexpr std::byte* data() // - noexcept(soagen::has_nothrow_data_member) - { - return soagen::assume_aligned>( - static_cast(*this).table().data()); - } - }; - - template - struct SOAGEN_EMPTY_BASES data_ptr - {}; - - //--- data() (const) ----------------------------------------------------------------------------------------------- - - template >> - struct SOAGEN_EMPTY_BASES const_data_ptr - { - using table_type = soagen::table_type; - using table_traits = soagen::table_traits_type; - using allocator_type = soagen::allocator_type; - - SOAGEN_ALIGNED_COLUMN(0) - constexpr const std::byte* data() const // - noexcept(soagen::has_nothrow_data_member) - { - return soagen::assume_aligned>( - static_cast(*this).table().data()); - } - }; - - template - struct SOAGEN_EMPTY_BASES const_data_ptr {}; } -//******** iterator.hpp ********************************************************************************************** - -namespace soagen -{ - namespace detail - { - template - struct arrow_proxy - { - mutable T value; - - SOAGEN_PURE_INLINE_GETTER - constexpr T* operator->() const noexcept - { - return &value; - } - }; - - template - struct iterator_storage - { - std::add_const_t>* table; - typename remove_cvref
::difference_type offset; - }; - } - - template - struct SOAGEN_EMPTY_BASES iterator_base - {}; - - template - class SOAGEN_EMPTY_BASES iterator - SOAGEN_HIDDEN_BASE(protected detail::iterator_storage>, - public iterator_base>) - { - static_assert(std::is_empty_v>>, - "iterator_base specializations may not have data members"); - static_assert(std::is_trivial_v>>, - "iterator_base specializations must be trivial"); - - public: - using table_type = remove_cvref
; - static_assert(is_soa, "soagen iterators are for use with soagen-generated SoA table types."); - - using table_ref = Table; - static_assert(std::is_reference_v, - "Table must be a reference so row members can derive their reference category"); - - using size_type = typename table_type::size_type; - - using difference_type = typename table_type::difference_type; - - using row_type = row; - - using value_type = row_type; - - using reference = row_type; - - using iterator_category = std::random_access_iterator_tag; - -#if SOAGEN_CPP <= 17 - using pointer = void; -#endif - - private: - using base = detail::iterator_storage>; - using table_ptr = std::add_pointer_t>; - - template - friend class soagen::iterator; - - SOAGEN_NODISCARD_CTOR - constexpr iterator(base b) noexcept // - : base{ b } - {} - - public: - SOAGEN_NODISCARD_CTOR - constexpr iterator() noexcept = default; - - SOAGEN_NODISCARD_CTOR - constexpr iterator(table_ref tbl, difference_type pos) noexcept // - : base{ &tbl, pos } - {} - - friend constexpr iterator& operator++(iterator& it) noexcept // pre - { - ++it.offset; - return it; - } +#undef SOAGEN_BASE_NAME +#undef SOAGEN_BASE_TYPE - friend constexpr iterator operator++(iterator& it, int) noexcept // post - { - iterator pre = it; - ++it.offset; - return pre; - } +//******** mixins.hpp ************************************************************************************************ - friend constexpr iterator& operator+=(iterator& it, difference_type n) noexcept - { - it.offset += n; - return it; - } +namespace soagen::mixins +{ + //--- resize() ----------------------------------------------------------------------------------------------------- - SOAGEN_PURE_GETTER - friend constexpr iterator operator+(const iterator& it, difference_type n) noexcept - { - auto it2 = it; - it2 += n; - return it2; - } + template >> + struct SOAGEN_EMPTY_BASES resizable + { + using table_type = soagen::table_type; + using size_type = typename table_type::size_type; - friend constexpr iterator& operator--(iterator& it) noexcept // pre + SOAGEN_ALWAYS_INLINE + SOAGEN_CPP20_CONSTEXPR + Derived& resize(size_type new_size) // + noexcept(soagen::has_nothrow_resize_member) { - --it.offset; - return it; + static_cast(*this).table().resize(new_size); + return static_cast(*this); } + }; - friend constexpr iterator operator--(iterator& it, int) noexcept // post - { - iterator pre = it; - --it.offset; - return pre; - } + template + struct SOAGEN_EMPTY_BASES resizable + {}; - friend constexpr iterator& operator-=(iterator& it, difference_type n) noexcept - { - return it += (-n); - } + //--- swap() ------------------------------------------------------------------------------------------------------- - SOAGEN_PURE_INLINE_GETTER - friend constexpr iterator operator-(const iterator& it, difference_type n) noexcept - { - return it + (-n); - } + template >> + struct SOAGEN_EMPTY_BASES swappable + { + using table_type = soagen::table_type; - SOAGEN_CONSTRAINED_TEMPLATE((same_table_type), typename T, size_t... Cols) - SOAGEN_PURE_GETTER - constexpr difference_type operator-(const iterator& rhs) const noexcept + SOAGEN_ALWAYS_INLINE + SOAGEN_CPP20_CONSTEXPR + void swap(Derived& other) // + noexcept(soagen::has_nothrow_swap_member) { - return base::offset - rhs.offset; + return static_cast(*this).table().swap(other.table()); } + }; - SOAGEN_PURE_GETTER - constexpr reference operator*() const noexcept - { - SOAGEN_ASSUME(!!base::table); - SOAGEN_ASSUME(base::offset >= 0); + template + struct SOAGEN_EMPTY_BASES swappable + {}; - return static_cast(*const_cast(base::table)) - .template row(static_cast(base::offset)); - } + //--- equality-comparable------------------------------------------------------------------------------------------- - SOAGEN_PURE_INLINE_GETTER - constexpr detail::arrow_proxy operator->() const noexcept - { - return { *(*this) }; - } + template >> + struct SOAGEN_EMPTY_BASES equality_comparable + { + using table_type = soagen::table_type; - SOAGEN_PURE_GETTER - constexpr reference operator[](difference_type offset) const noexcept + SOAGEN_NODISCARD + SOAGEN_ALWAYS_INLINE + friend constexpr bool operator==(const Derived& lhs, const Derived& rhs) // + noexcept(soagen::is_nothrow_equality_comparable) { - SOAGEN_ASSUME(!!base::table); - SOAGEN_ASSUME(base::offset + offset >= 0); - - return static_cast(*const_cast(base::table)) - .template row(static_cast(base::offset + offset)); + return lhs.table() == rhs.table(); } - SOAGEN_CONSTRAINED_TEMPLATE((same_table_type), typename T, size_t... Cols) - SOAGEN_PURE_GETTER - constexpr bool operator==(const iterator& rhs) const noexcept + SOAGEN_NODISCARD + SOAGEN_ALWAYS_INLINE + friend constexpr bool operator!=(const Derived& lhs, const Derived& rhs) // + noexcept(soagen::is_nothrow_equality_comparable) { - return base::table == rhs.table && base::offset == rhs.offset; + return lhs.table() != rhs.table(); } + }; - SOAGEN_CONSTRAINED_TEMPLATE((same_table_type), typename T, size_t... Cols) - SOAGEN_PURE_INLINE_GETTER - friend constexpr bool operator!=(const iterator& lhs, const iterator& rhs) noexcept - { - return !(lhs == rhs); - } + template + struct SOAGEN_EMPTY_BASES equality_comparable + {}; - SOAGEN_CONSTRAINED_TEMPLATE((same_table_type), typename T, size_t... Cols) - SOAGEN_PURE_INLINE_GETTER - constexpr bool operator<(const iterator& rhs) const noexcept - { - return base::offset < rhs.offset; - } + //--- less-than-comparable------------------------------------------------------------------------------------------- - SOAGEN_CONSTRAINED_TEMPLATE((same_table_type), typename T, size_t... Cols) - SOAGEN_PURE_INLINE_GETTER - constexpr bool operator<=(const iterator& rhs) const noexcept - { - return base::offset <= rhs.offset; - } + template >> + struct SOAGEN_EMPTY_BASES less_than_comparable + { + using table_type = soagen::table_type; - SOAGEN_CONSTRAINED_TEMPLATE((same_table_type), typename T, size_t... Cols) - SOAGEN_PURE_INLINE_GETTER - constexpr bool operator>(const iterator& rhs) const noexcept + SOAGEN_NODISCARD + SOAGEN_ALWAYS_INLINE + friend constexpr bool operator<(const Derived& lhs, const Derived& rhs) // + noexcept(soagen::is_nothrow_less_than_comparable) { - return base::offset > rhs.offset; + return lhs.table() < rhs.table(); } - SOAGEN_CONSTRAINED_TEMPLATE((same_table_type), typename T, size_t... Cols) - SOAGEN_PURE_INLINE_GETTER - constexpr bool operator>=(const iterator& rhs) const noexcept + SOAGEN_NODISCARD + SOAGEN_ALWAYS_INLINE + friend constexpr bool operator<=(const Derived& lhs, const Derived& rhs) // + noexcept(soagen::is_nothrow_less_than_comparable) { - return base::offset >= rhs.offset; + return lhs.table() <= rhs.table(); } - SOAGEN_CONSTRAINED_TEMPLATE((detail::implicit_conversion_ok - && !detail::explicit_conversion_ok), - typename T, - size_t... Cols) - SOAGEN_PURE_INLINE_GETTER - constexpr operator iterator() const noexcept + SOAGEN_NODISCARD + SOAGEN_ALWAYS_INLINE + friend constexpr bool operator>(const Derived& lhs, const Derived& rhs) // + noexcept(soagen::is_nothrow_less_than_comparable) { - return iterator{ static_cast(*this) }; + return lhs.table() > rhs.table(); } - SOAGEN_CONSTRAINED_TEMPLATE((!detail::implicit_conversion_ok - && detail::explicit_conversion_ok), - typename T, - size_t... Cols) - SOAGEN_PURE_INLINE_GETTER - explicit constexpr operator iterator() const noexcept + SOAGEN_NODISCARD + SOAGEN_ALWAYS_INLINE + friend constexpr bool operator>=(const Derived& lhs, const Derived& rhs) // + noexcept(soagen::is_nothrow_less_than_comparable) { - return iterator{ static_cast(*this) }; + return lhs.table() >= rhs.table(); } + }; - SOAGEN_PURE_INLINE_GETTER - explicit constexpr operator difference_type() const noexcept - { - return base::offset; - } + template + struct SOAGEN_EMPTY_BASES less_than_comparable + {}; - SOAGEN_PURE_INLINE_GETTER - explicit constexpr operator size_type() const noexcept - { - SOAGEN_ASSUME(base::offset >= 0); + //--- data() ------------------------------------------------------------------------------------------------------- - return static_cast(base::offset); + template >> + struct SOAGEN_EMPTY_BASES data_ptr + { + using table_type = soagen::table_type; + using table_traits = soagen::table_traits_type; + using allocator_type = soagen::allocator_type; + + SOAGEN_ALIGNED_COLUMN(0) + constexpr std::byte* data() // + noexcept(soagen::has_nothrow_data_member) + { + return soagen::assume_aligned>( + static_cast(*this).table().data()); } }; - template - SOAGEN_PURE_INLINE_GETTER - constexpr iterator operator+(typename iterator::difference_type n, - const iterator& it) noexcept - { - return it + n; - } - - template - inline constexpr bool is_iterator = POXY_IMPLEMENTATION_DETAIL(false); + template + struct SOAGEN_EMPTY_BASES data_ptr + {}; - template - inline constexpr bool is_iterator> = true; - template - inline constexpr bool is_iterator = is_row; - template - inline constexpr bool is_iterator = is_row; - template - inline constexpr bool is_iterator = is_row; + //--- data() (const) ----------------------------------------------------------------------------------------------- - namespace detail + template >> + struct SOAGEN_EMPTY_BASES const_data_ptr { - template - struct table_type_> - { - using type = remove_cvref
; - }; - template - struct table_traits_type_> - { - using type = table_traits_type>; - }; + using table_type = soagen::table_type; + using table_traits = soagen::table_traits_type; + using allocator_type = soagen::allocator_type; - template - struct iterator_type_; - template - struct iterator_type_> + SOAGEN_ALIGNED_COLUMN(0) + constexpr const std::byte* data() const // + noexcept(soagen::has_nothrow_data_member) { - using type = iterator; - }; - template - struct iterator_type_> - : iterator_type_>::column_count>> - {}; - } + return soagen::assume_aligned>( + static_cast(*this).table().data()); + } + }; - template - using iterator_type = POXY_IMPLEMENTATION_DETAIL( - typename detail::iterator_type_, std::index_sequence>::type); + template + struct SOAGEN_EMPTY_BASES const_data_ptr + {}; } #if SOAGEN_ALWAYS_OPTIMIZE diff --git a/src/soagen/hpp/table.hpp b/src/soagen/hpp/table.hpp index 4206b37..1dadfbf 100644 --- a/src/soagen/hpp/table.hpp +++ b/src/soagen/hpp/table.hpp @@ -7,6 +7,8 @@ #include "generated/compressed_pair.hpp" #include "allocator.hpp" #include "table_traits.hpp" +#include "row.hpp" +#include "iterator.hpp" #include "header_start.hpp" /// @cond @@ -798,14 +800,14 @@ namespace soagen::detail && actual_column_alignment == actual_column_alignment); public: - template + template SOAGEN_CPP20_CONSTEXPR void swap_columns() // - noexcept(can_nothrow_swap_columns) + noexcept(can_nothrow_swap_columns(A), static_cast(B)>) { - static_assert(can_swap_columns); + static_assert(can_swap_columns(A), static_cast(B)>); - if constexpr (A != B) + if constexpr (static_cast(A) != static_cast(B)) { using storage_a = typename Traits::template storage_type; using storage_b = typename Traits::template storage_type; @@ -813,15 +815,17 @@ namespace soagen::detail // if they have the same base alignment, we can just swap the two pointers // rather than having to do an element-wise swap - if constexpr (actual_column_alignment - == actual_column_alignment) + if constexpr (actual_column_alignment(A)> + == actual_column_alignment(B)>) { - std::swap(base::alloc_.columns[A], base::alloc_.columns[B]); + std::swap(base::alloc_.columns[static_cast(A)], + base::alloc_.columns[static_cast(B)]); } - else { - Traits::template swap_columns(base::alloc_.columns, {}, base::count_); + Traits::template swap_columns(A), static_cast(B)>(base::alloc_.columns, + {}, + base::count_); } } } @@ -1409,12 +1413,248 @@ namespace soagen::detail SOAGEN_DEFAULT_RULE_OF_FIVE(table_data_ptr); }; -} + + //------------------------------------------------------------------------------------------------------------------ + // specialization: functions that require the column indices + //------------------------------------------------------------------------------------------------------------------ #undef SOAGEN_BASE_NAME #define SOAGEN_BASE_NAME table_data_ptr + + template + class table_row_and_column_funcs; + + template + class table_row_and_column_funcs> // + : public SOAGEN_BASE_TYPE + { + static_assert(std::is_same_v, std::make_index_sequence>); + + private: + using base = SOAGEN_BASE_TYPE; + + public: + using SOAGEN_BASE_TYPE::SOAGEN_BASE_NAME; + + SOAGEN_DEFAULT_RULE_OF_FIVE(table_row_and_column_funcs); + + using size_type = std::size_t; + using difference_type = std::ptrdiff_t; + using allocator_type = Allocator; + + using table_type = table; + using table_traits = Traits; + template + using column_traits = typename table_traits::template column(Column)>; + template + using column_type = typename column_traits(Column)>::value_type; + + using iterator = soagen::iterator_type; + using const_iterator = soagen::iterator_type; + using rvalue_iterator = soagen::iterator_type; + + using row_type = soagen::row_type; + using const_row_type = soagen::row_type; + using rvalue_row_type = soagen::row_type; + + template + SOAGEN_ALIGNED_COLUMN(Column) + constexpr column_type* column() noexcept + { + static_assert(static_cast(Column) < table_traits::column_count, "column index out of range"); + + using column = column_traits(Column)>; + using storage_type = typename column::storage_type; + using value_type = typename column::value_type; + + SOAGEN_CPP23_STATIC_CONSTEXPR size_t align = + detail::actual_column_alignment(Column)>; + + if constexpr (std::is_pointer_v) + { + static_assert(std::is_same_v); + + return soagen::assume_aligned( + SOAGEN_LAUNDER(reinterpret_cast(base::alloc_.columns[static_cast(Column)]))); + } + else + { + static_assert(std::is_same_v>); + + return soagen::assume_aligned(column::ptr(base::alloc_.columns[static_cast(Column)])); + } + } + + template + SOAGEN_ALIGNED_COLUMN(Column) + constexpr std::add_const_t>* column() const noexcept + { + return const_cast(*this).template column(Column)>(); + } + + template + SOAGEN_PURE_GETTER + SOAGEN_CPP20_CONSTEXPR + soagen::row_type row(size_type index) & noexcept + { + if constexpr (sizeof...(Cols)) + { + return { { this->template column(Cols)>()[index] }... }; + } + else + { + return { { this->template column(Columns)>()[index] }... }; + } + } + + template + SOAGEN_PURE_GETTER + SOAGEN_CPP20_CONSTEXPR + soagen::row_type row(size_type index) && noexcept + { + if constexpr (sizeof...(Cols)) + { + return { { std::move(this->template column(Cols)>()[index]) }... }; + } + else + { + return { { std::move(this->template column(Columns)>()[index]) }... }; + } + } + + template + SOAGEN_PURE_GETTER + SOAGEN_CPP20_CONSTEXPR + soagen::row_type row(size_type index) const& noexcept + { + if constexpr (sizeof...(Cols)) + { + return { { this->template column(Cols)>()[index] }... }; + } + else + { + return { { this->template column(Columns)>()[index] }... }; + } + } + + SOAGEN_PURE_INLINE_GETTER + SOAGEN_CPP20_CONSTEXPR + row_type operator[](size_type index) & noexcept + { + return (*this).row(index); + } + + SOAGEN_PURE_INLINE_GETTER + SOAGEN_CPP20_CONSTEXPR + rvalue_row_type operator[](size_type index) && noexcept + { + return std::move(*this).row(index); + } + + SOAGEN_PURE_INLINE_GETTER + SOAGEN_CPP20_CONSTEXPR + const_row_type operator[](size_type index) const& noexcept + { + return (*this).row(index); + } + + SOAGEN_PURE_GETTER + SOAGEN_CPP20_CONSTEXPR + row_type at(size_type index) & + { +#if SOAGEN_HAS_EXCEPTIONS + if (index >= base::size()) + throw std::out_of_range{ "bad element access" }; +#endif + return (*this).row(index); + } + + SOAGEN_PURE_GETTER + SOAGEN_CPP20_CONSTEXPR + rvalue_row_type at(size_type index) && + { +#if SOAGEN_HAS_EXCEPTIONS + if (index >= base::size()) + throw std::out_of_range{ "bad element access" }; +#endif + return std::move(*this).row(index); + } + + SOAGEN_PURE_GETTER + SOAGEN_CPP20_CONSTEXPR + const_row_type at(size_type index) const& + { +#if SOAGEN_HAS_EXCEPTIONS + if (index >= base::size()) + throw std::out_of_range{ "bad element access" }; +#endif + return (*this).row(index); + } + + SOAGEN_PURE_INLINE_GETTER + SOAGEN_CPP20_CONSTEXPR + row_type front() & noexcept + { + return (*this).row(0u); + } + + SOAGEN_PURE_INLINE_GETTER + SOAGEN_CPP20_CONSTEXPR + row_type back() & noexcept + { + return (*this).row(base::size() - 1u); + } + + SOAGEN_PURE_INLINE_GETTER + SOAGEN_CPP20_CONSTEXPR + rvalue_row_type front() && noexcept + { + return std::move(*this).row(0u); + } + + SOAGEN_PURE_INLINE_GETTER + SOAGEN_CPP20_CONSTEXPR + rvalue_row_type back() && noexcept + { + return std::move(*this).row(base::size() - 1u); + } + + SOAGEN_PURE_INLINE_GETTER + SOAGEN_CPP20_CONSTEXPR + const_row_type front() const& noexcept + { + return (*this).row(0u); + } + + SOAGEN_PURE_INLINE_GETTER + SOAGEN_CPP20_CONSTEXPR + const_row_type back() const& noexcept + { + return (*this).row(base::size() - 1u); + } + + template + constexpr void for_each_column(Func&& func) // + noexcept(table_traits::template for_each_column_nothrow_invocable) + { + (invoke_with_optional_index(static_cast(func), this->template column()), ...); + } + + template + constexpr void for_each_column(Func&& func) const // + noexcept(table_traits::template for_each_column_nothrow_invocable) + { + (invoke_with_optional_index(static_cast(func), this->template column()), ...); + } + }; + +} + +#undef SOAGEN_BASE_NAME +#define SOAGEN_BASE_NAME table_row_and_column_funcs #undef SOAGEN_BASE_TYPE -#define SOAGEN_BASE_TYPE detail::SOAGEN_BASE_NAME +#define SOAGEN_BASE_TYPE \ + detail::SOAGEN_BASE_NAME::column_count>> /// @endcond @@ -1454,15 +1694,21 @@ namespace soagen /// @endcond public: - using size_type = size_t; - using difference_type = ptrdiff_t; + /// @brief The unsigned integer size type used by tables. + using size_type = std::size_t; - /// @brief The #soagen::table_traits for the the table. - using table_traits = Traits; + /// @brief The signed integer difference type used by tables. + using difference_type = std::ptrdiff_t; /// @brief The allocator used by the table. using allocator_type = Allocator; + /// @brief The #soagen::table_traits for the the table. + using table_traits = Traits; + + /// @brief The number of columns in the table. + static constexpr size_type column_count = table_traits::column_count; + /// @brief Returns the #soagen::column_traits for the column at the specified index. template using column_traits = typename table_traits::template column(Column)>; @@ -1471,6 +1717,24 @@ namespace soagen template using column_type = typename column_traits(Column)>::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 tables. + using row_type = soagen::row_type
; + + /// @brief Const row type used by tables. + using const_row_type = soagen::row_type; + + /// @brief Rvalue row type used by tables. + using rvalue_row_type = soagen::row_type; + /// @copydoc table_traits::aligned_stride static constexpr size_t aligned_stride = Traits::aligned_stride; @@ -1558,14 +1822,6 @@ namespace soagen /// @availability This method is only available when all the column types are move-assignable. soagen::optional unordered_erase(size_type pos) noexcept(...); - /// @brief Constructs a new row in-place at the end of the table. - template - void emplace_back(Args&&... args) noexcept(...); - - /// @brief Constructs a new row in-place at an arbitrary position in the table. - template - void emplace(size_type index, Args&&... args) noexcept(...); - /// @brief Removes the last row(s) from the table. void pop_back(size_type num = 1) noexcept(...); @@ -1579,6 +1835,25 @@ namespace soagen /// @availability This method is only available when #allocator_type is swappable or non-propagating. constexpr void swap(table& other) noexcept(...); + /// @brief Swaps two columns. + /// + /// @availability The two columns must have the same underlying value_type. + template + void swap_columns() noexcept(...); + + /// @} + + /// @name Adding rows + /// @{ + + /// @brief Constructs a new row in-place at the end of the table. + template + void emplace_back(Args&&... args) noexcept(...); + + /// @brief Constructs a new row in-place at an arbitrary position in the table. + template + void emplace(size_type index, Args&&... args) noexcept(...); + /// @} /// @name Equality @@ -1614,12 +1889,9 @@ namespace soagen /// @brief Returns the allocator being used by the table. constexpr allocator_type get_allocator() const noexcept; -#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. @@ -1629,62 +1901,184 @@ namespace soagen /// /// @availability This method is only available when all the column types are trivially-copyable. constexpr const std::byte* data() const noexcept; -#endif /// @brief Returns a pointer to the elements of a specific column. template - SOAGEN_ALIGNED_COLUMN(Column) - constexpr column_type* column() noexcept + constexpr column_type* column() noexcept; + + /// @brief Returns a pointer to the elements of a specific column. + template + constexpr std::add_const_t>* column() const noexcept; + + /// @brief Invokes a function once for each column data pointer. + /// + /// @tparam Func A callable type compatible with one of the following signatures:
    + ///
  • `void(auto*, std::integral_constant)` + ///
  • `void(auto*, size_type)` + ///
  • `void(std::integral_constant, auto*)` + ///
  • `void(size_type, auto*)` + ///
  • `void(auto*)` + ///
+ /// Overload resolution is performed in the order listed above. + /// + /// @param func The callable to invoke. + template + constexpr void for_each_column(Func&& func) noexcept(...); + + /// @brief Invokes a function once for each column data pointer (const overload). + /// + /// @tparam Func A callable type compatible with one of the following signatures:
    + ///
  • `void(auto*, std::integral_constant)` + ///
  • `void(auto*, size_type)` + ///
  • `void(std::integral_constant, auto*)` + ///
  • `void(size_type, auto*)` + ///
  • `void(auto*)` + ///
+ /// Overload resolution is performed in the order listed above. + /// + /// @param func The callable to invoke. + template + constexpr void for_each_column(Func&& func) const noexcept(...); + + /// @} + + /// @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::row_type row(size_type index) & noexcept; + + /// @brief Returns the row at the given index. + row_type operator[](size_type index) & noexcept; + + /// @brief Returns the row at the given index. + /// + /// @throws std::out_of_range + row_type at(size_type index) &; + + /// @brief Returns the very first row in the table. + row_type front() & noexcept; + + /// @brief Returns the very last row in the table. + row_type back() & noexcept; + + /// @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::row_type row(size_type index) && noexcept; + + /// @brief Returns the row at the given index. + rvalue_row_type operator[](size_type index) && noexcept; + + /// @brief Returns the row at the given index. + /// + /// @throws std::out_of_range + rvalue_row_type at(size_type index) &&; + + /// @brief Returns the very first row in the table. + rvalue_row_type front() && noexcept; + + /// @brief Returns the very last row in the table. + rvalue_row_type back() && noexcept; + + /// @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::row_type row(size_type index) const& noexcept; + + /// @brief Returns the row at the given index. + const_row_type operator[](size_type index) const& noexcept; + + /// @brief Returns the row at the given index. + /// + /// @throws std::out_of_range + const_row_type at(size_type index) const&; + + /// @brief Returns the very first row in the table. + const_row_type front() const& noexcept; + + /// @brief Returns the very last row in the table. + const_row_type back() const& noexcept; + + /// @} + +#endif + + /// @name Iterators + /// @{ + + /// @brief Returns an iterator to the first row in the table. + template + SOAGEN_PURE_INLINE_GETTER + constexpr soagen::iterator_type begin() & noexcept { - static_assert(static_cast(Column) < table_traits::column_count, "column index out of range"); + return { *this, 0 }; + } - using column = column_traits(Column)>; - using storage_type = typename column::storage_type; - using value_type = typename column::value_type; + /// @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(base::size()) }; + } - SOAGEN_CPP23_STATIC_CONSTEXPR size_t align = - detail::actual_column_alignment(Column)>; + /// @brief Returns an iterator to the first row in the table. + template + SOAGEN_PURE_INLINE_GETTER + constexpr soagen::iterator_type begin() && noexcept + { + return { static_cast(*this), 0 }; + } - if constexpr (std::is_pointer_v) - { - static_assert(std::is_same_v); + /// @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 { static_cast(*this), static_cast(base::size()) }; + } - return soagen::assume_aligned( - SOAGEN_LAUNDER(reinterpret_cast(base::alloc_.columns[static_cast(Column)]))); - } - else - { - static_assert(std::is_same_v>); + /// @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 }; + } - return soagen::assume_aligned(column::ptr(base::alloc_.columns[static_cast(Column)])); - } + /// @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(base::size()) }; } - /// @brief Returns a pointer to the elements of a specific column. - template - SOAGEN_ALIGNED_COLUMN(Column) - constexpr std::add_const_t>* column() const noexcept + /// @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 const_cast(*this).template column(Column)>(); + return { *this, static_cast(base::size()) }; } /// @} }; - /// @brief True if `T` is an instance of #soagen::table. - template - inline constexpr bool is_table = POXY_IMPLEMENTATION_DETAIL(false); - /// @cond - template - inline constexpr bool is_table> = true; - template - inline constexpr bool is_table = is_table; - template - inline constexpr bool is_table = is_table; - template - inline constexpr bool is_table = is_table; - /// @endcond - /// @brief Swaps the contents of two tables. /// /// @availability This method is only available when the table's `allocator_type` is swappable or non-propagating. @@ -1706,17 +2100,29 @@ namespace soagen::detail using type = table; }; - template typename Transformation = soagen::identity_type> + template + struct table_traits_type_> + { + using type = Traits; + }; + + template + struct allocator_type_> + { + using type = Allocator; + }; + + template struct unnamed_ref { protected: - Transformation val_; + T val_; SOAGEN_PURE_INLINE_GETTER constexpr decltype(auto) get_ref() const noexcept { - if constexpr (std::is_reference_v>) - return static_cast>(val_); + if constexpr (std::is_reference_v) + return static_cast(val_); else return val_; } @@ -1732,7 +2138,7 @@ namespace soagen::detail }; template - struct column_ref&, Column> + struct column_ref, Column> : unnamed_ref, Column>>> {}; @@ -1742,7 +2148,7 @@ namespace soagen::detail {}; template - struct column_ref&, Column> + struct column_ref, Column> : unnamed_ref, Column>>>> {}; diff --git a/src/soagen/hpp/table_traits.hpp b/src/soagen/hpp/table_traits.hpp index 23f0e1f..3a81423 100644 --- a/src/soagen/hpp/table_traits.hpp +++ b/src/soagen/hpp/table_traits.hpp @@ -21,6 +21,8 @@ namespace soagen::detail template struct table_traits_base, Columns...> { + static_assert((... && is_column_traits), "columns must be instances of soagen::column_traits"); + static_assert(std::is_same_v, std::make_index_sequence>, "index sequence must match columns"); @@ -754,22 +756,24 @@ namespace soagen::detail //--- swap columns --------------------------------------------------------------------------------------------- - template + template static constexpr bool can_swap_columns = - A == B || (std::is_same_v, storage_type> && column
::is_swappable); + static_cast(A) == static_cast(B) + || (std::is_same_v, storage_type> && column::is_swappable); - template + template static constexpr bool can_nothrow_swap_columns = - A == B || (std::is_same_v, storage_type> && column::is_nothrow_swappable); + static_cast(A) == static_cast(B) + || (std::is_same_v, storage_type> && column::is_nothrow_swappable); - SOAGEN_HIDDEN_CONSTRAINT((can_swap_columns), size_t A, size_t B) + SOAGEN_HIDDEN_CONSTRAINT((can_swap_columns), auto A, auto B) SOAGEN_CPP20_CONSTEXPR static void swap_columns([[maybe_unused]] column_pointers& columns, [[maybe_unused]] size_t start, [[maybe_unused]] size_t count) // noexcept(can_nothrow_swap_columns) { - if constexpr (A != B) + if constexpr (static_cast(A) != static_cast(B)) { static_assert(std::is_same_v, storage_type>); static_assert(column::is_swappable); @@ -830,6 +834,8 @@ namespace soagen::detail struct table_traits_base_specialized, Columns...> // : public table_traits_base, to_base_traits...> { + static_assert((... && is_column_traits), "columns must be instances of soagen::column_traits"); + template static constexpr bool for_each_column_invocable = (is_invocable_with_optional_index struct SOAGEN_EMPTY_BASES table_traits // - SOAGEN_HIDDEN_BASE( - public detail::table_traits_base_specialized, Columns...>) + SOAGEN_HIDDEN_BASE(public detail::table_traits_base_specialized, + detail::as_column...>) { /// @brief The number of columns in the table. static constexpr size_t column_count = sizeof...(Columns); static_assert(column_count, "tables must have at least one column"); - static_assert((... && is_column_traits), "columns must be instances of soagen::column_traits"); /// @brief The number of rows to advance to maintain the requested `alignment` for every column. /// @@ -877,29 +878,31 @@ namespace soagen /// /// @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_t aligned_stride = lcm(size_t{ 1 }, Columns::aligned_stride...); + static constexpr size_t aligned_stride = lcm(size_t{ 1 }, detail::as_column::aligned_stride...); // columns // (note that these hide the base class typedefs - this is intentional) /// @brief Returns the #soagen::column_traits for the column at the specified index. template - using column = type_at_index(Index), Columns...>; + using column = type_at_index(Index), detail::as_column...>; /// @brief Same as #column but takes an #index_constant. template - using column_from_ic = type_at_index(IndexConstant::value), Columns...>; + using column_from_ic = type_at_index(IndexConstant::value), detail::as_column...>; /// @brief Array containing the `alignment` for each column. - static constexpr size_t column_alignments[column_count] = { Columns::alignment... }; + static constexpr size_t column_alignments[column_count] = { detail::as_column::alignment... }; /// @brief The max `alignment` of all columns in the table. - static constexpr size_t largest_alignment = max(size_t{ 1 }, Columns::alignment...); + static constexpr size_t largest_alignment = max(size_t{ 1 }, detail::as_column::alignment...); /// @brief True if the arguments passed to the rvalue overloads of `push_back()` /// and `insert()` would be distinct from the regular const lvalue overload. - static constexpr bool rvalue_type_list_is_distinct = POXY_IMPLEMENTATION_DETAIL( - !(std::is_same_v && ...)); + static constexpr bool rvalue_type_list_is_distinct = + POXY_IMPLEMENTATION_DETAIL(!(std::is_same_v::param_type, + typename detail::as_column::rvalue_type> + && ...)); /// @brief True if a generated class's `emplace_back()` would be nothrow. /// @tparam BackingTable The backing #soagen::table type. @@ -912,13 +915,13 @@ namespace soagen /// @tparam BackingTable The backing #soagen::table type. template static constexpr bool push_back_is_nothrow = - emplace_back_is_nothrow; + emplace_back_is_nothrow::param_forward_type...>; /// @brief True if a generated class's rvalue `push_back()` would be nothrow. /// @tparam BackingTable The backing #soagen::table type. template static constexpr bool rvalue_push_back_is_nothrow = - emplace_back_is_nothrow; + emplace_back_is_nothrow::rvalue_forward_type...>; /// @brief True if a generated class's row `push_back()` would be nothrow. /// @tparam BackingTable The backing #soagen::table type. @@ -938,13 +941,13 @@ namespace soagen /// @tparam BackingTable The backing #soagen::table type. template static constexpr bool insert_is_nothrow = - emplace_is_nothrow; + emplace_is_nothrow::param_forward_type...>; /// @brief True if a generated class's rvalue `insert()` would be nothrow. /// @tparam BackingTable The backing #soagen::table type. template static constexpr bool rvalue_insert_is_nothrow = - emplace_is_nothrow; + emplace_is_nothrow::rvalue_forward_type...>; /// @brief True if a generated class's row `insert()` would be nothrow. /// @tparam BackingTable The backing #soagen::table type. @@ -969,21 +972,24 @@ namespace soagen #endif }; - /// @brief True if `T` is an instance of #soagen::table_traits. - template - inline constexpr bool is_table_traits = POXY_IMPLEMENTATION_DETAIL(false); /// @cond - template - inline constexpr bool is_table_traits> = true; - template - inline constexpr bool is_table_traits> = true; - template - inline constexpr bool is_table_traits = is_table_traits; - template - inline constexpr bool is_table_traits = is_table_traits; - template - inline constexpr bool is_table_traits = is_table_traits; + namespace detail + { + template + struct is_table_traits_ : std::false_type + {}; + template + struct is_table_traits_> : std::true_type + {}; + template + struct is_table_traits_> : std::true_type + {}; + } /// @endcond + /// @brief True if `T` is a #soagen::table_traits. + template + inline constexpr bool is_table_traits = + POXY_IMPLEMENTATION_DETAIL(detail::is_table_traits_>::value); } /// @cond @@ -992,7 +998,7 @@ namespace soagen::detail template struct to_base_traits_> { - using type = table_traits_base...>; + using type = table_traits_base>...>; static_assert(std::is_base_of_v>); }; diff --git a/src/soagen/main.py b/src/soagen/main.py index 7596baa..b3788af 100644 --- a/src/soagen/main.py +++ b/src/soagen/main.py @@ -455,7 +455,6 @@ def on_flush(o: Writer, s: str) -> str: rep = '\nSOAGEN_DISABLE_WARNINGS;' for inc in includes: rep += f'\n#include <{inc}>' - rep += '\n#if SOAGEN_HAS_EXCEPTIONS\n\t#include \n#endif' rep += '\nSOAGEN_ENABLE_WARNINGS;\n' s = re.sub(EXTERNAL_HEADERS, rep, s) # strip doxygen stuff if we have that disabled diff --git a/src/soagen/struct.py b/src/soagen/struct.py index da34f1d..32ee0aa 100644 --- a/src/soagen/struct.py +++ b/src/soagen/struct.py @@ -193,15 +193,6 @@ def write_forward_declarations(self, o: Writer): with MetaScope(self): o(rf'class {self.type};') - def write_soagen_specializations(self, o: Writer): - with MetaScope(self): - o( - rf''' - template <> - inline constexpr bool is_soa<{self.qualified_name}> = true; - ''' - ) - def write_soagen_detail_specializations(self, o: Writer): with MetaScope(self): max_length = 0 @@ -235,6 +226,10 @@ def write_soagen_detail_specializations(self, o: Writer): {{ using type = {self.allocator}; }}; + + template <> + struct is_soa_<{self.qualified_name}> : std::true_type + {{}}; ''' ) @@ -326,7 +321,7 @@ def doxygen(s: str) -> str: using table_traits = soagen::table_traits_type<{self.name}>; {doxygen(r"@brief The number of columns in the table.")} - static constexpr size_type column_count = soagen::table_traits_type<{self.name}>::column_count; + static constexpr size_type column_count = table_traits::column_count; {doxygen(r"@brief Gets the soagen::column_traits for a specific column of the table.")} template @@ -343,10 +338,10 @@ def doxygen(s: str) -> str: o( rf''' {doxygen(r"@brief Row iterators returned by iterator functions.")} - using iterator = soagen::iterator_type<{self.name}&>; + using iterator = soagen::iterator_type<{self.name}>; {doxygen(r"@brief Row iterators returned by const-qualified iterator functions.")} - using const_iterator = soagen::iterator_type; + using const_iterator = soagen::iterator_type; ''' ) @@ -381,10 +376,10 @@ def doxygen(s: str) -> str: o( rf''' {doxygen(r"@brief Regular (lvalue-qualified) row type used by this class.")} - using row_type = soagen::row_type<{self.name}&>; + using row_type = soagen::row_type<{self.name}>; {doxygen(r"@brief Const row type used by this class.")} - using const_row_type = soagen::row_type; + using const_row_type = soagen::row_type; {doxygen(r"@brief Rvalue row type used by this class.")} using rvalue_row_type = soagen::row_type<{self.name}&&>; @@ -683,7 +678,7 @@ def doxygen(s: str) -> str: 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 {const}iterator{{ table_, static_cast(*moved_pos) }}; return {{}}; }} @@ -713,9 +708,9 @@ def doxygen(s: str) -> str: SOAGEN_ALWAYS_INLINE SOAGEN_CPP20_CONSTEXPR {self.name}& swap_columns() // - noexcept(noexcept(std::declval().template swap_columns(A), static_cast(B)>())) + noexcept(noexcept(std::declval().template swap_columns(A), static_cast(B)>())) {{ - table_.template swap_columns(A), static_cast(B)>(); + table_.template swap_columns(A), static_cast(B)>(); return *this; }} @@ -1188,7 +1183,7 @@ def doxygen(s: str) -> str: template SOAGEN_PURE_GETTER SOAGEN_CPP20_CONSTEXPR - soagen::row_type<{const}{self.name}{ref}, Columns...> row(size_type index) {const}{ref} noexcept + soagen::row_type<{const}{self.name}{'&&' if ref == '&&' else ''}, Columns...> row(size_type index) {const}{ref} noexcept {{ if constexpr (sizeof...(Columns)) {{ @@ -1258,34 +1253,35 @@ def doxygen(s: str) -> str: move_l = 'std::move(' if ref == '&&' else '' move_r = ')' if ref == '&&' else '' - begin = rf'{func}end()' if reverse else rf'{move_l}*this{move_r}, 0' + begin = rf'{func}end()' if reverse else rf'{move_l}table_{move_r}, 0' end = ( rf'{func}begin()' if reverse - else rf'{move_l}*this{move_r}, static_cast(size())' + else rf'{move_l}table_{move_r}, static_cast(size())' ) + func_ref = ref if (self.rvalue_iterators and 'c' not in func) else '' an = 'a reverse' if reverse else 'an' past = 'before' if reverse else 'past' first = 'last' if reverse else 'first' last = 'first' if reverse else 'last' - iterator = rf'soagen::iterator_type<{const}{self.name}{ref}, Columns...>' + iterator = rf'soagen::iterator_type<{const}{self.name}{"&&" if ref == "&&" else ""}, Columns...>' o( rf''' {doxygen(rf"""@brief Returns {an} iterator to the {first} row in the table.""")} - template + template SOAGEN_PURE_INLINE_GETTER - constexpr {iterator} {func}begin() {const}{ref if self.rvalue_iterators else ''} noexcept + constexpr {iterator} {func}begin() {const}{func_ref} noexcept {{ return {{ {begin} }}; }} {doxygen(rf"""@brief Returns {an} iterator to one-{past}-the-{last} row in the table.""")} - template + template SOAGEN_PURE_INLINE_GETTER - constexpr {iterator} {func}end() {const}{ref if self.rvalue_iterators else ''} noexcept + constexpr {iterator} {func}end() {const}{func_ref} noexcept {{ return {{ {end} }}; }} diff --git a/src/soagen/version.txt b/src/soagen/version.txt index 1d0ba9e..8f0916f 100644 --- a/src/soagen/version.txt +++ b/src/soagen/version.txt @@ -1 +1 @@ -0.4.0 +0.5.0 diff --git a/tests/employees.cpp b/tests/employees.cpp index accb3e8..9e57abf 100644 --- a/tests/employees.cpp +++ b/tests/employees.cpp @@ -118,11 +118,11 @@ static_assert(!table_traits::row_nothrow_constructible_from); #define CHECK_ROW_EQ(row, Name, Id, Date_of_birth, Salary, Tag) \ - CHECK((row).name == Name); \ - CHECK((row).id == static_cast(Id)); \ - CHECK((row).date_of_birth == std::tuple Date_of_birth); \ - CHECK((row).salary == static_cast(Salary)); \ - REQUIRE((row).tag == Tag) + CHECK((row).column() == Name); \ + CHECK((row).column() == static_cast(Id)); \ + CHECK((row).column() == std::tuple Date_of_birth); \ + CHECK((row).column() == static_cast(Salary)); \ + REQUIRE((row).column() == Tag) TEST_CASE("employees - general use") { @@ -505,6 +505,13 @@ TEST_CASE("employees - general use") CHECK_ROW_EQ(emp[2], "joe bloggs", 1, (1970, 1, 1), 50000, nullptr); CHECK_ROW_EQ(emp[3], "AAAAAAAAAA", 2, (1980, 2, 2), 40000, &someval); CHECK_ROW_EQ(emp.back(), "AAAAAAAAAA", 2, (1980, 2, 2), 40000, &someval); + + CHECK_ROW_EQ(emp.table().front(), "mark gillard", 0, (1987, 03, 16), 999999, nullptr); + CHECK_ROW_EQ(emp.table()[0], "mark gillard", 0, (1987, 03, 16), 999999, nullptr); + CHECK_ROW_EQ(emp.table()[1], "hot diggity", 3, (1955, 3, 3), 70000, nullptr); + CHECK_ROW_EQ(emp.table()[2], "joe bloggs", 1, (1970, 1, 1), 50000, nullptr); + CHECK_ROW_EQ(emp.table()[3], "AAAAAAAAAA", 2, (1980, 2, 2), 40000, &someval); + CHECK_ROW_EQ(emp.table().back(), "AAAAAAAAAA", 2, (1980, 2, 2), 40000, &someval); } { @@ -668,38 +675,79 @@ TEST_CASE("employees - general use") { INFO("iterating"); - std::vector names; - for (auto&& row : emp) - names.push_back(row.name); - - REQUIRE(names.size() == 4u); - CHECK(names[0] == "BBBBBBBBBB"); - CHECK(names[1] == "mark gillard"); - CHECK(names[2] == "joe bloggs"); - CHECK(names[3] == "AAAAAAAAAA"); - - auto it = emp.begin(); - CHECK_ROW_EQ(*it, "BBBBBBBBBB", 4, (1990, 3, 3), 30000, nullptr); - it++; - CHECK_ROW_EQ(*it, "mark gillard", 0, (1987, 03, 16), 999999, nullptr); - it++; - CHECK_ROW_EQ(*it, "joe bloggs", 1, (1970, 1, 1), 50000, nullptr); - it++; - CHECK_ROW_EQ(*it, "AAAAAAAAAA", 2, (1980, 2, 2), 40000, &someval); - it++; - CHECK(it == emp.end()); - - it--; - CHECK_ROW_EQ(*it, "AAAAAAAAAA", 2, (1980, 2, 2), 40000, &someval); - it--; - CHECK_ROW_EQ(*it, "joe bloggs", 1, (1970, 1, 1), 50000, nullptr); - it--; - CHECK_ROW_EQ(*it, "mark gillard", 0, (1987, 03, 16), 999999, nullptr); - it--; - CHECK_ROW_EQ(*it, "BBBBBBBBBB", 4, (1990, 3, 3), 30000, nullptr); - CHECK(it == emp.begin()); - - CHECK(it->name == "BBBBBBBBBB"); + { + INFO("generated table"); + + std::vector names; + for (auto&& row : emp) + names.push_back(row.name); + + REQUIRE(names.size() == 4u); + CHECK(names[0] == "BBBBBBBBBB"); + CHECK(names[1] == "mark gillard"); + CHECK(names[2] == "joe bloggs"); + CHECK(names[3] == "AAAAAAAAAA"); + + auto it = emp.begin(); + CHECK_ROW_EQ(*it, "BBBBBBBBBB", 4, (1990, 3, 3), 30000, nullptr); + it++; + CHECK_ROW_EQ(*it, "mark gillard", 0, (1987, 03, 16), 999999, nullptr); + it++; + CHECK_ROW_EQ(*it, "joe bloggs", 1, (1970, 1, 1), 50000, nullptr); + it++; + CHECK_ROW_EQ(*it, "AAAAAAAAAA", 2, (1980, 2, 2), 40000, &someval); + it++; + CHECK(it == emp.end()); + + it--; + CHECK_ROW_EQ(*it, "AAAAAAAAAA", 2, (1980, 2, 2), 40000, &someval); + it--; + CHECK_ROW_EQ(*it, "joe bloggs", 1, (1970, 1, 1), 50000, nullptr); + it--; + CHECK_ROW_EQ(*it, "mark gillard", 0, (1987, 03, 16), 999999, nullptr); + it--; + CHECK_ROW_EQ(*it, "BBBBBBBBBB", 4, (1990, 3, 3), 30000, nullptr); + CHECK(it == emp.begin()); + + CHECK(it->name == "BBBBBBBBBB"); + } + + { + INFO("underlying table"); + + std::vector names; + for (auto&& row : emp.table()) + names.push_back(row.column()); + + REQUIRE(names.size() == 4u); + CHECK(names[0] == "BBBBBBBBBB"); + CHECK(names[1] == "mark gillard"); + CHECK(names[2] == "joe bloggs"); + CHECK(names[3] == "AAAAAAAAAA"); + + auto it = emp.table().begin(); + CHECK_ROW_EQ(*it, "BBBBBBBBBB", 4, (1990, 3, 3), 30000, nullptr); + it++; + CHECK_ROW_EQ(*it, "mark gillard", 0, (1987, 03, 16), 999999, nullptr); + it++; + CHECK_ROW_EQ(*it, "joe bloggs", 1, (1970, 1, 1), 50000, nullptr); + it++; + CHECK_ROW_EQ(*it, "AAAAAAAAAA", 2, (1980, 2, 2), 40000, &someval); + it++; + CHECK(it == emp.table().end()); + + it--; + CHECK_ROW_EQ(*it, "AAAAAAAAAA", 2, (1980, 2, 2), 40000, &someval); + it--; + CHECK_ROW_EQ(*it, "joe bloggs", 1, (1970, 1, 1), 50000, nullptr); + it--; + CHECK_ROW_EQ(*it, "mark gillard", 0, (1987, 03, 16), 999999, nullptr); + it--; + CHECK_ROW_EQ(*it, "BBBBBBBBBB", 4, (1990, 3, 3), 30000, nullptr); + CHECK(it == emp.table().begin()); + + CHECK(it->column() == "BBBBBBBBBB"); + } } { diff --git a/tests/employees.hpp b/tests/employees.hpp index c8e9209..bd067ad 100644 --- a/tests/employees.hpp +++ b/tests/employees.hpp @@ -1,20 +1,17 @@ //---------------------------------------------------------------------------------------------------------------------- -// This file was generated by soagen v0.4.0 - do not modify it directly +// This file was generated by soagen v0.5.0 - do not modify it directly // https://marzer.github.io/soagen //---------------------------------------------------------------------------------------------------------------------- #pragma once #include -#if SOAGEN_VERSION_MAJOR != 0 || SOAGEN_VERSION_MINOR < 4 - #error soagen version mismatch - expected v0.4.X +#if SOAGEN_VERSION_MAJOR != 0 || SOAGEN_VERSION_MINOR < 5 + #error soagen version mismatch - expected v0.5.X #endif SOAGEN_DISABLE_WARNINGS; #include #include -#if SOAGEN_HAS_EXCEPTIONS - #include -#endif SOAGEN_ENABLE_WARNINGS; SOAGEN_PUSH_WARNINGS; @@ -40,12 +37,6 @@ namespace tests class employees; } -namespace soagen -{ - template <> - inline constexpr bool is_soa = true; -} - namespace soagen::detail { #ifndef SOAGEN_NAME_date_of_birth @@ -90,6 +81,10 @@ namespace soagen::detail using type = soagen::allocator; }; + template <> + struct is_soa_ : std::true_type + {}; + SOAGEN_MAKE_COLUMN(tests::employees, 0, name); SOAGEN_MAKE_COLUMN(tests::employees, 1, id); SOAGEN_MAKE_COLUMN(tests::employees, 2, date_of_birth); @@ -130,7 +125,7 @@ namespace tests using table_traits = soagen::table_traits_type; - static constexpr size_type column_count = soagen::table_traits_type::column_count; + static constexpr size_type column_count = table_traits::column_count; template using column_traits = typename table_traits::template column(Column)>; @@ -138,15 +133,15 @@ namespace tests template using column_type = typename column_traits(Column)>::value_type; - using iterator = soagen::iterator_type; + using iterator = soagen::iterator_type; - using const_iterator = soagen::iterator_type; + using const_iterator = soagen::iterator_type; using rvalue_iterator = soagen::iterator_type; - using row_type = soagen::row_type; + using row_type = soagen::row_type; - using const_row_type = soagen::row_type; + using const_row_type = soagen::row_type; using rvalue_row_type = soagen::row_type; @@ -311,7 +306,7 @@ namespace tests 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 iterator{ table_, static_cast(*moved_pos) }; return {}; } @@ -332,7 +327,7 @@ namespace tests 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 const_iterator{ table_, static_cast(*moved_pos) }; return {}; } @@ -349,10 +344,10 @@ namespace tests SOAGEN_ALWAYS_INLINE SOAGEN_CPP20_CONSTEXPR employees& swap_columns() // - noexcept(noexcept( - std::declval().template swap_columns(A), static_cast(B)>())) + noexcept(noexcept(std::declval() + .template swap_columns(A), static_cast(B)>())) { - table_.template swap_columns(A), static_cast(B)>(); + table_.template swap_columns(A), static_cast(B)>(); return *this; } @@ -796,7 +791,7 @@ namespace tests template SOAGEN_PURE_GETTER SOAGEN_CPP20_CONSTEXPR - soagen::row_type row(size_type index) & noexcept + soagen::row_type row(size_type index) & noexcept { if constexpr (sizeof...(Columns)) { @@ -890,7 +885,7 @@ namespace tests template SOAGEN_PURE_GETTER SOAGEN_CPP20_CONSTEXPR - soagen::row_type row(size_type index) const& noexcept + soagen::row_type row(size_type index) const& noexcept { if constexpr (sizeof...(Columns)) { @@ -934,60 +929,60 @@ namespace tests return (*this).row(size() - 1u); } - template + template SOAGEN_PURE_INLINE_GETTER - constexpr soagen::iterator_type begin() & noexcept + constexpr soagen::iterator_type begin() & noexcept { - return { *this, 0 }; + return { table_, 0 }; } - template + template SOAGEN_PURE_INLINE_GETTER - constexpr soagen::iterator_type end() & noexcept + constexpr soagen::iterator_type end() & noexcept { - return { *this, static_cast(size()) }; + return { table_, static_cast(size()) }; } - template + template SOAGEN_PURE_INLINE_GETTER constexpr soagen::iterator_type begin() && noexcept { - return { std::move(*this), 0 }; + return { std::move(table_), 0 }; } - template + template SOAGEN_PURE_INLINE_GETTER constexpr soagen::iterator_type end() && noexcept { - return { std::move(*this), static_cast(size()) }; + return { std::move(table_), static_cast(size()) }; } - template + template SOAGEN_PURE_INLINE_GETTER - constexpr soagen::iterator_type begin() const& noexcept + constexpr soagen::iterator_type begin() const& noexcept { - return { *this, 0 }; + return { table_, 0 }; } - template + template SOAGEN_PURE_INLINE_GETTER - constexpr soagen::iterator_type end() const& noexcept + constexpr soagen::iterator_type end() const& noexcept { - return { *this, static_cast(size()) }; + return { table_, static_cast(size()) }; } - template + template SOAGEN_PURE_INLINE_GETTER - constexpr soagen::iterator_type cbegin() const& noexcept + constexpr soagen::iterator_type cbegin() const noexcept { - return { *this, 0 }; + return { table_, 0 }; } - template + template SOAGEN_PURE_INLINE_GETTER - constexpr soagen::iterator_type cend() const& noexcept + constexpr soagen::iterator_type cend() const noexcept { - return { *this, static_cast(size()) }; + return { table_, static_cast(size()) }; } }; diff --git a/tests/employees.natvis b/tests/employees.natvis index 654474b..1b14749 100644 --- a/tests/employees.natvis +++ b/tests/employees.natvis @@ -1,6 +1,6 @@