Skip to content

Commit

Permalink
Fine-grained compiler feature detection and conformance fallbacks
Browse files Browse the repository at this point in the history
This commit makes unit tests compile with Visual C++ 2015 Update 3 v14.0 [toolset msvc 14.0] and possibly other older compilers that don't fully support C++14 yet or have still incomplete template parsing support.
This commit most likely addresses #525, #809, #957

* static_if must be called with a value of type bool, not one convertible to bool
* Fallback for missing relaxed constexpr
* Provided aggregate constructors for compilers not supporting default member initializers
* Made carray pointer bindings only available when compiler supports inline variables
* Simplified SFINAE with dependent `decltype` expressions
* Fallback for Visual Studio 2017 v15.9 and earlier [toolset msvc 14.16] still having broken variadic template pack expansion
* Simplified tuple helpers
* Versions of `coalesce`, `ifnull` with implicit return type are only available with C++17
  • Loading branch information
trueqbit committed Apr 29, 2022
1 parent 72039b5 commit 7bb1a39
Show file tree
Hide file tree
Showing 68 changed files with 1,123 additions and 524 deletions.
13 changes: 11 additions & 2 deletions dev/carray.h
Original file line number Diff line number Diff line change
@@ -1,13 +1,21 @@
#pragma once

#include <type_traits>
/*
* Note: This feature needs constexpr variables with external linkage.
* which can be achieved before C++17's inline variables, but differs from compiler to compiler.
* Hence we make it only available for compilers supporting inline variables.
*/

#ifdef SQLITE_ORM_INLINE_VARIABLES_SUPPORTED
#include <type_traits> // std::integral_constant
#include <utility> // std::move

#include "start_macros.h"
#include "pointer_value.h"

