From 20a1fcb2d2a18f46d4c189cd74b8e9b7716ba219 Mon Sep 17 00:00:00 2001 From: Mark Gillard Date: Sun, 6 Aug 2023 16:07:28 +0300 Subject: [PATCH] added `row` conversions --- CHANGELOG.md | 3 +- docs/pages/intro.md | 11 +- src/soagen/hpp/core.hpp | 6 +- src/soagen/hpp/generated/functions.hpp | 63 ----- src/soagen/hpp/iterator.hpp | 64 +---- src/soagen/hpp/row.hpp | 126 +++++++++ src/soagen/hpp/single/soagen.hpp | 300 +++++++++------------- src/soagen/hpp/templates/functions.hpp.in | 15 -- src/soagen/hpp/tuples.hpp | 35 ++- tests/employees.cpp | 45 ++++ tests/tests.hpp | 10 + 11 files changed, 351 insertions(+), 327 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e1e341f..e78e0fe 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,11 +1,12 @@ # Changelog -## Unreleased +## v0.4.0 - Fixed `soagen::is_table<>` - Added support for emplace-constructing column values by unpacking all `std::tuple`-like types (not just the `emplacer`) - Added support for taking `std::integral_constants` in `for_each_column()` - Added `soagen::same_table_type<>` +- Added conversions between `soagen::row<>` specializations - Optimized instantiation overhead for most type-traits ## v0.3.0 diff --git a/docs/pages/intro.md b/docs/pages/intro.md index 10568b9..2ef238b 100644 --- a/docs/pages/intro.md +++ b/docs/pages/intro.md @@ -875,15 +875,15 @@ game::entities e1; game::entities e2; e1.push_back(0, "cat"); -e1.push_back(1, "dog"); - e2.push_back(0, "cat"); + +e1.push_back(1, "dog"); e2.push_back(1, "dog"); std::cout << (e1 < e2) << "\n"; std::cout << (e1 <= e2) << "\n"; std::cout << (e1 > e2) << "\n"; -std::cout << (e1 >= e2) << "\n"; +std::cout << (e1 >= e2) << "\n\n"; e2[1].name = "bird"; @@ -895,6 +895,11 @@ std::cout << (e1 >= e2) << "\n"; ``` @out +false +true +false +true + true true false diff --git a/src/soagen/hpp/core.hpp b/src/soagen/hpp/core.hpp index 53b16f4..6dfcd28 100644 --- a/src/soagen/hpp/core.hpp +++ b/src/soagen/hpp/core.hpp @@ -149,10 +149,14 @@ namespace soagen template inline constexpr bool is_unsigned = is_integer && std::is_unsigned_v; - /// @brief True if all `U` are the same as `T`. + /// @brief True if any `U` are the same as `T`. template inline constexpr bool any_same = (false || ... || std::is_same_v); + /// @brief True if `Value` is in the list `Values`. + 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 diff --git a/src/soagen/hpp/generated/functions.hpp b/src/soagen/hpp/generated/functions.hpp index b69c2f0..93603c1 100644 --- a/src/soagen/hpp/generated/functions.hpp +++ b/src/soagen/hpp/generated/functions.hpp @@ -5,9 +5,6 @@ #pragma once #include "../core.hpp" -#if SOAGEN_CPP >= 20 && defined(__cpp_lib_bit_cast) && __cpp_lib_bit_cast >= 201806 - #include -#endif #include "../header_start.hpp" namespace soagen @@ -165,66 +162,6 @@ namespace soagen } } -#if SOAGEN_CPP >= 20 && defined(__cpp_lib_bit_cast) && __cpp_lib_bit_cast >= 201806 - - #define SOAGEN_HAS_INTRINSIC_BIT_CAST 1 - - using std::bit_cast; - -#else - - SOAGEN_CONSTRAINED_TEMPLATE((std::is_trivially_copyable_v // - && std::is_trivially_copyable_v // - && sizeof(From) == sizeof(To)), - typename To, - typename From) - SOAGEN_PURE_INLINE_GETTER - constexpr To bit_cast(const From& from) noexcept - { - static_assert(!std::is_reference_v && !std::is_reference_v); - - #if SOAGEN_CLANG >= 11 || SOAGEN_GCC >= 11 || SOAGEN_MSVC >= 1926 \ - || (!SOAGEN_CLANG && !SOAGEN_GCC && SOAGEN_HAS_BUILTIN(__builtin_bit_cast)) - - #define SOAGEN_HAS_INTRINSIC_BIT_CAST 1 - return __builtin_bit_cast(To, from); - - #else - - #define SOAGEN_HAS_INTRINSIC_BIT_CAST 0 - - if constexpr (std::is_same_v, std::remove_cv_t>) - { - return from; - } - else if constexpr (!std::is_nothrow_default_constructible_v>) - { - union proxy_t - { - alignas(To) unsigned char dummy[sizeof(To)]; - std::remove_cv_t to; - - proxy_t() noexcept - {} - }; - - proxy_t proxy; - std::memcpy(&proxy.to, &from, sizeof(To)); - return proxy.to; - } - else - { - static_assert(std::is_nothrow_default_constructible_v>, - "Bit-cast fallback requires the To type be nothrow default-constructible"); - - std::remove_cv_t to; - std::memcpy(&to, &from, sizeof(To)); - return to; - } - #endif - } - -#endif } #include "../header_end.hpp" diff --git a/src/soagen/hpp/iterator.hpp b/src/soagen/hpp/iterator.hpp index 1c11f0d..25a4d1b 100644 --- a/src/soagen/hpp/iterator.hpp +++ b/src/soagen/hpp/iterator.hpp @@ -10,63 +10,8 @@ namespace soagen { /// @cond - template - class iterator; - namespace detail { - // iterator implicit conversions are allowed when: - // - changing columns - // - losing rvalue (Table&& -> Table&), (const Table&& -> const Table&) - // - gaining const (Table& -> const Table&, Table&& -> const Table&&) - // - any combination of the three - - template - inline constexpr bool iterator_implicit_conversion_ok = false; - - template - inline constexpr bool iterator_implicit_conversion_ok, // - iterator> = true; - - template - inline constexpr bool iterator_implicit_conversion_ok, // - iterator> = true; - - template - inline constexpr bool iterator_implicit_conversion_ok, // - iterator> = true; - - template - inline constexpr bool iterator_implicit_conversion_ok, // - iterator> = true; - - template - inline constexpr bool iterator_implicit_conversion_ok, // - iterator> = true; - - template - inline constexpr bool iterator_implicit_conversion_ok, // - iterator> = true; - - // iterator explicit conversions are allowed when gaining rvalue and optionally gaining const - // (note that we specifically avoid providing anything that would be the moral equivalent of - // a const_cast because that armory is filled with very large and powerful footguns) - - template - inline constexpr bool iterator_explicit_conversion_ok = false; - - template - inline constexpr bool iterator_explicit_conversion_ok, // - iterator> = true; - - template - inline constexpr bool iterator_explicit_conversion_ok, // - iterator> = true; - - template - inline constexpr bool iterator_explicit_conversion_ok, // - iterator> = true; - template struct arrow_proxy { @@ -359,9 +304,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::iterator_implicit_conversion_ok> - && !detail::iterator_explicit_conversion_ok>), + SOAGEN_CONSTRAINED_TEMPLATE((detail::implicit_conversion_ok + && !detail::explicit_conversion_ok), typename T, size_t... Cols) SOAGEN_PURE_INLINE_GETTER @@ -372,8 +316,8 @@ namespace soagen /// @cond - SOAGEN_CONSTRAINED_TEMPLATE((!detail::iterator_implicit_conversion_ok> - && detail::iterator_explicit_conversion_ok>), + SOAGEN_CONSTRAINED_TEMPLATE((!detail::implicit_conversion_ok + && detail::explicit_conversion_ok), typename T, size_t... Cols) SOAGEN_PURE_INLINE_GETTER diff --git a/src/soagen/hpp/row.hpp b/src/soagen/hpp/row.hpp index 1225356..5b28e78 100644 --- a/src/soagen/hpp/row.hpp +++ b/src/soagen/hpp/row.hpp @@ -9,6 +9,83 @@ namespace soagen { + /// @cond + template + struct row; + + namespace detail + { + // general rules for allowing implicit conversions: + // - losing rvalue (T&& -> T&), (const T&& -> const T&) + // - gaining const (T& -> const T&, T&& -> const T&&) + // - both + + template + inline constexpr bool implicit_conversion_ok = false; + + template + inline constexpr bool implicit_conversion_ok = true; + + template + inline constexpr bool implicit_conversion_ok = true; + + template + inline constexpr bool implicit_conversion_ok = true; + + template + inline constexpr bool implicit_conversion_ok = true; + + template + inline constexpr bool implicit_conversion_ok = true; + + // general rules for allowing explicit conversions: + + template + inline constexpr bool explicit_conversion_ok = false; + + template + inline constexpr bool explicit_conversion_ok = true; + + template + inline constexpr bool explicit_conversion_ok = true; + + // tests for compatible column permutations: + + template + inline constexpr bool column_conversion_ok = false; + + template + inline constexpr bool column_conversion_ok, std::index_sequence> = + true; + + template + inline constexpr bool column_conversion_ok, std::index_sequence> = + (any_same_value && ...); + + // row implicit conversions: + + template + inline constexpr bool row_implicit_conversion_ok = false; + + template + inline constexpr bool row_implicit_conversion_ok, // + row> = + implicit_conversion_ok + && column_conversion_ok, std::index_sequence>; + + // row explicit conversions: + + template + inline constexpr bool row_explicit_conversion_ok = false; + + template + inline constexpr bool row_explicit_conversion_ok, // + row> = + explicit_conversion_ok + && column_conversion_ok, std::index_sequence>; + } + /// @endcond + /// @brief Base class for soagen::row. /// @details Specialize this to add functionality to all rows of a particular type via CRTP. template @@ -148,6 +225,55 @@ namespace soagen } /// @} + + /// @name Conversion + /// @{ + + /// @brief Converts between different rows for the same table type. + /// + /// @details This operator allows the following conversions, only some of which are implicit: + ///
From To `explicit`? Note + ///
`Table&` `const Table&` gains `const` + ///
`Table&&` `Table&` `&&` → `&` + ///
`Table&&` `const Table&` `&&` → `&`, gains `const` + ///
`Table&&` `const Table&&` gains `const` + ///
`const Table&&` `const Table&` `&&` → `&` + ///
`Table&` `Table&&` `explicit` Equivalent to `std::move()` + ///
`Table&` `const Table&&` `explicit` Equivalent to `std::move()` + ///
`const Table&` `const Table&&` `explicit` Equivalent to `std::move()` + ///
+ /// + /// @note Any of these conversions can also reduce or re-order the columns viewed by the row. + /// + /// @attention There are no conversions provided which offer the equivalent of a `const_cast`-by-proxy. + /// This is by design. + SOAGEN_CONSTRAINED_TEMPLATE((detail::row_implicit_conversion_ok> + && !detail::row_explicit_conversion_ok>), + typename T, + size_t... Cols) + SOAGEN_PURE_INLINE_GETTER + constexpr operator row() const noexcept + { + return row{ { static_cast>().template column())>( + this->template column()) }... }; + } + + /// @cond + + SOAGEN_CONSTRAINED_TEMPLATE((!detail::row_implicit_conversion_ok> + && detail::row_explicit_conversion_ok>), + typename T, + size_t... Cols) + SOAGEN_PURE_INLINE_GETTER + explicit constexpr operator row() const noexcept + { + return row{ { static_cast>().template column())>( + this->template column()) }... }; + } + + /// @endcond + + /// @} }; /// @brief True if `T` is an instance of #soagen::row. diff --git a/src/soagen/hpp/single/soagen.hpp b/src/soagen/hpp/single/soagen.hpp index 50927ad..a6d6250 100644 --- a/src/soagen/hpp/single/soagen.hpp +++ b/src/soagen/hpp/single/soagen.hpp @@ -1176,6 +1176,9 @@ namespace soagen template inline constexpr bool any_same = (false || ... || std::is_same_v); + template + inline constexpr bool any_same_value = ((Value == Values) || ...); + template inline constexpr bool is_soa = POXY_IMPLEMENTATION_DETAIL(false); // specialized in generated code @@ -1623,6 +1626,77 @@ namespace soagen namespace soagen { + template + struct row; + + namespace detail + { + // general rules for allowing implicit conversions: + // - losing rvalue (T&& -> T&), (const T&& -> const T&) + // - gaining const (T& -> const T&, T&& -> const T&&) + // - both + + template + inline constexpr bool implicit_conversion_ok = false; + + template + inline constexpr bool implicit_conversion_ok = true; + + template + inline constexpr bool implicit_conversion_ok = true; + + template + inline constexpr bool implicit_conversion_ok = true; + + template + inline constexpr bool implicit_conversion_ok = true; + + template + inline constexpr bool implicit_conversion_ok = true; + + // general rules for allowing explicit conversions: + template + inline constexpr bool explicit_conversion_ok = false; + + template + inline constexpr bool explicit_conversion_ok = true; + + template + inline constexpr bool explicit_conversion_ok = true; + + // tests for compatible column permutations: + template + inline constexpr bool column_conversion_ok = false; + + template + inline constexpr bool column_conversion_ok, std::index_sequence> = + true; + + template + inline constexpr bool column_conversion_ok, std::index_sequence> = + (any_same_value && ...); + + // row implicit conversions: + template + inline constexpr bool row_implicit_conversion_ok = false; + + template + inline constexpr bool row_implicit_conversion_ok, // + row> = + implicit_conversion_ok + && column_conversion_ok, std::index_sequence>; + + // row explicit conversions: + template + inline constexpr bool row_explicit_conversion_ok = false; + + template + inline constexpr bool row_explicit_conversion_ok, // + row> = + explicit_conversion_ok + && column_conversion_ok, std::index_sequence>; + } + template struct SOAGEN_EMPTY_BASES row_base {}; @@ -1737,6 +1811,28 @@ namespace soagen { return row_compare_impl<0>(lhs, rhs) >= 0; } + + SOAGEN_CONSTRAINED_TEMPLATE((detail::row_implicit_conversion_ok> + && !detail::row_explicit_conversion_ok>), + typename T, + size_t... Cols) + SOAGEN_PURE_INLINE_GETTER + constexpr operator row() const noexcept + { + return row{ { static_cast>().template column())>( + this->template column()) }... }; + } + + SOAGEN_CONSTRAINED_TEMPLATE((!detail::row_implicit_conversion_ok> + && detail::row_explicit_conversion_ok>), + typename T, + size_t... Cols) + SOAGEN_PURE_INLINE_GETTER + explicit constexpr operator row() const noexcept + { + return row{ { static_cast>().template column())>( + this->template column()) }... }; + } }; template @@ -2043,55 +2139,8 @@ namespace std }; } -#if SOAGEN_ALWAYS_OPTIMIZE - #if SOAGEN_MSVC - #pragma strict_gs_check(pop) - #pragma runtime_checks("", restore) - #pragma optimize("", on) - #pragma inline_recursion(off) - #elif SOAGEN_GCC - #pragma GCC pop_options - #endif -#endif - -#if SOAGEN_MSVC_LIKE - #pragma pop_macro("min") - #pragma pop_macro("max") -#endif - -SOAGEN_POP_WARNINGS; - //******** generated/functions.hpp *********************************************************************************** -#if SOAGEN_CPP >= 20 && defined(__cpp_lib_bit_cast) && __cpp_lib_bit_cast >= 201806 - #include -#endif - -SOAGEN_PUSH_WARNINGS; -SOAGEN_DISABLE_SPAM_WARNINGS; -#if SOAGEN_CLANG >= 16 - #pragma clang diagnostic ignored "-Wunsafe-buffer-usage" -#endif - -#if SOAGEN_MSVC_LIKE - #pragma push_macro("min") - #pragma push_macro("max") - #undef min - #undef max -#endif - -#if SOAGEN_ALWAYS_OPTIMIZE - #if SOAGEN_MSVC - #pragma inline_recursion(on) - #pragma optimize("gt", on) - #pragma runtime_checks("", off) - #pragma strict_gs_check(push, off) - #elif SOAGEN_GCC - #pragma GCC push_options - #pragma GCC optimize("O2") - #endif -#endif - namespace soagen { SOAGEN_CONST_INLINE_GETTER @@ -2246,67 +2295,6 @@ namespace soagen } } } - -#if SOAGEN_CPP >= 20 && defined(__cpp_lib_bit_cast) && __cpp_lib_bit_cast >= 201806 - - #define SOAGEN_HAS_INTRINSIC_BIT_CAST 1 - - using std::bit_cast; - -#else - - SOAGEN_CONSTRAINED_TEMPLATE((std::is_trivially_copyable_v // - && std::is_trivially_copyable_v // - && sizeof(From) == sizeof(To)), - typename To, - typename From) - SOAGEN_PURE_INLINE_GETTER - constexpr To bit_cast(const From& from) noexcept - { - static_assert(!std::is_reference_v && !std::is_reference_v); - - #if SOAGEN_CLANG >= 11 || SOAGEN_GCC >= 11 || SOAGEN_MSVC >= 1926 \ - || (!SOAGEN_CLANG && !SOAGEN_GCC && SOAGEN_HAS_BUILTIN(__builtin_bit_cast)) - - #define SOAGEN_HAS_INTRINSIC_BIT_CAST 1 - return __builtin_bit_cast(To, from); - - #else - - #define SOAGEN_HAS_INTRINSIC_BIT_CAST 0 - - if constexpr (std::is_same_v, std::remove_cv_t>) - { - return from; - } - else if constexpr (!std::is_nothrow_default_constructible_v>) - { - union proxy_t - { - alignas(To) unsigned char dummy[sizeof(To)]; - std::remove_cv_t to; - - proxy_t() noexcept - {} - }; - - proxy_t proxy; - std::memcpy(&proxy.to, &from, sizeof(To)); - return proxy.to; - } - else - { - static_assert(std::is_nothrow_default_constructible_v>, - "Bit-cast fallback requires the To type be nothrow default-constructible"); - - std::remove_cv_t to; - std::memcpy(&to, &from, sizeof(To)); - return to; - } - #endif - } - -#endif } //******** allocator.hpp ********************************************************************************************* @@ -2592,28 +2580,45 @@ namespace soagen namespace detail { template - using has_tuple_size_ = decltype(std::tuple_size>::value); + using has_tuple_size_impl_ = decltype(std::tuple_size>::value); + template + using has_tuple_element_impl_ = decltype(std::declval>::type>()); + template + using has_tuple_get_member_impl_ = decltype(std::declval().template get<0>()); + namespace adl_dummy + { + template + auto get(); + template + using has_tuple_get_adl_impl_ = decltype(get<0>(std::declval())); + } + + template + using has_tuple_size_ = is_detected_>; template - using has_tuple_element_ = decltype(std::declval>::type>()); + using has_tuple_element_ = is_detected_>; template - using has_tuple_get_member_ = decltype(std::declval().template get<0>()); + using has_tuple_get_member_ = is_detected_; + template + using has_tuple_get_adl_ = is_detected_; } template - inline constexpr bool is_tuple = - POXY_IMPLEMENTATION_DETAIL(is_detected> - && is_detected>); + inline constexpr bool is_tuple = POXY_IMPLEMENTATION_DETAIL( + std::conjunction_v, + detail::has_tuple_element_, + std::disjunction, detail::has_tuple_get_adl_>>); template SOAGEN_NODISCARD SOAGEN_ALWAYS_INLINE constexpr decltype(auto) get_from_tuple(T&& tuple) noexcept { - if constexpr (is_detected) + if constexpr (detail::has_tuple_get_member_::value) { return static_cast(tuple).template get(); } - else // adl + else if constexpr (detail::has_tuple_get_adl_::value) { using std::get; return get(static_cast(tuple)); @@ -6319,63 +6324,8 @@ namespace soagen::mixins namespace soagen { - template - class iterator; - namespace detail { - // iterator implicit conversions are allowed when: - // - changing columns - // - losing rvalue (Table&& -> Table&), (const Table&& -> const Table&) - // - gaining const (Table& -> const Table&, Table&& -> const Table&&) - // - any combination of the three - - template - inline constexpr bool iterator_implicit_conversion_ok = false; - - template - inline constexpr bool iterator_implicit_conversion_ok, // - iterator> = true; - - template - inline constexpr bool iterator_implicit_conversion_ok, // - iterator> = true; - - template - inline constexpr bool iterator_implicit_conversion_ok, // - iterator> = true; - - template - inline constexpr bool iterator_implicit_conversion_ok, // - iterator> = true; - - template - inline constexpr bool iterator_implicit_conversion_ok, // - iterator> = true; - - template - inline constexpr bool iterator_implicit_conversion_ok, // - iterator> = true; - - // iterator explicit conversions are allowed when gaining rvalue and optionally gaining const - // (note that we specifically avoid providing anything that would be the moral equivalent of - // a const_cast because that armory is filled with very large and powerful footguns) - - template - inline constexpr bool iterator_explicit_conversion_ok = false; - - template - inline constexpr bool iterator_explicit_conversion_ok, // - iterator> = true; - - template - inline constexpr bool iterator_explicit_conversion_ok, // - iterator> = true; - - template - inline constexpr bool iterator_explicit_conversion_ok, // - iterator> = true; - template struct arrow_proxy { @@ -6581,8 +6531,8 @@ namespace soagen return base::offset >= rhs.offset; } - SOAGEN_CONSTRAINED_TEMPLATE((detail::iterator_implicit_conversion_ok> - && !detail::iterator_explicit_conversion_ok>), + SOAGEN_CONSTRAINED_TEMPLATE((detail::implicit_conversion_ok + && !detail::explicit_conversion_ok), typename T, size_t... Cols) SOAGEN_PURE_INLINE_GETTER @@ -6591,8 +6541,8 @@ namespace soagen return iterator{ static_cast(*this) }; } - SOAGEN_CONSTRAINED_TEMPLATE((!detail::iterator_implicit_conversion_ok> - && detail::iterator_explicit_conversion_ok>), + SOAGEN_CONSTRAINED_TEMPLATE((!detail::implicit_conversion_ok + && detail::explicit_conversion_ok), typename T, size_t... Cols) SOAGEN_PURE_INLINE_GETTER diff --git a/src/soagen/hpp/templates/functions.hpp.in b/src/soagen/hpp/templates/functions.hpp.in index 7046d65..014f876 100644 --- a/src/soagen/hpp/templates/functions.hpp.in +++ b/src/soagen/hpp/templates/functions.hpp.in @@ -5,9 +5,6 @@ #pragma once #include "../core.hpp" -#if SOAGEN_CPP >= 20 && defined(__cpp_lib_bit_cast) && __cpp_lib_bit_cast >= 201806 - #include -#endif #include "../header_start.hpp" namespace {% namespace %} @@ -23,18 +20,6 @@ namespace {% namespace %} //% has_single_bit //% assume_aligned - -#if {% macros::prefix %}CPP >= 20 && defined(__cpp_lib_bit_cast) && __cpp_lib_bit_cast >= 201806 - - #define {% macros::prefix %}HAS_INTRINSIC_BIT_CAST 1 - - using std::bit_cast; - -#else - - //% bit_cast - -#endif } diff --git a/src/soagen/hpp/tuples.hpp b/src/soagen/hpp/tuples.hpp index 974893c..a2d59e7 100644 --- a/src/soagen/hpp/tuples.hpp +++ b/src/soagen/hpp/tuples.hpp @@ -13,19 +13,36 @@ namespace soagen namespace detail { template - using has_tuple_size_ = decltype(std::tuple_size>::value); + using has_tuple_size_impl_ = decltype(std::tuple_size>::value); template - using has_tuple_element_ = decltype(std::declval>::type>()); + using has_tuple_element_impl_ = decltype(std::declval>::type>()); template - using has_tuple_get_member_ = decltype(std::declval().template get<0>()); + using has_tuple_get_member_impl_ = decltype(std::declval().template get<0>()); + namespace adl_dummy + { + template + auto get(); + template + using has_tuple_get_adl_impl_ = decltype(get<0>(std::declval())); + } + + template + using has_tuple_size_ = is_detected_>; + template + using has_tuple_element_ = is_detected_>; + template + using has_tuple_get_member_ = is_detected_; + template + using has_tuple_get_adl_ = is_detected_; } /// @endcond - /// @brief True if `T` implements std::tuple_size and std::tuple_element. + /// @brief True if `T` implements the tuple protocol. template - inline constexpr bool is_tuple = - POXY_IMPLEMENTATION_DETAIL(is_detected> - && is_detected>); + inline constexpr bool is_tuple = POXY_IMPLEMENTATION_DETAIL( + std::conjunction_v, + detail::has_tuple_element_, + std::disjunction, detail::has_tuple_get_adl_>>); /// @brief Gets the member at index `I` from tuple-like `T`. template @@ -33,11 +50,11 @@ namespace soagen SOAGEN_ALWAYS_INLINE constexpr decltype(auto) get_from_tuple(T&& tuple) noexcept { - if constexpr (is_detected) + if constexpr (detail::has_tuple_get_member_::value) { return static_cast(tuple).template get(); } - else // adl + else if constexpr (detail::has_tuple_get_adl_::value) { using std::get; return get(static_cast(tuple)); diff --git a/tests/employees.cpp b/tests/employees.cpp index 0ac589a..ff4881d 100644 --- a/tests/employees.cpp +++ b/tests/employees.cpp @@ -885,4 +885,49 @@ TEST_CASE("employees - general use") CHECK_ROW_EQ(emp[0], "AAAAAAAAAA", 2, (1980, 2, 2), 40000, &someval); CHECK_ROW_EQ(emp[1], "mark gillard", 0, (1987, 03, 16), 999999, nullptr); } + + { + INFO("row conversions"); + + // same + static_assert(is_implicitly_convertible, row>); + static_assert(is_implicitly_convertible, row>); + + // gaining const, losing rvalue, or both: + static_assert(is_implicitly_convertible, row>); + static_assert(is_implicitly_convertible, row>); + static_assert(is_implicitly_convertible, row>); + static_assert(is_implicitly_convertible, row>); + { + using to = row; // salary, id, name + to dest = std::move(emp[1]); // rvalue -> const lvalue + CHECK(dest.column<3>() == 999999); + CHECK(dest.column<1>() == 0); + CHECK(dest.column<0>() == "mark gillard"); + CHECK(dest.get<0>() == 999999); + CHECK(dest.get<1>() == 0); + CHECK(dest.get<2>() == "mark gillard"); + CHECK(dest.salary == 999999); + CHECK(dest.id == 0); + CHECK(dest.name == "mark gillard"); + } + + // gaining rvalue: + static_assert(is_explicitly_convertible, row>); + static_assert(is_explicitly_convertible, row>); + static_assert(is_explicitly_convertible, row>); + { + using to = row; // salary, id, name + to dest = static_cast(emp[1]); // lvalue -> const rvalue + CHECK(dest.column<3>() == 999999); + CHECK(dest.column<1>() == 0); + CHECK(dest.column<0>() == "mark gillard"); + CHECK(dest.get<0>() == 999999); + CHECK(dest.get<1>() == 0); + CHECK(dest.get<2>() == "mark gillard"); + CHECK(dest.salary == 999999); + CHECK(dest.id == 0); + CHECK(dest.name == "mark gillard"); + } + } } diff --git a/tests/tests.hpp b/tests/tests.hpp index 75cf4bb..f68d068 100644 --- a/tests/tests.hpp +++ b/tests/tests.hpp @@ -28,6 +28,16 @@ namespace tests using std::uintptr_t; using soagen::emplacer; + + template + using row = soagen::row_type; + + template + inline constexpr bool is_implicitly_convertible = std::is_convertible_v; + + template + inline constexpr bool is_explicitly_convertible = + !std::is_convertible_v && std::is_constructible_v; } SOAGEN_ENABLE_WARNINGS;