From 35eebef10e0f6c3ac0551880b2a573c938de7795 Mon Sep 17 00:00:00 2001 From: klaus triendl Date: Tue, 14 Nov 2023 02:02:26 +0200 Subject: [PATCH 01/11] Variable template for framing and calling user-defined functions --- dev/alias.h | 8 +- dev/alias_traits.h | 14 +- dev/column_pointer.h | 5 +- dev/function.h | 148 +++++++--- dev/storage_base.h | 111 ++++--- dev/type_traits.h | 8 +- examples/user_defined_functions.cpp | 53 ++++ include/sqlite_orm/sqlite_orm.h | 294 ++++++++++++------- tests/ast_iterator_tests.cpp | 1 + tests/static_tests/alias.cpp | 9 +- tests/static_tests/column_result_t.cpp | 1 + tests/static_tests/function_static_tests.cpp | 84 ++++-- tests/static_tests/node_tuple.cpp | 1 + 13 files changed, 501 insertions(+), 236 deletions(-) diff --git a/dev/alias.h b/dev/alias.h index 4511a5a3a..bdc5b07f9 100644 --- a/dev/alias.h +++ b/dev/alias.h @@ -1,6 +1,6 @@ #pragma once -#include // std::enable_if, std::is_base_of, std::is_member_pointer, std::remove_const +#include // std::enable_if #include // std::index_sequence, std::make_index_sequence #include // std::string #include // std::stringstream @@ -219,7 +219,7 @@ namespace sqlite_orm { template constexpr auto alias_column(C field) { using namespace ::sqlite_orm::internal; - using A = std::remove_const_t; + using A = decltype(als); using aliased_type = type_t; static_assert(is_field_of_v, "Column must be from aliased table"); @@ -263,7 +263,7 @@ namespace sqlite_orm { */ template auto as(E expression) { - return internal::as_t, E>{std::move(expression)}; + return internal::as_t{std::move(expression)}; } /** @@ -283,7 +283,7 @@ namespace sqlite_orm { #ifdef SQLITE_ORM_WITH_CPP20_ALIASES template auto get() { - return internal::alias_holder>{}; + return internal::alias_holder{}; } #endif diff --git a/dev/alias_traits.h b/dev/alias_traits.h index 489bbba86..3fe7f63fb 100644 --- a/dev/alias_traits.h +++ b/dev/alias_traits.h @@ -74,7 +74,7 @@ namespace sqlite_orm { template concept orm_alias = std::derived_from; - /** @short Alias of a column in a record set. + /** @short Specifies that a type is an alias of a column in a record set. * * A column alias has the following traits: * - is derived from `alias_tag` @@ -83,7 +83,7 @@ namespace sqlite_orm { template concept orm_column_alias = (orm_alias && !orm_names_type); - /** @short Alias of any type of record set. + /** @short Specifies that a type is an alias of any type of record set. * * A record set alias has the following traits: * - is derived from `alias_tag`. @@ -92,7 +92,7 @@ namespace sqlite_orm { template concept orm_recordset_alias = (orm_alias && orm_names_type); - /** @short Alias of a concrete table. + /** @short Specifies that a type is an alias of a concrete table. * * A concrete table alias has the following traits: * - is derived from `alias_tag`. @@ -101,7 +101,7 @@ namespace sqlite_orm { template concept orm_table_alias = (orm_recordset_alias && !std::same_as>); - /** @short Reference of a concrete table, especially of a derived class. + /** @short Specifies that a type is a reference of a concrete table, especially of a derived class. * * A concrete table reference has the following traits: * - specialization of `table_reference`, whose `type` typename references a mapped object. @@ -109,12 +109,18 @@ namespace sqlite_orm { template concept orm_table_reference = polyfill::is_specialization_of_v, internal::table_reference>; + /** @short Specifies that a type refers to a mapped table (possibly aliased). + */ template concept orm_refers_to_table = (orm_table_reference || orm_table_alias); + /** @short Specifies that a type refers to a recordset. + */ template concept orm_refers_to_recordset = (orm_table_reference || orm_recordset_alias); + /** @short Specifies that a type is a mapped recordset (table reference). + */ template concept orm_mapped_recordset = (orm_table_reference); #endif diff --git a/dev/column_pointer.h b/dev/column_pointer.h index bb8d5af76..044856b7f 100644 --- a/dev/column_pointer.h +++ b/dev/column_pointer.h @@ -1,6 +1,6 @@ #pragma once -#include // std::enable_if, std::remove_const +#include // std::enable_if #include // std::move #include "functional/cxx_type_traits_polyfill.h" @@ -48,8 +48,7 @@ namespace sqlite_orm { */ template constexpr auto column(F O::*field) { - using R = std::remove_const_t; - return column(field); + return column>(field); } /** diff --git a/dev/function.h b/dev/function.h index 8ffb8f775..e46581594 100644 --- a/dev/function.h +++ b/dev/function.h @@ -1,9 +1,9 @@ #pragma once #include -#include +#include // std::is_member_function_pointer, std::remove_const, std::decay, std::is_same, std::false_type, std::true_type #include // std::string -#include // std::tuple +#include // std::tuple, std::tuple_size, std::tuple_element #include // std::function #include // std::min #include // std::move, std::forward @@ -22,7 +22,7 @@ namespace sqlite_orm { namespace internal { - struct user_defined_function_base { + struct udf_proxy_base { using func_call = std::function< void(sqlite3_context* context, void* functionPointer, int argsCount, sqlite3_value** values)>; using final_call = std::function; @@ -33,38 +33,38 @@ namespace sqlite_orm { void (*destroy)(int*) = nullptr; #ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED - user_defined_function_base(decltype(name) name_, - decltype(argumentsCount) argumentsCount_, - decltype(create) create_, - decltype(destroy) destroy_) : + udf_proxy_base(decltype(name) name_, + decltype(argumentsCount) argumentsCount_, + decltype(create) create_, + decltype(destroy) destroy_) : name(std::move(name_)), argumentsCount(argumentsCount_), create(std::move(create_)), destroy(destroy_) {} #endif }; - struct user_defined_scalar_function_t : user_defined_function_base { + struct scalar_udf_proxy : udf_proxy_base { func_call run; - user_defined_scalar_function_t(decltype(name) name_, - int argumentsCount_, - decltype(create) create_, - decltype(run) run_, - decltype(destroy) destroy_) : - user_defined_function_base{std::move(name_), argumentsCount_, std::move(create_), destroy_}, + scalar_udf_proxy(decltype(name) name_, + int argumentsCount_, + decltype(create) create_, + decltype(run) run_, + decltype(destroy) destroy_) : + udf_proxy_base{std::move(name_), argumentsCount_, std::move(create_), destroy_}, run(std::move(run_)) {} }; - struct user_defined_aggregate_function_t : user_defined_function_base { + struct aggregate_udf_proxy : udf_proxy_base { func_call step; final_call finalCall; - user_defined_aggregate_function_t(decltype(name) name_, - int argumentsCount_, - decltype(create) create_, - decltype(step) step_, - decltype(finalCall) finalCall_, - decltype(destroy) destroy_) : - user_defined_function_base{std::move(name_), argumentsCount_, std::move(create_), destroy_}, + aggregate_udf_proxy(decltype(name) name_, + int argumentsCount_, + decltype(create) create_, + decltype(step) step_, + decltype(finalCall) finalCall_, + decltype(destroy) destroy_) : + udf_proxy_base{std::move(name_), argumentsCount_, std::move(create_), destroy_}, step(std::move(step_)), finalCall(std::move(finalCall_)) {} }; @@ -78,15 +78,14 @@ namespace sqlite_orm { using aggregate_fin_function_t = decltype(&F::fin); template - SQLITE_ORM_INLINE_VAR constexpr bool is_scalar_function_v = false; + SQLITE_ORM_INLINE_VAR constexpr bool is_scalar_udf_v = false; template - SQLITE_ORM_INLINE_VAR constexpr bool is_scalar_function_v>> = - true; + SQLITE_ORM_INLINE_VAR constexpr bool is_scalar_udf_v>> = true; template - SQLITE_ORM_INLINE_VAR constexpr bool is_aggregate_function_v = false; + SQLITE_ORM_INLINE_VAR constexpr bool is_aggregate_udf_v = false; template - SQLITE_ORM_INLINE_VAR constexpr bool is_aggregate_function_v< + SQLITE_ORM_INLINE_VAR constexpr bool is_aggregate_udf_v< F, polyfill::void_t, aggregate_fin_function_t, @@ -115,13 +114,13 @@ namespace sqlite_orm { struct callable_arguments_impl; template - struct callable_arguments_impl>> { + struct callable_arguments_impl>> { using args_tuple = typename member_function_arguments>::tuple_type; using return_type = typename member_function_arguments>::return_type; }; template - struct callable_arguments_impl>> { + struct callable_arguments_impl>> { using args_tuple = typename member_function_arguments>::tuple_type; using return_type = typename member_function_arguments>::return_type; }; @@ -129,9 +128,9 @@ namespace sqlite_orm { template struct callable_arguments : callable_arguments_impl {}; - template + template struct function_call { - using function_type = F; + using udf_type = UDF; using args_tuple = std::tuple; args_tuple args; @@ -223,24 +222,79 @@ namespace sqlite_orm { (polyfill::is_specialization_of_v) > {}); #endif } + + /* + * Generator of a user-defined function call in a sql query expression. + * Use the variable template `func<>` to instantiate. + * Calling the function captures the parameters in a `function_call` node. + */ + template + struct function : polyfill::type_identity { + template + function_call operator()(Args... args) const { + using args_tuple = std::tuple; + using function_args_tuple = typename callable_arguments::args_tuple; + constexpr size_t argsCount = std::tuple_size::value; + constexpr size_t functionArgsCount = std::tuple_size::value; + static_assert((argsCount == functionArgsCount && + !std::is_same>::value && + validate_pointer_value_types( + polyfill::index_constant{})) || + std::is_same>::value, + "The number of arguments does not match"); + return {{std::forward(args)...}}; + } + }; } - /** - * Used to call user defined function: `func(...);` +#ifdef SQLITE_ORM_WITH_CPP20_ALIASES + /** @short Specifies that a type is a user-defined scalar function. */ - template - internal::function_call func(Args... args) { - using args_tuple = std::tuple; - using function_args_tuple = typename internal::callable_arguments::args_tuple; - constexpr auto argsCount = std::tuple_size::value; - constexpr auto functionArgsCount = std::tuple_size::value; - static_assert((argsCount == functionArgsCount && - !std::is_same>::value && - internal::validate_pointer_value_types( - polyfill::index_constant(functionArgsCount, argsCount) - 1>{})) || - std::is_same>::value, - "Number of arguments does not match"); - return {std::make_tuple(std::forward(args)...)}; - } + template + concept orm_scalar_udf = requires { + UDF::name(); + typename internal::scalar_call_function_t; + }; + /** @short Specifies that a type is a user-defined aggregate function. + */ + template + concept orm_aggregate_udf = requires { + UDF::name(); + typename internal::aggregate_step_function_t; + typename internal::aggregate_fin_function_t; + requires std::is_member_function_pointer_v>; + requires std::is_member_function_pointer_v>; + }; + + /** @short Specifies that a type is a framed user-defined scalar function. + */ + template + concept orm_scalar_function = (polyfill::is_specialization_of_v, internal::function> && + orm_scalar_udf); + + /** @short Specifies that a type is a framed user-defined aggregate function. + */ + template + concept orm_aggregate_function = (polyfill::is_specialization_of_v, internal::function> && + orm_aggregate_udf); +#endif + + /** + * Call a user-defined function. + * + * Example: + * struct IdFunc { int oeprator(int arg)() const { return arg; } }; + * // inline: + * select(func(42)); + * // As this is a variable template, you can frame the user-defined function and define a variable for syntactic sugar and legibility: + * inline constexpr auto idfunc = func; + * select(idfunc(42)); + * + */ + template +#ifdef SQLITE_ORM_WITH_CPP20_ALIASES + requires(orm_scalar_udf || orm_aggregate_udf) +#endif + SQLITE_ORM_INLINE_VAR constexpr internal::function func{}; } diff --git a/dev/storage_base.h b/dev/storage_base.h index 4ce2be4c4..0701a6057 100644 --- a/dev/storage_base.h +++ b/dev/storage_base.h @@ -251,7 +251,7 @@ namespace sqlite_orm { */ template void create_scalar_function() { - static_assert(is_scalar_function_v, "F can't be an aggregate function"); + static_assert(is_scalar_udf_v, "F cannot be an aggregate function"); std::stringstream ss; ss << F::name() << std::flush; @@ -261,30 +261,35 @@ namespace sqlite_orm { constexpr auto argsCount = std::is_same>::value ? -1 : int(std::tuple_size::value); - this->scalarFunctions.emplace_back(new user_defined_scalar_function_t{ + this->scalarFunctions.push_back(std::make_unique( std::move(name), argsCount, []() -> int* { return (int*)(new F()); }, /* call = */ - [](sqlite3_context* context, void* functionVoidPointer, int argsCount, sqlite3_value** values) { - auto& function = *static_cast(functionVoidPointer); + [](sqlite3_context* context, void* udfVoidPointer, int argsCount, sqlite3_value** values) { + F& function = *static_cast(udfVoidPointer); args_tuple argsTuple; values_to_tuple{}(values, argsTuple, argsCount); auto result = call(function, std::move(argsTuple)); statement_binder().result(context, result); }, - delete_function_callback, - }); + delete_function_callback)); if(this->connection->retain_count() > 0) { sqlite3* db = this->connection->get(); - try_to_create_function(db, - static_cast(*this->scalarFunctions.back())); + try_to_create_function(db, static_cast(*this->scalarFunctions.back())); } } +#ifdef SQLITE_ORM_WITH_CPP20_ALIASES + template + void create_scalar_function() { + return this->create_scalar_function>(); + } +#endif + /** * Call this to create user defined aggregate function. Can be called at any time no matter connection is opened or no. * T - function class. T must have step member function, fin member function and static name function like this: @@ -312,7 +317,7 @@ namespace sqlite_orm { */ template void create_aggregate_function() { - static_assert(is_aggregate_function_v, "F can't be a scalar function"); + static_assert(is_aggregate_udf_v, "F cannot be a scalar function"); std::stringstream ss; ss << F::name() << std::flush; @@ -322,7 +327,7 @@ namespace sqlite_orm { constexpr auto argsCount = std::is_same>::value ? -1 : int(std::tuple_size::value); - this->aggregateFunctions.emplace_back(new user_defined_aggregate_function_t{ + this->aggregateFunctions.push_back(std::make_unique( std::move(name), argsCount, /* create = */ @@ -330,51 +335,69 @@ namespace sqlite_orm { return (int*)(new F()); }, /* step = */ - [](sqlite3_context*, void* functionVoidPointer, int argsCount, sqlite3_value** values) { - auto& function = *static_cast(functionVoidPointer); + [](sqlite3_context*, void* udfVoidPointer, int argsCount, sqlite3_value** values) { + F& function = *static_cast(udfVoidPointer); args_tuple argsTuple; values_to_tuple{}(values, argsTuple, argsCount); call(function, &F::step, std::move(argsTuple)); }, /* finalCall = */ - [](sqlite3_context* context, void* functionVoidPointer) { - auto& function = *static_cast(functionVoidPointer); + [](sqlite3_context* context, void* udfVoidPointer) { + F& function = *static_cast(udfVoidPointer); auto result = function.fin(); statement_binder().result(context, result); }, - delete_function_callback, - }); + delete_function_callback)); if(this->connection->retain_count() > 0) { sqlite3* db = this->connection->get(); - try_to_create_function( - db, - static_cast(*this->aggregateFunctions.back())); + try_to_create_function(db, static_cast(*this->aggregateFunctions.back())); } } +#ifdef SQLITE_ORM_WITH_CPP20_ALIASES + template + void create_aggregate_function() { + return this->create_aggregate_function>(); + } +#endif + /** * Use it to delete scalar function you created before. Can be called at any time no matter connection is open or no. */ template void delete_scalar_function() { - static_assert(is_scalar_function_v, "F cannot be an aggregate function"); + static_assert(is_scalar_udf_v, "F cannot be an aggregate function"); std::stringstream ss; ss << F::name() << std::flush; this->delete_function_impl(ss.str(), this->scalarFunctions); } +#ifdef SQLITE_ORM_WITH_CPP20_ALIASES + template + void delete_scalar_function() { + this->delete_scalar_function>(); + } +#endif + /** * Use it to delete aggregate function you created before. Can be called at any time no matter connection is open or no. */ template void delete_aggregate_function() { - static_assert(is_aggregate_function_v, "F cannot be a scalar function"); + static_assert(is_aggregate_udf_v, "F cannot be a scalar function"); std::stringstream ss; ss << F::name() << std::flush; this->delete_function_impl(ss.str(), this->aggregateFunctions); } +#ifdef SQLITE_ORM_WITH_CPP20_ALIASES + template + void delete_aggregate_function() { + this->delete_aggregate_function>(); + } +#endif + template void create_collation() { collating_function func = [](int leftLength, const void* lhs, int rightLength, const void* rhs) { @@ -620,12 +643,12 @@ namespace sqlite_orm { sqlite3_busy_handler(this->connection->get(), busy_handler_callback, this); } - for(auto& functionPointer: this->scalarFunctions) { - try_to_create_function(db, static_cast(*functionPointer)); + for(auto& udfProxy: this->scalarFunctions) { + try_to_create_function(db, static_cast(*udfProxy)); } - for(auto& functionPointer: this->aggregateFunctions) { - try_to_create_function(db, static_cast(*functionPointer)); + for(auto& udfProxy: this->aggregateFunctions) { + try_to_create_function(db, static_cast(*udfProxy)); } if(this->on_open) { @@ -634,9 +657,9 @@ namespace sqlite_orm { } void delete_function_impl(const std::string& name, - std::vector>& functionsVector) const { - auto it = find_if(functionsVector.begin(), functionsVector.end(), [&name](auto& functionPointer) { - return functionPointer->name == name; + std::vector>& functionsVector) const { + auto it = find_if(functionsVector.begin(), functionsVector.end(), [&name](auto& udfProxy) { + return udfProxy->name == name; }); if(it != functionsVector.end()) { functionsVector.erase(it); @@ -662,7 +685,7 @@ namespace sqlite_orm { } } - void try_to_create_function(sqlite3* db, user_defined_scalar_function_t& function) { + void try_to_create_function(sqlite3* db, scalar_udf_proxy& function) { auto resultCode = sqlite3_create_function_v2(db, function.name.c_str(), function.argumentsCount, @@ -677,7 +700,7 @@ namespace sqlite_orm { } } - void try_to_create_function(sqlite3* db, user_defined_aggregate_function_t& function) { + void try_to_create_function(sqlite3* db, aggregate_udf_proxy& function) { auto resultCode = sqlite3_create_function(db, function.name.c_str(), function.argumentsCount, @@ -693,34 +716,30 @@ namespace sqlite_orm { static void aggregate_function_step_callback(sqlite3_context* context, int argsCount, sqlite3_value** values) { - auto functionVoidPointer = sqlite3_user_data(context); - auto functionPointer = static_cast(functionVoidPointer); + auto udfProxy = static_cast(sqlite3_user_data(context)); auto aggregateContextVoidPointer = sqlite3_aggregate_context(context, sizeof(int**)); auto aggregateContextIntPointer = static_cast(aggregateContextVoidPointer); if(*aggregateContextIntPointer == nullptr) { - *aggregateContextIntPointer = functionPointer->create(); + *aggregateContextIntPointer = udfProxy->create(); } - functionPointer->step(context, *aggregateContextIntPointer, argsCount, values); + udfProxy->step(context, *aggregateContextIntPointer, argsCount, values); } static void aggregate_function_final_callback(sqlite3_context* context) { - auto functionVoidPointer = sqlite3_user_data(context); - auto functionPointer = static_cast(functionVoidPointer); + auto udfProxy = static_cast(sqlite3_user_data(context)); auto aggregateContextVoidPointer = sqlite3_aggregate_context(context, sizeof(int**)); auto aggregateContextIntPointer = static_cast(aggregateContextVoidPointer); - functionPointer->finalCall(context, *aggregateContextIntPointer); - functionPointer->destroy(*aggregateContextIntPointer); + udfProxy->finalCall(context, *aggregateContextIntPointer); + udfProxy->destroy(*aggregateContextIntPointer); } static void scalar_function_callback(sqlite3_context* context, int argsCount, sqlite3_value** values) { - auto functionVoidPointer = sqlite3_user_data(context); - auto functionPointer = static_cast(functionVoidPointer); - std::unique_ptr callablePointer(functionPointer->create(), - functionPointer->destroy); - if(functionPointer->argumentsCount != -1 && functionPointer->argumentsCount != argsCount) { + auto udfProxy = static_cast(sqlite3_user_data(context)); + const std::unique_ptr callableGuard(udfProxy->create(), udfProxy->destroy); + if(udfProxy->argumentsCount != -1 && udfProxy->argumentsCount != argsCount) { throw std::system_error{orm_error_code::arguments_count_does_not_match}; } - functionPointer->run(context, functionPointer, argsCount, values); + udfProxy->run(context, udfProxy, argsCount, values); } template @@ -814,8 +833,8 @@ namespace sqlite_orm { std::map collatingFunctions; const int cachedForeignKeysCount; std::function _busy_handler; - std::vector> scalarFunctions; - std::vector> aggregateFunctions; + std::vector> scalarFunctions; + std::vector> aggregateFunctions; }; } } diff --git a/dev/type_traits.h b/dev/type_traits.h index cc7da32ee..1316adda1 100644 --- a/dev/type_traits.h +++ b/dev/type_traits.h @@ -1,9 +1,6 @@ #pragma once #include // std::enable_if, std::is_same -#ifdef SQLITE_ORM_WITH_CPP20_ALIASES -#include -#endif #include "functional/cxx_type_traits_polyfill.h" @@ -43,6 +40,11 @@ namespace sqlite_orm { template using type_t = typename T::type; +#ifdef SQLITE_ORM_WITH_CPP20_ALIASES + template + using auto_type_t = typename decltype(a)::type; +#endif + template using field_type_t = typename T::field_type; diff --git a/examples/user_defined_functions.cpp b/examples/user_defined_functions.cpp index 34bb30c33..f329a93d2 100644 --- a/examples/user_defined_functions.cpp +++ b/examples/user_defined_functions.cpp @@ -34,6 +34,9 @@ struct SignFunction { return "SIGN"; } }; +#ifdef SQLITE_ORM_WITH_CPP20_ALIASES +inline constexpr auto sign = func; +#endif /** * Aggregate function must be defined as a dedicated class with at least three functions: @@ -77,6 +80,9 @@ struct AcceleratedSumFunction { return result; } }; +#ifdef SQLITE_ORM_WITH_CPP20_ALIASES +inline constexpr auto accelerated_sum = func; +#endif /** * This is also a scalar function just like `SignFunction` but this function @@ -105,6 +111,9 @@ struct ArithmeticMeanFunction { return result; } }; +#ifdef SQLITE_ORM_WITH_CPP20_ALIASES +inline constexpr auto arithmetic_mean = func; +#endif int main() { @@ -124,6 +133,49 @@ int main() { make_table("t", make_column("a", &Table::a), make_column("b", &Table::b), make_column("c", &Table::c))); storage.sync_schema(); +#ifdef SQLITE_ORM_WITH_CPP20_ALIASES + /** + * This function can be called at any time doesn't matter whether connection is open or not. + * To delete created scalar function use `storage.delete_scalar_function()` function call. + */ + storage.create_scalar_function(); + + // SELECT SIGN(3) + auto signRows = storage.select(sign(3)); + cout << "SELECT SIGN(3) = " << signRows.at(0) << endl; + + storage.insert(Table{1, -1, 2}); + storage.insert(Table{2, -2, 4}); + storage.insert(Table{3, -3, 8}); + storage.insert(Table{4, -4, 16}); + + storage.create_aggregate_function(); + + // SELECT ASUM(a), ASUM(b), ASUM(c) + // FROM t + auto aSumRows = + storage.select(columns(accelerated_sum(&Table::a), accelerated_sum(&Table::b), accelerated_sum(&Table::c))); + cout << "SELECT ASUM(a), ASUM(b), ASUM(c) FROM t:" << endl; + for(auto& row: aSumRows) { + cout << '\t' << get<0>(row) << endl; + cout << '\t' << get<1>(row) << endl; + cout << '\t' << get<2>(row) << endl; + } + + storage.create_scalar_function(); + + // SELECT ARITHMETIC_MEAN(5, 6, 7) + auto arithmeticMeanRows1 = storage.select(arithmetic_mean(5, 6, 7)); + cout << "SELECT ARITHMETIC_MEAN(5, 6, 7) = " << arithmeticMeanRows1.front() << endl; + + // SELECT ARITHMETIC_MEAN(-2, 1) + auto arithmeticMeanRows2 = storage.select(arithmetic_mean(-2, 1)); + cout << "SELECT ARITHMETIC_MEAN(-2, 1) = " << arithmeticMeanRows2.front() << endl; + + // SELECT ARITHMETIC_MEAN(-5.5, 4, 13.2, 256.4) + auto arithmeticMeanRows3 = storage.select(arithmetic_mean(-5.5, 4, 13.2, 256.4)); + cout << "SELECT ARITHMETIC_MEAN(-5.5, 4, 13.2, 256.4) = " << arithmeticMeanRows3.front() << endl; +#else /** * This function can be called at any time doesn't matter whether connection is open or not. * To delete created scalar function use `storage.delete_scalar_function()` function call. @@ -166,6 +218,7 @@ int main() { // SELECT ARITHMETIC_MEAN(-5.5, 4, 13.2, 256.4) auto arithmeticMeanRows3 = storage.select(func(-5.5, 4, 13.2, 256.4)); cout << "SELECT ARITHMETIC_MEAN(-5.5, 4, 13.2, 256.4) = " << arithmeticMeanRows3.front() << endl; +#endif return 0; } diff --git a/include/sqlite_orm/sqlite_orm.h b/include/sqlite_orm/sqlite_orm.h index bb20e33b0..a1ecec67b 100644 --- a/include/sqlite_orm/sqlite_orm.h +++ b/include/sqlite_orm/sqlite_orm.h @@ -203,9 +203,6 @@ using std::nullptr_t; #pragma once #include // std::enable_if, std::is_same -#ifdef SQLITE_ORM_WITH_CPP20_ALIASES -#include -#endif // #include "functional/cxx_type_traits_polyfill.h" @@ -390,6 +387,11 @@ namespace sqlite_orm { template using type_t = typename T::type; +#ifdef SQLITE_ORM_WITH_CPP20_ALIASES + template + using auto_type_t = typename decltype(a)::type; +#endif + template using field_type_t = typename T::field_type; @@ -3009,7 +3011,7 @@ namespace sqlite_orm { template concept orm_alias = std::derived_from; - /** @short Alias of a column in a record set. + /** @short Specifies that a type is an alias of a column in a record set. * * A column alias has the following traits: * - is derived from `alias_tag` @@ -3018,7 +3020,7 @@ namespace sqlite_orm { template concept orm_column_alias = (orm_alias && !orm_names_type); - /** @short Alias of any type of record set. + /** @short Specifies that a type is an alias of any type of record set. * * A record set alias has the following traits: * - is derived from `alias_tag`. @@ -3027,7 +3029,7 @@ namespace sqlite_orm { template concept orm_recordset_alias = (orm_alias && orm_names_type); - /** @short Alias of a concrete table. + /** @short Specifies that a type is an alias of a concrete table. * * A concrete table alias has the following traits: * - is derived from `alias_tag`. @@ -3036,7 +3038,7 @@ namespace sqlite_orm { template concept orm_table_alias = (orm_recordset_alias && !std::same_as>); - /** @short Reference of a concrete table, especially of a derived class. + /** @short Specifies that a type is a reference of a concrete table, especially of a derived class. * * A concrete table reference has the following traits: * - specialization of `table_reference`, whose `type` typename references a mapped object. @@ -3044,12 +3046,18 @@ namespace sqlite_orm { template concept orm_table_reference = polyfill::is_specialization_of_v, internal::table_reference>; + /** @short Specifies that a type refers to a mapped table (possibly aliased). + */ template concept orm_refers_to_table = (orm_table_reference || orm_table_alias); + /** @short Specifies that a type refers to a recordset. + */ template concept orm_refers_to_recordset = (orm_table_reference || orm_recordset_alias); + /** @short Specifies that a type is a mapped recordset (table reference). + */ template concept orm_mapped_recordset = (orm_table_reference); #endif @@ -3152,7 +3160,7 @@ namespace sqlite_orm { // #include "column_pointer.h" -#include // std::enable_if, std::remove_const +#include // std::enable_if #include // std::move // #include "functional/cxx_type_traits_polyfill.h" @@ -3202,8 +3210,7 @@ namespace sqlite_orm { */ template constexpr auto column(F O::*field) { - using R = std::remove_const_t; - return column(field); + return column>(field); } /** @@ -4512,7 +4519,7 @@ namespace sqlite_orm { } #pragma once -#include // std::enable_if, std::is_base_of, std::is_member_pointer, std::remove_const +#include // std::enable_if #include // std::index_sequence, std::make_index_sequence #include // std::string #include // std::stringstream @@ -4736,7 +4743,7 @@ namespace sqlite_orm { template constexpr auto alias_column(C field) { using namespace ::sqlite_orm::internal; - using A = std::remove_const_t; + using A = decltype(als); using aliased_type = type_t; static_assert(is_field_of_v, "Column must be from aliased table"); @@ -4780,7 +4787,7 @@ namespace sqlite_orm { */ template auto as(E expression) { - return internal::as_t, E>{std::move(expression)}; + return internal::as_t{std::move(expression)}; } /** @@ -4800,7 +4807,7 @@ namespace sqlite_orm { #ifdef SQLITE_ORM_WITH_CPP20_ALIASES template auto get() { - return internal::alias_holder>{}; + return internal::alias_holder{}; } #endif @@ -10863,9 +10870,9 @@ namespace sqlite_orm { // #include "function.h" #include -#include +#include // std::is_member_function_pointer, std::remove_const, std::decay, std::is_same, std::false_type, std::true_type #include // std::string -#include // std::tuple +#include // std::tuple, std::tuple_size, std::tuple_element #include // std::function #include // std::min #include // std::move, std::forward @@ -10885,7 +10892,7 @@ namespace sqlite_orm { namespace internal { - struct user_defined_function_base { + struct udf_proxy_base { using func_call = std::function< void(sqlite3_context* context, void* functionPointer, int argsCount, sqlite3_value** values)>; using final_call = std::function; @@ -10896,38 +10903,38 @@ namespace sqlite_orm { void (*destroy)(int*) = nullptr; #ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED - user_defined_function_base(decltype(name) name_, - decltype(argumentsCount) argumentsCount_, - decltype(create) create_, - decltype(destroy) destroy_) : + udf_proxy_base(decltype(name) name_, + decltype(argumentsCount) argumentsCount_, + decltype(create) create_, + decltype(destroy) destroy_) : name(std::move(name_)), argumentsCount(argumentsCount_), create(std::move(create_)), destroy(destroy_) {} #endif }; - struct user_defined_scalar_function_t : user_defined_function_base { + struct scalar_udf_proxy : udf_proxy_base { func_call run; - user_defined_scalar_function_t(decltype(name) name_, - int argumentsCount_, - decltype(create) create_, - decltype(run) run_, - decltype(destroy) destroy_) : - user_defined_function_base{std::move(name_), argumentsCount_, std::move(create_), destroy_}, + scalar_udf_proxy(decltype(name) name_, + int argumentsCount_, + decltype(create) create_, + decltype(run) run_, + decltype(destroy) destroy_) : + udf_proxy_base{std::move(name_), argumentsCount_, std::move(create_), destroy_}, run(std::move(run_)) {} }; - struct user_defined_aggregate_function_t : user_defined_function_base { + struct aggregate_udf_proxy : udf_proxy_base { func_call step; final_call finalCall; - user_defined_aggregate_function_t(decltype(name) name_, - int argumentsCount_, - decltype(create) create_, - decltype(step) step_, - decltype(finalCall) finalCall_, - decltype(destroy) destroy_) : - user_defined_function_base{std::move(name_), argumentsCount_, std::move(create_), destroy_}, + aggregate_udf_proxy(decltype(name) name_, + int argumentsCount_, + decltype(create) create_, + decltype(step) step_, + decltype(finalCall) finalCall_, + decltype(destroy) destroy_) : + udf_proxy_base{std::move(name_), argumentsCount_, std::move(create_), destroy_}, step(std::move(step_)), finalCall(std::move(finalCall_)) {} }; @@ -10941,15 +10948,14 @@ namespace sqlite_orm { using aggregate_fin_function_t = decltype(&F::fin); template - SQLITE_ORM_INLINE_VAR constexpr bool is_scalar_function_v = false; + SQLITE_ORM_INLINE_VAR constexpr bool is_scalar_udf_v = false; template - SQLITE_ORM_INLINE_VAR constexpr bool is_scalar_function_v>> = - true; + SQLITE_ORM_INLINE_VAR constexpr bool is_scalar_udf_v>> = true; template - SQLITE_ORM_INLINE_VAR constexpr bool is_aggregate_function_v = false; + SQLITE_ORM_INLINE_VAR constexpr bool is_aggregate_udf_v = false; template - SQLITE_ORM_INLINE_VAR constexpr bool is_aggregate_function_v< + SQLITE_ORM_INLINE_VAR constexpr bool is_aggregate_udf_v< F, polyfill::void_t, aggregate_fin_function_t, @@ -10978,13 +10984,13 @@ namespace sqlite_orm { struct callable_arguments_impl; template - struct callable_arguments_impl>> { + struct callable_arguments_impl>> { using args_tuple = typename member_function_arguments>::tuple_type; using return_type = typename member_function_arguments>::return_type; }; template - struct callable_arguments_impl>> { + struct callable_arguments_impl>> { using args_tuple = typename member_function_arguments>::tuple_type; using return_type = typename member_function_arguments>::return_type; }; @@ -10992,9 +10998,9 @@ namespace sqlite_orm { template struct callable_arguments : callable_arguments_impl {}; - template + template struct function_call { - using function_type = F; + using udf_type = UDF; using args_tuple = std::tuple; args_tuple args; @@ -11086,26 +11092,81 @@ namespace sqlite_orm { (polyfill::is_specialization_of_v) > {}); #endif } + + /* + * Generator of a user-defined function call in a sql query expression. + * Use the variable template `func<>` to instantiate. + * Calling the function captures the parameters in a `function_call` node. + */ + template + struct function : polyfill::type_identity { + template + function_call operator()(Args... args) const { + using args_tuple = std::tuple; + using function_args_tuple = typename callable_arguments::args_tuple; + constexpr size_t argsCount = std::tuple_size::value; + constexpr size_t functionArgsCount = std::tuple_size::value; + static_assert((argsCount == functionArgsCount && + !std::is_same>::value && + validate_pointer_value_types( + polyfill::index_constant{})) || + std::is_same>::value, + "The number of arguments does not match"); + return {{std::forward(args)...}}; + } + }; } - /** - * Used to call user defined function: `func(...);` +#ifdef SQLITE_ORM_WITH_CPP20_ALIASES + /** @short Specifies that a type is a user-defined scalar function. */ - template - internal::function_call func(Args... args) { - using args_tuple = std::tuple; - using function_args_tuple = typename internal::callable_arguments::args_tuple; - constexpr auto argsCount = std::tuple_size::value; - constexpr auto functionArgsCount = std::tuple_size::value; - static_assert((argsCount == functionArgsCount && - !std::is_same>::value && - internal::validate_pointer_value_types( - polyfill::index_constant(functionArgsCount, argsCount) - 1>{})) || - std::is_same>::value, - "Number of arguments does not match"); - return {std::make_tuple(std::forward(args)...)}; - } + template + concept orm_scalar_udf = requires { + UDF::name(); + typename internal::scalar_call_function_t; + }; + + /** @short Specifies that a type is a user-defined aggregate function. + */ + template + concept orm_aggregate_udf = requires { + UDF::name(); + typename internal::aggregate_step_function_t; + typename internal::aggregate_fin_function_t; + requires std::is_member_function_pointer_v>; + requires std::is_member_function_pointer_v>; + }; + + /** @short Specifies that a type is a framed user-defined scalar function. + */ + template + concept orm_scalar_function = (polyfill::is_specialization_of_v, internal::function> && + orm_scalar_udf); + /** @short Specifies that a type is a framed user-defined aggregate function. + */ + template + concept orm_aggregate_function = (polyfill::is_specialization_of_v, internal::function> && + orm_aggregate_udf); +#endif + + /** + * Call a user-defined function. + * + * Example: + * struct IdFunc { int oeprator(int arg)() const { return arg; } }; + * // inline: + * select(func(42)); + * // As this is a variable template, you can frame the user-defined function and define a variable for syntactic sugar and legibility: + * inline constexpr auto idfunc = func; + * select(idfunc(42)); + * + */ + template +#ifdef SQLITE_ORM_WITH_CPP20_ALIASES + requires(orm_scalar_udf || orm_aggregate_udf) +#endif + SQLITE_ORM_INLINE_VAR constexpr internal::function func{}; } // #include "ast/special_keywords.h" @@ -14998,7 +15059,7 @@ namespace sqlite_orm { */ template void create_scalar_function() { - static_assert(is_scalar_function_v, "F can't be an aggregate function"); + static_assert(is_scalar_udf_v, "F cannot be an aggregate function"); std::stringstream ss; ss << F::name() << std::flush; @@ -15008,30 +15069,35 @@ namespace sqlite_orm { constexpr auto argsCount = std::is_same>::value ? -1 : int(std::tuple_size::value); - this->scalarFunctions.emplace_back(new user_defined_scalar_function_t{ + this->scalarFunctions.push_back(std::make_unique( std::move(name), argsCount, []() -> int* { return (int*)(new F()); }, /* call = */ - [](sqlite3_context* context, void* functionVoidPointer, int argsCount, sqlite3_value** values) { - auto& function = *static_cast(functionVoidPointer); + [](sqlite3_context* context, void* udfVoidPointer, int argsCount, sqlite3_value** values) { + F& function = *static_cast(udfVoidPointer); args_tuple argsTuple; values_to_tuple{}(values, argsTuple, argsCount); auto result = call(function, std::move(argsTuple)); statement_binder().result(context, result); }, - delete_function_callback, - }); + delete_function_callback)); if(this->connection->retain_count() > 0) { sqlite3* db = this->connection->get(); - try_to_create_function(db, - static_cast(*this->scalarFunctions.back())); + try_to_create_function(db, static_cast(*this->scalarFunctions.back())); } } +#ifdef SQLITE_ORM_WITH_CPP20_ALIASES + template + void create_scalar_function() { + return this->create_scalar_function>(); + } +#endif + /** * Call this to create user defined aggregate function. Can be called at any time no matter connection is opened or no. * T - function class. T must have step member function, fin member function and static name function like this: @@ -15059,7 +15125,7 @@ namespace sqlite_orm { */ template void create_aggregate_function() { - static_assert(is_aggregate_function_v, "F can't be a scalar function"); + static_assert(is_aggregate_udf_v, "F cannot be a scalar function"); std::stringstream ss; ss << F::name() << std::flush; @@ -15069,7 +15135,7 @@ namespace sqlite_orm { constexpr auto argsCount = std::is_same>::value ? -1 : int(std::tuple_size::value); - this->aggregateFunctions.emplace_back(new user_defined_aggregate_function_t{ + this->aggregateFunctions.push_back(std::make_unique( std::move(name), argsCount, /* create = */ @@ -15077,51 +15143,69 @@ namespace sqlite_orm { return (int*)(new F()); }, /* step = */ - [](sqlite3_context*, void* functionVoidPointer, int argsCount, sqlite3_value** values) { - auto& function = *static_cast(functionVoidPointer); + [](sqlite3_context*, void* udfVoidPointer, int argsCount, sqlite3_value** values) { + F& function = *static_cast(udfVoidPointer); args_tuple argsTuple; values_to_tuple{}(values, argsTuple, argsCount); call(function, &F::step, std::move(argsTuple)); }, /* finalCall = */ - [](sqlite3_context* context, void* functionVoidPointer) { - auto& function = *static_cast(functionVoidPointer); + [](sqlite3_context* context, void* udfVoidPointer) { + F& function = *static_cast(udfVoidPointer); auto result = function.fin(); statement_binder().result(context, result); }, - delete_function_callback, - }); + delete_function_callback)); if(this->connection->retain_count() > 0) { sqlite3* db = this->connection->get(); - try_to_create_function( - db, - static_cast(*this->aggregateFunctions.back())); + try_to_create_function(db, static_cast(*this->aggregateFunctions.back())); } } +#ifdef SQLITE_ORM_WITH_CPP20_ALIASES + template + void create_aggregate_function() { + return this->create_aggregate_function>(); + } +#endif + /** * Use it to delete scalar function you created before. Can be called at any time no matter connection is open or no. */ template void delete_scalar_function() { - static_assert(is_scalar_function_v, "F cannot be an aggregate function"); + static_assert(is_scalar_udf_v, "F cannot be an aggregate function"); std::stringstream ss; ss << F::name() << std::flush; this->delete_function_impl(ss.str(), this->scalarFunctions); } +#ifdef SQLITE_ORM_WITH_CPP20_ALIASES + template + void delete_scalar_function() { + this->delete_scalar_function>(); + } +#endif + /** * Use it to delete aggregate function you created before. Can be called at any time no matter connection is open or no. */ template void delete_aggregate_function() { - static_assert(is_aggregate_function_v, "F cannot be a scalar function"); + static_assert(is_aggregate_udf_v, "F cannot be a scalar function"); std::stringstream ss; ss << F::name() << std::flush; this->delete_function_impl(ss.str(), this->aggregateFunctions); } +#ifdef SQLITE_ORM_WITH_CPP20_ALIASES + template + void delete_aggregate_function() { + this->delete_aggregate_function>(); + } +#endif + template void create_collation() { collating_function func = [](int leftLength, const void* lhs, int rightLength, const void* rhs) { @@ -15367,12 +15451,12 @@ namespace sqlite_orm { sqlite3_busy_handler(this->connection->get(), busy_handler_callback, this); } - for(auto& functionPointer: this->scalarFunctions) { - try_to_create_function(db, static_cast(*functionPointer)); + for(auto& udfProxy: this->scalarFunctions) { + try_to_create_function(db, static_cast(*udfProxy)); } - for(auto& functionPointer: this->aggregateFunctions) { - try_to_create_function(db, static_cast(*functionPointer)); + for(auto& udfProxy: this->aggregateFunctions) { + try_to_create_function(db, static_cast(*udfProxy)); } if(this->on_open) { @@ -15381,9 +15465,9 @@ namespace sqlite_orm { } void delete_function_impl(const std::string& name, - std::vector>& functionsVector) const { - auto it = find_if(functionsVector.begin(), functionsVector.end(), [&name](auto& functionPointer) { - return functionPointer->name == name; + std::vector>& functionsVector) const { + auto it = find_if(functionsVector.begin(), functionsVector.end(), [&name](auto& udfProxy) { + return udfProxy->name == name; }); if(it != functionsVector.end()) { functionsVector.erase(it); @@ -15409,7 +15493,7 @@ namespace sqlite_orm { } } - void try_to_create_function(sqlite3* db, user_defined_scalar_function_t& function) { + void try_to_create_function(sqlite3* db, scalar_udf_proxy& function) { auto resultCode = sqlite3_create_function_v2(db, function.name.c_str(), function.argumentsCount, @@ -15424,7 +15508,7 @@ namespace sqlite_orm { } } - void try_to_create_function(sqlite3* db, user_defined_aggregate_function_t& function) { + void try_to_create_function(sqlite3* db, aggregate_udf_proxy& function) { auto resultCode = sqlite3_create_function(db, function.name.c_str(), function.argumentsCount, @@ -15440,34 +15524,30 @@ namespace sqlite_orm { static void aggregate_function_step_callback(sqlite3_context* context, int argsCount, sqlite3_value** values) { - auto functionVoidPointer = sqlite3_user_data(context); - auto functionPointer = static_cast(functionVoidPointer); + auto udfProxy = static_cast(sqlite3_user_data(context)); auto aggregateContextVoidPointer = sqlite3_aggregate_context(context, sizeof(int**)); auto aggregateContextIntPointer = static_cast(aggregateContextVoidPointer); if(*aggregateContextIntPointer == nullptr) { - *aggregateContextIntPointer = functionPointer->create(); + *aggregateContextIntPointer = udfProxy->create(); } - functionPointer->step(context, *aggregateContextIntPointer, argsCount, values); + udfProxy->step(context, *aggregateContextIntPointer, argsCount, values); } static void aggregate_function_final_callback(sqlite3_context* context) { - auto functionVoidPointer = sqlite3_user_data(context); - auto functionPointer = static_cast(functionVoidPointer); + auto udfProxy = static_cast(sqlite3_user_data(context)); auto aggregateContextVoidPointer = sqlite3_aggregate_context(context, sizeof(int**)); auto aggregateContextIntPointer = static_cast(aggregateContextVoidPointer); - functionPointer->finalCall(context, *aggregateContextIntPointer); - functionPointer->destroy(*aggregateContextIntPointer); + udfProxy->finalCall(context, *aggregateContextIntPointer); + udfProxy->destroy(*aggregateContextIntPointer); } static void scalar_function_callback(sqlite3_context* context, int argsCount, sqlite3_value** values) { - auto functionVoidPointer = sqlite3_user_data(context); - auto functionPointer = static_cast(functionVoidPointer); - std::unique_ptr callablePointer(functionPointer->create(), - functionPointer->destroy); - if(functionPointer->argumentsCount != -1 && functionPointer->argumentsCount != argsCount) { + auto udfProxy = static_cast(sqlite3_user_data(context)); + const std::unique_ptr callableGuard(udfProxy->create(), udfProxy->destroy); + if(udfProxy->argumentsCount != -1 && udfProxy->argumentsCount != argsCount) { throw std::system_error{orm_error_code::arguments_count_does_not_match}; } - functionPointer->run(context, functionPointer, argsCount, values); + udfProxy->run(context, udfProxy, argsCount, values); } template @@ -15561,8 +15641,8 @@ namespace sqlite_orm { std::map collatingFunctions; const int cachedForeignKeysCount; std::function _busy_handler; - std::vector> scalarFunctions; - std::vector> aggregateFunctions; + std::vector> scalarFunctions; + std::vector> aggregateFunctions; }; } } diff --git a/tests/ast_iterator_tests.cpp b/tests/ast_iterator_tests.cpp index f99bcf360..32a5a8a79 100644 --- a/tests/ast_iterator_tests.cpp +++ b/tests/ast_iterator_tests.cpp @@ -272,6 +272,7 @@ TEST_CASE("ast_iterator") { } SECTION("function_call") { struct Func { + static const char* name(); bool operator()(int value) const { return value % 2 == 0; } diff --git a/tests/static_tests/alias.cpp b/tests/static_tests/alias.cpp index b0e8e4dbd..51295a1cf 100644 --- a/tests/static_tests/alias.cpp +++ b/tests/static_tests/alias.cpp @@ -47,10 +47,11 @@ TEST_CASE("aliases") { runTest, column_pointer>>( alias_column>(&User::id)); #ifdef SQLITE_ORM_WITH_CPP20_ALIASES - runTest>>(get<"a"_col>()); - runTest>("a"_col); - runTest, int User::*>>(as<"a"_col>(&User::id)); - runTest, int User::*>>(&User::id >>= "a"_col); + constexpr auto a_col = "a"_col; + runTest>>(get()); + runTest>(a_col); + runTest, int User::*>>(as(&User::id)); + runTest, int User::*>>(&User::id >>= a_col); runTest>(alias<'a', 'l', 's'>.for_()); constexpr auto z_alias = "z"_alias.for_(); runTest>(z_alias); diff --git a/tests/static_tests/column_result_t.cpp b/tests/static_tests/column_result_t.cpp index f2207e7f6..2524951e3 100644 --- a/tests/static_tests/column_result_t.cpp +++ b/tests/static_tests/column_result_t.cpp @@ -79,6 +79,7 @@ TEST_CASE("column_result_of_t") { runTest(count()); { struct RandomFunc { + static const char* name(); int operator()() const { return 4; } diff --git a/tests/static_tests/function_static_tests.cpp b/tests/static_tests/function_static_tests.cpp index 9b0206990..f29092cb4 100644 --- a/tests/static_tests/function_static_tests.cpp +++ b/tests/static_tests/function_static_tests.cpp @@ -3,8 +3,24 @@ #include // std::is_same using namespace sqlite_orm; -using internal::is_aggregate_function_v; -using internal::is_scalar_function_v; +using internal::function; +using internal::function_call; +using internal::is_aggregate_udf_v; +using internal::is_scalar_udf_v; + +#ifdef SQLITE_ORM_WITH_CPP20_ALIASES +template +concept storage_scalar_callable = requires(S& storage) { + { storage.create_scalar_function() }; + { storage.delete_scalar_function() }; +}; + +template +concept storage_aggregate_callable = requires(S& storage) { + { storage.create_aggregate_function() }; + { storage.delete_aggregate_function() }; +}; +#endif TEST_CASE("function static") { SECTION("scalar") { @@ -35,8 +51,8 @@ TEST_CASE("function static") { } }; - STATIC_REQUIRE(is_scalar_function_v); - STATIC_REQUIRE_FALSE(is_aggregate_function_v); + STATIC_REQUIRE(is_scalar_udf_v); + STATIC_REQUIRE_FALSE(is_aggregate_udf_v); using RunMemberFunctionPointer = internal::scalar_call_function_t; using ExpectedType = double (Function::*)(double) const; @@ -57,8 +73,8 @@ TEST_CASE("function static") { } }; - STATIC_REQUIRE(is_scalar_function_v); - STATIC_REQUIRE_FALSE(is_aggregate_function_v); + STATIC_REQUIRE(is_scalar_udf_v); + STATIC_REQUIRE_FALSE(is_aggregate_udf_v); using RunMemberFunctionPointer = internal::scalar_call_function_t; using ExpectedType = double (Function::*)(double); @@ -79,8 +95,8 @@ TEST_CASE("function static") { } }; - STATIC_REQUIRE(is_scalar_function_v); - STATIC_REQUIRE_FALSE(is_aggregate_function_v); + STATIC_REQUIRE(is_scalar_udf_v); + STATIC_REQUIRE_FALSE(is_aggregate_udf_v); using RunMemberFunctionPointer = internal::scalar_call_function_t; using ExpectedType = int (Function::*)(std::string) const; @@ -101,8 +117,8 @@ TEST_CASE("function static") { } }; - STATIC_REQUIRE(is_scalar_function_v); - STATIC_REQUIRE_FALSE(is_aggregate_function_v); + STATIC_REQUIRE(is_scalar_udf_v); + STATIC_REQUIRE_FALSE(is_aggregate_udf_v); using RunMemberFunctionPointer = internal::scalar_call_function_t; using ExpectedType = int (Function::*)(std::string); @@ -123,8 +139,8 @@ TEST_CASE("function static") { } }; - STATIC_REQUIRE(is_scalar_function_v); - STATIC_REQUIRE_FALSE(is_aggregate_function_v); + STATIC_REQUIRE(is_scalar_udf_v); + STATIC_REQUIRE_FALSE(is_aggregate_udf_v); using RunMemberFunctionPointer = internal::scalar_call_function_t; using ExpectedType = std::string (Function::*)(const std::string&, const std::string&) const; @@ -145,8 +161,8 @@ TEST_CASE("function static") { } }; - STATIC_REQUIRE(is_scalar_function_v); - STATIC_REQUIRE_FALSE(is_aggregate_function_v); + STATIC_REQUIRE(is_scalar_udf_v); + STATIC_REQUIRE_FALSE(is_aggregate_udf_v); using RunMemberFunctionPointer = internal::scalar_call_function_t; using ExpectedType = std::string (Function::*)(const std::string&, const std::string&); @@ -178,8 +194,8 @@ TEST_CASE("function static") { } }; - STATIC_REQUIRE(is_aggregate_function_v); - STATIC_REQUIRE_FALSE(is_scalar_function_v); + STATIC_REQUIRE(is_aggregate_udf_v); + STATIC_REQUIRE_FALSE(is_scalar_udf_v); using StepMemberFunctionPointer = internal::aggregate_step_function_t; using ExpectedStepType = void (Function::*)(int); @@ -205,8 +221,8 @@ TEST_CASE("function static") { } }; - STATIC_REQUIRE(is_aggregate_function_v); - STATIC_REQUIRE_FALSE(is_scalar_function_v); + STATIC_REQUIRE(is_aggregate_udf_v); + STATIC_REQUIRE_FALSE(is_scalar_udf_v); using StepMemberFunctionPointer = internal::aggregate_step_function_t; using ExpectedStepType = void (Function::*)(std::string) const; @@ -221,4 +237,36 @@ TEST_CASE("function static") { std::is_same::args_tuple, std::tuple>::value); } } + SECTION("function call expressions") { + struct SFunction { + static const char* name(); + int operator()(int) const; + }; + struct AFunction { + static const char* name(); + void step(int); + int fin() const; + }; + + constexpr auto scalar = func; + constexpr auto aggregate = func; + + STATIC_REQUIRE(std::is_same>::value); + STATIC_REQUIRE(std::is_same>::value); + STATIC_REQUIRE(std::is_same>::value); + STATIC_REQUIRE(std::is_same>::value); + +#ifdef SQLITE_ORM_WITH_CPP20_ALIASES + STATIC_REQUIRE(orm_scalar_function); + STATIC_REQUIRE_FALSE(orm_aggregate_function); + STATIC_REQUIRE(orm_aggregate_function); + STATIC_REQUIRE_FALSE(orm_scalar_function); + + using storage_type = decltype(make_storage("")); + STATIC_REQUIRE(storage_scalar_callable); + STATIC_REQUIRE_FALSE(storage_aggregate_callable); + STATIC_REQUIRE(storage_aggregate_callable); + STATIC_REQUIRE_FALSE(storage_scalar_callable); +#endif + } } diff --git a/tests/static_tests/node_tuple.cpp b/tests/static_tests/node_tuple.cpp index 45234416c..ea114f9e7 100644 --- a/tests/static_tests/node_tuple.cpp +++ b/tests/static_tests/node_tuple.cpp @@ -916,6 +916,7 @@ TEST_CASE("Node tuple") { } SECTION("function_call") { struct Func { + static const char* name(); bool operator()(int value) const { return value % 2; } From 36b5976bc06eb5ba8a08feb7a0c6e384a1c16385 Mon Sep 17 00:00:00 2001 From: klaus triendl Date: Tue, 14 Nov 2023 13:29:56 +0200 Subject: [PATCH 02/11] Table reference can be specified in call to `make_table()` --- dev/column_pointer.h | 4 +- dev/schema/table.h | 13 ++ include/sqlite_orm/sqlite_orm.h | 18 +- tests/static_tests/column_pointer.cpp | 239 +++++++++++++------------- 4 files changed, 151 insertions(+), 123 deletions(-) diff --git a/dev/column_pointer.h b/dev/column_pointer.h index 044856b7f..4d9e4f200 100644 --- a/dev/column_pointer.h +++ b/dev/column_pointer.h @@ -64,7 +64,7 @@ namespace sqlite_orm { */ template requires(!orm_recordset_alias) - constexpr internal::table_reference column() { + consteval internal::table_reference column() { return {}; } @@ -73,7 +73,7 @@ namespace sqlite_orm { */ template requires(!orm_recordset_alias) - constexpr internal::table_reference c() { + consteval internal::table_reference c() { return {}; } #endif diff --git a/dev/schema/table.h b/dev/schema/table.h index 86837602f..a8ed5ef42 100644 --- a/dev/schema/table.h +++ b/dev/schema/table.h @@ -19,6 +19,7 @@ #include "../member_traits/member_traits.h" #include "../typed_comparator.h" #include "../type_traits.h" +#include "../alias_traits.h" #include "../constraints.h" #include "../table_info.h" #include "column.h" @@ -408,6 +409,18 @@ namespace sqlite_orm { return {std::move(name), std::make_tuple(std::forward(args)...)}); } +#ifdef SQLITE_ORM_WITH_CPP20_ALIASES + /** + * Factory function for a table definition. + * + * The mapped object type is explicitly specified. + */ + template + auto make_table(std::string name, Cs... args) { + return make_table>(std::move(name), std::forward(args)...); + } +#endif + template internal::virtual_table_t make_virtual_table(std::string name, M module_details) { SQLITE_ORM_CLANG_SUPPRESS_MISSING_BRACES(return {std::move(name), std::move(module_details)}); diff --git a/include/sqlite_orm/sqlite_orm.h b/include/sqlite_orm/sqlite_orm.h index a1ecec67b..659231273 100644 --- a/include/sqlite_orm/sqlite_orm.h +++ b/include/sqlite_orm/sqlite_orm.h @@ -3226,7 +3226,7 @@ namespace sqlite_orm { */ template requires(!orm_recordset_alias) - constexpr internal::table_reference column() { + consteval internal::table_reference column() { return {}; } @@ -3235,7 +3235,7 @@ namespace sqlite_orm { */ template requires(!orm_recordset_alias) - constexpr internal::table_reference c() { + consteval internal::table_reference c() { return {}; } #endif @@ -10043,6 +10043,8 @@ namespace sqlite_orm { // #include "../type_traits.h" +// #include "../alias_traits.h" + // #include "../constraints.h" // #include "../table_info.h" @@ -10434,6 +10436,18 @@ namespace sqlite_orm { return {std::move(name), std::make_tuple(std::forward(args)...)}); } +#ifdef SQLITE_ORM_WITH_CPP20_ALIASES + /** + * Factory function for a table definition. + * + * The mapped object type is explicitly specified. + */ + template + auto make_table(std::string name, Cs... args) { + return make_table>(std::move(name), std::forward(args)...); + } +#endif + template internal::virtual_table_t make_virtual_table(std::string name, M module_details) { SQLITE_ORM_CLANG_SUPPRESS_MISSING_BRACES(return {std::move(name), std::move(module_details)}); diff --git a/tests/static_tests/column_pointer.cpp b/tests/static_tests/column_pointer.cpp index d0d4950a4..ceac4a6c6 100644 --- a/tests/static_tests/column_pointer.cpp +++ b/tests/static_tests/column_pointer.cpp @@ -1,119 +1,120 @@ -#include -#include // std::is_same -#include - -using namespace sqlite_orm; -using internal::column_pointer; -#ifdef SQLITE_ORM_WITH_CPP20_ALIASES -using internal::is_recordset_alias_v; -using internal::is_table_alias_v; -using internal::table_reference; -using internal::using_t; -#endif - -template -void do_assert() { - STATIC_REQUIRE(std::is_same::value); -} - -template -void runTest(const T& /*test*/) { - do_assert(); -} - -#ifdef SQLITE_ORM_WITH_CPP20_ALIASES -template -concept field_callable = requires(C field) { - { count(field) }; - { avg(field) }; - { max(field) }; - { min(field) }; - { sum(field) }; - { total(field) }; - { group_concat(field) }; -}; - -template -concept storage_field_callable = requires(S& storage, C field) { - { storage.count(field) }; - { storage.avg(field) }; - { storage.max(field) }; - { storage.min(field) }; - { storage.sum(field) }; - { storage.total(field) }; - { storage.group_concat(field) }; - { storage.group_concat(field, "") }; - { storage.group_concat(field, std::string{}) }; - { storage.group_concat(field, 42) }; -}; - -template -concept storage_table_reference_callable = requires(S& storage) { - { storage.get(42) }; - { storage.get_all() }; - { storage.count() }; -}; -#endif - -TEST_CASE("column pointers") { - struct User { - int id; - }; - struct DerivedUser : User {}; -#ifdef SQLITE_ORM_WITH_CPP20_ALIASES - constexpr auto derived_user = c(); -#endif - - SECTION("table reference") { -#ifdef SQLITE_ORM_WITH_CPP20_ALIASES - STATIC_REQUIRE(orm_table_reference); - STATIC_REQUIRE_FALSE(is_table_alias_v); - STATIC_REQUIRE_FALSE(is_recordset_alias_v); - STATIC_REQUIRE_FALSE(orm_table_alias); - STATIC_REQUIRE_FALSE(orm_recordset_alias); - runTest>(derived_user); - runTest(internal::decay_table_reference_t{}); -#endif - } - SECTION("column pointer expressions") { - runTest>(column(&User::id)); - runTest>(column(&DerivedUser::id)); -#ifdef SQLITE_ORM_WITH_CPP20_ALIASES - runTest>(derived_user->*&DerivedUser::id); - STATIC_REQUIRE(field_callable); - STATIC_REQUIRE(field_callable*&DerivedUser::id)>); - - using storage_type = decltype(make_storage( - "", - make_table("user", make_column("id", &User::id, primary_key())), - make_table("derived_user", make_column("id", &DerivedUser::id, primary_key())))); - - STATIC_REQUIRE(storage_field_callable); - STATIC_REQUIRE(storage_field_callable*&DerivedUser::id)>); -#endif - } -#ifdef SQLITE_ORM_WITH_CPP20_ALIASES - SECTION("table reference expressions") { - runTest>(from()); - runTest>(asterisk()); - runTest>(object()); - runTest>(count()); - runTest>(get(42)); - runTest>>(get_all()); - runTest>>( - left_join(using_(derived_user->*&DerivedUser::id))); - runTest>>( - join(using_(derived_user->*&DerivedUser::id))); - runTest>>( - left_outer_join(using_(derived_user->*&DerivedUser::id))); - runTest>>( - inner_join(using_(derived_user->*&DerivedUser::id))); - - using storage_type = decltype(make_storage( - "", - make_table("derived_user", make_column("id", &DerivedUser::id, primary_key())))); - - STATIC_REQUIRE(storage_table_reference_callable); - } -#endif -} +#include +#include // std::is_same +#include + +using namespace sqlite_orm; +using internal::column_pointer; +#ifdef SQLITE_ORM_WITH_CPP20_ALIASES +using internal::is_recordset_alias_v; +using internal::is_table_alias_v; +using internal::table_reference; +using internal::using_t; +#endif + +template +void do_assert() { + STATIC_REQUIRE(std::is_same::value); +} + +template +void runTest(const T& /*test*/) { + do_assert(); +} + +#ifdef SQLITE_ORM_WITH_CPP20_ALIASES +template +concept field_callable = requires(C field) { + { count(field) }; + { avg(field) }; + { max(field) }; + { min(field) }; + { sum(field) }; + { total(field) }; + { group_concat(field) }; +}; + +template +concept storage_field_callable = requires(S& storage, C field) { + { storage.count(field) }; + { storage.avg(field) }; + { storage.max(field) }; + { storage.min(field) }; + { storage.sum(field) }; + { storage.total(field) }; + { storage.group_concat(field) }; + { storage.group_concat(field, "") }; + { storage.group_concat(field, std::string{}) }; + { storage.group_concat(field, 42) }; +}; + +template +concept storage_table_reference_callable = requires(S& storage) { + { storage.get(42) }; + { storage.get_all() }; + { storage.count() }; +}; +#endif + +TEST_CASE("column pointers") { + struct User { + int id; + }; + struct DerivedUser : User {}; +#ifdef SQLITE_ORM_WITH_CPP20_ALIASES + constexpr auto derived_user = c(); +#endif + + SECTION("table reference") { +#ifdef SQLITE_ORM_WITH_CPP20_ALIASES + STATIC_REQUIRE(orm_table_reference); + STATIC_REQUIRE_FALSE(is_table_alias_v); + STATIC_REQUIRE_FALSE(is_recordset_alias_v); + STATIC_REQUIRE_FALSE(orm_table_alias); + STATIC_REQUIRE_FALSE(orm_recordset_alias); + runTest>(derived_user); + runTest(internal::decay_table_reference_t{}); +#endif + } + SECTION("column pointer expressions") { + runTest>(column(&User::id)); + runTest>(column(&DerivedUser::id)); +#ifdef SQLITE_ORM_WITH_CPP20_ALIASES + runTest>(derived_user->*&DerivedUser::id); + STATIC_REQUIRE(field_callable); + STATIC_REQUIRE(field_callable*&DerivedUser::id)>); + + using storage_type = decltype(make_storage( + "", + make_table("user", make_column("id", &User::id, primary_key())), + make_table("derived_user", make_column("id", &DerivedUser::id, primary_key())))); + + STATIC_REQUIRE(storage_field_callable); + STATIC_REQUIRE(storage_field_callable*&DerivedUser::id)>); +#endif + } +#ifdef SQLITE_ORM_WITH_CPP20_ALIASES + SECTION("table reference expressions") { + runTest>(make_table("derived_user")); + runTest>(from()); + runTest>(asterisk()); + runTest>(object()); + runTest>(count()); + runTest>(get(42)); + runTest>>(get_all()); + runTest>>( + left_join(using_(derived_user->*&DerivedUser::id))); + runTest>>( + join(using_(derived_user->*&DerivedUser::id))); + runTest>>( + left_outer_join(using_(derived_user->*&DerivedUser::id))); + runTest>>( + inner_join(using_(derived_user->*&DerivedUser::id))); + + using storage_type = decltype(make_storage( + "", + make_table("derived_user", make_column("id", &DerivedUser::id, primary_key())))); + + STATIC_REQUIRE(storage_table_reference_callable); + } +#endif +} From 4730cc34d8753e2d5c390bb3a2e4f3e0f15c74cd Mon Sep 17 00:00:00 2001 From: klaus triendl Date: Tue, 14 Nov 2023 13:37:47 +0200 Subject: [PATCH 03/11] Corrected requirement of `storage_t<>.count()` --- dev/storage.h | 6 +++--- include/sqlite_orm/sqlite_orm.h | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/dev/storage.h b/dev/storage.h index a0ebb932e..e2eb8393e 100644 --- a/dev/storage.h +++ b/dev/storage.h @@ -383,9 +383,9 @@ namespace sqlite_orm { } #ifdef SQLITE_ORM_WITH_CPP20_ALIASES - template + template auto get(Ids... ids) { - return this->get>(std::forward(ids)...); + return this->get>(std::forward(ids)...); } #endif @@ -448,7 +448,7 @@ namespace sqlite_orm { } #ifdef SQLITE_ORM_WITH_CPP20_ALIASES - template + template int count(Args&&... args) { return this->count>(std::forward(args)...); } diff --git a/include/sqlite_orm/sqlite_orm.h b/include/sqlite_orm/sqlite_orm.h index 659231273..c4c75a220 100644 --- a/include/sqlite_orm/sqlite_orm.h +++ b/include/sqlite_orm/sqlite_orm.h @@ -18562,9 +18562,9 @@ namespace sqlite_orm { } #ifdef SQLITE_ORM_WITH_CPP20_ALIASES - template + template auto get(Ids... ids) { - return this->get>(std::forward(ids)...); + return this->get>(std::forward(ids)...); } #endif @@ -18627,7 +18627,7 @@ namespace sqlite_orm { } #ifdef SQLITE_ORM_WITH_CPP20_ALIASES - template + template int count(Args&&... args) { return this->count>(std::forward(args)...); } From c6d51042adf06880f952ee3207c8dd3d41fcacd0 Mon Sep 17 00:00:00 2001 From: klaus triendl Date: Tue, 14 Nov 2023 13:49:55 +0200 Subject: [PATCH 04/11] Renamed a few variables related to the context of user-defined functions --- dev/storage_base.h | 46 ++++++++++++++++----------------- include/sqlite_orm/sqlite_orm.h | 46 ++++++++++++++++----------------- 2 files changed, 46 insertions(+), 46 deletions(-) diff --git a/dev/storage_base.h b/dev/storage_base.h index 0701a6057..0132399ca 100644 --- a/dev/storage_base.h +++ b/dev/storage_base.h @@ -268,8 +268,8 @@ namespace sqlite_orm { return (int*)(new F()); }, /* call = */ - [](sqlite3_context* context, void* udfVoidPointer, int argsCount, sqlite3_value** values) { - F& function = *static_cast(udfVoidPointer); + [](sqlite3_context* context, void* udfHandle, int argsCount, sqlite3_value** values) { + F& function = *static_cast(udfHandle); args_tuple argsTuple; values_to_tuple{}(values, argsTuple, argsCount); auto result = call(function, std::move(argsTuple)); @@ -335,15 +335,15 @@ namespace sqlite_orm { return (int*)(new F()); }, /* step = */ - [](sqlite3_context*, void* udfVoidPointer, int argsCount, sqlite3_value** values) { - F& function = *static_cast(udfVoidPointer); + [](sqlite3_context*, void* udfHandle, int argsCount, sqlite3_value** values) { + F& function = *static_cast(udfHandle); args_tuple argsTuple; values_to_tuple{}(values, argsTuple, argsCount); call(function, &F::step, std::move(argsTuple)); }, /* finalCall = */ - [](sqlite3_context* context, void* udfVoidPointer) { - F& function = *static_cast(udfVoidPointer); + [](sqlite3_context* context, void* udfHandle) { + F& function = *static_cast(udfHandle); auto result = function.fin(); statement_binder().result(context, result); }, @@ -685,12 +685,12 @@ namespace sqlite_orm { } } - void try_to_create_function(sqlite3* db, scalar_udf_proxy& function) { + void try_to_create_function(sqlite3* db, scalar_udf_proxy& udfProxy) { auto resultCode = sqlite3_create_function_v2(db, - function.name.c_str(), - function.argumentsCount, + udfProxy.name.c_str(), + udfProxy.argumentsCount, SQLITE_UTF8, - &function, + &udfProxy, scalar_function_callback, nullptr, nullptr, @@ -700,12 +700,12 @@ namespace sqlite_orm { } } - void try_to_create_function(sqlite3* db, aggregate_udf_proxy& function) { + void try_to_create_function(sqlite3* db, aggregate_udf_proxy& udfProxy) { auto resultCode = sqlite3_create_function(db, - function.name.c_str(), - function.argumentsCount, + udfProxy.name.c_str(), + udfProxy.argumentsCount, SQLITE_UTF8, - &function, + &udfProxy, nullptr, aggregate_function_step_callback, aggregate_function_final_callback); @@ -717,20 +717,20 @@ namespace sqlite_orm { static void aggregate_function_step_callback(sqlite3_context* context, int argsCount, sqlite3_value** values) { auto udfProxy = static_cast(sqlite3_user_data(context)); - auto aggregateContextVoidPointer = sqlite3_aggregate_context(context, sizeof(int**)); - auto aggregateContextIntPointer = static_cast(aggregateContextVoidPointer); - if(*aggregateContextIntPointer == nullptr) { - *aggregateContextIntPointer = udfProxy->create(); + auto aggregateContextHandle = sqlite3_aggregate_context(context, sizeof(int**)); + auto aggregateContextIntHandle = static_cast(aggregateContextHandle); + if(*aggregateContextIntHandle == nullptr) { + *aggregateContextIntHandle = udfProxy->create(); } - udfProxy->step(context, *aggregateContextIntPointer, argsCount, values); + udfProxy->step(context, *aggregateContextIntHandle, argsCount, values); } static void aggregate_function_final_callback(sqlite3_context* context) { auto udfProxy = static_cast(sqlite3_user_data(context)); - auto aggregateContextVoidPointer = sqlite3_aggregate_context(context, sizeof(int**)); - auto aggregateContextIntPointer = static_cast(aggregateContextVoidPointer); - udfProxy->finalCall(context, *aggregateContextIntPointer); - udfProxy->destroy(*aggregateContextIntPointer); + auto aggregateContextHandle = sqlite3_aggregate_context(context, sizeof(int**)); + auto aggregateContextIntHandle = static_cast(aggregateContextHandle); + udfProxy->finalCall(context, *aggregateContextIntHandle); + udfProxy->destroy(*aggregateContextIntHandle); } static void scalar_function_callback(sqlite3_context* context, int argsCount, sqlite3_value** values) { diff --git a/include/sqlite_orm/sqlite_orm.h b/include/sqlite_orm/sqlite_orm.h index c4c75a220..9cb078ee9 100644 --- a/include/sqlite_orm/sqlite_orm.h +++ b/include/sqlite_orm/sqlite_orm.h @@ -15090,8 +15090,8 @@ namespace sqlite_orm { return (int*)(new F()); }, /* call = */ - [](sqlite3_context* context, void* udfVoidPointer, int argsCount, sqlite3_value** values) { - F& function = *static_cast(udfVoidPointer); + [](sqlite3_context* context, void* udfHandle, int argsCount, sqlite3_value** values) { + F& function = *static_cast(udfHandle); args_tuple argsTuple; values_to_tuple{}(values, argsTuple, argsCount); auto result = call(function, std::move(argsTuple)); @@ -15157,15 +15157,15 @@ namespace sqlite_orm { return (int*)(new F()); }, /* step = */ - [](sqlite3_context*, void* udfVoidPointer, int argsCount, sqlite3_value** values) { - F& function = *static_cast(udfVoidPointer); + [](sqlite3_context*, void* udfHandle, int argsCount, sqlite3_value** values) { + F& function = *static_cast(udfHandle); args_tuple argsTuple; values_to_tuple{}(values, argsTuple, argsCount); call(function, &F::step, std::move(argsTuple)); }, /* finalCall = */ - [](sqlite3_context* context, void* udfVoidPointer) { - F& function = *static_cast(udfVoidPointer); + [](sqlite3_context* context, void* udfHandle) { + F& function = *static_cast(udfHandle); auto result = function.fin(); statement_binder().result(context, result); }, @@ -15507,12 +15507,12 @@ namespace sqlite_orm { } } - void try_to_create_function(sqlite3* db, scalar_udf_proxy& function) { + void try_to_create_function(sqlite3* db, scalar_udf_proxy& udfProxy) { auto resultCode = sqlite3_create_function_v2(db, - function.name.c_str(), - function.argumentsCount, + udfProxy.name.c_str(), + udfProxy.argumentsCount, SQLITE_UTF8, - &function, + &udfProxy, scalar_function_callback, nullptr, nullptr, @@ -15522,12 +15522,12 @@ namespace sqlite_orm { } } - void try_to_create_function(sqlite3* db, aggregate_udf_proxy& function) { + void try_to_create_function(sqlite3* db, aggregate_udf_proxy& udfProxy) { auto resultCode = sqlite3_create_function(db, - function.name.c_str(), - function.argumentsCount, + udfProxy.name.c_str(), + udfProxy.argumentsCount, SQLITE_UTF8, - &function, + &udfProxy, nullptr, aggregate_function_step_callback, aggregate_function_final_callback); @@ -15539,20 +15539,20 @@ namespace sqlite_orm { static void aggregate_function_step_callback(sqlite3_context* context, int argsCount, sqlite3_value** values) { auto udfProxy = static_cast(sqlite3_user_data(context)); - auto aggregateContextVoidPointer = sqlite3_aggregate_context(context, sizeof(int**)); - auto aggregateContextIntPointer = static_cast(aggregateContextVoidPointer); - if(*aggregateContextIntPointer == nullptr) { - *aggregateContextIntPointer = udfProxy->create(); + auto aggregateContextHandle = sqlite3_aggregate_context(context, sizeof(int**)); + auto aggregateContextIntHandle = static_cast(aggregateContextHandle); + if(*aggregateContextIntHandle == nullptr) { + *aggregateContextIntHandle = udfProxy->create(); } - udfProxy->step(context, *aggregateContextIntPointer, argsCount, values); + udfProxy->step(context, *aggregateContextIntHandle, argsCount, values); } static void aggregate_function_final_callback(sqlite3_context* context) { auto udfProxy = static_cast(sqlite3_user_data(context)); - auto aggregateContextVoidPointer = sqlite3_aggregate_context(context, sizeof(int**)); - auto aggregateContextIntPointer = static_cast(aggregateContextVoidPointer); - udfProxy->finalCall(context, *aggregateContextIntPointer); - udfProxy->destroy(*aggregateContextIntPointer); + auto aggregateContextHandle = sqlite3_aggregate_context(context, sizeof(int**)); + auto aggregateContextIntHandle = static_cast(aggregateContextHandle); + udfProxy->finalCall(context, *aggregateContextIntHandle); + udfProxy->destroy(*aggregateContextIntHandle); } static void scalar_function_callback(sqlite3_context* context, int argsCount, sqlite3_value** values) { From d86520e0f8e90ce86983e357a5bac970cbec5315 Mon Sep 17 00:00:00 2001 From: klaus triendl Date: Tue, 14 Nov 2023 13:54:10 +0200 Subject: [PATCH 05/11] Explicitly typed result code variables --- dev/storage_base.h | 77 ++++++++++++++++----------------- include/sqlite_orm/sqlite_orm.h | 77 ++++++++++++++++----------------- 2 files changed, 76 insertions(+), 78 deletions(-) diff --git a/dev/storage_base.h b/dev/storage_base.h index 0132399ca..cc2a2fe83 100644 --- a/dev/storage_base.h +++ b/dev/storage_base.h @@ -421,12 +421,12 @@ namespace sqlite_orm { // create collations if db is open if(this->connection->retain_count() > 0) { sqlite3* db = this->connection->get(); - auto resultCode = sqlite3_create_collation(db, - name.c_str(), - SQLITE_UTF8, - function, - functionExists ? collate_callback : nullptr); - if(resultCode != SQLITE_OK) { + int rc = sqlite3_create_collation(db, + name.c_str(), + SQLITE_UTF8, + function, + functionExists ? collate_callback : nullptr); + if(rc != SQLITE_OK) { throw_translated_sqlite_error(db); } } @@ -628,9 +628,8 @@ namespace sqlite_orm { } for(auto& p: this->collatingFunctions) { - auto resultCode = - sqlite3_create_collation(db, p.first.c_str(), SQLITE_UTF8, &p.second, collate_callback); - if(resultCode != SQLITE_OK) { + int rc = sqlite3_create_collation(db, p.first.c_str(), SQLITE_UTF8, &p.second, collate_callback); + if(rc != SQLITE_OK) { throw_translated_sqlite_error(db); } } @@ -667,16 +666,16 @@ namespace sqlite_orm { if(this->connection->retain_count() > 0) { sqlite3* db = this->connection->get(); - auto resultCode = sqlite3_create_function_v2(db, - name.c_str(), - 0, - SQLITE_UTF8, - nullptr, - nullptr, - nullptr, - nullptr, - nullptr); - if(resultCode != SQLITE_OK) { + int rc = sqlite3_create_function_v2(db, + name.c_str(), + 0, + SQLITE_UTF8, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr); + if(rc != SQLITE_OK) { throw_translated_sqlite_error(db); } } @@ -686,31 +685,31 @@ namespace sqlite_orm { } void try_to_create_function(sqlite3* db, scalar_udf_proxy& udfProxy) { - auto resultCode = sqlite3_create_function_v2(db, - udfProxy.name.c_str(), - udfProxy.argumentsCount, - SQLITE_UTF8, - &udfProxy, - scalar_function_callback, - nullptr, - nullptr, - nullptr); - if(resultCode != SQLITE_OK) { + int rc = sqlite3_create_function_v2(db, + udfProxy.name.c_str(), + udfProxy.argumentsCount, + SQLITE_UTF8, + &udfProxy, + scalar_function_callback, + nullptr, + nullptr, + nullptr); + if(rc != SQLITE_OK) { throw_translated_sqlite_error(db); } } void try_to_create_function(sqlite3* db, aggregate_udf_proxy& udfProxy) { - auto resultCode = sqlite3_create_function(db, - udfProxy.name.c_str(), - udfProxy.argumentsCount, - SQLITE_UTF8, - &udfProxy, - nullptr, - aggregate_function_step_callback, - aggregate_function_final_callback); - if(resultCode != SQLITE_OK) { - throw_translated_sqlite_error(resultCode); + int rc = sqlite3_create_function(db, + udfProxy.name.c_str(), + udfProxy.argumentsCount, + SQLITE_UTF8, + &udfProxy, + nullptr, + aggregate_function_step_callback, + aggregate_function_final_callback); + if(rc != SQLITE_OK) { + throw_translated_sqlite_error(rc); } } diff --git a/include/sqlite_orm/sqlite_orm.h b/include/sqlite_orm/sqlite_orm.h index 9cb078ee9..7bc16e57e 100644 --- a/include/sqlite_orm/sqlite_orm.h +++ b/include/sqlite_orm/sqlite_orm.h @@ -15243,12 +15243,12 @@ namespace sqlite_orm { // create collations if db is open if(this->connection->retain_count() > 0) { sqlite3* db = this->connection->get(); - auto resultCode = sqlite3_create_collation(db, - name.c_str(), - SQLITE_UTF8, - function, - functionExists ? collate_callback : nullptr); - if(resultCode != SQLITE_OK) { + int rc = sqlite3_create_collation(db, + name.c_str(), + SQLITE_UTF8, + function, + functionExists ? collate_callback : nullptr); + if(rc != SQLITE_OK) { throw_translated_sqlite_error(db); } } @@ -15450,9 +15450,8 @@ namespace sqlite_orm { } for(auto& p: this->collatingFunctions) { - auto resultCode = - sqlite3_create_collation(db, p.first.c_str(), SQLITE_UTF8, &p.second, collate_callback); - if(resultCode != SQLITE_OK) { + int rc = sqlite3_create_collation(db, p.first.c_str(), SQLITE_UTF8, &p.second, collate_callback); + if(rc != SQLITE_OK) { throw_translated_sqlite_error(db); } } @@ -15489,16 +15488,16 @@ namespace sqlite_orm { if(this->connection->retain_count() > 0) { sqlite3* db = this->connection->get(); - auto resultCode = sqlite3_create_function_v2(db, - name.c_str(), - 0, - SQLITE_UTF8, - nullptr, - nullptr, - nullptr, - nullptr, - nullptr); - if(resultCode != SQLITE_OK) { + int rc = sqlite3_create_function_v2(db, + name.c_str(), + 0, + SQLITE_UTF8, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr); + if(rc != SQLITE_OK) { throw_translated_sqlite_error(db); } } @@ -15508,31 +15507,31 @@ namespace sqlite_orm { } void try_to_create_function(sqlite3* db, scalar_udf_proxy& udfProxy) { - auto resultCode = sqlite3_create_function_v2(db, - udfProxy.name.c_str(), - udfProxy.argumentsCount, - SQLITE_UTF8, - &udfProxy, - scalar_function_callback, - nullptr, - nullptr, - nullptr); - if(resultCode != SQLITE_OK) { + int rc = sqlite3_create_function_v2(db, + udfProxy.name.c_str(), + udfProxy.argumentsCount, + SQLITE_UTF8, + &udfProxy, + scalar_function_callback, + nullptr, + nullptr, + nullptr); + if(rc != SQLITE_OK) { throw_translated_sqlite_error(db); } } void try_to_create_function(sqlite3* db, aggregate_udf_proxy& udfProxy) { - auto resultCode = sqlite3_create_function(db, - udfProxy.name.c_str(), - udfProxy.argumentsCount, - SQLITE_UTF8, - &udfProxy, - nullptr, - aggregate_function_step_callback, - aggregate_function_final_callback); - if(resultCode != SQLITE_OK) { - throw_translated_sqlite_error(resultCode); + int rc = sqlite3_create_function(db, + udfProxy.name.c_str(), + udfProxy.argumentsCount, + SQLITE_UTF8, + &udfProxy, + nullptr, + aggregate_function_step_callback, + aggregate_function_final_callback); + if(rc != SQLITE_OK) { + throw_translated_sqlite_error(rc); } } From f997adce6c506d302d96e1ef312626ea5bc49045 Mon Sep 17 00:00:00 2001 From: klaus triendl Date: Tue, 14 Nov 2023 14:24:33 +0200 Subject: [PATCH 06/11] Delete functions after successfully hitting sqlite3 --- dev/storage_base.h | 34 ++++++++++++++++++++------------- include/sqlite_orm/sqlite_orm.h | 34 ++++++++++++++++++++------------- 2 files changed, 42 insertions(+), 26 deletions(-) diff --git a/dev/storage_base.h b/dev/storage_base.h index cc2a2fe83..46851cd4c 100644 --- a/dev/storage_base.h +++ b/dev/storage_base.h @@ -10,7 +10,7 @@ #include // std::make_unique, std::unique_ptr #include // std::map #include // std::is_same -#include // std::find_if +#include // std::find_if, std::ranges::find #include "functional/cxx_universal.h" // ::size_t #include "tuple_helper/tuple_iteration.h" @@ -410,12 +410,10 @@ namespace sqlite_orm { } void create_collation(const std::string& name, collating_function f) { - collating_function* function = nullptr; const auto functionExists = bool(f); + collating_function* function = nullptr; if(functionExists) { function = &(collatingFunctions[name] = std::move(f)); - } else { - collatingFunctions.erase(name); } // create collations if db is open @@ -430,6 +428,10 @@ namespace sqlite_orm { throw_translated_sqlite_error(db); } } + + if(!functionExists) { + collatingFunctions.erase(name); + } } template @@ -656,14 +658,15 @@ namespace sqlite_orm { } void delete_function_impl(const std::string& name, - std::vector>& functionsVector) const { - auto it = find_if(functionsVector.begin(), functionsVector.end(), [&name](auto& udfProxy) { + std::vector>& functions) const { +#if __cpp_lib_ranges >= 201911L + auto it = std::ranges::find(functions, name, &udf_proxy_base::name); +#else + auto it = std::find_if(functions.begin(), functions.end(), [&name](auto& udfProxy) { return udfProxy->name == name; }); - if(it != functionsVector.end()) { - functionsVector.erase(it); - it = functionsVector.end(); - +#endif + if(it != functions.end()) { if(this->connection->retain_count() > 0) { sqlite3* db = this->connection->get(); int rc = sqlite3_create_function_v2(db, @@ -679,6 +682,7 @@ namespace sqlite_orm { throw_translated_sqlite_error(db); } } + it = functions.erase(it); } else { throw std::system_error{orm_error_code::function_not_found}; } @@ -796,13 +800,17 @@ namespace sqlite_orm { ++storageColumnInfoIndex) { // get storage's column info - auto& storageColumnInfo = storageTableInfo[storageColumnInfoIndex]; - auto& columnName = storageColumnInfo.name; + table_xinfo& storageColumnInfo = storageTableInfo[storageColumnInfoIndex]; + const std::string& columnName = storageColumnInfo.name; - // search for a column in db eith the same name + // search for a column in db with the same name +#if __cpp_lib_ranges >= 201911L + auto dbColumnInfoIt = std::ranges::find(dbTableInfo, columnName, &table_xinfo::name); +#else auto dbColumnInfoIt = std::find_if(dbTableInfo.begin(), dbTableInfo.end(), [&columnName](auto& ti) { return ti.name == columnName; }); +#endif if(dbColumnInfoIt != dbTableInfo.end()) { auto& dbColumnInfo = *dbColumnInfoIt; auto columnsAreEqual = diff --git a/include/sqlite_orm/sqlite_orm.h b/include/sqlite_orm/sqlite_orm.h index 7bc16e57e..cf96219f8 100644 --- a/include/sqlite_orm/sqlite_orm.h +++ b/include/sqlite_orm/sqlite_orm.h @@ -13682,7 +13682,7 @@ namespace sqlite_orm { #include // std::make_unique, std::unique_ptr #include // std::map #include // std::is_same -#include // std::find_if +#include // std::find_if, std::ranges::find // #include "functional/cxx_universal.h" // ::size_t @@ -15232,12 +15232,10 @@ namespace sqlite_orm { } void create_collation(const std::string& name, collating_function f) { - collating_function* function = nullptr; const auto functionExists = bool(f); + collating_function* function = nullptr; if(functionExists) { function = &(collatingFunctions[name] = std::move(f)); - } else { - collatingFunctions.erase(name); } // create collations if db is open @@ -15252,6 +15250,10 @@ namespace sqlite_orm { throw_translated_sqlite_error(db); } } + + if(!functionExists) { + collatingFunctions.erase(name); + } } template @@ -15478,14 +15480,15 @@ namespace sqlite_orm { } void delete_function_impl(const std::string& name, - std::vector>& functionsVector) const { - auto it = find_if(functionsVector.begin(), functionsVector.end(), [&name](auto& udfProxy) { + std::vector>& functions) const { +#if __cpp_lib_ranges >= 201911L + auto it = std::ranges::find(functions, name, &udf_proxy_base::name); +#else + auto it = std::find_if(functions.begin(), functions.end(), [&name](auto& udfProxy) { return udfProxy->name == name; }); - if(it != functionsVector.end()) { - functionsVector.erase(it); - it = functionsVector.end(); - +#endif + if(it != functions.end()) { if(this->connection->retain_count() > 0) { sqlite3* db = this->connection->get(); int rc = sqlite3_create_function_v2(db, @@ -15501,6 +15504,7 @@ namespace sqlite_orm { throw_translated_sqlite_error(db); } } + it = functions.erase(it); } else { throw std::system_error{orm_error_code::function_not_found}; } @@ -15618,13 +15622,17 @@ namespace sqlite_orm { ++storageColumnInfoIndex) { // get storage's column info - auto& storageColumnInfo = storageTableInfo[storageColumnInfoIndex]; - auto& columnName = storageColumnInfo.name; + table_xinfo& storageColumnInfo = storageTableInfo[storageColumnInfoIndex]; + const std::string& columnName = storageColumnInfo.name; - // search for a column in db eith the same name + // search for a column in db with the same name +#if __cpp_lib_ranges >= 201911L + auto dbColumnInfoIt = std::ranges::find(dbTableInfo, columnName, &table_xinfo::name); +#else auto dbColumnInfoIt = std::find_if(dbTableInfo.begin(), dbTableInfo.end(), [&columnName](auto& ti) { return ti.name == columnName; }); +#endif if(dbColumnInfoIt != dbTableInfo.end()) { auto& dbColumnInfo = *dbColumnInfoIt; auto columnsAreEqual = From 6f2f7d15931dd935a99ed8db5fea8de8fa5deeea Mon Sep 17 00:00:00 2001 From: klaus triendl Date: Tue, 14 Nov 2023 19:37:04 +0200 Subject: [PATCH 07/11] Fixed a memory leak that occurs when deleting user-defined functions Either the destructor of `udf_proxy_base` (formerly `user_defined_function_base` must be virtual, or unique pointers need to carry type-erased deleter --- dev/function.h | 14 ++++----- dev/storage_base.h | 40 ++++++++++++------------ include/sqlite_orm/sqlite_orm.h | 54 ++++++++++++++++----------------- 3 files changed, 54 insertions(+), 54 deletions(-) diff --git a/dev/function.h b/dev/function.h index e46581594..868b31dfc 100644 --- a/dev/function.h +++ b/dev/function.h @@ -23,23 +23,23 @@ namespace sqlite_orm { namespace internal { struct udf_proxy_base { - using func_call = std::function< - void(sqlite3_context* context, void* functionPointer, int argsCount, sqlite3_value** values)>; - using final_call = std::function; + using func_call = + std::function; + using final_call = std::function; std::string name; int argumentsCount = 0; - std::function create; - void (*destroy)(int*) = nullptr; + std::function create; + xdestroy_fn_t destroy = nullptr; -#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED udf_proxy_base(decltype(name) name_, decltype(argumentsCount) argumentsCount_, decltype(create) create_, decltype(destroy) destroy_) : name(std::move(name_)), argumentsCount(argumentsCount_), create(std::move(create_)), destroy(destroy_) {} -#endif + + virtual ~udf_proxy_base() = default; }; struct scalar_udf_proxy : udf_proxy_base { diff --git a/dev/storage_base.h b/dev/storage_base.h index 46851cd4c..79c72eeee 100644 --- a/dev/storage_base.h +++ b/dev/storage_base.h @@ -264,8 +264,9 @@ namespace sqlite_orm { this->scalarFunctions.push_back(std::make_unique( std::move(name), argsCount, - []() -> int* { - return (int*)(new F()); + /* create = */ + []() -> void* { + return new F(); }, /* call = */ [](sqlite3_context* context, void* udfHandle, int argsCount, sqlite3_value** values) { @@ -331,8 +332,8 @@ namespace sqlite_orm { std::move(name), argsCount, /* create = */ - []() -> int* { - return (int*)(new F()); + []() -> void* { + return new F(); }, /* step = */ [](sqlite3_context*, void* udfHandle, int argsCount, sqlite3_value** values) { @@ -671,7 +672,7 @@ namespace sqlite_orm { sqlite3* db = this->connection->get(); int rc = sqlite3_create_function_v2(db, name.c_str(), - 0, + (*it)->argumentsCount, SQLITE_UTF8, nullptr, nullptr, @@ -719,26 +720,26 @@ namespace sqlite_orm { static void aggregate_function_step_callback(sqlite3_context* context, int argsCount, sqlite3_value** values) { - auto udfProxy = static_cast(sqlite3_user_data(context)); - auto aggregateContextHandle = sqlite3_aggregate_context(context, sizeof(int**)); - auto aggregateContextIntHandle = static_cast(aggregateContextHandle); - if(*aggregateContextIntHandle == nullptr) { - *aggregateContextIntHandle = udfProxy->create(); + auto* udfProxy = static_cast(sqlite3_user_data(context)); + void* aggregateStateMem = sqlite3_aggregate_context(context, sizeof(void**)); + void* udfHandle = *static_cast(aggregateStateMem); + if(udfHandle == nullptr) { + udfHandle = udfProxy->create(); } - udfProxy->step(context, *aggregateContextIntHandle, argsCount, values); + udfProxy->step(context, udfHandle, argsCount, values); } static void aggregate_function_final_callback(sqlite3_context* context) { - auto udfProxy = static_cast(sqlite3_user_data(context)); - auto aggregateContextHandle = sqlite3_aggregate_context(context, sizeof(int**)); - auto aggregateContextIntHandle = static_cast(aggregateContextHandle); - udfProxy->finalCall(context, *aggregateContextIntHandle); - udfProxy->destroy(*aggregateContextIntHandle); + auto* udfProxy = static_cast(sqlite3_user_data(context)); + void* aggregateStateMem = sqlite3_aggregate_context(context, sizeof(void**)); + void* udfHandle = *static_cast(aggregateStateMem); + udfProxy->finalCall(context, udfHandle); + udfProxy->destroy(udfHandle); } static void scalar_function_callback(sqlite3_context* context, int argsCount, sqlite3_value** values) { auto udfProxy = static_cast(sqlite3_user_data(context)); - const std::unique_ptr callableGuard(udfProxy->create(), udfProxy->destroy); + const std::unique_ptr udfHandleGuard(udfProxy->create(), udfProxy->destroy); if(udfProxy->argumentsCount != -1 && udfProxy->argumentsCount != argsCount) { throw std::system_error{orm_error_code::arguments_count_does_not_match}; } @@ -746,9 +747,8 @@ namespace sqlite_orm { } template - static void delete_function_callback(int* pointer) { - auto voidPointer = static_cast(pointer); - auto fPointer = static_cast(voidPointer); + static void delete_function_callback(void* pointer) { + auto fPointer = static_cast(pointer); delete fPointer; } diff --git a/include/sqlite_orm/sqlite_orm.h b/include/sqlite_orm/sqlite_orm.h index cf96219f8..729c9225f 100644 --- a/include/sqlite_orm/sqlite_orm.h +++ b/include/sqlite_orm/sqlite_orm.h @@ -10907,23 +10907,23 @@ namespace sqlite_orm { namespace internal { struct udf_proxy_base { - using func_call = std::function< - void(sqlite3_context* context, void* functionPointer, int argsCount, sqlite3_value** values)>; - using final_call = std::function; + using func_call = + std::function; + using final_call = std::function; std::string name; int argumentsCount = 0; - std::function create; - void (*destroy)(int*) = nullptr; + std::function create; + xdestroy_fn_t destroy = nullptr; -#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED udf_proxy_base(decltype(name) name_, decltype(argumentsCount) argumentsCount_, decltype(create) create_, decltype(destroy) destroy_) : name(std::move(name_)), argumentsCount(argumentsCount_), create(std::move(create_)), destroy(destroy_) {} -#endif + + virtual ~udf_proxy_base() = default; }; struct scalar_udf_proxy : udf_proxy_base { @@ -15086,8 +15086,9 @@ namespace sqlite_orm { this->scalarFunctions.push_back(std::make_unique( std::move(name), argsCount, - []() -> int* { - return (int*)(new F()); + /* create = */ + []() -> void* { + return new F(); }, /* call = */ [](sqlite3_context* context, void* udfHandle, int argsCount, sqlite3_value** values) { @@ -15153,8 +15154,8 @@ namespace sqlite_orm { std::move(name), argsCount, /* create = */ - []() -> int* { - return (int*)(new F()); + []() -> void* { + return new F(); }, /* step = */ [](sqlite3_context*, void* udfHandle, int argsCount, sqlite3_value** values) { @@ -15493,7 +15494,7 @@ namespace sqlite_orm { sqlite3* db = this->connection->get(); int rc = sqlite3_create_function_v2(db, name.c_str(), - 0, + (*it)->argumentsCount, SQLITE_UTF8, nullptr, nullptr, @@ -15541,26 +15542,26 @@ namespace sqlite_orm { static void aggregate_function_step_callback(sqlite3_context* context, int argsCount, sqlite3_value** values) { - auto udfProxy = static_cast(sqlite3_user_data(context)); - auto aggregateContextHandle = sqlite3_aggregate_context(context, sizeof(int**)); - auto aggregateContextIntHandle = static_cast(aggregateContextHandle); - if(*aggregateContextIntHandle == nullptr) { - *aggregateContextIntHandle = udfProxy->create(); + auto* udfProxy = static_cast(sqlite3_user_data(context)); + void* aggregateStateMem = sqlite3_aggregate_context(context, sizeof(void**)); + void* udfHandle = *static_cast(aggregateStateMem); + if(udfHandle == nullptr) { + udfHandle = udfProxy->create(); } - udfProxy->step(context, *aggregateContextIntHandle, argsCount, values); + udfProxy->step(context, udfHandle, argsCount, values); } static void aggregate_function_final_callback(sqlite3_context* context) { - auto udfProxy = static_cast(sqlite3_user_data(context)); - auto aggregateContextHandle = sqlite3_aggregate_context(context, sizeof(int**)); - auto aggregateContextIntHandle = static_cast(aggregateContextHandle); - udfProxy->finalCall(context, *aggregateContextIntHandle); - udfProxy->destroy(*aggregateContextIntHandle); + auto* udfProxy = static_cast(sqlite3_user_data(context)); + void* aggregateStateMem = sqlite3_aggregate_context(context, sizeof(void**)); + void* udfHandle = *static_cast(aggregateStateMem); + udfProxy->finalCall(context, udfHandle); + udfProxy->destroy(udfHandle); } static void scalar_function_callback(sqlite3_context* context, int argsCount, sqlite3_value** values) { auto udfProxy = static_cast(sqlite3_user_data(context)); - const std::unique_ptr callableGuard(udfProxy->create(), udfProxy->destroy); + const std::unique_ptr udfHandleGuard(udfProxy->create(), udfProxy->destroy); if(udfProxy->argumentsCount != -1 && udfProxy->argumentsCount != argsCount) { throw std::system_error{orm_error_code::arguments_count_does_not_match}; } @@ -15568,9 +15569,8 @@ namespace sqlite_orm { } template - static void delete_function_callback(int* pointer) { - auto voidPointer = static_cast(pointer); - auto fPointer = static_cast(voidPointer); + static void delete_function_callback(void* pointer) { + auto fPointer = static_cast(pointer); delete fPointer; } From 3eecc45c88bfd05f0c4d20c56417b4069e2e505d Mon Sep 17 00:00:00 2001 From: klaus triendl Date: Tue, 14 Nov 2023 19:50:13 +0200 Subject: [PATCH 08/11] Improved compilation times under C++14 The statement value binder for tuples and tuple creator from result values now benefit from a parameter pack expansion trick --- dev/statement_binder.h | 11 ++++------- dev/values_to_tuple.h | 10 ++++------ include/sqlite_orm/sqlite_orm.h | 21 ++++++++------------- 3 files changed, 16 insertions(+), 26 deletions(-) diff --git a/dev/statement_binder.h b/dev/statement_binder.h index dd6e556dc..4f741ae2f 100644 --- a/dev/statement_binder.h +++ b/dev/statement_binder.h @@ -332,14 +332,11 @@ namespace sqlite_orm { (this->bind(polyfill::invoke(project, std::get(tpl)), Idx), ...); } #else - template - void operator()(const Tpl& tpl, std::index_sequence, Projection project) const { - this->bind(polyfill::invoke(project, std::get(tpl)), I); - (*this)(tpl, std::index_sequence{}, std::forward(project)); + template + void operator()(const Tpl& tpl, std::index_sequence, Projection project) const { + using Sink = int[sizeof...(Idx)]; + (void)Sink{(this->bind(polyfill::invoke(project, std::get(tpl)), Idx), 0)...}; } - - template - void operator()(const Tpl&, std::index_sequence<>, Projection) const {} #endif template diff --git a/dev/values_to_tuple.h b/dev/values_to_tuple.h index f5a477f6f..d708e0284 100644 --- a/dev/values_to_tuple.h +++ b/dev/values_to_tuple.h @@ -29,13 +29,11 @@ namespace sqlite_orm { (this->extract(values[Idx], std::get(tuple)), ...); } #else - template - void operator()(sqlite3_value** values, Tpl& tuple, std::index_sequence) const { - this->extract(values[I], std::get(tuple)); - (*this)(values, tuple, std::index_sequence{}); - } template - void operator()(sqlite3_value** /*values*/, Tpl&, std::index_sequence) const {} + void operator()(sqlite3_value** values, Tpl& tuple, std::index_sequence) const { + using Sink = int[sizeof...(Idx)]; + (void)Sink{(this->extract(values[Idx], std::get(tuple)), 0)...}; + } #endif template void extract(sqlite3_value* value, T& t) const { diff --git a/include/sqlite_orm/sqlite_orm.h b/include/sqlite_orm/sqlite_orm.h index 729c9225f..2b42c527d 100644 --- a/include/sqlite_orm/sqlite_orm.h +++ b/include/sqlite_orm/sqlite_orm.h @@ -8860,14 +8860,11 @@ namespace sqlite_orm { (this->bind(polyfill::invoke(project, std::get(tpl)), Idx), ...); } #else - template - void operator()(const Tpl& tpl, std::index_sequence, Projection project) const { - this->bind(polyfill::invoke(project, std::get(tpl)), I); - (*this)(tpl, std::index_sequence{}, std::forward(project)); + template + void operator()(const Tpl& tpl, std::index_sequence, Projection project) const { + using Sink = int[sizeof...(Idx)]; + (void)Sink{(this->bind(polyfill::invoke(project, std::get(tpl)), Idx), 0)...}; } - - template - void operator()(const Tpl&, std::index_sequence<>, Projection) const {} #endif template @@ -14825,13 +14822,11 @@ namespace sqlite_orm { (this->extract(values[Idx], std::get(tuple)), ...); } #else - template - void operator()(sqlite3_value** values, Tpl& tuple, std::index_sequence) const { - this->extract(values[I], std::get(tuple)); - (*this)(values, tuple, std::index_sequence{}); - } template - void operator()(sqlite3_value** /*values*/, Tpl&, std::index_sequence) const {} + void operator()(sqlite3_value** values, Tpl& tuple, std::index_sequence) const { + using Sink = int[sizeof...(Idx)]; + (void)Sink{(this->extract(values[Idx], std::get(tuple)), 0)...}; + } #endif template void extract(sqlite3_value* value, T& t) const { From cb4bcacb697cf3fc39df02cf22c980189cc7bfc0 Mon Sep 17 00:00:00 2001 From: klaus triendl Date: Tue, 14 Nov 2023 20:00:19 +0200 Subject: [PATCH 09/11] Fix possible crash in final aggregation callback --- dev/storage_base.h | 6 ++++++ include/sqlite_orm/sqlite_orm.h | 6 ++++++ 2 files changed, 12 insertions(+) diff --git a/dev/storage_base.h b/dev/storage_base.h index 79c72eeee..64fed38d3 100644 --- a/dev/storage_base.h +++ b/dev/storage_base.h @@ -721,6 +721,7 @@ namespace sqlite_orm { static void aggregate_function_step_callback(sqlite3_context* context, int argsCount, sqlite3_value** values) { auto* udfProxy = static_cast(sqlite3_user_data(context)); + // allocate or fetch pointer handle to user-defined function void* aggregateStateMem = sqlite3_aggregate_context(context, sizeof(void**)); void* udfHandle = *static_cast(aggregateStateMem); if(udfHandle == nullptr) { @@ -731,8 +732,13 @@ namespace sqlite_orm { static void aggregate_function_final_callback(sqlite3_context* context) { auto* udfProxy = static_cast(sqlite3_user_data(context)); + // allocate or fetch pointer handle to user-defined function void* aggregateStateMem = sqlite3_aggregate_context(context, sizeof(void**)); void* udfHandle = *static_cast(aggregateStateMem); + // note: it is possible that the 'step' function was never called + if(udfHandle == nullptr) { + udfHandle = udfProxy->create(); + } udfProxy->finalCall(context, udfHandle); udfProxy->destroy(udfHandle); } diff --git a/include/sqlite_orm/sqlite_orm.h b/include/sqlite_orm/sqlite_orm.h index 2b42c527d..e3a27ed81 100644 --- a/include/sqlite_orm/sqlite_orm.h +++ b/include/sqlite_orm/sqlite_orm.h @@ -15538,6 +15538,7 @@ namespace sqlite_orm { static void aggregate_function_step_callback(sqlite3_context* context, int argsCount, sqlite3_value** values) { auto* udfProxy = static_cast(sqlite3_user_data(context)); + // allocate or fetch pointer handle to user-defined function void* aggregateStateMem = sqlite3_aggregate_context(context, sizeof(void**)); void* udfHandle = *static_cast(aggregateStateMem); if(udfHandle == nullptr) { @@ -15548,8 +15549,13 @@ namespace sqlite_orm { static void aggregate_function_final_callback(sqlite3_context* context) { auto* udfProxy = static_cast(sqlite3_user_data(context)); + // allocate or fetch pointer handle to user-defined function void* aggregateStateMem = sqlite3_aggregate_context(context, sizeof(void**)); void* udfHandle = *static_cast(aggregateStateMem); + // note: it is possible that the 'step' function was never called + if(udfHandle == nullptr) { + udfHandle = udfProxy->create(); + } udfProxy->finalCall(context, udfHandle); udfProxy->destroy(udfHandle); } From 60fff04126e1cf162d29c4edbbafae611e6cfe3b Mon Sep 17 00:00:00 2001 From: klaus triendl Date: Tue, 14 Nov 2023 21:10:58 +0200 Subject: [PATCH 10/11] Updated a few things concerning user-defined function creation --- dev/storage_base.h | 37 ++++++++++++++++++--------------- include/sqlite_orm/sqlite_orm.h | 37 ++++++++++++++++++--------------- 2 files changed, 40 insertions(+), 34 deletions(-) diff --git a/dev/storage_base.h b/dev/storage_base.h index 64fed38d3..9c45bc53e 100644 --- a/dev/storage_base.h +++ b/dev/storage_base.h @@ -251,7 +251,7 @@ namespace sqlite_orm { */ template void create_scalar_function() { - static_assert(is_scalar_udf_v, "F cannot be an aggregate function"); + static_assert(is_scalar_udf_v, "F must be a scalar function"); std::stringstream ss; ss << F::name() << std::flush; @@ -270,10 +270,10 @@ namespace sqlite_orm { }, /* call = */ [](sqlite3_context* context, void* udfHandle, int argsCount, sqlite3_value** values) { - F& function = *static_cast(udfHandle); + F& udf = *static_cast(udfHandle); args_tuple argsTuple; values_to_tuple{}(values, argsTuple, argsCount); - auto result = call(function, std::move(argsTuple)); + auto result = call(udf, std::move(argsTuple)); statement_binder().result(context, result); }, delete_function_callback)); @@ -318,7 +318,7 @@ namespace sqlite_orm { */ template void create_aggregate_function() { - static_assert(is_aggregate_udf_v, "F cannot be a scalar function"); + static_assert(is_aggregate_udf_v, "F must be an aggregate function"); std::stringstream ss; ss << F::name() << std::flush; @@ -337,15 +337,15 @@ namespace sqlite_orm { }, /* step = */ [](sqlite3_context*, void* udfHandle, int argsCount, sqlite3_value** values) { - F& function = *static_cast(udfHandle); + F& udf = *static_cast(udfHandle); args_tuple argsTuple; values_to_tuple{}(values, argsTuple, argsCount); - call(function, &F::step, std::move(argsTuple)); + call(udf, &F::step, std::move(argsTuple)); }, /* finalCall = */ [](sqlite3_context* context, void* udfHandle) { - F& function = *static_cast(udfHandle); - auto result = function.fin(); + F& udf = *static_cast(udfHandle); + auto result = udf.fin(); statement_binder().result(context, result); }, delete_function_callback)); @@ -689,7 +689,7 @@ namespace sqlite_orm { } } - void try_to_create_function(sqlite3* db, scalar_udf_proxy& udfProxy) { + static void try_to_create_function(sqlite3* db, scalar_udf_proxy& udfProxy) { int rc = sqlite3_create_function_v2(db, udfProxy.name.c_str(), udfProxy.argumentsCount, @@ -704,7 +704,7 @@ namespace sqlite_orm { } } - void try_to_create_function(sqlite3* db, aggregate_udf_proxy& udfProxy) { + static void try_to_create_function(sqlite3* db, aggregate_udf_proxy& udfProxy) { int rc = sqlite3_create_function(db, udfProxy.name.c_str(), udfProxy.argumentsCount, @@ -723,8 +723,11 @@ namespace sqlite_orm { auto* udfProxy = static_cast(sqlite3_user_data(context)); // allocate or fetch pointer handle to user-defined function void* aggregateStateMem = sqlite3_aggregate_context(context, sizeof(void**)); - void* udfHandle = *static_cast(aggregateStateMem); + void*& udfHandle = *static_cast(aggregateStateMem); if(udfHandle == nullptr) { + if(udfProxy->argumentsCount != -1 && udfProxy->argumentsCount != argsCount) { + throw std::system_error{orm_error_code::arguments_count_does_not_match}; + } udfHandle = udfProxy->create(); } udfProxy->step(context, udfHandle, argsCount, values); @@ -734,7 +737,7 @@ namespace sqlite_orm { auto* udfProxy = static_cast(sqlite3_user_data(context)); // allocate or fetch pointer handle to user-defined function void* aggregateStateMem = sqlite3_aggregate_context(context, sizeof(void**)); - void* udfHandle = *static_cast(aggregateStateMem); + void*& udfHandle = *static_cast(aggregateStateMem); // note: it is possible that the 'step' function was never called if(udfHandle == nullptr) { udfHandle = udfProxy->create(); @@ -745,17 +748,17 @@ namespace sqlite_orm { static void scalar_function_callback(sqlite3_context* context, int argsCount, sqlite3_value** values) { auto udfProxy = static_cast(sqlite3_user_data(context)); - const std::unique_ptr udfHandleGuard(udfProxy->create(), udfProxy->destroy); if(udfProxy->argumentsCount != -1 && udfProxy->argumentsCount != argsCount) { throw std::system_error{orm_error_code::arguments_count_does_not_match}; } - udfProxy->run(context, udfProxy, argsCount, values); + const std::unique_ptr udfHandle(udfProxy->create(), udfProxy->destroy); + udfProxy->run(context, udfHandle.get(), argsCount, values); } template - static void delete_function_callback(void* pointer) { - auto fPointer = static_cast(pointer); - delete fPointer; + static void delete_function_callback(void* udfHandle) { + F* udf = static_cast(udfHandle); + delete udf; } std::string current_time(sqlite3* db) { diff --git a/include/sqlite_orm/sqlite_orm.h b/include/sqlite_orm/sqlite_orm.h index e3a27ed81..f73528b6d 100644 --- a/include/sqlite_orm/sqlite_orm.h +++ b/include/sqlite_orm/sqlite_orm.h @@ -15068,7 +15068,7 @@ namespace sqlite_orm { */ template void create_scalar_function() { - static_assert(is_scalar_udf_v, "F cannot be an aggregate function"); + static_assert(is_scalar_udf_v, "F must be a scalar function"); std::stringstream ss; ss << F::name() << std::flush; @@ -15087,10 +15087,10 @@ namespace sqlite_orm { }, /* call = */ [](sqlite3_context* context, void* udfHandle, int argsCount, sqlite3_value** values) { - F& function = *static_cast(udfHandle); + F& udf = *static_cast(udfHandle); args_tuple argsTuple; values_to_tuple{}(values, argsTuple, argsCount); - auto result = call(function, std::move(argsTuple)); + auto result = call(udf, std::move(argsTuple)); statement_binder().result(context, result); }, delete_function_callback)); @@ -15135,7 +15135,7 @@ namespace sqlite_orm { */ template void create_aggregate_function() { - static_assert(is_aggregate_udf_v, "F cannot be a scalar function"); + static_assert(is_aggregate_udf_v, "F must be an aggregate function"); std::stringstream ss; ss << F::name() << std::flush; @@ -15154,15 +15154,15 @@ namespace sqlite_orm { }, /* step = */ [](sqlite3_context*, void* udfHandle, int argsCount, sqlite3_value** values) { - F& function = *static_cast(udfHandle); + F& udf = *static_cast(udfHandle); args_tuple argsTuple; values_to_tuple{}(values, argsTuple, argsCount); - call(function, &F::step, std::move(argsTuple)); + call(udf, &F::step, std::move(argsTuple)); }, /* finalCall = */ [](sqlite3_context* context, void* udfHandle) { - F& function = *static_cast(udfHandle); - auto result = function.fin(); + F& udf = *static_cast(udfHandle); + auto result = udf.fin(); statement_binder().result(context, result); }, delete_function_callback)); @@ -15506,7 +15506,7 @@ namespace sqlite_orm { } } - void try_to_create_function(sqlite3* db, scalar_udf_proxy& udfProxy) { + static void try_to_create_function(sqlite3* db, scalar_udf_proxy& udfProxy) { int rc = sqlite3_create_function_v2(db, udfProxy.name.c_str(), udfProxy.argumentsCount, @@ -15521,7 +15521,7 @@ namespace sqlite_orm { } } - void try_to_create_function(sqlite3* db, aggregate_udf_proxy& udfProxy) { + static void try_to_create_function(sqlite3* db, aggregate_udf_proxy& udfProxy) { int rc = sqlite3_create_function(db, udfProxy.name.c_str(), udfProxy.argumentsCount, @@ -15540,8 +15540,11 @@ namespace sqlite_orm { auto* udfProxy = static_cast(sqlite3_user_data(context)); // allocate or fetch pointer handle to user-defined function void* aggregateStateMem = sqlite3_aggregate_context(context, sizeof(void**)); - void* udfHandle = *static_cast(aggregateStateMem); + void*& udfHandle = *static_cast(aggregateStateMem); if(udfHandle == nullptr) { + if(udfProxy->argumentsCount != -1 && udfProxy->argumentsCount != argsCount) { + throw std::system_error{orm_error_code::arguments_count_does_not_match}; + } udfHandle = udfProxy->create(); } udfProxy->step(context, udfHandle, argsCount, values); @@ -15551,7 +15554,7 @@ namespace sqlite_orm { auto* udfProxy = static_cast(sqlite3_user_data(context)); // allocate or fetch pointer handle to user-defined function void* aggregateStateMem = sqlite3_aggregate_context(context, sizeof(void**)); - void* udfHandle = *static_cast(aggregateStateMem); + void*& udfHandle = *static_cast(aggregateStateMem); // note: it is possible that the 'step' function was never called if(udfHandle == nullptr) { udfHandle = udfProxy->create(); @@ -15562,17 +15565,17 @@ namespace sqlite_orm { static void scalar_function_callback(sqlite3_context* context, int argsCount, sqlite3_value** values) { auto udfProxy = static_cast(sqlite3_user_data(context)); - const std::unique_ptr udfHandleGuard(udfProxy->create(), udfProxy->destroy); if(udfProxy->argumentsCount != -1 && udfProxy->argumentsCount != argsCount) { throw std::system_error{orm_error_code::arguments_count_does_not_match}; } - udfProxy->run(context, udfProxy, argsCount, values); + const std::unique_ptr udfHandle(udfProxy->create(), udfProxy->destroy); + udfProxy->run(context, udfHandle.get(), argsCount, values); } template - static void delete_function_callback(void* pointer) { - auto fPointer = static_cast(pointer); - delete fPointer; + static void delete_function_callback(void* udfHandle) { + F* udf = static_cast(udfHandle); + delete udf; } std::string current_time(sqlite3* db) { From cc85bc401d238f4969c7414124ead85dc99ddc96 Mon Sep 17 00:00:00 2001 From: klaus triendl Date: Wed, 15 Nov 2023 00:25:58 +0200 Subject: [PATCH 11/11] Merged the data elements of "udf proxies" into the base class --- dev/function.h | 59 +++++----- dev/storage_base.h | 33 +++--- dev/xdestroy_handling.h | 24 ++-- include/sqlite_orm/sqlite_orm.h | 117 +++++++++---------- tests/static_tests/function_static_tests.cpp | 4 + 5 files changed, 113 insertions(+), 124 deletions(-) diff --git a/dev/function.h b/dev/function.h index 868b31dfc..67a593214 100644 --- a/dev/function.h +++ b/dev/function.h @@ -23,49 +23,44 @@ namespace sqlite_orm { namespace internal { struct udf_proxy_base { - using func_call = - std::function; - using final_call = std::function; + using func_call_fn_t = void (*)(void* udfHandle, + sqlite3_context* context, + int argsCount, + sqlite3_value** values); + using final_call_fn_t = void (*)(void* udfHandle, sqlite3_context* context); std::string name; int argumentsCount = 0; std::function create; xdestroy_fn_t destroy = nullptr; - - udf_proxy_base(decltype(name) name_, - decltype(argumentsCount) argumentsCount_, - decltype(create) create_, - decltype(destroy) destroy_) : - name(std::move(name_)), - argumentsCount(argumentsCount_), create(std::move(create_)), destroy(destroy_) {} - - virtual ~udf_proxy_base() = default; + func_call_fn_t func = nullptr; + final_call_fn_t finalAggregateCall = nullptr; + + udf_proxy_base(std::string name, + int argumentsCount, + std::function create, + xdestroy_fn_t destroy, + func_call_fn_t run) : + name(std::move(name)), + argumentsCount(argumentsCount), create(std::move(create)), destroy(destroy), func(run) {} + + udf_proxy_base(std::string name, + int argumentsCount, + std::function create, + xdestroy_fn_t destroy, + func_call_fn_t step, + final_call_fn_t finalCall) : + name(std::move(name)), + argumentsCount(argumentsCount), create(std::move(create)), destroy(destroy), func(step), + finalAggregateCall(finalCall) {} }; struct scalar_udf_proxy : udf_proxy_base { - func_call run; - - scalar_udf_proxy(decltype(name) name_, - int argumentsCount_, - decltype(create) create_, - decltype(run) run_, - decltype(destroy) destroy_) : - udf_proxy_base{std::move(name_), argumentsCount_, std::move(create_), destroy_}, - run(std::move(run_)) {} + using udf_proxy_base::udf_proxy_base; }; struct aggregate_udf_proxy : udf_proxy_base { - func_call step; - final_call finalCall; - - aggregate_udf_proxy(decltype(name) name_, - int argumentsCount_, - decltype(create) create_, - decltype(step) step_, - decltype(finalCall) finalCall_, - decltype(destroy) destroy_) : - udf_proxy_base{std::move(name_), argumentsCount_, std::move(create_), destroy_}, - step(std::move(step_)), finalCall(std::move(finalCall_)) {} + using udf_proxy_base::udf_proxy_base; }; template diff --git a/dev/storage_base.h b/dev/storage_base.h index 9c45bc53e..9f935cefc 100644 --- a/dev/storage_base.h +++ b/dev/storage_base.h @@ -24,6 +24,7 @@ #include "values_to_tuple.h" #include "arg_values.h" #include "util.h" +#include "xdestroy_handling.h" #include "serializing_util.h" namespace sqlite_orm { @@ -268,15 +269,16 @@ namespace sqlite_orm { []() -> void* { return new F(); }, + /* destroy = */ + obtain_xdestroy_for(std::default_delete{}), /* call = */ - [](sqlite3_context* context, void* udfHandle, int argsCount, sqlite3_value** values) { + [](void* udfHandle, sqlite3_context* context, int argsCount, sqlite3_value** values) { F& udf = *static_cast(udfHandle); args_tuple argsTuple; values_to_tuple{}(values, argsTuple, argsCount); auto result = call(udf, std::move(argsTuple)); statement_binder().result(context, result); - }, - delete_function_callback)); + })); if(this->connection->retain_count() > 0) { sqlite3* db = this->connection->get(); @@ -335,20 +337,21 @@ namespace sqlite_orm { []() -> void* { return new F(); }, + /* destroy = */ + obtain_xdestroy_for(std::default_delete{}), /* step = */ - [](sqlite3_context*, void* udfHandle, int argsCount, sqlite3_value** values) { + [](void* udfHandle, sqlite3_context*, int argsCount, sqlite3_value** values) { F& udf = *static_cast(udfHandle); args_tuple argsTuple; values_to_tuple{}(values, argsTuple, argsCount); call(udf, &F::step, std::move(argsTuple)); }, /* finalCall = */ - [](sqlite3_context* context, void* udfHandle) { + [](void* udfHandle, sqlite3_context* context) { F& udf = *static_cast(udfHandle); auto result = udf.fin(); statement_binder().result(context, result); - }, - delete_function_callback)); + })); if(this->connection->retain_count() > 0) { sqlite3* db = this->connection->get(); @@ -730,7 +733,7 @@ namespace sqlite_orm { } udfHandle = udfProxy->create(); } - udfProxy->step(context, udfHandle, argsCount, values); + udfProxy->func(udfHandle, context, argsCount, values); } static void aggregate_function_final_callback(sqlite3_context* context) { @@ -742,23 +745,17 @@ namespace sqlite_orm { if(udfHandle == nullptr) { udfHandle = udfProxy->create(); } - udfProxy->finalCall(context, udfHandle); + udfProxy->finalAggregateCall(udfHandle, context); udfProxy->destroy(udfHandle); } static void scalar_function_callback(sqlite3_context* context, int argsCount, sqlite3_value** values) { - auto udfProxy = static_cast(sqlite3_user_data(context)); + auto* udfProxy = static_cast(sqlite3_user_data(context)); if(udfProxy->argumentsCount != -1 && udfProxy->argumentsCount != argsCount) { throw std::system_error{orm_error_code::arguments_count_does_not_match}; } - const std::unique_ptr udfHandle(udfProxy->create(), udfProxy->destroy); - udfProxy->run(context, udfHandle.get(), argsCount, values); - } - - template - static void delete_function_callback(void* udfHandle) { - F* udf = static_cast(udfHandle); - delete udf; + const std::unique_ptr udfHandle{udfProxy->create(), udfProxy->destroy}; + udfProxy->func(udfHandle.get(), context, argsCount, values); } std::string current_time(sqlite3* db) { diff --git a/dev/xdestroy_handling.h b/dev/xdestroy_handling.h index b545e0c81..0b4b40afd 100644 --- a/dev/xdestroy_handling.h +++ b/dev/xdestroy_handling.h @@ -181,8 +181,8 @@ namespace sqlite_orm { * * Explicitly declared for better error messages. */ - template - constexpr xdestroy_fn_t obtain_xdestroy_for(D, P*) noexcept + template + constexpr xdestroy_fn_t obtain_xdestroy_for(D, P* = nullptr) noexcept requires(internal::is_unusable_for_xdestroy) { static_assert(polyfill::always_false_v, @@ -202,8 +202,8 @@ namespace sqlite_orm { * Type-safety is garanteed by checking whether the deleter or yielded function pointer * is invocable with the non-q-qualified pointer value. */ - template - constexpr xdestroy_fn_t obtain_xdestroy_for(D, P*) noexcept + template + constexpr xdestroy_fn_t obtain_xdestroy_for(D, P* = nullptr) noexcept requires(internal::needs_xdestroy_proxy) { return internal::xdestroy_proxy; @@ -223,27 +223,27 @@ namespace sqlite_orm { * Type-safety is garanteed by checking whether the deleter or yielded function pointer * is invocable with the non-q-qualified pointer value. */ - template - constexpr xdestroy_fn_t obtain_xdestroy_for(D d, P*) noexcept + template + constexpr xdestroy_fn_t obtain_xdestroy_for(D d, P* = nullptr) noexcept requires(internal::yields_xdestroy) { return d; } #else - template, bool> = true> - constexpr xdestroy_fn_t obtain_xdestroy_for(D, P*) { + template, bool> = true> + constexpr xdestroy_fn_t obtain_xdestroy_for(D, P* = nullptr) { static_assert(polyfill::always_false_v, "A function pointer, which is not of type xdestroy_fn_t, is prohibited."); return nullptr; } - template, bool> = true> - constexpr xdestroy_fn_t obtain_xdestroy_for(D, P*) noexcept { + template, bool> = true> + constexpr xdestroy_fn_t obtain_xdestroy_for(D, P* = nullptr) noexcept { return internal::xdestroy_proxy; } - template, bool> = true> - constexpr xdestroy_fn_t obtain_xdestroy_for(D d, P*) noexcept { + template, bool> = true> + constexpr xdestroy_fn_t obtain_xdestroy_for(D d, P* = nullptr) noexcept { return d; } #endif diff --git a/include/sqlite_orm/sqlite_orm.h b/include/sqlite_orm/sqlite_orm.h index f73528b6d..a1ae6f586 100644 --- a/include/sqlite_orm/sqlite_orm.h +++ b/include/sqlite_orm/sqlite_orm.h @@ -8285,8 +8285,8 @@ namespace sqlite_orm { * * Explicitly declared for better error messages. */ - template - constexpr xdestroy_fn_t obtain_xdestroy_for(D, P*) noexcept + template + constexpr xdestroy_fn_t obtain_xdestroy_for(D, P* = nullptr) noexcept requires(internal::is_unusable_for_xdestroy) { static_assert(polyfill::always_false_v, @@ -8306,8 +8306,8 @@ namespace sqlite_orm { * Type-safety is garanteed by checking whether the deleter or yielded function pointer * is invocable with the non-q-qualified pointer value. */ - template - constexpr xdestroy_fn_t obtain_xdestroy_for(D, P*) noexcept + template + constexpr xdestroy_fn_t obtain_xdestroy_for(D, P* = nullptr) noexcept requires(internal::needs_xdestroy_proxy) { return internal::xdestroy_proxy; @@ -8327,27 +8327,27 @@ namespace sqlite_orm { * Type-safety is garanteed by checking whether the deleter or yielded function pointer * is invocable with the non-q-qualified pointer value. */ - template - constexpr xdestroy_fn_t obtain_xdestroy_for(D d, P*) noexcept + template + constexpr xdestroy_fn_t obtain_xdestroy_for(D d, P* = nullptr) noexcept requires(internal::yields_xdestroy) { return d; } #else - template, bool> = true> - constexpr xdestroy_fn_t obtain_xdestroy_for(D, P*) { + template, bool> = true> + constexpr xdestroy_fn_t obtain_xdestroy_for(D, P* = nullptr) { static_assert(polyfill::always_false_v, "A function pointer, which is not of type xdestroy_fn_t, is prohibited."); return nullptr; } - template, bool> = true> - constexpr xdestroy_fn_t obtain_xdestroy_for(D, P*) noexcept { + template, bool> = true> + constexpr xdestroy_fn_t obtain_xdestroy_for(D, P* = nullptr) noexcept { return internal::xdestroy_proxy; } - template, bool> = true> - constexpr xdestroy_fn_t obtain_xdestroy_for(D d, P*) noexcept { + template, bool> = true> + constexpr xdestroy_fn_t obtain_xdestroy_for(D d, P* = nullptr) noexcept { return d; } #endif @@ -10904,49 +10904,44 @@ namespace sqlite_orm { namespace internal { struct udf_proxy_base { - using func_call = - std::function; - using final_call = std::function; + using func_call_fn_t = void (*)(void* udfHandle, + sqlite3_context* context, + int argsCount, + sqlite3_value** values); + using final_call_fn_t = void (*)(void* udfHandle, sqlite3_context* context); std::string name; int argumentsCount = 0; std::function create; xdestroy_fn_t destroy = nullptr; - - udf_proxy_base(decltype(name) name_, - decltype(argumentsCount) argumentsCount_, - decltype(create) create_, - decltype(destroy) destroy_) : - name(std::move(name_)), - argumentsCount(argumentsCount_), create(std::move(create_)), destroy(destroy_) {} - - virtual ~udf_proxy_base() = default; + func_call_fn_t func = nullptr; + final_call_fn_t finalAggregateCall = nullptr; + + udf_proxy_base(std::string name, + int argumentsCount, + std::function create, + xdestroy_fn_t destroy, + func_call_fn_t run) : + name(std::move(name)), + argumentsCount(argumentsCount), create(std::move(create)), destroy(destroy), func(run) {} + + udf_proxy_base(std::string name, + int argumentsCount, + std::function create, + xdestroy_fn_t destroy, + func_call_fn_t step, + final_call_fn_t finalCall) : + name(std::move(name)), + argumentsCount(argumentsCount), create(std::move(create)), destroy(destroy), func(step), + finalAggregateCall(finalCall) {} }; struct scalar_udf_proxy : udf_proxy_base { - func_call run; - - scalar_udf_proxy(decltype(name) name_, - int argumentsCount_, - decltype(create) create_, - decltype(run) run_, - decltype(destroy) destroy_) : - udf_proxy_base{std::move(name_), argumentsCount_, std::move(create_), destroy_}, - run(std::move(run_)) {} + using udf_proxy_base::udf_proxy_base; }; struct aggregate_udf_proxy : udf_proxy_base { - func_call step; - final_call finalCall; - - aggregate_udf_proxy(decltype(name) name_, - int argumentsCount_, - decltype(create) create_, - decltype(step) step_, - decltype(finalCall) finalCall_, - decltype(destroy) destroy_) : - udf_proxy_base{std::move(name_), argumentsCount_, std::move(create_), destroy_}, - step(std::move(step_)), finalCall(std::move(finalCall_)) {} + using udf_proxy_base::udf_proxy_base; }; template @@ -14841,6 +14836,8 @@ namespace sqlite_orm { // #include "util.h" +// #include "xdestroy_handling.h" + // #include "serializing_util.h" namespace sqlite_orm { @@ -15085,15 +15082,16 @@ namespace sqlite_orm { []() -> void* { return new F(); }, + /* destroy = */ + obtain_xdestroy_for(std::default_delete{}), /* call = */ - [](sqlite3_context* context, void* udfHandle, int argsCount, sqlite3_value** values) { + [](void* udfHandle, sqlite3_context* context, int argsCount, sqlite3_value** values) { F& udf = *static_cast(udfHandle); args_tuple argsTuple; values_to_tuple{}(values, argsTuple, argsCount); auto result = call(udf, std::move(argsTuple)); statement_binder().result(context, result); - }, - delete_function_callback)); + })); if(this->connection->retain_count() > 0) { sqlite3* db = this->connection->get(); @@ -15152,20 +15150,21 @@ namespace sqlite_orm { []() -> void* { return new F(); }, + /* destroy = */ + obtain_xdestroy_for(std::default_delete{}), /* step = */ - [](sqlite3_context*, void* udfHandle, int argsCount, sqlite3_value** values) { + [](void* udfHandle, sqlite3_context*, int argsCount, sqlite3_value** values) { F& udf = *static_cast(udfHandle); args_tuple argsTuple; values_to_tuple{}(values, argsTuple, argsCount); call(udf, &F::step, std::move(argsTuple)); }, /* finalCall = */ - [](sqlite3_context* context, void* udfHandle) { + [](void* udfHandle, sqlite3_context* context) { F& udf = *static_cast(udfHandle); auto result = udf.fin(); statement_binder().result(context, result); - }, - delete_function_callback)); + })); if(this->connection->retain_count() > 0) { sqlite3* db = this->connection->get(); @@ -15547,7 +15546,7 @@ namespace sqlite_orm { } udfHandle = udfProxy->create(); } - udfProxy->step(context, udfHandle, argsCount, values); + udfProxy->func(udfHandle, context, argsCount, values); } static void aggregate_function_final_callback(sqlite3_context* context) { @@ -15559,23 +15558,17 @@ namespace sqlite_orm { if(udfHandle == nullptr) { udfHandle = udfProxy->create(); } - udfProxy->finalCall(context, udfHandle); + udfProxy->finalAggregateCall(udfHandle, context); udfProxy->destroy(udfHandle); } static void scalar_function_callback(sqlite3_context* context, int argsCount, sqlite3_value** values) { - auto udfProxy = static_cast(sqlite3_user_data(context)); + auto* udfProxy = static_cast(sqlite3_user_data(context)); if(udfProxy->argumentsCount != -1 && udfProxy->argumentsCount != argsCount) { throw std::system_error{orm_error_code::arguments_count_does_not_match}; } - const std::unique_ptr udfHandle(udfProxy->create(), udfProxy->destroy); - udfProxy->run(context, udfHandle.get(), argsCount, values); - } - - template - static void delete_function_callback(void* udfHandle) { - F* udf = static_cast(udfHandle); - delete udf; + const std::unique_ptr udfHandle{udfProxy->create(), udfProxy->destroy}; + udfProxy->func(udfHandle.get(), context, argsCount, values); } std::string current_time(sqlite3* db) { diff --git a/tests/static_tests/function_static_tests.cpp b/tests/static_tests/function_static_tests.cpp index f29092cb4..7cd8e0122 100644 --- a/tests/static_tests/function_static_tests.cpp +++ b/tests/static_tests/function_static_tests.cpp @@ -268,5 +268,9 @@ TEST_CASE("function static") { STATIC_REQUIRE(storage_aggregate_callable); STATIC_REQUIRE_FALSE(storage_scalar_callable); #endif + + // must be veneers (no additional data members) + STATIC_REQUIRE(sizeof(internal::scalar_udf_proxy) == sizeof(internal::udf_proxy_base)); + STATIC_REQUIRE(sizeof(internal::aggregate_udf_proxy) == sizeof(internal::udf_proxy_base)); } }