Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 11 additions & 4 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,18 @@ set(PACKAGE_VERSION ${sqlite_orm_VERSION})
project("sqlite_orm" VERSION ${PACKAGE_VERSION})

# Handling C++ standard version to use
if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.25)
option(SQLITE_ORM_ENABLE_CXX_26 "Enable C++ 26" OFF)
endif()
option(SQLITE_ORM_ENABLE_CXX_23 "Enable C++ 23" OFF)
option(SQLITE_ORM_ENABLE_CXX_20 "Enable C++ 20" OFF)
option(SQLITE_ORM_ENABLE_CXX_17 "Enable C++ 17" OFF)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
if(SQLITE_ORM_ENABLE_CXX_23)

if(SQLITE_ORM_ENABLE_CXX_26)
set(CMAKE_CXX_STANDARD 26)
message(STATUS "SQLITE_ORM: Build with C++26 features")
elseif(SQLITE_ORM_ENABLE_CXX_23)
set(CMAKE_CXX_STANDARD 23)
message(STATUS "SQLITE_ORM: Build with C++23 features")
elseif(SQLITE_ORM_ENABLE_CXX_20)
Expand All @@ -25,9 +32,9 @@ elseif(SQLITE_ORM_ENABLE_CXX_17)
set(CMAKE_CXX_STANDARD 17)
message(STATUS "SQLITE_ORM: Build with C++17 features")
else()
# fallback to C++14 if there is no special instruction
set(CMAKE_CXX_STANDARD 14)
message(STATUS "SQLITE_ORM: Build with C++14 features")
# fallback to C++17 if there is no special instruction
set(CMAKE_CXX_STANDARD 17)
message(STATUS "SQLITE_ORM: Build with C++17 features")
endif()
set(CMAKE_CXX_EXTENSIONS OFF)

Expand Down
6 changes: 3 additions & 3 deletions appveyor.yml
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ for:
install:
- |-
cd C:\Tools\vcpkg
git fetch --tags && git checkout 2025.03.19
git fetch --tags && git checkout 2025.04.09
cd %APPVEYOR_BUILD_FOLDER%
C:\Tools\vcpkg\bootstrap-vcpkg.bat -disableMetrics
C:\Tools\vcpkg\vcpkg integrate install
Expand Down Expand Up @@ -142,7 +142,7 @@ for:
install:
- |-
pushd $HOME/vcpkg
git fetch --tags && git checkout 2025.03.19
git fetch --tags && git checkout 2025.04.09
popd
$HOME/vcpkg/bootstrap-vcpkg.sh -disableMetrics
$HOME/vcpkg/vcpkg integrate install --overlay-triplets=vcpkg/triplets
Expand Down Expand Up @@ -170,7 +170,7 @@ for:
# using custom vcpkg triplets for building and linking dynamic dependent libraries
install:
- |-
git clone --depth 1 --branch 2025.03.19 https://github.com/microsoft/vcpkg.git $HOME/vcpkg
git clone --depth 1 --branch 2025.04.09 https://github.com/microsoft/vcpkg.git $HOME/vcpkg
$HOME/vcpkg/bootstrap-vcpkg.sh -disableMetrics
$HOME/vcpkg/vcpkg integrate install --overlay-triplets=vcpkg/triplets
vcpkg install sqlite3[core,dbstat,math,json1,fts5,soundex] catch2 --overlay-triplets=vcpkg/triplets
Expand Down
4 changes: 2 additions & 2 deletions dev/arg_values.h
Original file line number Diff line number Diff line change
Expand Up @@ -111,9 +111,9 @@ SQLITE_ORM_EXPORT namespace sqlite_orm {
mutable arg_value currentValue;
};

arg_values() : arg_values(0, nullptr) {}
arg_values() = default;

arg_values(int argsCount_, sqlite3_value** values_) : argsCount(argsCount_), values(values_) {}
arg_values(int nValues, sqlite3_value** values) : argsCount(nValues), values(values) {}