namespace sqlite_orm {

SQLITE_ORM_INLINE_VAR constexpr const char carray_pvt_name[] = "carray";
inline constexpr const char carray_pvt_name[] = "carray";
using carray_pvt = std::integral_constant<const char*, carray_pvt_name>;

template<typename P>
Expand Down Expand Up @@ -68,3 +76,4 @@ namespace sqlite_orm {
}
};
}
#endif
16 changes: 6 additions & 10 deletions dev/column.h
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ namespace sqlite_orm {
iterate_tuple(this->constraints, [&res](auto& constraint) {
using constraint_type = std::decay_t<decltype(constraint)>;
if(!res) {
res = is_generated_always<constraint_type>::value;
res = is_generated_always_v<constraint_type>;
}
});
return res;
Expand Down Expand Up @@ -145,17 +145,13 @@ namespace sqlite_orm {

}

/**
* Column traits. Common case.
*/
template<class T>
struct is_column : public std::false_type {};

/**
* Column traits. Specialized case case.
*/
SQLITE_ORM_INLINE_VAR constexpr bool is_column_v = false;
template<class O, class T, class... Op>
struct is_column<column_t<O, T, Op...>> : public std::true_type {};
SQLITE_ORM_INLINE_VAR constexpr bool is_column_v<column_t<O, T, Op...>> = true;

template<class T>
using is_column = polyfill::bool_constant<is_column_v<T>>;

/**
* Column with insertable primary key traits.
Expand Down
4 changes: 4 additions & 0 deletions dev/conditions.h
Original file line number Diff line number Diff line change
Expand Up @@ -355,6 +355,10 @@ namespace sqlite_orm {

struct in_base {
bool negative = false; // used in not_in

#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED
in_base(bool negative) : negative{negative} {}
#endif
};

/**
Expand Down
41 changes: 22 additions & 19 deletions dev/constraints.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
#include <tuple> // std::tuple, std::make_tuple
#include <type_traits> // std::is_base_of, std::false_type, std::true_type

#include "start_macros.h"
#include "cxx_polyfill.h"
#include "collate_argument.h"
#include "error_code.h"
#include "table_type.h"
Expand Down Expand Up @@ -309,6 +311,9 @@ namespace sqlite_orm {

struct collate_constraint_t {
collate_argument argument = collate_argument::binary;
#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED
collate_constraint_t(collate_argument argument) : argument{argument} {}
#endif

operator std::string() const {
return "COLLATE " + this->string_from_collate_argument(this->argument);
Expand Down Expand Up @@ -343,6 +348,10 @@ namespace sqlite_orm {

bool full = true;
storage_type storage = storage_type::not_specified;

#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED
basic_generated_always(bool full, storage_type storage) : full{full}, storage{storage} {}
#endif
};

template<class T>
Expand All @@ -364,10 +373,12 @@ namespace sqlite_orm {
};

template<class T>
struct is_generated_always : std::false_type {};
SQLITE_ORM_INLINE_VAR constexpr bool is_generated_always_v = false;
template<class T>
SQLITE_ORM_INLINE_VAR constexpr bool is_generated_always_v<generated_always_t<T>> = true;

template<class T>
struct is_generated_always<generated_always_t<T>> : std::true_type {};
using is_generated_always = polyfill::bool_constant<is_generated_always_v<T>>;

template<class T>
struct is_constraint : std::false_type {};
Expand Down Expand Up @@ -481,29 +492,21 @@ namespace sqlite_orm {

namespace internal {

/**
* FOREIGN KEY traits. Common case
*/
template<class T>
struct is_foreign_key : std::false_type {};

/**
* FOREIGN KEY traits. Specialized case
*/
SQLITE_ORM_INLINE_VAR constexpr bool is_foreign_key_v = false;
template<class C, class R>
struct is_foreign_key<internal::foreign_key_t<C, R>> : std::true_type {};
SQLITE_ORM_INLINE_VAR constexpr bool is_foreign_key_v<foreign_key_t<C, R>> = true;

/**
* PRIMARY KEY traits. Common case
*/
template<class T>
struct is_primary_key : public std::false_type {};
using is_foreign_key = polyfill::bool_constant<is_foreign_key_v<T>>;

/**
* PRIMARY KEY traits. Specialized case
*/
template<class T>
SQLITE_ORM_INLINE_VAR constexpr bool is_primary_key_v = false;
template<class... Cs>
struct is_primary_key<internal::primary_key_t<Cs...>> : public std::true_type {};
SQLITE_ORM_INLINE_VAR constexpr bool is_primary_key_v<primary_key_t<Cs...>> = true;

template<class T>
using is_primary_key = polyfill::bool_constant<is_primary_key_v<T>>;

/**
* PRIMARY KEY INSERTABLE traits.
Expand Down
44 changes: 41 additions & 3 deletions dev/core_functions.h
Original file line number Diff line number Diff line change
Expand Up @@ -1696,11 +1696,22 @@ namespace sqlite_orm {
/**
* COALESCE(X,Y,...) function https://www.sqlite.org/lang_corefunc.html#coalesce
*/
template<class R, class... Args>
template<
class R,
class... Args
#ifdef SQLITE_ORM_SFINAE_FRIENDLY_COMMON_TYPE
,
std::enable_if_t<
polyfill::negation_v<
std::is_same<R, polyfill::detected_t<std::common_type_t, internal::field_type_or_type_t<Args>...>>>,
bool> = true
#endif
>
internal::built_in_function_t<R, internal::coalesce_string, Args...> coalesce(Args... args) {
return {std::make_tuple(std::forward<Args>(args)...)};
}

#ifdef SQLITE_ORM_SFINAE_FRIENDLY_COMMON_TYPE
/**
* COALESCE(X,Y,...) using common return type of X, Y, ...
*/
Expand All @@ -1711,15 +1722,29 @@ namespace sqlite_orm {
Args...> {
return {std::make_tuple(std::forward<Args>(args)...)};
}
#endif

/**
* IFNULL(X,Y) function https://www.sqlite.org/lang_corefunc.html#ifnull
*/
template<class R, class X, class Y>
template<
class R,
class X,
class Y
#ifdef SQLITE_ORM_SFINAE_FRIENDLY_COMMON_TYPE
,
std::enable_if_t<polyfill::negation_v<std::is_same<R,
polyfill::detected_t<std::common_type_t,
internal::field_type_or_type_t<X>,
internal::field_type_or_type_t<Y>>>>,
bool> = true
#endif
>
internal::built_in_function_t<R, internal::ifnull_string, X, Y> ifnull(X x, Y y) {
return {std::make_tuple(std::move(x), std::move(y))};
}

#ifdef SQLITE_ORM_SFINAE_FRIENDLY_COMMON_TYPE
/**
* IFNULL(X,Y) using common return type of X and Y
*/
Expand All @@ -1731,11 +1756,24 @@ namespace sqlite_orm {
Y> {
return {std::make_tuple(std::move(x), std::move(y))};
}
#endif

/**
* NULLIF(X,Y) function https://www.sqlite.org/lang_corefunc.html#nullif
*/
template<class R, class X, class Y>
template<
class R,
class X,
class Y
#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED
,
std::enable_if_t<polyfill::negation_v<std::is_same<R,
polyfill::detected_t<std::common_type_t,
internal::field_type_or_type_t<X>,
internal::field_type_or_type_t<Y>>>>,
bool> = true
#endif
>
internal::built_in_function_t<R, internal::nullif_string, X, Y> nullif(X x, Y y) {
return {std::make_tuple(std::move(x), std::move(y))};
}
Expand Down
16 changes: 16 additions & 0 deletions dev/cxx_core_features.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@
#define SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED
#endif

#if __cpp_constexpr >= 201304L
#define SQLITE_ORM_RELAXED_CONSTEXPR
#endif

#if __cpp_noexcept_function_type >= 201510L
#define SQLITE_ORM_NOTHROW_ALIASES_SUPPORTED
#endif
Expand All @@ -18,6 +22,10 @@
#define SQLITE_ORM_AGGREGATE_BASES_SUPPORTED
#endif

#if __cpp_inline_variables >= 201606L
#define SQLITE_ORM_INLINE_VARIABLES_SUPPORTED
#endif

#if __cpp_inline_variables >= 201606L
#define SQLITE_ORM_INLINE_VAR inline
#else
Expand Down Expand Up @@ -48,4 +56,12 @@
#if __has_include(<string_view>)
#define SQLITE_ORM_STRING_VIEW_SUPPORTED
#endif

// SFINAE friendly common_type, LWG 2408.
// Visual C++: since msvc 1911
#define SQLITE_ORM_SFINAE_FRIENDLY_COMMON_TYPE
#endif

#if defined(_MSC_VER) && (_MSC_VER < 1920)
#define SQLITE_ORM_BROKEN_VARIADIC_PACK_EXPANSION
#endif
8 changes: 4 additions & 4 deletions dev/cxx_polyfill.h
Original file line number Diff line number Diff line change
Expand Up @@ -107,8 +107,10 @@ namespace sqlite_orm {
SQLITE_ORM_INLINE_VAR constexpr bool is_detected_v = is_detected<Op, Args...>::value;
#endif

#if 1 // proposed but not pursued \
// is_specialization_of: https://github.com/cplusplus/papers/issues/812
#if 0 // proposed but not pursued
using std::is_specialization_of, std::is_specialization_of_t, std::is_specialization_of_v;
#else
// is_specialization_of: https://github.com/cplusplus/papers/issues/812

template<typename Type, template<typename...> class Primary>
SQLITE_ORM_INLINE_VAR constexpr bool is_specialization_of_v = false;
Expand All @@ -121,8 +123,6 @@ namespace sqlite_orm {

template<typename... T>
using is_specialization_of_t = typename is_specialization_of<T...>::type;
#else
using std::is_specialization_of, std::is_specialization_of_t, std::is_specialization_of_v;
#endif

template<typename...>
Expand Down
18 changes: 11 additions & 7 deletions dev/field_printer.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,9 @@ namespace sqlite_orm {
template<class T, class SFINAE = void>
SQLITE_ORM_INLINE_VAR constexpr bool is_printable_v = false;
template<class T>
SQLITE_ORM_INLINE_VAR constexpr bool is_printable_v<T, polyfill::void_t<decltype(field_printer<T>{})>> = true;
SQLITE_ORM_INLINE_VAR constexpr bool is_printable_v<T, polyfill::void_t<decltype(field_printer<T>{})>> = true
// Also see implementation note for `is_bindable_v`
;
template<class T>
using is_printable = polyfill::bool_constant<is_printable_v<T>>;
}
Expand Down Expand Up @@ -125,9 +127,10 @@ namespace sqlite_orm {
};
#endif // SQLITE_ORM_OPTIONAL_SUPPORTED
template<class T>
struct field_printer<T,
std::enable_if_t<is_std_ptr<T>::value &&
internal::is_printable_v<std::remove_cv_t<typename T::element_type>>>> {
struct field_printer<
T,
std::enable_if_t<polyfill::conjunction_v<is_std_ptr<T>,
internal::is_printable<std::remove_cv_t<typename T::element_type>>>>> {
using unqualified_type = std::remove_cv_t<typename T::element_type>;

std::string operator()(const T& t) const {
Expand All @@ -141,9 +144,10 @@ namespace sqlite_orm {

#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED
template<class T>
struct field_printer<T,
std::enable_if_t<polyfill::is_specialization_of_v<T, std::optional> &&
internal::is_printable_v<std::remove_cv_t<typename T::value_type>>>> {
struct field_printer<
T,
std::enable_if_t<polyfill::conjunction_v<polyfill::is_specialization_of<T, std::optional>,
internal::is_printable<std::remove_cv_t<typename T::value_type>>>>> {
using unqualified_type = std::remove_cv_t<typename T::value_type>;

std::string operator()(const T& t) const {
Expand Down
9 changes: 9 additions & 0 deletions dev/function.h
Original file line number Diff line number Diff line change
Expand Up @@ -246,13 +246,22 @@ namespace sqlite_orm {
using func_arg_t = std::tuple_element_t<I, FnArgs>;
using passed_arg_t = unpacked_arg_t<std::tuple_element_t<I, CallArgs>>;

#ifdef SQLITE_ORM_RELAXED_CONSTEXPR
constexpr bool valid = validate_pointer_value_type<I,
std::tuple_element_t<I, FnArgs>,
unpacked_arg_t<std::tuple_element_t<I, CallArgs>>>(
polyfill::bool_constant < (polyfill::is_specialization_of_v<func_arg_t, pointer_arg>) ||
(polyfill::is_specialization_of_v<passed_arg_t, pointer_binding>) > {});

return validate_pointer_value_types<FnArgs, CallArgs>(polyfill::index_constant<I - 1>{}) && valid;
#else
return validate_pointer_value_types<FnArgs, CallArgs>(polyfill::index_constant<I - 1>{}) &&
validate_pointer_value_type<I,
std::tuple_element_t<I, FnArgs>,
unpacked_arg_t<std::tuple_element_t<I, CallArgs>>>(
polyfill::bool_constant < (polyfill::is_specialization_of_v<func_arg_t, pointer_arg>) ||
(polyfill::is_specialization_of_v<passed_arg_t, pointer_binding>) > {});
#endif
}
}

Expand Down
4 changes: 2 additions & 2 deletions dev/get_prepared_statement.h
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ namespace sqlite_orm {
++index;
}
if(index == N) {
internal::static_if<std::is_same<result_tupe, node_type>{}>([](auto& r, auto& n) {
internal::static_if<std::is_same<result_tupe, node_type>::value>([](auto& r, auto& n) {
r = const_cast<typename std::remove_reference<decltype(r)>::type>(&n);
})(result, node);
}
Expand All @@ -157,7 +157,7 @@ namespace sqlite_orm {
++index;
}
if(index == N) {
internal::static_if<std::is_same<result_tupe, node_type>{}>([](auto& r, auto& n) {
internal::static_if<std::is_same<result_tupe, node_type>::value>([](auto& r, auto& n) {
r = const_cast<typename std::remove_reference<decltype(r)>::type>(&n);
})(result, node);
}
Expand Down
4 changes: 4 additions & 0 deletions dev/index.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@ namespace sqlite_orm {
struct index_base {
std::string name;
bool unique = false;

#ifndef SQLITE_ORM_AGGREGATE_NSDMI_SUPPORTED
index_base(std::string name, bool unique) : name{move(name)}, unique{unique} {}
#endif
};

template<class... Els>
Expand Down
2 changes: 1 addition & 1 deletion dev/is_base_of_template.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ namespace sqlite_orm {
* This is because of bug in MSVC, for more information, please visit
* https://stackoverflow.com/questions/34672441/stdis-base-of-for-template-classes/34672753#34672753
*/
#if defined(_MSC_VER) && (_MSC_VER < 1920)
#ifdef SQLITE_ORM_BROKEN_VARIADIC_PACK_EXPANSION
template<template<typename...> class Base, typename Derived>
struct is_base_of_template_impl {
template<typename... Ts>
Expand Down
Loading

0 comments on commit 7bb1a39

Please sign in to comment.