From dc3b1642016aa6d146130e54e1f2af93f3e3bd9a Mon Sep 17 00:00:00 2001 From: klaus triendl Date: Wed, 25 Oct 2023 11:11:28 +0200 Subject: [PATCH 01/11] Allow compound statements to return a common type --- dev/column_result.h | 21 ++++----- dev/select_constraints.h | 8 ++-- dev/tuple_helper/same_or_void.h | 14 ++++-- include/sqlite_orm/sqlite_orm.h | 44 ++++++++++++------- tests/static_tests/column_result_t.cpp | 6 +++ .../static_tests/functional/same_or_void.cpp | 8 ++-- 6 files changed, 63 insertions(+), 38 deletions(-) diff --git a/dev/column_result.h b/dev/column_result.h index 255ce2d80..db9714280 100644 --- a/dev/column_result.h +++ b/dev/column_result.h @@ -1,14 +1,15 @@ #pragma once #include // std::enable_if, std::is_same, std::decay, std::is_arithmetic, std::is_base_of -#include // std::tuple #include // std::reference_wrapper -#include "functional/cxx_universal.h" +#include "functional/cxx_universal.h" // ::nullptr_t +#include "functional/cxx_type_traits_polyfill.h" #include "functional/mpl.h" #include "tuple_helper/tuple_traits.h" #include "tuple_helper/tuple_fy.h" #include "tuple_helper/tuple_filter.h" +#include "tuple_helper/tuple_transformer.h" #include "tuple_helper/same_or_void.h" #include "type_traits.h" #include "member_traits/member_traits.h" @@ -222,20 +223,20 @@ namespace sqlite_orm { struct column_result_t, void> : column_result_t {}; template - struct column_result_t, void> { - using type = tuple_cat_t>>...>; - }; + struct column_result_t, void> + : conc_tuple>>...> {}; template struct column_result_t> : column_result_t {}; template struct column_result_t> { - using column_result_fn = mpl::bind_front_fn; - using types_tuple = transform_tuple_t; - using type = std::tuple_element_t<0, types_tuple>; - static_assert(!std::is_same>::value, - "Compound subselect queries must return same types"); + using type = + polyfill::detected_t::template fn>>; + static_assert(!std::is_same::value, + "Compound select statements must return a common type"); }; template diff --git a/dev/select_constraints.h b/dev/select_constraints.h index 8b9375c85..f38ffc0e0 100644 --- a/dev/select_constraints.h +++ b/dev/select_constraints.h @@ -345,7 +345,7 @@ namespace sqlite_orm { */ template internal::union_t union_(E... expressions) { - static_assert(sizeof...(E) >= 2, "Compound operators must have at least 2 expressions"); + static_assert(sizeof...(E) >= 2, "Compound operators must have at least 2 select statements"); return {{std::forward(expressions)...}, false}; } @@ -356,7 +356,7 @@ namespace sqlite_orm { */ template internal::union_t union_all(E... expressions) { - static_assert(sizeof...(E) >= 2, "Compound operators must have at least 2 expressions"); + static_assert(sizeof...(E) >= 2, "Compound operators must have at least 2 select statements"); return {{std::forward(expressions)...}, true}; } @@ -367,13 +367,13 @@ namespace sqlite_orm { */ template internal::except_t except(E... expressions) { - static_assert(sizeof...(E) >= 2, "Compound operators must have at least 2 expressions"); + static_assert(sizeof...(E) >= 2, "Compound operators must have at least 2 select statements"); return {{std::forward(expressions)...}}; } template internal::intersect_t intersect(E... expressions) { - static_assert(sizeof...(E) >= 2, "Compound operators must have at least 2 expressions"); + static_assert(sizeof...(E) >= 2, "Compound operators must have at least 2 select statements"); return {{std::forward(expressions)...}}; } diff --git a/dev/tuple_helper/same_or_void.h b/dev/tuple_helper/same_or_void.h index 78788af73..ce2726084 100644 --- a/dev/tuple_helper/same_or_void.h +++ b/dev/tuple_helper/same_or_void.h @@ -1,10 +1,11 @@ #pragma once +#include // std::common_type namespace sqlite_orm { namespace internal { /** - * Accepts any number of arguments and evaluates `type` alias as T if all arguments are the same or void otherwise + * Accepts any number of arguments and evaluates a nested `type` typename as `T` if all arguments are the same, otherwise `void`. */ template struct same_or_void { @@ -28,12 +29,17 @@ namespace sqlite_orm { struct same_or_void : same_or_void {}; template - struct same_or_void_of; + struct common_type_of; template class Pack, class... Types> - struct same_or_void_of> : same_or_void {}; + struct common_type_of> : std::common_type {}; + /** + * Accepts a pack of types and defines a nested `type` typename to a common type if possible, otherwise nonexistent. + * + * @note: SFINAE friendly like `std::common_type`. + */ template - using same_or_void_of_t = typename same_or_void_of::type; + using common_type_of_t = typename common_type_of::type; } } diff --git a/include/sqlite_orm/sqlite_orm.h b/include/sqlite_orm/sqlite_orm.h index c42f14a3f..a51f7a564 100644 --- a/include/sqlite_orm/sqlite_orm.h +++ b/include/sqlite_orm/sqlite_orm.h @@ -1088,11 +1088,13 @@ namespace sqlite_orm { // #include "tuple_helper/same_or_void.h" +#include // std::common_type + namespace sqlite_orm { namespace internal { /** - * Accepts any number of arguments and evaluates `type` alias as T if all arguments are the same or void otherwise + * Accepts any number of arguments and evaluates a nested `type` typename as `T` if all arguments are the same, otherwise `void`. */ template struct same_or_void { @@ -1116,13 +1118,18 @@ namespace sqlite_orm { struct same_or_void : same_or_void {}; template - struct same_or_void_of; + struct common_type_of; template class Pack, class... Types> - struct same_or_void_of> : same_or_void {}; + struct common_type_of> : std::common_type {}; + /** + * Accepts a pack of types and defines a nested `type` typename to a common type if possible, otherwise nonexistent. + * + * @note: SFINAE friendly like `std::common_type`. + */ template - using same_or_void_of_t = typename same_or_void_of::type; + using common_type_of_t = typename common_type_of::type; } } @@ -7561,7 +7568,7 @@ namespace sqlite_orm { */ template internal::union_t union_(E... expressions) { - static_assert(sizeof...(E) >= 2, "Compound operators must have at least 2 expressions"); + static_assert(sizeof...(E) >= 2, "Compound operators must have at least 2 select statements"); return {{std::forward(expressions)...}, false}; } @@ -7572,7 +7579,7 @@ namespace sqlite_orm { */ template internal::union_t union_all(E... expressions) { - static_assert(sizeof...(E) >= 2, "Compound operators must have at least 2 expressions"); + static_assert(sizeof...(E) >= 2, "Compound operators must have at least 2 select statements"); return {{std::forward(expressions)...}, true}; } @@ -7583,13 +7590,13 @@ namespace sqlite_orm { */ template internal::except_t except(E... expressions) { - static_assert(sizeof...(E) >= 2, "Compound operators must have at least 2 expressions"); + static_assert(sizeof...(E) >= 2, "Compound operators must have at least 2 select statements"); return {{std::forward(expressions)...}}; } template internal::intersect_t intersect(E... expressions) { - static_assert(sizeof...(E) >= 2, "Compound operators must have at least 2 expressions"); + static_assert(sizeof...(E) >= 2, "Compound operators must have at least 2 select statements"); return {{std::forward(expressions)...}}; } @@ -10664,10 +10671,11 @@ namespace sqlite_orm { // #include "column_result.h" #include // std::enable_if, std::is_same, std::decay, std::is_arithmetic, std::is_base_of -#include // std::tuple #include // std::reference_wrapper // #include "functional/cxx_universal.h" +// ::nullptr_t +// #include "functional/cxx_type_traits_polyfill.h" // #include "functional/mpl.h" @@ -10697,6 +10705,8 @@ namespace sqlite_orm { // #include "tuple_helper/tuple_filter.h" +// #include "tuple_helper/tuple_transformer.h" + // #include "tuple_helper/same_or_void.h" // #include "type_traits.h" @@ -11252,20 +11262,20 @@ namespace sqlite_orm { struct column_result_t, void> : column_result_t {}; template - struct column_result_t, void> { - using type = tuple_cat_t>>...>; - }; + struct column_result_t, void> + : conc_tuple>>...> {}; template struct column_result_t> : column_result_t {}; template struct column_result_t> { - using column_result_fn = mpl::bind_front_fn; - using types_tuple = transform_tuple_t; - using type = std::tuple_element_t<0, types_tuple>; - static_assert(!std::is_same>::value, - "Compound subselect queries must return same types"); + using type = + polyfill::detected_t::template fn>>; + static_assert(!std::is_same::value, + "Compound select statements must return a common type"); }; template diff --git a/tests/static_tests/column_result_t.cpp b/tests/static_tests/column_result_t.cpp index dbbe96395..f2207e7f6 100644 --- a/tests/static_tests/column_result_t.cpp +++ b/tests/static_tests/column_result_t.cpp @@ -47,6 +47,10 @@ TEST_CASE("column_result_of_t") { runTest(&User::id); runTest(&User::name); + { + int n = 0; + runTest(std::ref(n)); + } runTest(in(&User::id, {1, 2, 3})); { std::vector vector; @@ -115,4 +119,6 @@ TEST_CASE("column_result_of_t") { runTest(column(&User::id)); runTest(alias_column>(&User::id)); runTest(object()); + runTest(union_all(select(1), select(2))); + runTest(union_all(select(1ll), select(2))); } diff --git a/tests/static_tests/functional/same_or_void.cpp b/tests/static_tests/functional/same_or_void.cpp index 235861c3e..a83c2dd6a 100644 --- a/tests/static_tests/functional/same_or_void.cpp +++ b/tests/static_tests/functional/same_or_void.cpp @@ -8,8 +8,8 @@ using namespace sqlite_orm; TEST_CASE("same_or_void") { + using internal::common_type_of_t; using internal::same_or_void; - using internal::same_or_void_of_t; // one argument STATIC_REQUIRE(std::is_same::type, int>::value); @@ -35,6 +35,8 @@ TEST_CASE("same_or_void") { STATIC_REQUIRE(std::is_same::type, void>::value); // type pack, e.g. tuple - STATIC_REQUIRE(std::is_same>, int>::value); - STATIC_REQUIRE(std::is_same>, void>::value); + STATIC_REQUIRE(std::is_same>, int>::value); + STATIC_REQUIRE(std::is_same>, long>::value); + STATIC_REQUIRE( + std::is_same>, polyfill::nonesuch>::value); } From fdf9ab898745618165c0636bc5341f4bb99f625a Mon Sep 17 00:00:00 2001 From: klaus triendl Date: Wed, 25 Oct 2023 13:49:29 +0200 Subject: [PATCH 02/11] Runner-up: Allow compound statements to return a common type --- dev/column_result.h | 10 ++++++---- include/sqlite_orm/sqlite_orm.h | 10 ++++++---- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/dev/column_result.h b/dev/column_result.h index db9714280..df6d383d0 100644 --- a/dev/column_result.h +++ b/dev/column_result.h @@ -45,6 +45,10 @@ namespace sqlite_orm { template using column_result_of_t = typename column_result_t::type; + template + using column_result_for_tuple_t = + transform_tuple_t::template fn>; + #ifdef SQLITE_ORM_OPTIONAL_SUPPORTED template struct column_result_t, void> { @@ -232,10 +236,8 @@ namespace sqlite_orm { template struct column_result_t> { using type = - polyfill::detected_t::template fn>>; - static_assert(!std::is_same::value, + polyfill::detected_t>; + static_assert(!std::is_same::value, "Compound select statements must return a common type"); }; diff --git a/include/sqlite_orm/sqlite_orm.h b/include/sqlite_orm/sqlite_orm.h index a51f7a564..4d40f6de2 100644 --- a/include/sqlite_orm/sqlite_orm.h +++ b/include/sqlite_orm/sqlite_orm.h @@ -11084,6 +11084,10 @@ namespace sqlite_orm { template using column_result_of_t = typename column_result_t::type; + template + using column_result_for_tuple_t = + transform_tuple_t::template fn>; + #ifdef SQLITE_ORM_OPTIONAL_SUPPORTED template struct column_result_t, void> { @@ -11271,10 +11275,8 @@ namespace sqlite_orm { template struct column_result_t> { using type = - polyfill::detected_t::template fn>>; - static_assert(!std::is_same::value, + polyfill::detected_t>; + static_assert(!std::is_same::value, "Compound select statements must return a common type"); }; From a226d23b4b30b91bb661ab23bcac905014742c4e Mon Sep 17 00:00:00 2001 From: klaus triendl Date: Wed, 25 Oct 2023 23:21:40 +0200 Subject: [PATCH 03/11] A few more unit tests for tuple transformation --- dev/functional/mpl.h | 10 ++-- dev/tuple_helper/tuple_transformer.h | 1 + include/sqlite_orm/sqlite_orm.h | 11 ++--- .../statement_serializer_tests/bindables.cpp | 6 +-- tests/static_tests/functional/mpl.cpp | 5 ++ .../functional/tuple_transform.cpp | 49 ++++++++++++++----- 6 files changed, 56 insertions(+), 26 deletions(-) diff --git a/dev/functional/mpl.h b/dev/functional/mpl.h index 44c33dc7f..7d57714f5 100644 --- a/dev/functional/mpl.h +++ b/dev/functional/mpl.h @@ -9,9 +9,9 @@ * Hence it contains only a very small subset of a full MPL. * * Three key concepts are critical to understanding: - * 1. A 'metafunction' is a class template that represents a function invocable at compile-time. + * 1. A 'metafunction' is a class template with a nested `type` typename, and represents a function invocable at compile-time. * E.g. `template struct x { using type = int; };` - * 2. A 'metafunction operation' is an alias template that represents a nested template expression, whose instantiation yields a type. + * 2. A 'metafunction operation' is an alias template for a class template or a nested template expression, whose instantiation yields a type. * E.g. `template using alias_op_t = typename x::type` * 3. A 'metafunction class' is a certain form of metafunction representation that enables higher-order metaprogramming. * More precisely, it's a class with a nested metafunction called "fn" @@ -103,16 +103,14 @@ namespace sqlite_orm { }; template class Fn, class... Args> - struct invoke_meta>, Fn, Args...> { - using type = typename Fn::type; - }; + struct invoke_meta>, Fn, Args...> : Fn {}; /* * Invoke metafunction or metafunction operation. * * @attention If using an alias template, be aware that it isn't recognizable whether an alias template is * a. an alias of a class template (e.g. `template using aliased = x`) or - * b. an alias of a nested template expression yielding a result (e.g. `template alias_op_t = typename x::type`) + * b. an alias of a class template or nested template expression yielding a result (e.g. `template alias_op_t = typename x::type`) * Therefore, one cannot use `invoke_meta_t` with an alias of a class template (a.), or * in other words, `invoke_meta_t` expects an alias of a nested template expression (b.). * The alternative in that case is to use `invoke_fn_t`. diff --git a/dev/tuple_helper/tuple_transformer.h b/dev/tuple_helper/tuple_transformer.h index 027cd22eb..c98adb54a 100644 --- a/dev/tuple_helper/tuple_transformer.h +++ b/dev/tuple_helper/tuple_transformer.h @@ -27,6 +27,7 @@ namespace sqlite_orm { template class Op> using transform_tuple_t = typename tuple_transformer::type; + // note: applying a combiner like `plus_fold_integrals` needs fold expressions #if defined(SQLITE_ORM_FOLD_EXPRESSIONS_SUPPORTED) /* * Apply a projection to a tuple's elements filtered by the specified indexes, and combine the results. diff --git a/include/sqlite_orm/sqlite_orm.h b/include/sqlite_orm/sqlite_orm.h index 4d40f6de2..bb49e728f 100644 --- a/include/sqlite_orm/sqlite_orm.h +++ b/include/sqlite_orm/sqlite_orm.h @@ -746,9 +746,9 @@ namespace sqlite_orm { * Hence it contains only a very small subset of a full MPL. * * Three key concepts are critical to understanding: - * 1. A 'metafunction' is a class template that represents a function invocable at compile-time. + * 1. A 'metafunction' is a class template with a nested `type` typename, and represents a function invocable at compile-time. * E.g. `template struct x { using type = int; };` - * 2. A 'metafunction operation' is an alias template that represents a nested template expression, whose instantiation yields a type. + * 2. A 'metafunction operation' is an alias template for a class template or a nested template expression, whose instantiation yields a type. * E.g. `template using alias_op_t = typename x::type` * 3. A 'metafunction class' is a certain form of metafunction representation that enables higher-order metaprogramming. * More precisely, it's a class with a nested metafunction called "fn" @@ -841,16 +841,14 @@ namespace sqlite_orm { }; template class Fn, class... Args> - struct invoke_meta>, Fn, Args...> { - using type = typename Fn::type; - }; + struct invoke_meta>, Fn, Args...> : Fn {}; /* * Invoke metafunction or metafunction operation. * * @attention If using an alias template, be aware that it isn't recognizable whether an alias template is * a. an alias of a class template (e.g. `template using aliased = x`) or - * b. an alias of a nested template expression yielding a result (e.g. `template alias_op_t = typename x::type`) + * b. an alias of a class template or nested template expression yielding a result (e.g. `template alias_op_t = typename x::type`) * Therefore, one cannot use `invoke_meta_t` with an alias of a class template (a.), or * in other words, `invoke_meta_t` expects an alias of a nested template expression (b.). * The alternative in that case is to use `invoke_fn_t`. @@ -9889,6 +9887,7 @@ namespace sqlite_orm { template class Op> using transform_tuple_t = typename tuple_transformer::type; + // note: applying a combiner like `plus_fold_integrals` needs fold expressions #if defined(SQLITE_ORM_FOLD_EXPRESSIONS_SUPPORTED) /* * Apply a projection to a tuple's elements filtered by the specified indexes, and combine the results. diff --git a/tests/statement_serializer_tests/bindables.cpp b/tests/statement_serializer_tests/bindables.cpp index 51bd88c3b..1cd0210da 100644 --- a/tests/statement_serializer_tests/bindables.cpp +++ b/tests/statement_serializer_tests/bindables.cpp @@ -75,7 +75,7 @@ array single_value_array(const char* s) { } template -using wrap_in_literal = internal::literal_holder; +using make_literal_holder = internal::literal_holder; inline void require_string(const string& value, const string& expected) { REQUIRE(value == expected); @@ -192,7 +192,7 @@ TEST_CASE("bindables") { } SECTION("non-bindable literals") { context.replace_bindable_with_question = true; - constexpr auto t = make_default_tuple>(); + constexpr auto t = make_default_tuple>(); test_tuple(t, context, e); } } @@ -254,7 +254,7 @@ TEST_CASE("bindables") { } SECTION("non-bindable literals") { context.replace_bindable_with_question = true; - auto t = make_default_tuple>(); + auto t = make_default_tuple>(); test_tuple(t, context, e); } } diff --git a/tests/static_tests/functional/mpl.cpp b/tests/static_tests/functional/mpl.cpp index 322e34d8c..f68f3cc83 100644 --- a/tests/static_tests/functional/mpl.cpp +++ b/tests/static_tests/functional/mpl.cpp @@ -5,10 +5,14 @@ #include // std::less using namespace sqlite_orm; +using internal::literal_holder; template using transparent_of_t = typename TransparentFunction::is_transparent; +template +using make_literal_holder = literal_holder; + TEST_CASE("mpl") { using mpl_is_same = mpl::quote_fn; @@ -20,6 +24,7 @@ TEST_CASE("mpl") { STATIC_REQUIRE(std::is_same, int>::value); STATIC_REQUIRE(std::is_same, int>::value); STATIC_REQUIRE(std::is_same, int>::value); + STATIC_REQUIRE(std::is_same, literal_holder>::value); STATIC_REQUIRE(mpl::invoke_t::value); STATIC_REQUIRE_FALSE(mpl::invoke_t, int, int>::value); STATIC_REQUIRE(mpl::invoke_t>, size_t, int>::value); diff --git a/tests/static_tests/functional/tuple_transform.cpp b/tests/static_tests/functional/tuple_transform.cpp index d2f61788f..c3f89fb8c 100644 --- a/tests/static_tests/functional/tuple_transform.cpp +++ b/tests/static_tests/functional/tuple_transform.cpp @@ -3,6 +3,24 @@ #include // std::is_same using namespace sqlite_orm; +using internal::field_type_t; +using internal::literal_holder; +using internal::transform_tuple_t; +#if defined(SQLITE_ORM_FOLD_EXPRESSIONS_SUPPORTED) +using internal::nested_tuple_size_for_t; +#endif + +template +using make_literal_holder = literal_holder; + +#if defined(SQLITE_ORM_FOLD_EXPRESSIONS_SUPPORTED) +struct tuple_maker { + template + auto operator()(Types&&... types) const { + return std::make_tuple(std::forward(types)...); + } +}; +#endif TEST_CASE("tuple_helper static") { SECTION("tuple_transformer") { @@ -12,18 +30,27 @@ TEST_CASE("tuple_helper static") { std::string b; std::string c; }; - auto column1 = make_column("id", &Table::id); - auto column2 = make_column("a", &Table::a); - auto column3 = make_column("b", &Table::b); - auto column4 = make_column("c", &Table::c); - - using Column1 = decltype(column1); - using Column2 = decltype(column2); - using Column3 = decltype(column3); - using Column4 = decltype(column4); - using ColumnsTuple = std::tuple; - using ColumnsMappedTypes = internal::transform_tuple_t; + + auto columnsTuple = std::make_tuple(make_column("id", &Table::id), + make_column("a", &Table::a), + make_column("b", &Table::b), + make_column("c", &Table::c)); + using ColumnsTuple = decltype(columnsTuple); + using ColumnsMappedTypes = transform_tuple_t; using Expected = std::tuple; STATIC_REQUIRE(std::is_same::value); + + STATIC_REQUIRE(std::is_same, make_literal_holder>, + std::tuple, literal_holder>>::value); + +#if defined(SQLITE_ORM_FOLD_EXPRESSIONS_SUPPORTED) + // make tuples out of tuple elements, and count the first 2 of 3 resulting tuples + STATIC_REQUIRE( + std::is_same, std::make_index_sequence<2>>, + std::integral_constant>::value); + + REQUIRE(internal::recombine_tuple(tuple_maker{}, std::make_tuple(1, 2), polyfill::identity{}, 3) == + std::make_tuple(3, 1, 2)); +#endif } } From 9813ec16425e3e4f070a7c22ddafb5cfa16d792f Mon Sep 17 00:00:00 2001 From: klaus triendl Date: Wed, 25 Oct 2023 23:38:29 +0200 Subject: [PATCH 04/11] Runner-up: A few more unit tests for tuple transformation --- tests/static_tests/functional/mpl.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/static_tests/functional/mpl.cpp b/tests/static_tests/functional/mpl.cpp index f68f3cc83..0ddbdd123 100644 --- a/tests/static_tests/functional/mpl.cpp +++ b/tests/static_tests/functional/mpl.cpp @@ -20,6 +20,7 @@ TEST_CASE("mpl") { STATIC_REQUIRE(mpl::is_metafunction_class_v); STATIC_REQUIRE_FALSE(mpl::is_alias_template_v); STATIC_REQUIRE(mpl::is_alias_template_v); + STATIC_REQUIRE(mpl::is_alias_template_v); STATIC_REQUIRE(std::is_same, int>::value); STATIC_REQUIRE(std::is_same, int>::value); STATIC_REQUIRE(std::is_same, int>::value); From 0990d61bb06af9217912e17c646af4c10091e2fe Mon Sep 17 00:00:00 2001 From: klaus triendl Date: Sat, 28 Oct 2023 22:15:44 +0200 Subject: [PATCH 05/11] A couple of static unit tests for tuple transformation --- dev/tuple_helper/tuple_transformer.h | 4 ++-- include/sqlite_orm/sqlite_orm.h | 4 ++-- .../static_tests/functional/tuple_transform.cpp | 17 ++++++++++++----- 3 files changed, 16 insertions(+), 9 deletions(-) diff --git a/dev/tuple_helper/tuple_transformer.h b/dev/tuple_helper/tuple_transformer.h index c98adb54a..9ee7083f2 100644 --- a/dev/tuple_helper/tuple_transformer.h +++ b/dev/tuple_helper/tuple_transformer.h @@ -92,12 +92,12 @@ namespace sqlite_orm { #endif template - R create_from_tuple(Tpl&& tpl, std::index_sequence, Projection project = {}) { + constexpr R create_from_tuple(Tpl&& tpl, std::index_sequence, Projection project = {}) { return R{polyfill::invoke(project, std::get(std::forward(tpl)))...}; } template - R create_from_tuple(Tpl&& tpl, Projection project = {}) { + constexpr R create_from_tuple(Tpl&& tpl, Projection project = {}) { return create_from_tuple( std::forward(tpl), std::make_index_sequence>::value>{}, diff --git a/include/sqlite_orm/sqlite_orm.h b/include/sqlite_orm/sqlite_orm.h index bb49e728f..8dc2337b4 100644 --- a/include/sqlite_orm/sqlite_orm.h +++ b/include/sqlite_orm/sqlite_orm.h @@ -9952,12 +9952,12 @@ namespace sqlite_orm { #endif template - R create_from_tuple(Tpl&& tpl, std::index_sequence, Projection project = {}) { + constexpr R create_from_tuple(Tpl&& tpl, std::index_sequence, Projection project = {}) { return R{polyfill::invoke(project, std::get(std::forward(tpl)))...}; } template - R create_from_tuple(Tpl&& tpl, Projection project = {}) { + constexpr R create_from_tuple(Tpl&& tpl, Projection project = {}) { return create_from_tuple( std::forward(tpl), std::make_index_sequence>::value>{}, diff --git a/tests/static_tests/functional/tuple_transform.cpp b/tests/static_tests/functional/tuple_transform.cpp index c3f89fb8c..cd89688d7 100644 --- a/tests/static_tests/functional/tuple_transform.cpp +++ b/tests/static_tests/functional/tuple_transform.cpp @@ -1,13 +1,16 @@ #include #include #include // std::is_same +#include using namespace sqlite_orm; +using internal::create_from_tuple; using internal::field_type_t; using internal::literal_holder; using internal::transform_tuple_t; #if defined(SQLITE_ORM_FOLD_EXPRESSIONS_SUPPORTED) using internal::nested_tuple_size_for_t; +using internal::recombine_tuple; #endif template @@ -16,7 +19,7 @@ using make_literal_holder = literal_holder; #if defined(SQLITE_ORM_FOLD_EXPRESSIONS_SUPPORTED) struct tuple_maker { template - auto operator()(Types&&... types) const { + constexpr auto operator()(Types&&... types) const { return std::make_tuple(std::forward(types)...); } }; @@ -43,14 +46,18 @@ TEST_CASE("tuple_helper static") { STATIC_REQUIRE(std::is_same, make_literal_holder>, std::tuple, literal_holder>>::value); +#if __cpp_lib_constexpr_algorithms >= 201806L + STATIC_REQUIRE(create_from_tuple>(std::make_tuple(1, 2), polyfill::identity{}) == + std::array{1, 2}); +#endif #if defined(SQLITE_ORM_FOLD_EXPRESSIONS_SUPPORTED) - // make tuples out of tuple elements, and count the first 2 of 3 resulting tuples + STATIC_REQUIRE(recombine_tuple(tuple_maker{}, std::make_tuple(1, 2), polyfill::identity{}, 3) == + std::make_tuple(3, 1, 2)); + + // make tuples out of tuple elements, and sump up the size of the first 2 of 3 resulting tuples STATIC_REQUIRE( std::is_same, std::make_index_sequence<2>>, std::integral_constant>::value); - - REQUIRE(internal::recombine_tuple(tuple_maker{}, std::make_tuple(1, 2), polyfill::identity{}, 3) == - std::make_tuple(3, 1, 2)); #endif } } From 65fb0e30302fb9170711e1fc6b545ae16737a626 Mon Sep 17 00:00:00 2001 From: klaus triendl Date: Sun, 29 Oct 2023 01:51:24 +0200 Subject: [PATCH 06/11] Quoted metafunctions --- dev/column_names_getter.h | 4 +- dev/column_result.h | 2 +- dev/constraints.h | 26 +-- dev/functional/mpl.h | 96 +++++----- dev/implementations/column_definitions.h | 2 +- dev/schema/column.h | 6 +- dev/schema/table.h | 8 +- dev/storage.h | 2 +- dev/tuple_helper/tuple_filter.h | 19 +- dev/tuple_helper/tuple_traits.h | 4 +- dev/tuple_helper/tuple_transformer.h | 4 +- include/sqlite_orm/sqlite_orm.h | 173 +++++++++--------- tests/static_tests/functional/mpl.cpp | 28 +-- .../is_column_with_insertable_primary_key.cpp | 2 +- .../static_tests_storage_traits.h | 4 +- 15 files changed, 199 insertions(+), 181 deletions(-) diff --git a/dev/column_names_getter.h b/dev/column_names_getter.h index 05820ed83..f08a744ec 100644 --- a/dev/column_names_getter.h +++ b/dev/column_names_getter.h @@ -98,8 +98,8 @@ namespace sqlite_orm { (*this)(colExpr, context); }); // note: `capacity() > size()` can occur in case `asterisk_t<>` does spell out the columns in defined order - if(mpl::instantiate, - typename columns_t::columns_type>::value && + if(mpl::invoke_t, + typename columns_t::columns_type>::value && collectedExpressions.capacity() > collectedExpressions.size()) { collectedExpressions.shrink_to_fit(); } diff --git a/dev/column_result.h b/dev/column_result.h index df6d383d0..c46e30354 100644 --- a/dev/column_result.h +++ b/dev/column_result.h @@ -47,7 +47,7 @@ namespace sqlite_orm { template using column_result_for_tuple_t = - transform_tuple_t::template fn>; + transform_tuple_t>>; #ifdef SQLITE_ORM_OPTIONAL_SUPPORTED template diff --git a/dev/constraints.h b/dev/constraints.h index 65412574a..cf7bb4ea9 100644 --- a/dev/constraints.h +++ b/dev/constraints.h @@ -458,25 +458,25 @@ namespace sqlite_orm { template struct is_primary_key_insertable : polyfill::disjunction< - mpl::instantiate, - check_if_tuple_has_template>, - constraints_type_t>, + mpl::invoke_t, + check_if_tuple_has_template>, + constraints_type_t>, std::is_base_of>>> { static_assert(tuple_has>::value, "an unexpected type was passed"); }; template - using is_constraint = mpl::instantiate, - check_if, - check_if_is_type, - check_if_is_type, - check_if_is_template, - check_if_is_template, - check_if_is_template, - check_if_is_type, - check_if>, - T>; + using is_constraint = mpl::invoke_t, + check_if, + check_if_is_type, + check_if_is_type, + check_if_is_template, + check_if_is_template, + check_if_is_template, + check_if_is_type, + check_if>, + T>; } #if SQLITE_VERSION_NUMBER >= 3031000 diff --git a/dev/functional/mpl.h b/dev/functional/mpl.h index 7d57714f5..cb35ec0ea 100644 --- a/dev/functional/mpl.h +++ b/dev/functional/mpl.h @@ -56,16 +56,6 @@ namespace sqlite_orm { struct is_metafunction_class : polyfill::bool_constant> {}; #endif - /* - * Given suitable template arguments, detect whether a template is an alias template. - * - * @note It exploits the fact that the `is_specialization_of` trait is only true for class templates - * but always false for alias templates. - */ - template class Template, class... Args> - SQLITE_ORM_INLINE_VAR constexpr bool is_alias_template_v = - !polyfill::is_specialization_of_v, Template>; - /* * Invoke metafunction. */ @@ -94,30 +84,6 @@ namespace sqlite_orm { using invoke_op_t = typename wrap_op::type; #endif - template class F, class... Args> - struct invoke_meta; - - template class Op, class... Args> - struct invoke_meta>, Op, Args...> { - using type = Op; - }; - - template class Fn, class... Args> - struct invoke_meta>, Fn, Args...> : Fn {}; - - /* - * Invoke metafunction or metafunction operation. - * - * @attention If using an alias template, be aware that it isn't recognizable whether an alias template is - * a. an alias of a class template (e.g. `template using aliased = x`) or - * b. an alias of a class template or nested template expression yielding a result (e.g. `template alias_op_t = typename x::type`) - * Therefore, one cannot use `invoke_meta_t` with an alias of a class template (a.), or - * in other words, `invoke_meta_t` expects an alias of a nested template expression (b.). - * The alternative in that case is to use `invoke_fn_t`. - */ - template class F, class... Args> - using invoke_meta_t = typename invoke_meta::type; - /* * Invoke metafunction class by invoking its nested metafunction. */ @@ -131,12 +97,21 @@ namespace sqlite_orm { using instantiate = typename FnCls::template fn; /* - * Wrap given type such that `typename T::type` is valid. + * Provides the metafunction operation of metafunction class as alias template `op`. */ - template - struct type_wrap : polyfill::type_identity {}; - template - struct type_wrap> : T {}; + template + struct op_of { + template + using op = invoke_t; + }; + + /* + * Turn a metafunction class into a metafunction operation. + * + * Useful when a metafunction class needs to be passed as a metafunction operation outside the mpl framework. + */ + template + using as_op = typename op_of::template op; /* * Turn metafunction into a metafunction class. @@ -148,15 +123,15 @@ namespace sqlite_orm { template class Fn> struct quote_fn { template class, class...> - struct invoke_fn; + struct invoke_fn {}; template class F, class... Args> struct invoke_fn>, F, Args...> { - using type = type_wrap>; + using type = F; }; template - using fn = typename invoke_fn::type; + struct fn : invoke_fn {}; }; /* @@ -174,7 +149,9 @@ namespace sqlite_orm { template class Fn, class... Args2> class HigherFn> struct quote_fn { template - struct fn : HigherFn {}; + struct fn { + using type = HigherFn, Args2...>; + }; }; }; @@ -232,7 +209,9 @@ namespace sqlite_orm { template struct always { template - struct fn : type_wrap {}; + struct fn { + using type = T; + }; }; /* @@ -240,16 +219,29 @@ namespace sqlite_orm { */ struct identity { template - struct fn : type_wrap {}; + struct fn { + using type = T; + }; + }; + + /* + * Assume that the invocation of metafunction class yields a trait, and provide members `type` and `value` from the resulting trait. + * + * The goal of using this struct would be to attain lazy evaluation for trait metafunction classes like `conjunction` and `disjunction`. + */ + template + struct yield_trait { + using type = invoke_t; + static constexpr auto value = type::value; }; /* * Metafunction class equivalent to std::negation. */ - template + template struct not_ { template - struct fn : polyfill::negation> {}; + struct fn : polyfill::negation> {}; }; /* @@ -258,7 +250,7 @@ namespace sqlite_orm { template struct conjunction { template - struct fn : polyfill::conjunction...> {}; + struct fn : polyfill::conjunction...> {}; }; /* @@ -267,7 +259,7 @@ namespace sqlite_orm { template struct disjunction { template - struct fn : polyfill::disjunction...> {}; + struct fn : polyfill::disjunction...> {}; }; #ifndef SQLITE_ORM_BROKEN_ALIAS_TEMPLATE_DEPENDENT_EXPR_SFINAE @@ -332,9 +324,11 @@ namespace sqlite_orm { /* * Trait metafunction class that checks if a type (possibly projected) is the same as the specified type. + * + * `Proj` is a projection applied to `Type` and must be a metafunction operation. */ - template class Proj = polyfill::type_identity_t> - using check_if_is_type = mpl::pass_result_to>; + template class ProjOp = polyfill::type_identity_t> + using check_if_is_type = mpl::pass_result_to>; /* * Trait metafunction class that checks if a type's template matches the specified template diff --git a/dev/implementations/column_definitions.h b/dev/implementations/column_definitions.h index f8b70f508..1b07924b7 100644 --- a/dev/implementations/column_definitions.h +++ b/dev/implementations/column_definitions.h @@ -20,7 +20,7 @@ namespace sqlite_orm { template std::unique_ptr column_constraints::default_value() const { using default_op_index_sequence = - filter_tuple_sequence_t::template fn>; + filter_tuple_sequence_t>>; std::unique_ptr value; call_if_constexpr( diff --git a/dev/schema/column.h b/dev/schema/column.h index f7654a1cf..05e00ecaf 100644 --- a/dev/schema/column.h +++ b/dev/schema/column.h @@ -113,19 +113,19 @@ namespace sqlite_orm { template using col_index_sequence_with_field_type = filter_tuple_sequence_t::template fn, + mpl::as_op>, field_type_t, filter_tuple_sequence_t>; template class TraitFn> using col_index_sequence_with = filter_tuple_sequence_t::template fn, + mpl::as_op>, constraints_type_t, filter_tuple_sequence_t>; template class TraitFn> using col_index_sequence_excluding = filter_tuple_sequence_t::template fn, + mpl::as_op>, constraints_type_t, filter_tuple_sequence_t>; } diff --git a/dev/schema/table.h b/dev/schema/table.h index 32ca06970..8e392c261 100644 --- a/dev/schema/table.h +++ b/dev/schema/table.h @@ -219,7 +219,7 @@ namespace sqlite_orm { void for_each_foreign_key_to(L&& lambda) const { using fk_index_sequence = filter_tuple_sequence_t; using filtered_index_sequence = filter_tuple_sequence_t::template fn, + mpl::as_op>, target_type_t, fk_index_sequence>; iterate_tuple(this->elements, filtered_index_sequence{}, lambda); @@ -250,7 +250,7 @@ namespace sqlite_orm { */ template = true> void for_each_column_excluding(L&& lambda) const { - this->for_each_column_excluding(lambda); + this->for_each_column_excluding>(lambda); } std::vector get_table_info() const; @@ -336,7 +336,7 @@ namespace sqlite_orm { */ template = true> void for_each_column_excluding(L&& lambda) const { - this->for_each_column_excluding(lambda); + this->for_each_column_excluding>(lambda); } /** @@ -358,7 +358,7 @@ namespace sqlite_orm { using colrefs_tuple = decltype(primaryKey.columns); using same_type_index_sequence = filter_tuple_sequence_t>::template fn, + mpl::as_op>>, member_field_type_t>; iterate_tuple(primaryKey.columns, same_type_index_sequence{}, [&res, &column](auto& memberPointer) { if(compare_any(memberPointer, column.member_pointer) || compare_any(memberPointer, column.setter)) { diff --git a/dev/storage.h b/dev/storage.h index 729d9ccc0..e0c634e5c 100644 --- a/dev/storage.h +++ b/dev/storage.h @@ -210,7 +210,7 @@ namespace sqlite_orm { "Insertable table cannot contain > 1 primary keys. Please use 'replace' instead of " "'insert', or you can use 'insert' with explicit column listing."); static_assert(count_filtered_tuple::template fn, + mpl::as_op>, pkcol_index_sequence>::value == 0, "Attempting to execute 'insert' request into an noninsertable table was detected. " "Insertable table cannot contain non-standard primary keys. Please use 'replace' instead " diff --git a/dev/tuple_helper/tuple_filter.h b/dev/tuple_helper/tuple_filter.h index 0cd049d67..1679426b9 100644 --- a/dev/tuple_helper/tuple_filter.h +++ b/dev/tuple_helper/tuple_filter.h @@ -53,13 +53,21 @@ namespace sqlite_orm { : flatten_idxseq>, Pred>::type...> {}; #endif + /* + * `Pred` is a metafunction that defines a bool member named `value` + * `FilterProj` is a metafunction operation + */ template class Pred, - template class Proj = polyfill::type_identity_t, + template class FilterProj = polyfill::type_identity_t, class Seq = std::make_index_sequence::value>> - using filter_tuple_sequence_t = typename filter_tuple_sequence::type; + using filter_tuple_sequence_t = typename filter_tuple_sequence::type; + /* + * `Pred` is a metafunction that defines a bool member named `value` + * `FilterProj` is a metafunction operation + */ template class Pred, @@ -67,6 +75,10 @@ namespace sqlite_orm { class Seq = std::make_index_sequence::value>> using filter_tuple_t = tuple_from_index_sequence_t>; + /* + * `Pred` is a metafunction that defines a bool member named `value` + * `FilterProj` is a metafunction operation + */ template class Pred, @@ -76,6 +88,9 @@ namespace sqlite_orm { /* * Count a tuple, picking only those elements specified in the index sequence. * + * `Pred` is a metafunction that defines a bool member named `value` + * `FilterProj` is a metafunction operation + * * Implementation note: must be distinct from `count_tuple` because legacy compilers have problems * with a default Sequence in function template parameters [SQLITE_ORM_BROKEN_VARIADIC_PACK_EXPANSION]. */ diff --git a/dev/tuple_helper/tuple_traits.h b/dev/tuple_helper/tuple_traits.h index 71f1c13ff..71af31b90 100644 --- a/dev/tuple_helper/tuple_traits.h +++ b/dev/tuple_helper/tuple_traits.h @@ -33,7 +33,7 @@ namespace sqlite_orm { */ template class Proj = polyfill::type_identity_t> using check_if_tuple_has_type = - mpl::bind_front_higherorder_fn::template fn>; + mpl::bind_front_higherorder_fn>>; /* * Metafunction class that checks whether a tuple contains a given template. @@ -49,6 +49,6 @@ namespace sqlite_orm { */ template class Primary> using check_if_tuple_has_template = - mpl::bind_front_higherorder_fn::template fn>; + mpl::bind_front_higherorder_fn>>; } } \ No newline at end of file diff --git a/dev/tuple_helper/tuple_transformer.h b/dev/tuple_helper/tuple_transformer.h index 9ee7083f2..badf61210 100644 --- a/dev/tuple_helper/tuple_transformer.h +++ b/dev/tuple_helper/tuple_transformer.h @@ -16,13 +16,13 @@ namespace sqlite_orm { template class Pack, class... Types, template class Op> struct tuple_transformer, Op> { - using type = Pack...>; + using type = Pack...>; }; /* * Transform specified tuple. * - * `Op` is a metafunction or metafunction operation. + * `Op` is a metafunction operation. */ template class Op> using transform_tuple_t = typename tuple_transformer::type; diff --git a/include/sqlite_orm/sqlite_orm.h b/include/sqlite_orm/sqlite_orm.h index 8dc2337b4..a37c2aae6 100644 --- a/include/sqlite_orm/sqlite_orm.h +++ b/include/sqlite_orm/sqlite_orm.h @@ -794,16 +794,6 @@ namespace sqlite_orm { struct is_metafunction_class : polyfill::bool_constant> {}; #endif - /* - * Given suitable template arguments, detect whether a template is an alias template. - * - * @note It exploits the fact that the `is_specialization_of` trait is only true for class templates - * but always false for alias templates. - */ - template class Template, class... Args> - SQLITE_ORM_INLINE_VAR constexpr bool is_alias_template_v = - !polyfill::is_specialization_of_v, Template>; - /* * Invoke metafunction. */ @@ -832,30 +822,6 @@ namespace sqlite_orm { using invoke_op_t = typename wrap_op::type; #endif - template class F, class... Args> - struct invoke_meta; - - template class Op, class... Args> - struct invoke_meta>, Op, Args...> { - using type = Op; - }; - - template class Fn, class... Args> - struct invoke_meta>, Fn, Args...> : Fn {}; - - /* - * Invoke metafunction or metafunction operation. - * - * @attention If using an alias template, be aware that it isn't recognizable whether an alias template is - * a. an alias of a class template (e.g. `template using aliased = x`) or - * b. an alias of a class template or nested template expression yielding a result (e.g. `template alias_op_t = typename x::type`) - * Therefore, one cannot use `invoke_meta_t` with an alias of a class template (a.), or - * in other words, `invoke_meta_t` expects an alias of a nested template expression (b.). - * The alternative in that case is to use `invoke_fn_t`. - */ - template class F, class... Args> - using invoke_meta_t = typename invoke_meta::type; - /* * Invoke metafunction class by invoking its nested metafunction. */ @@ -869,12 +835,21 @@ namespace sqlite_orm { using instantiate = typename FnCls::template fn; /* - * Wrap given type such that `typename T::type` is valid. + * Provides the metafunction operation of metafunction class as alias template `op`. */ - template - struct type_wrap : polyfill::type_identity {}; - template - struct type_wrap> : T {}; + template + struct op_of { + template + using op = invoke_t; + }; + + /* + * Turn a metafunction class into a metafunction operation. + * + * Useful when a metafunction class needs to be passed as a metafunction operation outside the mpl framework. + */ + template + using as_op = typename op_of::template op; /* * Turn metafunction into a metafunction class. @@ -886,15 +861,15 @@ namespace sqlite_orm { template class Fn> struct quote_fn { template class, class...> - struct invoke_fn; + struct invoke_fn {}; template class F, class... Args> struct invoke_fn>, F, Args...> { - using type = type_wrap>; + using type = F; }; template - using fn = typename invoke_fn::type; + struct fn : invoke_fn {}; }; /* @@ -912,7 +887,9 @@ namespace sqlite_orm { template class Fn, class... Args2> class HigherFn> struct quote_fn { template - struct fn : HigherFn {}; + struct fn { + using type = HigherFn, Args2...>; + }; }; }; @@ -970,7 +947,9 @@ namespace sqlite_orm { template struct always { template - struct fn : type_wrap {}; + struct fn { + using type = T; + }; }; /* @@ -978,16 +957,29 @@ namespace sqlite_orm { */ struct identity { template - struct fn : type_wrap {}; + struct fn { + using type = T; + }; + }; + + /* + * Assume that the invocation of metafunction class yields a trait, and provide members `type` and `value` from the resulting trait. + * + * The goal of using this struct would be to attain lazy evaluation for trait metafunction classes like `conjunction` and `disjunction`. + */ + template + struct yield_trait { + using type = invoke_t; + static constexpr auto value = type::value; }; /* * Metafunction class equivalent to std::negation. */ - template + template struct not_ { template - struct fn : polyfill::negation> {}; + struct fn : polyfill::negation> {}; }; /* @@ -996,7 +988,7 @@ namespace sqlite_orm { template struct conjunction { template - struct fn : polyfill::conjunction...> {}; + struct fn : polyfill::conjunction...> {}; }; /* @@ -1005,7 +997,7 @@ namespace sqlite_orm { template struct disjunction { template - struct fn : polyfill::disjunction...> {}; + struct fn : polyfill::disjunction...> {}; }; #ifndef SQLITE_ORM_BROKEN_ALIAS_TEMPLATE_DEPENDENT_EXPR_SFINAE @@ -1070,9 +1062,11 @@ namespace sqlite_orm { /* * Trait metafunction class that checks if a type (possibly projected) is the same as the specified type. + * + * `Proj` is a projection applied to `Type` and must be a metafunction operation. */ - template class Proj = polyfill::type_identity_t> - using check_if_is_type = mpl::pass_result_to>; + template class ProjOp = polyfill::type_identity_t> + using check_if_is_type = mpl::pass_result_to>; /* * Trait metafunction class that checks if a type's template matches the specified template @@ -1167,7 +1161,7 @@ namespace sqlite_orm { */ template class Proj = polyfill::type_identity_t> using check_if_tuple_has_type = - mpl::bind_front_higherorder_fn::template fn>; + mpl::bind_front_higherorder_fn>>; /* * Metafunction class that checks whether a tuple contains a given template. @@ -1183,7 +1177,7 @@ namespace sqlite_orm { */ template class Primary> using check_if_tuple_has_template = - mpl::bind_front_higherorder_fn::template fn>; + mpl::bind_front_higherorder_fn>>; } } // #include "tuple_helper/tuple_filter.h" @@ -1275,13 +1269,21 @@ namespace sqlite_orm { : flatten_idxseq>, Pred>::type...> {}; #endif + /* + * `Pred` is a metafunction that defines a bool member named `value` + * `FilterProj` is a metafunction operation + */ template class Pred, - template class Proj = polyfill::type_identity_t, + template class FilterProj = polyfill::type_identity_t, class Seq = std::make_index_sequence::value>> - using filter_tuple_sequence_t = typename filter_tuple_sequence::type; + using filter_tuple_sequence_t = typename filter_tuple_sequence::type; + /* + * `Pred` is a metafunction that defines a bool member named `value` + * `FilterProj` is a metafunction operation + */ template class Pred, @@ -1289,6 +1291,10 @@ namespace sqlite_orm { class Seq = std::make_index_sequence::value>> using filter_tuple_t = tuple_from_index_sequence_t>; + /* + * `Pred` is a metafunction that defines a bool member named `value` + * `FilterProj` is a metafunction operation + */ template class Pred, @@ -1298,6 +1304,9 @@ namespace sqlite_orm { /* * Count a tuple, picking only those elements specified in the index sequence. * + * `Pred` is a metafunction that defines a bool member named `value` + * `FilterProj` is a metafunction operation + * * Implementation note: must be distinct from `count_tuple` because legacy compilers have problems * with a default Sequence in function template parameters [SQLITE_ORM_BROKEN_VARIADIC_PACK_EXPANSION]. */ @@ -1825,25 +1834,25 @@ namespace sqlite_orm { template struct is_primary_key_insertable : polyfill::disjunction< - mpl::instantiate, - check_if_tuple_has_template>, - constraints_type_t>, + mpl::invoke_t, + check_if_tuple_has_template>, + constraints_type_t>, std::is_base_of>>> { static_assert(tuple_has>::value, "an unexpected type was passed"); }; template - using is_constraint = mpl::instantiate, - check_if, - check_if_is_type, - check_if_is_type, - check_if_is_template, - check_if_is_template, - check_if_is_template, - check_if_is_type, - check_if>, - T>; + using is_constraint = mpl::invoke_t, + check_if, + check_if_is_type, + check_if_is_type, + check_if_is_template, + check_if_is_template, + check_if_is_template, + check_if_is_type, + check_if>, + T>; } #if SQLITE_VERSION_NUMBER >= 3031000 @@ -2629,19 +2638,19 @@ namespace sqlite_orm { template using col_index_sequence_with_field_type = filter_tuple_sequence_t::template fn, + mpl::as_op>, field_type_t, filter_tuple_sequence_t>; template class TraitFn> using col_index_sequence_with = filter_tuple_sequence_t::template fn, + mpl::as_op>, constraints_type_t, filter_tuple_sequence_t>; template class TraitFn> using col_index_sequence_excluding = filter_tuple_sequence_t::template fn, + mpl::as_op>, constraints_type_t, filter_tuple_sequence_t>; } @@ -9876,13 +9885,13 @@ namespace sqlite_orm { template class Pack, class... Types, template class Op> struct tuple_transformer, Op> { - using type = Pack...>; + using type = Pack...>; }; /* * Transform specified tuple. * - * `Op` is a metafunction or metafunction operation. + * `Op` is a metafunction operation. */ template class Op> using transform_tuple_t = typename tuple_transformer::type; @@ -10174,7 +10183,7 @@ namespace sqlite_orm { void for_each_foreign_key_to(L&& lambda) const { using fk_index_sequence = filter_tuple_sequence_t; using filtered_index_sequence = filter_tuple_sequence_t::template fn, + mpl::as_op>, target_type_t, fk_index_sequence>; iterate_tuple(this->elements, filtered_index_sequence{}, lambda); @@ -10205,7 +10214,7 @@ namespace sqlite_orm { */ template = true> void for_each_column_excluding(L&& lambda) const { - this->for_each_column_excluding(lambda); + this->for_each_column_excluding>(lambda); } std::vector get_table_info() const; @@ -10291,7 +10300,7 @@ namespace sqlite_orm { */ template = true> void for_each_column_excluding(L&& lambda) const { - this->for_each_column_excluding(lambda); + this->for_each_column_excluding>(lambda); } /** @@ -10313,7 +10322,7 @@ namespace sqlite_orm { using colrefs_tuple = decltype(primaryKey.columns); using same_type_index_sequence = filter_tuple_sequence_t>::template fn, + mpl::as_op>>, member_field_type_t>; iterate_tuple(primaryKey.columns, same_type_index_sequence{}, [&res, &column](auto& memberPointer) { if(compare_any(memberPointer, column.member_pointer) || compare_any(memberPointer, column.setter)) { @@ -11085,7 +11094,7 @@ namespace sqlite_orm { template using column_result_for_tuple_t = - transform_tuple_t::template fn>; + transform_tuple_t>>; #ifdef SQLITE_ORM_OPTIONAL_SUPPORTED template @@ -15814,8 +15823,8 @@ namespace sqlite_orm { (*this)(colExpr, context); }); // note: `capacity() > size()` can occur in case `asterisk_t<>` does spell out the columns in defined order - if(mpl::instantiate, - typename columns_t::columns_type>::value && + if(mpl::invoke_t, + typename columns_t::columns_type>::value && collectedExpressions.capacity() > collectedExpressions.size()) { collectedExpressions.shrink_to_fit(); } @@ -18219,7 +18228,7 @@ namespace sqlite_orm { "Insertable table cannot contain > 1 primary keys. Please use 'replace' instead of " "'insert', or you can use 'insert' with explicit column listing."); static_assert(count_filtered_tuple::template fn, + mpl::as_op>, pkcol_index_sequence>::value == 0, "Attempting to execute 'insert' request into an noninsertable table was detected. " "Insertable table cannot contain non-standard primary keys. Please use 'replace' instead " @@ -20149,7 +20158,7 @@ namespace sqlite_orm { template std::unique_ptr column_constraints::default_value() const { using default_op_index_sequence = - filter_tuple_sequence_t::template fn>; + filter_tuple_sequence_t>>; std::unique_ptr value; call_if_constexpr( diff --git a/tests/static_tests/functional/mpl.cpp b/tests/static_tests/functional/mpl.cpp index 0ddbdd123..78d131f50 100644 --- a/tests/static_tests/functional/mpl.cpp +++ b/tests/static_tests/functional/mpl.cpp @@ -13,19 +13,25 @@ using transparent_of_t = typename TransparentFunction::is_transparent; template using make_literal_holder = literal_holder; +template +using value_type_t = typename T::value_type; + TEST_CASE("mpl") { using mpl_is_same = mpl::quote_fn; + using check_if_same_type = mpl::bind_front_fn; + using check_if_same_template = + mpl::pass_extracted_fn_to>>; + using check_if_names_value_type = mpl::bind_front_higherorder_fn; + using predicate_type = std::less; + using check_if_is_projected_type = internal::check_if_is_type; STATIC_REQUIRE_FALSE(mpl::is_metafunction_class_v); STATIC_REQUIRE(mpl::is_metafunction_class_v); - STATIC_REQUIRE_FALSE(mpl::is_alias_template_v); - STATIC_REQUIRE(mpl::is_alias_template_v); - STATIC_REQUIRE(mpl::is_alias_template_v); STATIC_REQUIRE(std::is_same, int>::value); STATIC_REQUIRE(std::is_same, int>::value); - STATIC_REQUIRE(std::is_same, int>::value); - STATIC_REQUIRE(std::is_same, int>::value); - STATIC_REQUIRE(std::is_same, literal_holder>::value); + STATIC_REQUIRE(std::is_same, literal_holder>::value); + STATIC_REQUIRE(std::is_same>>, + literal_holder>::value); STATIC_REQUIRE(mpl::invoke_t::value); STATIC_REQUIRE_FALSE(mpl::invoke_t, int, int>::value); STATIC_REQUIRE(mpl::invoke_t>, size_t, int>::value); @@ -42,19 +48,13 @@ TEST_CASE("mpl") { STATIC_REQUIRE(mpl::invoke_t, mpl::quote_fn, mpl::quote_fn>::value); - using check_if_same_type = mpl::bind_front_fn; STATIC_REQUIRE(mpl::invoke_t::value); - using check_if_same_template = - mpl::pass_extracted_fn_to>>; STATIC_REQUIRE(mpl::invoke_t>::value); - using check_if_names_type = mpl::bind_front_higherorder_fn; - STATIC_REQUIRE_FALSE(mpl::invoke_t::value); - STATIC_REQUIRE(mpl::invoke_t::value); - using predicate_type = std::less; + STATIC_REQUIRE_FALSE(mpl::invoke_t::value); + STATIC_REQUIRE(mpl::invoke_t::value); STATIC_REQUIRE(std::is_same, predicate_type>, predicate_type::is_transparent>::value); STATIC_REQUIRE(mpl::invoke_t, predicate_type>::value); - using check_if_is_projected_type = internal::check_if_is_type; STATIC_REQUIRE(mpl::invoke_t::value); } diff --git a/tests/static_tests/is_column_with_insertable_primary_key.cpp b/tests/static_tests/is_column_with_insertable_primary_key.cpp index 4be302d29..a4f85d43f 100644 --- a/tests/static_tests/is_column_with_insertable_primary_key.cpp +++ b/tests/static_tests/is_column_with_insertable_primary_key.cpp @@ -11,7 +11,7 @@ using insertable_index_sequence = filter_tuple_sequence_t>; template using noninsertable_index_sequence = filter_tuple_sequence_t::template fn, + mpl::as_op>, polyfill::type_identity_t, col_index_sequence_with>; diff --git a/tests/static_tests/static_tests_storage_traits.h b/tests/static_tests/static_tests_storage_traits.h index b5247aae6..3a224f2f9 100644 --- a/tests/static_tests/static_tests_storage_traits.h +++ b/tests/static_tests/static_tests_storage_traits.h @@ -40,7 +40,7 @@ namespace sqlite_orm { template struct table_foreign_keys_count : count_filtered_tuple, - check_if_is_type::template fn, + mpl::as_op>, filter_tuple_sequence_t, is_foreign_key>, target_type_t> {}; @@ -75,7 +75,7 @@ namespace sqlite_orm { template using table_foreign_keys_t = filter_tuple_t, - check_if_is_type::template fn, + mpl::as_op>, target_type_t, filter_tuple_sequence_t, is_foreign_key>>; From 45db8d8d6de7c091324af526a58443bbf0ea4362 Mon Sep 17 00:00:00 2001 From: klaus triendl Date: Wed, 8 Nov 2023 15:25:06 +0200 Subject: [PATCH 07/11] Updated metafunction programming ... with the knowledge gained from understanding the Boost Mp11 library. --- dev/column_result.h | 2 +- dev/functional/mpl.h | 251 ++++++--------- dev/implementations/column_definitions.h | 2 +- dev/schema/column.h | 6 +- dev/schema/table.h | 14 +- dev/storage.h | 2 +- dev/tuple_helper/tuple_filter.h | 8 +- dev/tuple_helper/tuple_traits.h | 4 +- dev/tuple_helper/tuple_transformer.h | 4 +- include/sqlite_orm/sqlite_orm.h | 293 ++++++++---------- tests/static_tests/functional/mpl.cpp | 16 +- .../is_column_with_insertable_primary_key.cpp | 2 +- .../static_tests_storage_traits.h | 4 +- 13 files changed, 255 insertions(+), 353 deletions(-) diff --git a/dev/column_result.h b/dev/column_result.h index c46e30354..df6d383d0 100644 --- a/dev/column_result.h +++ b/dev/column_result.h @@ -47,7 +47,7 @@ namespace sqlite_orm { template using column_result_for_tuple_t = - transform_tuple_t>>; + transform_tuple_t::template fn>; #ifdef SQLITE_ORM_OPTIONAL_SUPPORTED template diff --git a/dev/functional/mpl.h b/dev/functional/mpl.h index cb35ec0ea..ff5f77601 100644 --- a/dev/functional/mpl.h +++ b/dev/functional/mpl.h @@ -2,25 +2,29 @@ /* * Symbols for 'template metaprogramming' (compile-time template programming), - * inspired by the MPL of Aleksey Gurtovoy and David Abrahams. + * inspired by the MPL of Aleksey Gurtovoy and David Abrahams, and the Mp11 of Peter Dimov and Bjørn Reese. * * Currently, the focus is on facilitating advanced type filtering, * such as filtering columns by constraints having various traits. * Hence it contains only a very small subset of a full MPL. * * Three key concepts are critical to understanding: - * 1. A 'metafunction' is a class template with a nested `type` typename, and represents a function invocable at compile-time. - * E.g. `template struct x { using type = int; };` - * 2. A 'metafunction operation' is an alias template for a class template or a nested template expression, whose instantiation yields a type. + * 1. A 'trait' is a class template with a nested `type` typename. + * The term 'trait' might be too narrow or not entirely accurate, however in the STL those class templates are summarized as "Type transformations". + * hence being "transformation type traits". + * It was the traditional way of transforming types before the arrival of alias templates. + * E.g. `template struct x { using type = T; };` + * They are of course still available today, but are rather used as building blocks. + * 2. A 'metafunction' is an alias template for a class template or a nested template expression, whose instantiation yields a type. * E.g. `template using alias_op_t = typename x::type` - * 3. A 'metafunction class' is a certain form of metafunction representation that enables higher-order metaprogramming. - * More precisely, it's a class with a nested metafunction called "fn" - * Correspondingly, a metafunction class invocation is defined as invocation of its nested "fn" metafunction. + * 3. A 'quoted metafunction' (aka 'metafunction class') is a certain form of metafunction representation that enables higher-order metaprogramming. + * More precisely, it's a class with a nested metafunction called "fn". + * Correspondingly, a quoted metafunction invocation is defined as invocation of its nested "fn" metafunction. * * Conventions: - * - "Fn" is the name for a metafunction template template parameter. - * - "FnCls" is the name for a metafunction class template parameter. - * - "_fn" is a suffix for a type that accepts metafunctions and turns them into metafunction classes. + * - "Fn" is the name of a template template parameter for a metafunction. + * - "Q" is the name of class template parameter for a quoted metafunction. + * - "_fn" is a suffix for a class or alias template that accepts metafunctions and turns them into quoted metafunctions. * - "higher order" denotes a metafunction that operates on another metafunction (i.e. takes it as an argument). */ @@ -42,79 +46,53 @@ namespace sqlite_orm { * of older compilers having problems with the detection of dependent templates [SQLITE_ORM_BROKEN_ALIAS_TEMPLATE_DEPENDENT_EXPR_SFINAE]. */ template - SQLITE_ORM_INLINE_VAR constexpr bool is_metafunction_class_v = false; - template + SQLITE_ORM_INLINE_VAR constexpr bool is_quoted_metafuntion_v = false; + template SQLITE_ORM_INLINE_VAR constexpr bool - is_metafunction_class_v>> = - true; + is_quoted_metafuntion_v>> = true; #ifndef SQLITE_ORM_BROKEN_ALIAS_TEMPLATE_DEPENDENT_EXPR_SFINAE template - using is_metafunction_class = polyfill::bool_constant>; + using is_quoted_metafuntion = polyfill::bool_constant>; #else template - struct is_metafunction_class : polyfill::bool_constant> {}; + struct is_quoted_metafuntion : polyfill::bool_constant> {}; #endif /* - * Invoke metafunction. + * The indirection through `defer_fn` works around the language inability + * to expand `Args...` into a fixed parameter list of an alias template. */ template class Fn, class... Args> - using invoke_fn_t = typename Fn::type; + struct defer { + using type = Fn; + }; #ifndef SQLITE_ORM_BROKEN_ALIAS_TEMPLATE_DEPENDENT_EXPR_SFINAE /* - * Invoke metafunction operation. + * Invoke metafunction. */ - template class Op, class... Args> - using invoke_op_t = Op; + template class Fn, class... Args> + using invoke_fn_t = Fn; #else - template class Op, class... Args> - struct wrap_op { - using type = Op; - }; - /* - * Invoke metafunction operation. + * Invoke metafunction. * * Note: legacy compilers need an extra layer of indirection, otherwise type replacement may fail - * if alias template `Op` has a dependent expression in it. + * if alias template `Fn` has a dependent expression in it. */ - template class Op, class... Args> - using invoke_op_t = typename wrap_op::type; + template class Fn, class... Args> + using invoke_fn_t = typename defer::type; #endif /* - * Invoke metafunction class by invoking its nested metafunction. - */ - template - using invoke_t = typename FnCls::template fn::type; - - /* - * Instantiate metafunction class' nested metafunction. + * Invoke quoted metafunction by invoking its nested metafunction. */ - template - using instantiate = typename FnCls::template fn; + template + using invoke_t = typename Q::template fn; /* - * Provides the metafunction operation of metafunction class as alias template `op`. - */ - template - struct op_of { - template - using op = invoke_t; - }; - - /* - * Turn a metafunction class into a metafunction operation. - * - * Useful when a metafunction class needs to be passed as a metafunction operation outside the mpl framework. - */ - template - using as_op = typename op_of::template op; - - /* - * Turn metafunction into a metafunction class. + * Turn metafunction into a quoted metafunction. * * Invocation of the nested metafunction `fn` is SFINAE-friendly (detection idiom). * This is necessary because `fn` is a proxy to the originally quoted metafunction, @@ -123,15 +101,15 @@ namespace sqlite_orm { template class Fn> struct quote_fn { template class, class...> - struct invoke_fn {}; + struct invoke_this_fn {}; template class F, class... Args> - struct invoke_fn>, F, Args...> { + struct invoke_this_fn>, F, Args...> { using type = F; }; template - struct fn : invoke_fn {}; + using fn = typename invoke_this_fn::type; }; /* @@ -144,158 +122,131 @@ namespace sqlite_orm { template<> struct higherorder<0u> { /* - * Turn higher-order metafunction into a metafunction class. + * Turn higher-order metafunction into a quoted metafunction. */ template class Fn, class... Args2> class HigherFn> struct quote_fn { - template - struct fn { - using type = HigherFn, Args2...>; - }; + template + using fn = HigherFn; }; }; /* - * Metafunction class that extracts the nested metafunction of its metafunction class argument, - * quotes the extracted metafunction and passes it on to the next metafunction class + * Quoted metafunction that extracts the nested metafunction of its quoted metafunction argument, + * quotes the extracted metafunction and passes it on to the next quoted metafunction * (kind of the inverse of quoting). */ - template + template struct pass_extracted_fn_to { template - struct fn : FnCls::template fn {}; + struct invoke_this_fn { + using type = typename Q::template fn; + }; + + // extract class template, quote, pass on + template class Fn, class... T> + struct invoke_this_fn> { + using type = typename Q::template fn>; + }; - // extract, quote, pass on - template class Fn, class... Args> - struct fn> : FnCls::template fn> {}; + template + using fn = typename invoke_this_fn::type; }; /* - * Metafunction class that invokes the specified metafunction operation, - * and passes its result on to the next metafunction class. + * Quoted metafunction that invokes the specified metafunctions, + * and passes its result on to the next quoted metafunction. */ - template class Op, class FnCls> - struct pass_result_to { - // call Op, pass on its result + template class... Fn> + struct pass_result_of { + // invoke `Fn`, pass on their result template - struct fn : FnCls::template fn> {}; + using fn = typename Q::template fn::type...>; }; /* - * Bind arguments at the front of a metafunction class. - * Metafunction class equivalent to std::bind_front(). + * Bind arguments at the front of a quoted metafunction. */ - template + template struct bind_front { template - struct fn : FnCls::template fn {}; + using fn = typename Q::template fn; }; /* - * Bind arguments at the back of a metafunction class. - * Metafunction class equivalent to std::bind_back() + * Bind arguments at the back of a quoted metafunction. */ - template + template struct bind_back { template - struct fn : FnCls::template fn {}; + using fn = typename Q::template fn; }; /* - * Metafunction class equivalent to polyfill::always_false. - * It ignores arguments passed to the metafunction, - * and always returns the given type. + * Quoted metafunction equivalent to `polyfill::always_false`. + * It ignores arguments passed to the metafunction, and always returns the specified type. */ template struct always { - template - struct fn { - using type = T; - }; + template + using fn = T; }; /* - * Unary metafunction class equivalent to std::type_identity. + * Unary quoted metafunction equivalent to `std::type_identity_t`. */ - struct identity { - template - struct fn { - using type = T; - }; - }; + using identity = quote_fn; /* - * Assume that the invocation of metafunction class yields a trait, and provide members `type` and `value` from the resulting trait. - * - * The goal of using this struct would be to attain lazy evaluation for trait metafunction classes like `conjunction` and `disjunction`. + * Quoted metafunction equivalent to `std::negation`. */ - template - struct yield_trait { - using type = invoke_t; - static constexpr auto value = type::value; - }; + template + using not_ = pass_result_of, TraitQ::template fn>; /* - * Metafunction class equivalent to std::negation. + * Quoted metafunction equivalent to `std::conjunction`. */ - template - struct not_ { - template - struct fn : polyfill::negation> {}; - }; + template + using conjunction = pass_result_of, TraitQ::template fn...>; /* - * Metafunction class equivalent to std::conjunction + * Quoted metafunction equivalent to `std::disjunction`. */ - template - struct conjunction { - template - struct fn : polyfill::conjunction...> {}; - }; + template + using disjunction = pass_result_of, TraitQ::template fn...>; /* - * Metafunction class equivalent to std::disjunction. + * Metafunction equivalent to `std::conjunction`. */ - template - struct disjunction { - template - struct fn : polyfill::disjunction...> {}; - }; + template class... TraitFn> + using conjunction_fn = pass_result_of, TraitFn...>; -#ifndef SQLITE_ORM_BROKEN_ALIAS_TEMPLATE_DEPENDENT_EXPR_SFINAE /* - * Metafunction equivalent to std::conjunction. + * Metafunction equivalent to `std::disjunction`. */ template class... TraitFn> - using conjunction_fn = conjunction...>; + using disjunction_fn = pass_result_of, TraitFn...>; /* - * Metafunction equivalent to std::disjunction. + * Metafunction equivalent to `std::negation`. */ - template class... TraitFn> - using disjunction_fn = disjunction...>; -#else - template class... TraitFn> - struct conjunction_fn : conjunction...> {}; - - template class... TraitFn> - struct disjunction_fn : disjunction...> {}; -#endif + template class Fn> + using not_fn = not_>; /* - * Convenience template alias for binding arguments at the front of a metafunction. + * Bind arguments at the front of a metafunction. */ template class Fn, class... Bound> using bind_front_fn = bind_front, Bound...>; /* - * Convenience template alias for binding arguments at the back of a metafunction. + * Bind arguments at the back of a metafunction. */ template class Fn, class... Bound> using bind_back_fn = bind_back, Bound...>; /* - * Convenience template alias for binding a metafunction at the front of a higher-order metafunction. + * Bind a metafunction and arguments at the front of a higher-order metafunction. */ template class Fn, class... Args2> class HigherFn, template @@ -308,30 +259,30 @@ namespace sqlite_orm { namespace mpl = internal::mpl; - // convenience metafunction classes + // convenience quoted metafunctions namespace internal { /* - * Trait metafunction class that checks if a type has the specified trait. + * Quoted trait metafunction that checks if a type has the specified trait. */ template class TraitFn> using check_if = mpl::quote_fn; /* - * Trait metafunction class that checks if a type doesn't have the specified trait. + * Quoted trait metafunction that checks if a type doesn't have the specified trait. */ template class TraitFn> - using check_if_not = mpl::not_>; + using check_if_not = mpl::not_fn; /* - * Trait metafunction class that checks if a type (possibly projected) is the same as the specified type. + * Quoted trait metafunction that checks if a type (possibly projected) is the same as the specified type. * - * `Proj` is a projection applied to `Type` and must be a metafunction operation. + * `ProjOp` is a projection applied to `Type` and must be a metafunction. */ template class ProjOp = polyfill::type_identity_t> - using check_if_is_type = mpl::pass_result_to>; + using check_if_is_type = mpl::pass_result_of, ProjOp>; /* - * Trait metafunction class that checks if a type's template matches the specified template + * Quoted trait metafunction that checks if a type's template matches the specified template * (similar to `is_specialization_of`). */ template class Template> diff --git a/dev/implementations/column_definitions.h b/dev/implementations/column_definitions.h index 1b07924b7..f8b70f508 100644 --- a/dev/implementations/column_definitions.h +++ b/dev/implementations/column_definitions.h @@ -20,7 +20,7 @@ namespace sqlite_orm { template std::unique_ptr column_constraints::default_value() const { using default_op_index_sequence = - filter_tuple_sequence_t>>; + filter_tuple_sequence_t::template fn>; std::unique_ptr value; call_if_constexpr( diff --git a/dev/schema/column.h b/dev/schema/column.h index 05e00ecaf..f7654a1cf 100644 --- a/dev/schema/column.h +++ b/dev/schema/column.h @@ -113,19 +113,19 @@ namespace sqlite_orm { template using col_index_sequence_with_field_type = filter_tuple_sequence_t>, + check_if_is_type::template fn, field_type_t, filter_tuple_sequence_t>; template class TraitFn> using col_index_sequence_with = filter_tuple_sequence_t>, + check_if_tuple_has::template fn, constraints_type_t, filter_tuple_sequence_t>; template class TraitFn> using col_index_sequence_excluding = filter_tuple_sequence_t>, + check_if_tuple_has_not::template fn, constraints_type_t, filter_tuple_sequence_t>; } diff --git a/dev/schema/table.h b/dev/schema/table.h index 8e392c261..86837602f 100644 --- a/dev/schema/table.h +++ b/dev/schema/table.h @@ -219,7 +219,7 @@ namespace sqlite_orm { void for_each_foreign_key_to(L&& lambda) const { using fk_index_sequence = filter_tuple_sequence_t; using filtered_index_sequence = filter_tuple_sequence_t>, + check_if_is_type::template fn, target_type_t, fk_index_sequence>; iterate_tuple(this->elements, filtered_index_sequence{}, lambda); @@ -248,9 +248,9 @@ namespace sqlite_orm { * Call passed lambda with columns not having the specified constraint trait `OpTrait`. * @param lambda Lambda called for each column. */ - template = true> + template = true> void for_each_column_excluding(L&& lambda) const { - this->for_each_column_excluding>(lambda); + this->for_each_column_excluding(lambda); } std::vector get_table_info() const; @@ -291,7 +291,7 @@ namespace sqlite_orm { * Call passed lambda with columns not having the specified constraint trait `OpTrait`. * @param lambda Lambda called for each column. */ - template = true> + template = true> void for_each_column_excluding(L&& lambda) const { this->module_details.template for_each_column_excluding(lambda); } @@ -334,9 +334,9 @@ namespace sqlite_orm { * Call passed lambda with columns not having the specified constraint trait `OpTrait`. * @param lambda Lambda called for each column. */ - template = true> + template = true> void for_each_column_excluding(L&& lambda) const { - this->for_each_column_excluding>(lambda); + this->for_each_column_excluding(lambda); } /** @@ -358,7 +358,7 @@ namespace sqlite_orm { using colrefs_tuple = decltype(primaryKey.columns); using same_type_index_sequence = filter_tuple_sequence_t>>, + check_if_is_type>::template fn, member_field_type_t>; iterate_tuple(primaryKey.columns, same_type_index_sequence{}, [&res, &column](auto& memberPointer) { if(compare_any(memberPointer, column.member_pointer) || compare_any(memberPointer, column.setter)) { diff --git a/dev/storage.h b/dev/storage.h index e0c634e5c..729d9ccc0 100644 --- a/dev/storage.h +++ b/dev/storage.h @@ -210,7 +210,7 @@ namespace sqlite_orm { "Insertable table cannot contain > 1 primary keys. Please use 'replace' instead of " "'insert', or you can use 'insert' with explicit column listing."); static_assert(count_filtered_tuple>, + check_if_not::template fn, pkcol_index_sequence>::value == 0, "Attempting to execute 'insert' request into an noninsertable table was detected. " "Insertable table cannot contain non-standard primary keys. Please use 'replace' instead " diff --git a/dev/tuple_helper/tuple_filter.h b/dev/tuple_helper/tuple_filter.h index 1679426b9..1888e4ffb 100644 --- a/dev/tuple_helper/tuple_filter.h +++ b/dev/tuple_helper/tuple_filter.h @@ -55,7 +55,7 @@ namespace sqlite_orm { /* * `Pred` is a metafunction that defines a bool member named `value` - * `FilterProj` is a metafunction operation + * `FilterProj` is a metafunction */ template @@ -66,7 +66,7 @@ namespace sqlite_orm { /* * `Pred` is a metafunction that defines a bool member named `value` - * `FilterProj` is a metafunction operation + * `FilterProj` is a metafunction */ template @@ -77,7 +77,7 @@ namespace sqlite_orm { /* * `Pred` is a metafunction that defines a bool member named `value` - * `FilterProj` is a metafunction operation + * `FilterProj` is a metafunction */ template @@ -89,7 +89,7 @@ namespace sqlite_orm { * Count a tuple, picking only those elements specified in the index sequence. * * `Pred` is a metafunction that defines a bool member named `value` - * `FilterProj` is a metafunction operation + * `FilterProj` is a metafunction * * Implementation note: must be distinct from `count_tuple` because legacy compilers have problems * with a default Sequence in function template parameters [SQLITE_ORM_BROKEN_VARIADIC_PACK_EXPANSION]. diff --git a/dev/tuple_helper/tuple_traits.h b/dev/tuple_helper/tuple_traits.h index 71af31b90..71f1c13ff 100644 --- a/dev/tuple_helper/tuple_traits.h +++ b/dev/tuple_helper/tuple_traits.h @@ -33,7 +33,7 @@ namespace sqlite_orm { */ template class Proj = polyfill::type_identity_t> using check_if_tuple_has_type = - mpl::bind_front_higherorder_fn>>; + mpl::bind_front_higherorder_fn::template fn>; /* * Metafunction class that checks whether a tuple contains a given template. @@ -49,6 +49,6 @@ namespace sqlite_orm { */ template class Primary> using check_if_tuple_has_template = - mpl::bind_front_higherorder_fn>>; + mpl::bind_front_higherorder_fn::template fn>; } } \ No newline at end of file diff --git a/dev/tuple_helper/tuple_transformer.h b/dev/tuple_helper/tuple_transformer.h index badf61210..50c96c934 100644 --- a/dev/tuple_helper/tuple_transformer.h +++ b/dev/tuple_helper/tuple_transformer.h @@ -16,13 +16,13 @@ namespace sqlite_orm { template class Pack, class... Types, template class Op> struct tuple_transformer, Op> { - using type = Pack...>; + using type = Pack...>; }; /* * Transform specified tuple. * - * `Op` is a metafunction operation. + * `Op` is a metafunction. */ template class Op> using transform_tuple_t = typename tuple_transformer::type; diff --git a/include/sqlite_orm/sqlite_orm.h b/include/sqlite_orm/sqlite_orm.h index a37c2aae6..9aeadb9cc 100644 --- a/include/sqlite_orm/sqlite_orm.h +++ b/include/sqlite_orm/sqlite_orm.h @@ -739,25 +739,29 @@ namespace sqlite_orm { /* * Symbols for 'template metaprogramming' (compile-time template programming), - * inspired by the MPL of Aleksey Gurtovoy and David Abrahams. + * inspired by the MPL of Aleksey Gurtovoy and David Abrahams, and the Mp11 of Peter Dimov and Bjørn Reese. * * Currently, the focus is on facilitating advanced type filtering, * such as filtering columns by constraints having various traits. * Hence it contains only a very small subset of a full MPL. * * Three key concepts are critical to understanding: - * 1. A 'metafunction' is a class template with a nested `type` typename, and represents a function invocable at compile-time. - * E.g. `template struct x { using type = int; };` - * 2. A 'metafunction operation' is an alias template for a class template or a nested template expression, whose instantiation yields a type. + * 1. A 'trait' is a class template with a nested `type` typename. + * The term 'trait' might be too narrow or not entirely accurate, however in the STL those class templates are summarized as "Type transformations". + * hence being "transformation type traits". + * It was the traditional way of transforming types before the arrival of alias templates. + * E.g. `template struct x { using type = T; };` + * They are of course still available today, but are rather used as building blocks. + * 2. A 'metafunction' is an alias template for a class template or a nested template expression, whose instantiation yields a type. * E.g. `template using alias_op_t = typename x::type` - * 3. A 'metafunction class' is a certain form of metafunction representation that enables higher-order metaprogramming. - * More precisely, it's a class with a nested metafunction called "fn" - * Correspondingly, a metafunction class invocation is defined as invocation of its nested "fn" metafunction. + * 3. A 'quoted metafunction' (aka 'metafunction class') is a certain form of metafunction representation that enables higher-order metaprogramming. + * More precisely, it's a class with a nested metafunction called "fn". + * Correspondingly, a quoted metafunction invocation is defined as invocation of its nested "fn" metafunction. * * Conventions: - * - "Fn" is the name for a metafunction template template parameter. - * - "FnCls" is the name for a metafunction class template parameter. - * - "_fn" is a suffix for a type that accepts metafunctions and turns them into metafunction classes. + * - "Fn" is the name of a template template parameter for a metafunction. + * - "Q" is the name of class template parameter for a quoted metafunction. + * - "_fn" is a suffix for a class or alias template that accepts metafunctions and turns them into quoted metafunctions. * - "higher order" denotes a metafunction that operates on another metafunction (i.e. takes it as an argument). */ @@ -780,79 +784,53 @@ namespace sqlite_orm { * of older compilers having problems with the detection of dependent templates [SQLITE_ORM_BROKEN_ALIAS_TEMPLATE_DEPENDENT_EXPR_SFINAE]. */ template - SQLITE_ORM_INLINE_VAR constexpr bool is_metafunction_class_v = false; - template + SQLITE_ORM_INLINE_VAR constexpr bool is_quoted_metafuntion_v = false; + template SQLITE_ORM_INLINE_VAR constexpr bool - is_metafunction_class_v>> = - true; + is_quoted_metafuntion_v>> = true; #ifndef SQLITE_ORM_BROKEN_ALIAS_TEMPLATE_DEPENDENT_EXPR_SFINAE template - using is_metafunction_class = polyfill::bool_constant>; + using is_quoted_metafuntion = polyfill::bool_constant>; #else template - struct is_metafunction_class : polyfill::bool_constant> {}; + struct is_quoted_metafuntion : polyfill::bool_constant> {}; #endif /* - * Invoke metafunction. + * The indirection through `defer_fn` works around the language inability + * to expand `Args...` into a fixed parameter list of an alias template. */ template class Fn, class... Args> - using invoke_fn_t = typename Fn::type; + struct defer { + using type = Fn; + }; #ifndef SQLITE_ORM_BROKEN_ALIAS_TEMPLATE_DEPENDENT_EXPR_SFINAE /* - * Invoke metafunction operation. + * Invoke metafunction. */ - template class Op, class... Args> - using invoke_op_t = Op; + template class Fn, class... Args> + using invoke_fn_t = Fn; #else - template class Op, class... Args> - struct wrap_op { - using type = Op; - }; - /* - * Invoke metafunction operation. + * Invoke metafunction. * * Note: legacy compilers need an extra layer of indirection, otherwise type replacement may fail - * if alias template `Op` has a dependent expression in it. + * if alias template `Fn` has a dependent expression in it. */ - template class Op, class... Args> - using invoke_op_t = typename wrap_op::type; + template class Fn, class... Args> + using invoke_fn_t = typename defer::type; #endif /* - * Invoke metafunction class by invoking its nested metafunction. + * Invoke quoted metafunction by invoking its nested metafunction. */ - template - using invoke_t = typename FnCls::template fn::type; + template + using invoke_t = typename Q::template fn; /* - * Instantiate metafunction class' nested metafunction. - */ - template - using instantiate = typename FnCls::template fn; - - /* - * Provides the metafunction operation of metafunction class as alias template `op`. - */ - template - struct op_of { - template - using op = invoke_t; - }; - - /* - * Turn a metafunction class into a metafunction operation. - * - * Useful when a metafunction class needs to be passed as a metafunction operation outside the mpl framework. - */ - template - using as_op = typename op_of::template op; - - /* - * Turn metafunction into a metafunction class. + * Turn metafunction into a quoted metafunction. * * Invocation of the nested metafunction `fn` is SFINAE-friendly (detection idiom). * This is necessary because `fn` is a proxy to the originally quoted metafunction, @@ -861,15 +839,15 @@ namespace sqlite_orm { template class Fn> struct quote_fn { template class, class...> - struct invoke_fn {}; + struct invoke_this_fn {}; template class F, class... Args> - struct invoke_fn>, F, Args...> { + struct invoke_this_fn>, F, Args...> { using type = F; }; template - struct fn : invoke_fn {}; + using fn = typename invoke_this_fn::type; }; /* @@ -882,158 +860,131 @@ namespace sqlite_orm { template<> struct higherorder<0u> { /* - * Turn higher-order metafunction into a metafunction class. + * Turn higher-order metafunction into a quoted metafunction. */ template class Fn, class... Args2> class HigherFn> struct quote_fn { - template - struct fn { - using type = HigherFn, Args2...>; - }; + template + using fn = HigherFn; }; }; /* - * Metafunction class that extracts the nested metafunction of its metafunction class argument, - * quotes the extracted metafunction and passes it on to the next metafunction class + * Quoted metafunction that extracts the nested metafunction of its quoted metafunction argument, + * quotes the extracted metafunction and passes it on to the next quoted metafunction * (kind of the inverse of quoting). */ - template + template struct pass_extracted_fn_to { template - struct fn : FnCls::template fn {}; + struct invoke_this_fn { + using type = typename Q::template fn; + }; - // extract, quote, pass on - template class Fn, class... Args> - struct fn> : FnCls::template fn> {}; + // extract class template, quote, pass on + template class Fn, class... T> + struct invoke_this_fn> { + using type = typename Q::template fn>; + }; + + template + using fn = typename invoke_this_fn::type; }; /* - * Metafunction class that invokes the specified metafunction operation, - * and passes its result on to the next metafunction class. + * Quoted metafunction that invokes the specified metafunctions, + * and passes its result on to the next quoted metafunction. */ - template class Op, class FnCls> - struct pass_result_to { - // call Op, pass on its result + template class... Fn> + struct pass_result_of { + // invoke `Fn`, pass on their result template - struct fn : FnCls::template fn> {}; + using fn = typename Q::template fn::type...>; }; /* - * Bind arguments at the front of a metafunction class. - * Metafunction class equivalent to std::bind_front(). + * Bind arguments at the front of a quoted metafunction. */ - template + template struct bind_front { template - struct fn : FnCls::template fn {}; + using fn = typename Q::template fn; }; /* - * Bind arguments at the back of a metafunction class. - * Metafunction class equivalent to std::bind_back() + * Bind arguments at the back of a quoted metafunction. */ - template + template struct bind_back { template - struct fn : FnCls::template fn {}; + using fn = typename Q::template fn; }; /* - * Metafunction class equivalent to polyfill::always_false. - * It ignores arguments passed to the metafunction, - * and always returns the given type. + * Quoted metafunction equivalent to `polyfill::always_false`. + * It ignores arguments passed to the metafunction, and always returns the specified type. */ template struct always { - template - struct fn { - using type = T; - }; + template + using fn = T; }; /* - * Unary metafunction class equivalent to std::type_identity. + * Unary quoted metafunction equivalent to `std::type_identity_t`. */ - struct identity { - template - struct fn { - using type = T; - }; - }; + using identity = quote_fn; /* - * Assume that the invocation of metafunction class yields a trait, and provide members `type` and `value` from the resulting trait. - * - * The goal of using this struct would be to attain lazy evaluation for trait metafunction classes like `conjunction` and `disjunction`. + * Quoted metafunction equivalent to `std::negation`. */ - template - struct yield_trait { - using type = invoke_t; - static constexpr auto value = type::value; - }; + template + using not_ = pass_result_of, TraitQ::template fn>; /* - * Metafunction class equivalent to std::negation. + * Quoted metafunction equivalent to `std::conjunction`. */ - template - struct not_ { - template - struct fn : polyfill::negation> {}; - }; + template + using conjunction = pass_result_of, TraitQ::template fn...>; /* - * Metafunction class equivalent to std::conjunction + * Quoted metafunction equivalent to `std::disjunction`. */ - template - struct conjunction { - template - struct fn : polyfill::conjunction...> {}; - }; + template + using disjunction = pass_result_of, TraitQ::template fn...>; /* - * Metafunction class equivalent to std::disjunction. + * Metafunction equivalent to `std::conjunction`. */ - template - struct disjunction { - template - struct fn : polyfill::disjunction...> {}; - }; + template class... TraitFn> + using conjunction_fn = pass_result_of, TraitFn...>; -#ifndef SQLITE_ORM_BROKEN_ALIAS_TEMPLATE_DEPENDENT_EXPR_SFINAE /* - * Metafunction equivalent to std::conjunction. + * Metafunction equivalent to `std::disjunction`. */ template class... TraitFn> - using conjunction_fn = conjunction...>; + using disjunction_fn = pass_result_of, TraitFn...>; /* - * Metafunction equivalent to std::disjunction. + * Metafunction equivalent to `std::negation`. */ - template class... TraitFn> - using disjunction_fn = disjunction...>; -#else - template class... TraitFn> - struct conjunction_fn : conjunction...> {}; - - template class... TraitFn> - struct disjunction_fn : disjunction...> {}; -#endif + template class Fn> + using not_fn = not_>; /* - * Convenience template alias for binding arguments at the front of a metafunction. + * Bind arguments at the front of a metafunction. */ template class Fn, class... Bound> using bind_front_fn = bind_front, Bound...>; /* - * Convenience template alias for binding arguments at the back of a metafunction. + * Bind arguments at the back of a metafunction. */ template class Fn, class... Bound> using bind_back_fn = bind_back, Bound...>; /* - * Convenience template alias for binding a metafunction at the front of a higher-order metafunction. + * Bind a metafunction and arguments at the front of a higher-order metafunction. */ template class Fn, class... Args2> class HigherFn, template @@ -1046,30 +997,30 @@ namespace sqlite_orm { namespace mpl = internal::mpl; - // convenience metafunction classes + // convenience quoted metafunctions namespace internal { /* - * Trait metafunction class that checks if a type has the specified trait. + * Quoted trait metafunction that checks if a type has the specified trait. */ template class TraitFn> using check_if = mpl::quote_fn; /* - * Trait metafunction class that checks if a type doesn't have the specified trait. + * Quoted trait metafunction that checks if a type doesn't have the specified trait. */ template class TraitFn> - using check_if_not = mpl::not_>; + using check_if_not = mpl::not_fn; /* - * Trait metafunction class that checks if a type (possibly projected) is the same as the specified type. + * Quoted trait metafunction that checks if a type (possibly projected) is the same as the specified type. * - * `Proj` is a projection applied to `Type` and must be a metafunction operation. + * `ProjOp` is a projection applied to `Type` and must be a metafunction. */ template class ProjOp = polyfill::type_identity_t> - using check_if_is_type = mpl::pass_result_to>; + using check_if_is_type = mpl::pass_result_of, ProjOp>; /* - * Trait metafunction class that checks if a type's template matches the specified template + * Quoted trait metafunction that checks if a type's template matches the specified template * (similar to `is_specialization_of`). */ template class Template> @@ -1161,7 +1112,7 @@ namespace sqlite_orm { */ template class Proj = polyfill::type_identity_t> using check_if_tuple_has_type = - mpl::bind_front_higherorder_fn>>; + mpl::bind_front_higherorder_fn::template fn>; /* * Metafunction class that checks whether a tuple contains a given template. @@ -1177,7 +1128,7 @@ namespace sqlite_orm { */ template class Primary> using check_if_tuple_has_template = - mpl::bind_front_higherorder_fn>>; + mpl::bind_front_higherorder_fn::template fn>; } } // #include "tuple_helper/tuple_filter.h" @@ -1271,7 +1222,7 @@ namespace sqlite_orm { /* * `Pred` is a metafunction that defines a bool member named `value` - * `FilterProj` is a metafunction operation + * `FilterProj` is a metafunction */ template @@ -1282,7 +1233,7 @@ namespace sqlite_orm { /* * `Pred` is a metafunction that defines a bool member named `value` - * `FilterProj` is a metafunction operation + * `FilterProj` is a metafunction */ template @@ -1293,7 +1244,7 @@ namespace sqlite_orm { /* * `Pred` is a metafunction that defines a bool member named `value` - * `FilterProj` is a metafunction operation + * `FilterProj` is a metafunction */ template @@ -1305,7 +1256,7 @@ namespace sqlite_orm { * Count a tuple, picking only those elements specified in the index sequence. * * `Pred` is a metafunction that defines a bool member named `value` - * `FilterProj` is a metafunction operation + * `FilterProj` is a metafunction * * Implementation note: must be distinct from `count_tuple` because legacy compilers have problems * with a default Sequence in function template parameters [SQLITE_ORM_BROKEN_VARIADIC_PACK_EXPANSION]. @@ -2638,19 +2589,19 @@ namespace sqlite_orm { template using col_index_sequence_with_field_type = filter_tuple_sequence_t>, + check_if_is_type::template fn, field_type_t, filter_tuple_sequence_t>; template class TraitFn> using col_index_sequence_with = filter_tuple_sequence_t>, + check_if_tuple_has::template fn, constraints_type_t, filter_tuple_sequence_t>; template class TraitFn> using col_index_sequence_excluding = filter_tuple_sequence_t>, + check_if_tuple_has_not::template fn, constraints_type_t, filter_tuple_sequence_t>; } @@ -9885,13 +9836,13 @@ namespace sqlite_orm { template class Pack, class... Types, template class Op> struct tuple_transformer, Op> { - using type = Pack...>; + using type = Pack...>; }; /* * Transform specified tuple. * - * `Op` is a metafunction operation. + * `Op` is a metafunction. */ template class Op> using transform_tuple_t = typename tuple_transformer::type; @@ -10183,7 +10134,7 @@ namespace sqlite_orm { void for_each_foreign_key_to(L&& lambda) const { using fk_index_sequence = filter_tuple_sequence_t; using filtered_index_sequence = filter_tuple_sequence_t>, + check_if_is_type::template fn, target_type_t, fk_index_sequence>; iterate_tuple(this->elements, filtered_index_sequence{}, lambda); @@ -10212,9 +10163,9 @@ namespace sqlite_orm { * Call passed lambda with columns not having the specified constraint trait `OpTrait`. * @param lambda Lambda called for each column. */ - template = true> + template = true> void for_each_column_excluding(L&& lambda) const { - this->for_each_column_excluding>(lambda); + this->for_each_column_excluding(lambda); } std::vector get_table_info() const; @@ -10255,7 +10206,7 @@ namespace sqlite_orm { * Call passed lambda with columns not having the specified constraint trait `OpTrait`. * @param lambda Lambda called for each column. */ - template = true> + template = true> void for_each_column_excluding(L&& lambda) const { this->module_details.template for_each_column_excluding(lambda); } @@ -10298,9 +10249,9 @@ namespace sqlite_orm { * Call passed lambda with columns not having the specified constraint trait `OpTrait`. * @param lambda Lambda called for each column. */ - template = true> + template = true> void for_each_column_excluding(L&& lambda) const { - this->for_each_column_excluding>(lambda); + this->for_each_column_excluding(lambda); } /** @@ -10322,7 +10273,7 @@ namespace sqlite_orm { using colrefs_tuple = decltype(primaryKey.columns); using same_type_index_sequence = filter_tuple_sequence_t>>, + check_if_is_type>::template fn, member_field_type_t>; iterate_tuple(primaryKey.columns, same_type_index_sequence{}, [&res, &column](auto& memberPointer) { if(compare_any(memberPointer, column.member_pointer) || compare_any(memberPointer, column.setter)) { @@ -11094,7 +11045,7 @@ namespace sqlite_orm { template using column_result_for_tuple_t = - transform_tuple_t>>; + transform_tuple_t::template fn>; #ifdef SQLITE_ORM_OPTIONAL_SUPPORTED template @@ -18228,7 +18179,7 @@ namespace sqlite_orm { "Insertable table cannot contain > 1 primary keys. Please use 'replace' instead of " "'insert', or you can use 'insert' with explicit column listing."); static_assert(count_filtered_tuple>, + check_if_not::template fn, pkcol_index_sequence>::value == 0, "Attempting to execute 'insert' request into an noninsertable table was detected. " "Insertable table cannot contain non-standard primary keys. Please use 'replace' instead " @@ -20158,7 +20109,7 @@ namespace sqlite_orm { template std::unique_ptr column_constraints::default_value() const { using default_op_index_sequence = - filter_tuple_sequence_t>>; + filter_tuple_sequence_t::template fn>; std::unique_ptr value; call_if_constexpr( diff --git a/tests/static_tests/functional/mpl.cpp b/tests/static_tests/functional/mpl.cpp index 78d131f50..0646d8e8d 100644 --- a/tests/static_tests/functional/mpl.cpp +++ b/tests/static_tests/functional/mpl.cpp @@ -25,15 +25,15 @@ TEST_CASE("mpl") { using predicate_type = std::less; using check_if_is_projected_type = internal::check_if_is_type; - STATIC_REQUIRE_FALSE(mpl::is_metafunction_class_v); - STATIC_REQUIRE(mpl::is_metafunction_class_v); - STATIC_REQUIRE(std::is_same, int>::value); - STATIC_REQUIRE(std::is_same, int>::value); - STATIC_REQUIRE(std::is_same, literal_holder>::value); - STATIC_REQUIRE(std::is_same>>, - literal_holder>::value); + STATIC_REQUIRE_FALSE(mpl::is_quoted_metafuntion_v); + STATIC_REQUIRE(mpl::is_quoted_metafuntion_v); + STATIC_REQUIRE(std::is_same, int>::value); + STATIC_REQUIRE(std::is_same, literal_holder>::value); + STATIC_REQUIRE( + std::is_same::fn>, literal_holder>::value); STATIC_REQUIRE(mpl::invoke_t::value); STATIC_REQUIRE_FALSE(mpl::invoke_t, int, int>::value); + STATIC_REQUIRE_FALSE(mpl::invoke_t, int, int>::value); STATIC_REQUIRE(mpl::invoke_t>, size_t, int>::value); STATIC_REQUIRE(mpl::invoke_t, std::vector>::value); STATIC_REQUIRE(mpl::invoke_t>::value); @@ -52,7 +52,7 @@ TEST_CASE("mpl") { STATIC_REQUIRE(mpl::invoke_t>::value); STATIC_REQUIRE_FALSE(mpl::invoke_t::value); STATIC_REQUIRE(mpl::invoke_t::value); - STATIC_REQUIRE(std::is_same, predicate_type>, + STATIC_REQUIRE(std::is_same, predicate_type>, predicate_type::is_transparent>::value); STATIC_REQUIRE(mpl::invoke_t, predicate_type>::value); diff --git a/tests/static_tests/is_column_with_insertable_primary_key.cpp b/tests/static_tests/is_column_with_insertable_primary_key.cpp index a4f85d43f..4be302d29 100644 --- a/tests/static_tests/is_column_with_insertable_primary_key.cpp +++ b/tests/static_tests/is_column_with_insertable_primary_key.cpp @@ -11,7 +11,7 @@ using insertable_index_sequence = filter_tuple_sequence_t>; template using noninsertable_index_sequence = filter_tuple_sequence_t>, + check_if_not::template fn, polyfill::type_identity_t, col_index_sequence_with>; diff --git a/tests/static_tests/static_tests_storage_traits.h b/tests/static_tests/static_tests_storage_traits.h index 3a224f2f9..b5247aae6 100644 --- a/tests/static_tests/static_tests_storage_traits.h +++ b/tests/static_tests/static_tests_storage_traits.h @@ -40,7 +40,7 @@ namespace sqlite_orm { template struct table_foreign_keys_count : count_filtered_tuple, - mpl::as_op>, + check_if_is_type::template fn, filter_tuple_sequence_t, is_foreign_key>, target_type_t> {}; @@ -75,7 +75,7 @@ namespace sqlite_orm { template using table_foreign_keys_t = filter_tuple_t, - mpl::as_op>, + check_if_is_type::template fn, target_type_t, filter_tuple_sequence_t, is_foreign_key>>; From 63d197835262f26d1af825f7bb87050e76fcd04a Mon Sep 17 00:00:00 2001 From: klaus triendl Date: Wed, 8 Nov 2023 15:33:39 +0200 Subject: [PATCH 08/11] Updated tuple iteration for uninstantiated tuples with C++14 ... using pack expansion instead of unrolling --- dev/tuple_helper/tuple_iteration.h | 11 ++++------- include/sqlite_orm/sqlite_orm.h | 11 ++++------- 2 files changed, 8 insertions(+), 14 deletions(-) diff --git a/dev/tuple_helper/tuple_iteration.h b/dev/tuple_helper/tuple_iteration.h index 7a73b6c6e..f519f08ff 100644 --- a/dev/tuple_helper/tuple_iteration.h +++ b/dev/tuple_helper/tuple_iteration.h @@ -76,13 +76,10 @@ namespace sqlite_orm { (lambda((std::tuple_element_t*)nullptr), ...); } #else - template - void iterate_tuple(std::index_sequence<>, L&& /*lambda*/) {} - - template - void iterate_tuple(std::index_sequence, L&& lambda) { - lambda((std::tuple_element_t*)nullptr); - iterate_tuple(std::index_sequence{}, std::forward(lambda)); + template + void iterate_tuple(std::index_sequence, L&& lambda) { + using Sink = int[sizeof...(Idx)]; + (void)Sink{(lambda((std::tuple_element_t*)nullptr), 0)...}; } #endif template diff --git a/include/sqlite_orm/sqlite_orm.h b/include/sqlite_orm/sqlite_orm.h index 9aeadb9cc..141d89d9c 100644 --- a/include/sqlite_orm/sqlite_orm.h +++ b/include/sqlite_orm/sqlite_orm.h @@ -7043,13 +7043,10 @@ namespace sqlite_orm { (lambda((std::tuple_element_t*)nullptr), ...); } #else - template - void iterate_tuple(std::index_sequence<>, L&& /*lambda*/) {} - - template - void iterate_tuple(std::index_sequence, L&& lambda) { - lambda((std::tuple_element_t*)nullptr); - iterate_tuple(std::index_sequence{}, std::forward(lambda)); + template + void iterate_tuple(std::index_sequence, L&& lambda) { + using Sink = int[sizeof...(Idx)]; + (void)Sink{(lambda((std::tuple_element_t*)nullptr), 0)...}; } #endif template From 205977bdec626d70aed152d5abd15478e60e6ee4 Mon Sep 17 00:00:00 2001 From: klaus triendl Date: Wed, 8 Nov 2023 18:27:47 +0200 Subject: [PATCH 09/11] `std::invoke` has only been constexpr since C++20. --- dev/functional/config.h | 6 +++++ dev/tuple_helper/tuple_transformer.h | 17 +++++++++----- include/sqlite_orm/sqlite_orm.h | 23 ++++++++++++++----- .../functional/tuple_transform.cpp | 4 ++-- 4 files changed, 36 insertions(+), 14 deletions(-) diff --git a/dev/functional/config.h b/dev/functional/config.h index 6c4f72326..edf98cfb1 100644 --- a/dev/functional/config.h +++ b/dev/functional/config.h @@ -12,6 +12,12 @@ #define SQLITE_ORM_INLINE_VAR #endif +#if __cpp_lib_constexpr_functional >= 201907L +#define SQLITE_ORM_CONSTEXPR_CPP20 constexpr +#else +#define SQLITE_ORM_CONSTEXPR_CPP20 +#endif + #if SQLITE_ORM_HAS_CPP_ATTRIBUTE(no_unique_address) >= 201803L #define SQLITE_ORM_NOUNIQUEADDRESS [[no_unique_address]] #else diff --git a/dev/tuple_helper/tuple_transformer.h b/dev/tuple_helper/tuple_transformer.h index 50c96c934..e091d1b8f 100644 --- a/dev/tuple_helper/tuple_transformer.h +++ b/dev/tuple_helper/tuple_transformer.h @@ -34,13 +34,15 @@ namespace sqlite_orm { * * @note It's a glorified version of `std::apply()` and a variant of `std::accumulate()`. * It combines filtering the tuple (indexes), transforming the elements (projection) and finally applying the callable (combine). + * + * @note `project` is called using `std::invoke`, which is `constexpr` since C++20. */ template - constexpr auto recombine_tuple(CombineOp combine, - const Tpl& tpl, - std::index_sequence, - Projector project, - Init initial) { + SQLITE_ORM_CONSTEXPR_CPP20 auto recombine_tuple(CombineOp combine, + const Tpl& tpl, + std::index_sequence, + Projector project, + Init initial) { return combine(initial, polyfill::invoke(project, std::get(tpl))...); } @@ -49,9 +51,12 @@ namespace sqlite_orm { * * @note It's a glorified version of `std::apply()` and a variant of `std::accumulate()`. * It combines filtering the tuple (indexes), transforming the elements (projection) and finally applying the callable (combine). + * + * @note `project` is called using `std::invoke`, which is `constexpr` since C++20. */ template - constexpr auto recombine_tuple(CombineOp combine, const Tpl& tpl, Projector project, Init initial) { + SQLITE_ORM_CONSTEXPR_CPP20 auto + recombine_tuple(CombineOp combine, const Tpl& tpl, Projector project, Init initial) { return recombine_tuple(std::move(combine), std::forward(tpl), std::make_index_sequence::value>{}, diff --git a/include/sqlite_orm/sqlite_orm.h b/include/sqlite_orm/sqlite_orm.h index 141d89d9c..a9686e3e2 100644 --- a/include/sqlite_orm/sqlite_orm.h +++ b/include/sqlite_orm/sqlite_orm.h @@ -173,6 +173,12 @@ using std::nullptr_t; #define SQLITE_ORM_INLINE_VAR #endif +#if __cpp_lib_constexpr_functional >= 201907L +#define SQLITE_ORM_CONSTEXPR_CPP20 constexpr +#else +#define SQLITE_ORM_CONSTEXPR_CPP20 +#endif + #if SQLITE_ORM_HAS_CPP_ATTRIBUTE(no_unique_address) >= 201803L #define SQLITE_ORM_NOUNIQUEADDRESS [[no_unique_address]] #else @@ -9851,13 +9857,15 @@ namespace sqlite_orm { * * @note It's a glorified version of `std::apply()` and a variant of `std::accumulate()`. * It combines filtering the tuple (indexes), transforming the elements (projection) and finally applying the callable (combine). + * + * @note `project` is called using `std::invoke`, which is `constexpr` since C++20. */ template - constexpr auto recombine_tuple(CombineOp combine, - const Tpl& tpl, - std::index_sequence, - Projector project, - Init initial) { + SQLITE_ORM_CONSTEXPR_CPP20 auto recombine_tuple(CombineOp combine, + const Tpl& tpl, + std::index_sequence, + Projector project, + Init initial) { return combine(initial, polyfill::invoke(project, std::get(tpl))...); } @@ -9866,9 +9874,12 @@ namespace sqlite_orm { * * @note It's a glorified version of `std::apply()` and a variant of `std::accumulate()`. * It combines filtering the tuple (indexes), transforming the elements (projection) and finally applying the callable (combine). + * + * @note `project` is called using `std::invoke`, which is `constexpr` since C++20. */ template - constexpr auto recombine_tuple(CombineOp combine, const Tpl& tpl, Projector project, Init initial) { + SQLITE_ORM_CONSTEXPR_CPP20 auto + recombine_tuple(CombineOp combine, const Tpl& tpl, Projector project, Init initial) { return recombine_tuple(std::move(combine), std::forward(tpl), std::make_index_sequence::value>{}, diff --git a/tests/static_tests/functional/tuple_transform.cpp b/tests/static_tests/functional/tuple_transform.cpp index cd89688d7..d67539098 100644 --- a/tests/static_tests/functional/tuple_transform.cpp +++ b/tests/static_tests/functional/tuple_transform.cpp @@ -16,7 +16,7 @@ using internal::recombine_tuple; template using make_literal_holder = literal_holder; -#if defined(SQLITE_ORM_FOLD_EXPRESSIONS_SUPPORTED) +#if defined(SQLITE_ORM_FOLD_EXPRESSIONS_SUPPORTED) && (__cpp_lib_constexpr_functional >= 201907L) struct tuple_maker { template constexpr auto operator()(Types&&... types) const { @@ -50,7 +50,7 @@ TEST_CASE("tuple_helper static") { STATIC_REQUIRE(create_from_tuple>(std::make_tuple(1, 2), polyfill::identity{}) == std::array{1, 2}); #endif -#if defined(SQLITE_ORM_FOLD_EXPRESSIONS_SUPPORTED) +#if defined(SQLITE_ORM_FOLD_EXPRESSIONS_SUPPORTED) && (__cpp_lib_constexpr_functional >= 201907L) STATIC_REQUIRE(recombine_tuple(tuple_maker{}, std::make_tuple(1, 2), polyfill::identity{}, 3) == std::make_tuple(3, 1, 2)); From 5736c26e2c291789db9a3094ebcf66fe926c6eeb Mon Sep 17 00:00:00 2001 From: klaus triendl Date: Wed, 8 Nov 2023 18:33:30 +0200 Subject: [PATCH 10/11] Fixed an 'utf-8' codec that was not recognized by the linter --- dev/functional/mpl.h | 2 +- include/sqlite_orm/sqlite_orm.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/dev/functional/mpl.h b/dev/functional/mpl.h index ff5f77601..7a47a5089 100644 --- a/dev/functional/mpl.h +++ b/dev/functional/mpl.h @@ -2,7 +2,7 @@ /* * Symbols for 'template metaprogramming' (compile-time template programming), - * inspired by the MPL of Aleksey Gurtovoy and David Abrahams, and the Mp11 of Peter Dimov and Bjørn Reese. + * inspired by the MPL of Aleksey Gurtovoy and David Abrahams, and the Mp11 of Peter Dimov and Bjorn Reese. * * Currently, the focus is on facilitating advanced type filtering, * such as filtering columns by constraints having various traits. diff --git a/include/sqlite_orm/sqlite_orm.h b/include/sqlite_orm/sqlite_orm.h index a9686e3e2..c860ac1a5 100644 --- a/include/sqlite_orm/sqlite_orm.h +++ b/include/sqlite_orm/sqlite_orm.h @@ -745,7 +745,7 @@ namespace sqlite_orm { /* * Symbols for 'template metaprogramming' (compile-time template programming), - * inspired by the MPL of Aleksey Gurtovoy and David Abrahams, and the Mp11 of Peter Dimov and Bjørn Reese. + * inspired by the MPL of Aleksey Gurtovoy and David Abrahams, and the Mp11 of Peter Dimov and Bjorn Reese. * * Currently, the focus is on facilitating advanced type filtering, * such as filtering columns by constraints having various traits. From 505b85c7299c396b36b9080939f0d49abae35927 Mon Sep 17 00:00:00 2001 From: klaus triendl Date: Thu, 9 Nov 2023 00:29:49 +0200 Subject: [PATCH 11/11] Worked around problems with legacy compilers --- dev/node_tuple.h | 2 +- dev/storage.h | 4 ++-- dev/tuple_helper/tuple_filter.h | 6 ++++-- dev/tuple_helper/tuple_traits.h | 2 +- include/sqlite_orm/sqlite_orm.h | 14 ++++++++------ 5 files changed, 16 insertions(+), 12 deletions(-) diff --git a/dev/node_tuple.h b/dev/node_tuple.h index 632763372..9f32fe566 100644 --- a/dev/node_tuple.h +++ b/dev/node_tuple.h @@ -38,7 +38,7 @@ namespace sqlite_orm { * Node tuple for several types. */ template - using node_tuple_for = conc_tuple...>; + using node_tuple_for = conc_tuple::type...>; template<> struct node_tuple { diff --git a/dev/storage.h b/dev/storage.h index 729d9ccc0..85b1e8c33 100644 --- a/dev/storage.h +++ b/dev/storage.h @@ -107,7 +107,7 @@ namespace sqlite_orm { context_t context{this->db_objects}; statement_serializer serializer; - const auto sql = serializer.serialize(table, context, tableName); + const std::string sql = serializer.serialize(table, context, tableName); perform_void_exec(db, sql); } @@ -982,7 +982,7 @@ namespace sqlite_orm { context.replace_bindable_with_question = true; auto con = this->get_connection(); - const auto sql = serialize(statement, context); + const std::string sql = serialize(statement, context); sqlite3_stmt* stmt = prepare_stmt(con.get(), sql); return prepared_statement_t{std::forward(statement), stmt, con}; } diff --git a/dev/tuple_helper/tuple_filter.h b/dev/tuple_helper/tuple_filter.h index 1888e4ffb..ea58ace1f 100644 --- a/dev/tuple_helper/tuple_filter.h +++ b/dev/tuple_helper/tuple_filter.h @@ -34,7 +34,7 @@ namespace sqlite_orm { #ifndef SQLITE_ORM_BROKEN_VARIADIC_PACK_EXPANSION template class Pred, template class Proj, size_t... Idx> struct filter_tuple_sequence> - : flatten_idxseq>>::value, + : flatten_idxseq>>::value, std::index_sequence, std::index_sequence<>>...> {}; #else @@ -50,7 +50,9 @@ namespace sqlite_orm { template class Pred, template class Proj, size_t... Idx> struct filter_tuple_sequence> - : flatten_idxseq>, Pred>::type...> {}; + : flatten_idxseq>, + Pred>::type...> {}; #endif /* diff --git a/dev/tuple_helper/tuple_traits.h b/dev/tuple_helper/tuple_traits.h index 71f1c13ff..d8af0b737 100644 --- a/dev/tuple_helper/tuple_traits.h +++ b/dev/tuple_helper/tuple_traits.h @@ -14,7 +14,7 @@ namespace sqlite_orm { template class TraitFn, class Tuple> struct tuple_has {}; template class TraitFn, class... Types> - struct tuple_has> : polyfill::disjunction...> {}; + struct tuple_has> : polyfill::disjunction...> {}; /* * Trait metafunction class that checks whether a tuple contains a type with given trait. diff --git a/include/sqlite_orm/sqlite_orm.h b/include/sqlite_orm/sqlite_orm.h index c860ac1a5..a6e9b0d70 100644 --- a/include/sqlite_orm/sqlite_orm.h +++ b/include/sqlite_orm/sqlite_orm.h @@ -1099,7 +1099,7 @@ namespace sqlite_orm { template class TraitFn, class Tuple> struct tuple_has {}; template class TraitFn, class... Types> - struct tuple_has> : polyfill::disjunction...> {}; + struct tuple_has> : polyfill::disjunction...> {}; /* * Trait metafunction class that checks whether a tuple contains a type with given trait. @@ -1207,7 +1207,7 @@ namespace sqlite_orm { #ifndef SQLITE_ORM_BROKEN_VARIADIC_PACK_EXPANSION template class Pred, template class Proj, size_t... Idx> struct filter_tuple_sequence> - : flatten_idxseq>>::value, + : flatten_idxseq>>::value, std::index_sequence, std::index_sequence<>>...> {}; #else @@ -1223,7 +1223,9 @@ namespace sqlite_orm { template class Pred, template class Proj, size_t... Idx> struct filter_tuple_sequence> - : flatten_idxseq>, Pred>::type...> {}; + : flatten_idxseq>, + Pred>::type...> {}; #endif /* @@ -18084,7 +18086,7 @@ namespace sqlite_orm { context_t context{this->db_objects}; statement_serializer serializer; - const auto sql = serializer.serialize(table, context, tableName); + const std::string sql = serializer.serialize(table, context, tableName); perform_void_exec(db, sql); } @@ -18959,7 +18961,7 @@ namespace sqlite_orm { context.replace_bindable_with_question = true; auto con = this->get_connection(); - const auto sql = serialize(statement, context); + const std::string sql = serialize(statement, context); sqlite3_stmt* stmt = prepare_stmt(con.get(), sql); return prepared_statement_t{std::forward(statement), stmt, con}; } @@ -19555,7 +19557,7 @@ namespace sqlite_orm { * Node tuple for several types. */ template - using node_tuple_for = conc_tuple...>; + using node_tuple_for = conc_tuple::type...>; template<> struct node_tuple {