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..4d9e4f200 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); } /** @@ -65,7 +64,7 @@ namespace sqlite_orm { */ template requires(!orm_recordset_alias) - constexpr internal::table_reference column() { + consteval internal::table_reference column() { return {}; } @@ -74,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/function.h b/dev/function.h index 8ffb8f775..67a593214 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,50 +22,45 @@ namespace sqlite_orm { namespace internal { - struct user_defined_function_base { - using func_call = std::function< - void(sqlite3_context* context, void* functionPointer, int argsCount, sqlite3_value** values)>; - using final_call = std::function; + struct udf_proxy_base { + 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; - 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_) : - name(std::move(name_)), - argumentsCount(argumentsCount_), create(std::move(create_)), destroy(destroy_) {} -#endif + std::function create; + xdestroy_fn_t destroy = nullptr; + 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 user_defined_scalar_function_t : user_defined_function_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_}, - run(std::move(run_)) {} + struct scalar_udf_proxy : udf_proxy_base { + using udf_proxy_base::udf_proxy_base; }; - struct user_defined_aggregate_function_t : user_defined_function_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_}, - step(std::move(step_)), finalCall(std::move(finalCall_)) {} + struct aggregate_udf_proxy : udf_proxy_base { + using udf_proxy_base::udf_proxy_base; }; template @@ -78,15 +73,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 +109,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 +123,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 +217,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/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/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/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/dev/storage_base.h b/dev/storage_base.h index 4ce2be4c4..9f935cefc 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" @@ -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 { @@ -251,7 +252,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 must be a scalar function"); std::stringstream ss; ss << F::name() << std::flush; @@ -261,30 +262,37 @@ 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()); + /* create = */ + []() -> void* { + return new F(); }, + /* destroy = */ + obtain_xdestroy_for(std::default_delete{}), /* call = */ - [](sqlite3_context* context, void* functionVoidPointer, int argsCount, sqlite3_value** values) { - auto& function = *static_cast(functionVoidPointer); + [](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(function, std::move(argsTuple)); + 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(); - 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 +320,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 must be an aggregate function"); std::stringstream ss; ss << F::name() << std::flush; @@ -322,59 +330,78 @@ 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 = */ - []() -> int* { - return (int*)(new F()); + []() -> void* { + return new F(); }, + /* destroy = */ + obtain_xdestroy_for(std::default_delete{}), /* step = */ - [](sqlite3_context*, void* functionVoidPointer, int argsCount, sqlite3_value** values) { - auto& function = *static_cast(functionVoidPointer); + [](void* udfHandle, sqlite3_context*, int argsCount, sqlite3_value** values) { + 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* functionVoidPointer) { - auto& function = *static_cast(functionVoidPointer); - auto result = function.fin(); + [](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(); - 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) { @@ -387,26 +414,28 @@ 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 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); } } + + if(!functionExists) { + collatingFunctions.erase(name); + } } template @@ -605,9 +634,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); } } @@ -620,12 +648,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,100 +662,100 @@ 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>& 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(); - 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(), + (*it)->argumentsCount, + SQLITE_UTF8, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr); + if(rc != SQLITE_OK) { throw_translated_sqlite_error(db); } } + it = functions.erase(it); } else { throw std::system_error{orm_error_code::function_not_found}; } } - void try_to_create_function(sqlite3* db, user_defined_scalar_function_t& function) { - auto resultCode = sqlite3_create_function_v2(db, - function.name.c_str(), - function.argumentsCount, - SQLITE_UTF8, - &function, - scalar_function_callback, - nullptr, - nullptr, - nullptr); - if(resultCode != SQLITE_OK) { + 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, + 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, user_defined_aggregate_function_t& function) { - auto resultCode = sqlite3_create_function(db, - function.name.c_str(), - function.argumentsCount, - SQLITE_UTF8, - &function, - nullptr, - aggregate_function_step_callback, - aggregate_function_final_callback); - if(resultCode != SQLITE_OK) { - throw_translated_sqlite_error(resultCode); + static void try_to_create_function(sqlite3* db, aggregate_udf_proxy& udfProxy) { + 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); } } 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 aggregateContextVoidPointer = sqlite3_aggregate_context(context, sizeof(int**)); - auto aggregateContextIntPointer = static_cast(aggregateContextVoidPointer); - if(*aggregateContextIntPointer == nullptr) { - *aggregateContextIntPointer = functionPointer->create(); + 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) { + if(udfProxy->argumentsCount != -1 && udfProxy->argumentsCount != argsCount) { + throw std::system_error{orm_error_code::arguments_count_does_not_match}; + } + udfHandle = udfProxy->create(); } - functionPointer->step(context, *aggregateContextIntPointer, argsCount, values); + udfProxy->func(udfHandle, context, argsCount, values); } static void aggregate_function_final_callback(sqlite3_context* context) { - auto functionVoidPointer = sqlite3_user_data(context); - auto functionPointer = static_cast(functionVoidPointer); - auto aggregateContextVoidPointer = sqlite3_aggregate_context(context, sizeof(int**)); - auto aggregateContextIntPointer = static_cast(aggregateContextVoidPointer); - functionPointer->finalCall(context, *aggregateContextIntPointer); - functionPointer->destroy(*aggregateContextIntPointer); + 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->finalAggregateCall(udfHandle, context); + udfProxy->destroy(udfHandle); } 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)); + 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); - } - - template - static void delete_function_callback(int* pointer) { - auto voidPointer = static_cast(pointer); - auto fPointer = static_cast(voidPointer); - delete fPointer; + const std::unique_ptr udfHandle{udfProxy->create(), udfProxy->destroy}; + udfProxy->func(udfHandle.get(), context, argsCount, values); } std::string current_time(sqlite3* db) { @@ -778,13 +806,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 = @@ -814,8 +846,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/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/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/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..a1ae6f586 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); } /** @@ -3219,7 +3226,7 @@ namespace sqlite_orm { */ template requires(!orm_recordset_alias) - constexpr internal::table_reference column() { + consteval internal::table_reference column() { return {}; } @@ -3228,7 +3235,7 @@ namespace sqlite_orm { */ template requires(!orm_recordset_alias) - constexpr internal::table_reference c() { + consteval internal::table_reference c() { return {}; } #endif @@ -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 @@ -8278,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, @@ -8299,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; @@ -8320,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 @@ -8853,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 @@ -10036,6 +10040,8 @@ namespace sqlite_orm { // #include "../type_traits.h" +// #include "../alias_traits.h" + // #include "../constraints.h" // #include "../table_info.h" @@ -10427,6 +10433,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)}); @@ -10863,9 +10881,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,50 +10903,45 @@ namespace sqlite_orm { namespace internal { - struct user_defined_function_base { - using func_call = std::function< - void(sqlite3_context* context, void* functionPointer, int argsCount, sqlite3_value** values)>; - using final_call = std::function; + struct udf_proxy_base { + 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; - 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_) : - name(std::move(name_)), - argumentsCount(argumentsCount_), create(std::move(create_)), destroy(destroy_) {} -#endif - }; + std::function create; + xdestroy_fn_t destroy = nullptr; + func_call_fn_t func = nullptr; + final_call_fn_t finalAggregateCall = nullptr; - struct user_defined_scalar_function_t : user_defined_function_base { - func_call run; + 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) {} - 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_}, - run(std::move(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 user_defined_aggregate_function_t : user_defined_function_base { - func_call step; - final_call finalCall; + struct scalar_udf_proxy : udf_proxy_base { + using udf_proxy_base::udf_proxy_base; + }; - 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_}, - step(std::move(step_)), finalCall(std::move(finalCall_)) {} + struct aggregate_udf_proxy : udf_proxy_base { + using udf_proxy_base::udf_proxy_base; }; template @@ -10941,15 +10954,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 +10990,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 +11004,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 +11098,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" @@ -13607,7 +13674,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 @@ -14750,13 +14817,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 { @@ -14771,6 +14836,8 @@ namespace sqlite_orm { // #include "util.h" +// #include "xdestroy_handling.h" + // #include "serializing_util.h" namespace sqlite_orm { @@ -14998,7 +15065,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 must be a scalar function"); std::stringstream ss; ss << F::name() << std::flush; @@ -15008,30 +15075,37 @@ 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()); + /* create = */ + []() -> void* { + return new F(); }, + /* destroy = */ + obtain_xdestroy_for(std::default_delete{}), /* call = */ - [](sqlite3_context* context, void* functionVoidPointer, int argsCount, sqlite3_value** values) { - auto& function = *static_cast(functionVoidPointer); + [](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(function, std::move(argsTuple)); + 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(); - 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 +15133,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 must be an aggregate function"); std::stringstream ss; ss << F::name() << std::flush; @@ -15069,59 +15143,78 @@ 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 = */ - []() -> int* { - return (int*)(new F()); + []() -> void* { + return new F(); }, + /* destroy = */ + obtain_xdestroy_for(std::default_delete{}), /* step = */ - [](sqlite3_context*, void* functionVoidPointer, int argsCount, sqlite3_value** values) { - auto& function = *static_cast(functionVoidPointer); + [](void* udfHandle, sqlite3_context*, int argsCount, sqlite3_value** values) { + 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* functionVoidPointer) { - auto& function = *static_cast(functionVoidPointer); - auto result = function.fin(); + [](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(); - 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) { @@ -15134,26 +15227,28 @@ 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 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); } } + + if(!functionExists) { + collatingFunctions.erase(name); + } } template @@ -15352,9 +15447,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); } } @@ -15367,12 +15461,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,100 +15475,100 @@ 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>& 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(); - 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(), + (*it)->argumentsCount, + SQLITE_UTF8, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr); + if(rc != SQLITE_OK) { throw_translated_sqlite_error(db); } } + it = functions.erase(it); } else { throw std::system_error{orm_error_code::function_not_found}; } } - void try_to_create_function(sqlite3* db, user_defined_scalar_function_t& function) { - auto resultCode = sqlite3_create_function_v2(db, - function.name.c_str(), - function.argumentsCount, - SQLITE_UTF8, - &function, - scalar_function_callback, - nullptr, - nullptr, - nullptr); - if(resultCode != SQLITE_OK) { + 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, + 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, user_defined_aggregate_function_t& function) { - auto resultCode = sqlite3_create_function(db, - function.name.c_str(), - function.argumentsCount, - SQLITE_UTF8, - &function, - nullptr, - aggregate_function_step_callback, - aggregate_function_final_callback); - if(resultCode != SQLITE_OK) { - throw_translated_sqlite_error(resultCode); + static void try_to_create_function(sqlite3* db, aggregate_udf_proxy& udfProxy) { + 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); } } 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 aggregateContextVoidPointer = sqlite3_aggregate_context(context, sizeof(int**)); - auto aggregateContextIntPointer = static_cast(aggregateContextVoidPointer); - if(*aggregateContextIntPointer == nullptr) { - *aggregateContextIntPointer = functionPointer->create(); + 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) { + if(udfProxy->argumentsCount != -1 && udfProxy->argumentsCount != argsCount) { + throw std::system_error{orm_error_code::arguments_count_does_not_match}; + } + udfHandle = udfProxy->create(); } - functionPointer->step(context, *aggregateContextIntPointer, argsCount, values); + udfProxy->func(udfHandle, context, argsCount, values); } static void aggregate_function_final_callback(sqlite3_context* context) { - auto functionVoidPointer = sqlite3_user_data(context); - auto functionPointer = static_cast(functionVoidPointer); - auto aggregateContextVoidPointer = sqlite3_aggregate_context(context, sizeof(int**)); - auto aggregateContextIntPointer = static_cast(aggregateContextVoidPointer); - functionPointer->finalCall(context, *aggregateContextIntPointer); - functionPointer->destroy(*aggregateContextIntPointer); + 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->finalAggregateCall(udfHandle, context); + udfProxy->destroy(udfHandle); } 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)); + 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); - } - - template - static void delete_function_callback(int* pointer) { - auto voidPointer = static_cast(pointer); - auto fPointer = static_cast(voidPointer); - delete fPointer; + const std::unique_ptr udfHandle{udfProxy->create(), udfProxy->destroy}; + udfProxy->func(udfHandle.get(), context, argsCount, values); } std::string current_time(sqlite3* db) { @@ -15525,13 +15619,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 = @@ -15561,8 +15659,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; }; } } @@ -18468,9 +18566,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 @@ -18533,7 +18631,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/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_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 +} 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..7cd8e0122 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,40 @@ 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 + + // 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)); + } } 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; }