size_t size() const {
return this->argsCount;
Expand Down
132 changes: 87 additions & 45 deletions dev/function.h
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
#pragma once

#ifndef SQLITE_ORM_IMPORT_STD_MODULE
#include <type_traits> // std::enable_if, std::is_member_function_pointer, std::is_function, std::remove_const, std::decay, std::is_convertible, std::is_same, std::false_type, std::true_type
#include <type_traits> // std::enable_if, std::is_member_function_pointer, std::is_function, std::remove_const, std::decay, std::is_convertible, std::is_same, std::false_type, std::true_type, std::is_pointer
#ifdef SQLITE_ORM_CPP20_CONCEPTS_SUPPORTED
#include <concepts> // std::copy_constructible
#endif
#include <tuple> // std::tuple, std::tuple_size, std::tuple_element
#include <functional> // std::bind_front
#include <algorithm> // std::min, std::copy_n
#include <utility> // std::move, std::forward
#endif
Expand Down Expand Up @@ -130,7 +131,7 @@ SQLITE_ORM_EXPORT namespace sqlite_orm {
template<class Q>
concept orm_quoted_scalar_function = requires(const Q& quotedF) {
quotedF.name();
quotedF.callable();
quotedF._callable();
};
#endif
}
Expand Down Expand Up @@ -165,6 +166,17 @@ namespace sqlite_orm {
struct callable_arguments : callable_arguments_impl<F> {};

#ifdef SQLITE_ORM_WITH_CPP20_ALIASES
template<orm_function_sig Sig, class F>
using overloaded_callop_t = decltype(static_cast<Sig F::*>(&F::operator()));

template<orm_function_sig Sig, class F>
using overloaded_static_callop_t =
#ifdef SQLITE_ORM_WITH_CPP20_ALIASES
decltype(static_cast<Sig*>(&F::operator()));
#else
std::enable_if_t<polyfill::always_false_v<Sig>, void>;
#endif

/*
* Bundle of type and name of a quoted user-defined function.
*/
Expand Down Expand Up @@ -339,29 +351,29 @@ namespace sqlite_orm {
*
* Use the variable template `func<>` to instantiate.
*
* Calling the function captures the parameters in a `function_call` node.
* Calling the generator captures the parameters in a `function_call` expression.
*/
template<class UDF>
struct function {
using udf_type = UDF;
using callable_type = UDF;

/*
* Generates the SQL function call.
* Generates the SQL function call expression.
*/
template<typename... CallArgs>
function_call<UDF, CallArgs...> operator()(CallArgs... callArgs) const {
check_function_call<UDF, CallArgs...>();
return {this->udf_holder(), {std::forward<CallArgs>(callArgs)...}};
}

constexpr auto udf_holder() const {
return internal::udf_holder<UDF>{};
return {_udf_holder(), {std::forward<CallArgs>(callArgs)...}};
}

// returns a character range
constexpr auto name() const {
return this->udf_holder()();
return _udf_holder()();
}

constexpr auto _udf_holder() const {
return internal::udf_holder<UDF>{};
}
};

Expand All @@ -370,59 +382,82 @@ namespace sqlite_orm {
* Generator of a user-defined function call in a sql query expression.
*
* Use the string literal operator template `""_scalar.quote()` to quote
* a freestanding function, stateless lambda or function object.
* a freestanding function, lambda or function object.
*
* Calling the function captures the parameters in a `function_call` node.
* Calling the generator captures the parameters in a `function_call` expression.
*
* Internal note:
* 1. Captures and represents a function [pointer or object], especially one without side effects.
* If `F` is a stateless function object, `quoted_scalar_function::callable()` returns the original function object,
* otherwise it is assumed to have possibe side-effects and `quoted_scalar_function::callable()` returns a copy.
* 2. The nested `udf_type` typename is deliberately chosen to be the function signature,
* and will be the abstracted version of the user-defined function.
* Internal notes:
* 1. The nested `udf_type` typename is deliberately chosen to be the function signature,
* and will be the abstracted version of the specified quoted function.
*/
template<class F, class Sig, size_t N>
struct quoted_scalar_function {
using udf_type = Sig;
using callable_type = F;

/*
* Generates the SQL function call.
* Generates the SQL function call expression.
*/
template<typename... CallArgs>
function_call<udf_type, CallArgs...> operator()(CallArgs... callArgs) const {
check_function_call<udf_type, CallArgs...>();
return {this->udf_holder(), {std::forward<CallArgs>(callArgs)...}};
return {_udf_holder(), {std::forward<CallArgs>(callArgs)...}};
}

/*
* Return original `udf` if stateless or a copy of it otherwise
constexpr auto name() const {
return _nme;
}

/*
* Make the final callable that will be used to apply the user-defined function:
* - if `F` is a pointer to a freestanding function, return it as is
* - if `F` is a static call operator, return a pointer to it;
* the function signature is used to pick the function object's static call operator
* - if `F` is a stateless functor, bind a reference to the original function object its call operator;
* the function signature is used to pick the function object's call operator
* - if `F` is a stateful functor, bind a copy of the original function object to its call operator;
* the function signature is used to pick the function object's call operator
*/
constexpr decltype(auto) callable() const {
if constexpr (stateless<F>) {
return (this->udf);
} else {
constexpr auto _callable() const {
// function pointer
if constexpr (std::is_pointer_v<F>) {
return _udf;
}
// static call operator
else if constexpr (polyfill::is_detected_v<internal::overloaded_static_callop_t, Sig, F>) {
return static_cast<Sig*>(&F::operator());
}
// stateless functor
else if constexpr (stateless<F>) {
#if __cpp_lib_bind_front >= 202306L // NTTP callables
return std::bind_front<static_cast<Sig F::*>(&F::operator())>(std::ref(_udf));
#else
return std::bind_front(static_cast<Sig F::*>(&F::operator()), std::ref(_udf));
#endif
}
// stateful functor
else {
// non-const copy
return F(this->udf);
#if __cpp_lib_bind_front >= 202306L // NTTP callables
return std::bind_front<static_cast<Sig F::*>(&F::operator())>(_udf);
#else
return std::bind_front(static_cast<Sig F::*>(&F::operator()), _udf);
#endif
}
}

constexpr auto udf_holder() const {
constexpr auto _udf_holder() const {
return internal::udf_holder<udf_type>{this->name()};
}

constexpr auto name() const {
return this->nme;
}

template<class... Args>
consteval quoted_scalar_function(const char (&name)[N], Args&&... constructorArgs) :
udf(std::forward<Args>(constructorArgs)...) {
std::copy_n(name, N, this->nme);
_udf(std::forward<Args>(constructorArgs)...) {
std::copy_n(name, N, _nme);
}

F udf;
char nme[N];
F _udf;
char _nme[N];
};

template<size_t N>
Expand All @@ -440,21 +475,24 @@ namespace sqlite_orm {
/*
* From a classic function object instance.
*/
template<class F>
requires (orm_classic_function_object<F> && (stateless<F> || std::copy_constructible<F>))
template<orm_classic_function_object F>
requires (stateless<F> || std::copy_constructible<F>)
[[nodiscard]] consteval auto quote(F callable) const {
using Sig = function_signature_type_t<decltype(&F::operator())>;
// detect whether overloaded call operator can be picked using `Sig`
return quoted_scalar_function<F, Sig, N>{this->cstr, std::move(callable)};
}

/*
* From a function object instance, picking the overloaded call operator.
*/
template<orm_function_sig Sig, class F>
requires ((stateless<F> || std::copy_constructible<F>))
requires (stateless<F> || std::copy_constructible<F>)
[[nodiscard]] consteval auto quote(F callable) const {
// detect whether overloaded call operator can be picked using `Sig`
static_assert(polyfill::is_detected_v<overloaded_callop_t, Sig, F> ||
polyfill::is_detected_v<overloaded_static_callop_t, Sig, F>,
"No call operator with the specified signature available; have you overlooked the method "
"qualifiers?");
return quoted_scalar_function<F, Sig, N>{this->cstr, std::move(callable)};
}

Expand All @@ -472,9 +510,13 @@ namespace sqlite_orm {
* From a function object type, picking the overloaded call operator.
*/
template<orm_function_sig Sig, class F, class... Args>
requires ((stateless<F> || std::copy_constructible<F>))
requires (stateless<F> || std::copy_constructible<F>)
[[nodiscard]] consteval auto quote(Args&&... constructorArgs) const {
// detect whether overloaded call operator can be picked using `Sig`
static_assert(polyfill::is_detected_v<overloaded_callop_t, Sig, F> ||
polyfill::is_detected_v<overloaded_static_callop_t, Sig, F>,
"No call operator with the specified signature available; have you overlooked the method "
"qualifiers?");
return quoted_scalar_function<F, Sig, N>{this->cstr, std::forward<Args>(constructorArgs)...};
}
};
Expand All @@ -483,7 +525,7 @@ namespace sqlite_orm {
}

SQLITE_ORM_EXPORT namespace sqlite_orm {
/** @short Call a user-defined function.
/** @short Define a user-defined function.
*
* Note: Currently the number of call arguments is checked and whether the types of pointer values match,
* but other call argument types are not checked against the parameter types of the function.
Expand All @@ -505,17 +547,17 @@ SQLITE_ORM_EXPORT namespace sqlite_orm {

#ifdef SQLITE_ORM_WITH_CPP20_ALIASES
inline namespace literals {
/* @short Create a scalar function from a freestanding function, stateless lambda or function object,
/* @short Create a scalar function from a freestanding function, lambda or function object,
* and call such a user-defined function.
*
* If you need to pick a function or method from an overload set, or pick a template function you can
* specify an explicit function signature in the call to `from()`.
* specify an explicit function signature in the call to `quote()`.
*
* Examples:
* // freestanding function from a library
* constexpr orm_quoted_scalar_function auto clamp_int_f = "clamp_int"_scalar.quote(std::clamp<int>);
* // stateless lambda
* constexpr orm_quoted_scalar_function auto is_fatal_error_f = "IS_FATAL_ERROR"_scalar.quote([](unsigned long errcode) {
* constexpr orm_quoted_scalar_function auto is_fatal_error_f = "IS_FATAL_ERROR"_scalar.quote([](unsigned long errcode) static {
* return errcode != 0;
* });
* // function object instance
Expand Down
4 changes: 4 additions & 0 deletions dev/functional/cxx_core_features.h
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,10 @@
#define SQLITE_ORM_STRUCTURED_BINDING_PACK_SUPPORTED
#endif

#if __cpp_contracts >= 202502L
#define SQLITE_ORM_CONTRACTS_SUPPORTED
#endif

#if __cplusplus >= 202002L
#define SQLITE_ORM_DEFAULT_COMPARISONS_SUPPORTED
#define SQLITE_ORM_INITSTMT_RANGE_BASED_FOR_SUPPORTED
Expand Down
8 changes: 4 additions & 4 deletions dev/pragma.h
Original file line number Diff line number Diff line change
Expand Up @@ -171,9 +171,9 @@ namespace sqlite_orm {
this->executor.perform_exec(
connection.get(),
sql,
[](void* data, int argc, orm_gsl::zstring* argv, orm_gsl::zstring*) -> int {
[](void* data, int /*argc*/, orm_gsl::zstring* argv, orm_gsl::zstring*) -> int {
auto& res = *(std::vector<sqlite_orm::table_xinfo>*)data;
if (argc) {
{
auto index = 0;
auto cid = atoi(argv[index++]);
std::string name = argv[index++];
Expand Down Expand Up @@ -212,9 +212,9 @@ namespace sqlite_orm {
this->executor.perform_exec(
connection.get(),
sql,
[](void* data, int argc, orm_gsl::zstring* argv, orm_gsl::zstring*) -> int {
[](void* data, int /*argc*/, orm_gsl::zstring* argv, orm_gsl::zstring*) -> int {
auto& res = *(std::vector<sqlite_orm::table_info>*)data;
if (argc) {
{
auto index = 0;
auto cid = atoi(argv[index++]);
std::string name = argv[index++];
Expand Down
6 changes: 6 additions & 0 deletions dev/row_extractor.h
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,12 @@ namespace sqlite_orm {
row_extractor<R> boxed_value_extractor() {
return {};
}

template<class T>
T extract_boxed_value(sqlite3_value* value) {
const auto rowExtractor = boxed_value_extractor<T>();
return rowExtractor.extract(value);
}
}
}

Expand Down
Loading