Skip to content

Commit

Permalink
Merge pull request #1244 from FireDaemon/feature/uniform_column_pointer
Browse files Browse the repository at this point in the history
Uniform syntax for column pointer expressions through table references
  • Loading branch information
trueqbit authored Nov 12, 2023
2 parents 07a10dc + 5d75d1b commit 2e1916b
Show file tree
Hide file tree
Showing 19 changed files with 673 additions and 169 deletions.
37 changes: 36 additions & 1 deletion dev/alias_traits.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#pragma once

#include <type_traits> // std::remove_const, std::is_base_of, std::is_same
#include <type_traits> // std::remove_const, std::is_base_of, std::is_same, std::type_identity
#ifdef SQLITE_ORM_WITH_CPP20_ALIASES
#include <concepts>
#endif
Expand Down Expand Up @@ -50,6 +50,24 @@ namespace sqlite_orm {

template<class A>
using is_table_alias = polyfill::bool_constant<is_table_alias_v<A>>;

#ifdef SQLITE_ORM_WITH_CPP20_ALIASES
/*
* Identity wrapper around a mapped object, facilitating uniform column pointer expressions.
*/
template<class O>
struct table_reference : std::type_identity<O> {};

template<class RecordSet>
struct decay_table_reference : std::remove_const<RecordSet> {};
template<class O>
struct decay_table_reference<table_reference<O>> : std::type_identity<O> {};
template<class O>
struct decay_table_reference<const table_reference<O>> : std::type_identity<O> {};

template<auto recordset>
using decay_table_reference_t = typename decay_table_reference<decltype(recordset)>::type;
#endif
}

#ifdef SQLITE_ORM_WITH_CPP20_ALIASES
Expand Down Expand Up @@ -82,5 +100,22 @@ namespace sqlite_orm {
*/
template<class A>
concept orm_table_alias = (orm_recordset_alias<A> && !std::same_as<typename A::type, std::remove_const_t<A>>);

/** @short 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.
*/
template<class R>
concept orm_table_reference = polyfill::is_specialization_of_v<std::remove_const_t<R>, internal::table_reference>;

template<class T>
concept orm_refers_to_table = (orm_table_reference<T> || orm_table_alias<T>);

template<class T>
concept orm_refers_to_recordset = (orm_table_reference<T> || orm_recordset_alias<T>);

template<class T>
concept orm_mapped_recordset = (orm_table_reference<T>);
#endif
}
47 changes: 43 additions & 4 deletions dev/column_pointer.h
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
#pragma once

#include <type_traits> // std::enable_if
#include <type_traits> // std::enable_if, std::remove_const
#include <utility> // std::move

#include "functional/cxx_type_traits_polyfill.h"
#include "tags.h"
#include "alias_traits.h"

namespace sqlite_orm {
namespace internal {
Expand Down Expand Up @@ -35,8 +36,46 @@ namespace sqlite_orm {
* struct MyType : BaseType { ... };
* storage.select(column<MyType>(&BaseType::id));
*/
template<class Object, class F, internal::satisfies_not<internal::is_recordset_alias, Object> = true>
constexpr internal::column_pointer<Object, F> column(F field) {
return {std::move(field)};
template<class Object, class F, class O, internal::satisfies_not<internal::is_recordset_alias, Object> = true>
constexpr internal::column_pointer<Object, F O::*> column(F O::*field) {
static_assert(internal::is_field_of_v<F O::*, Object>, "Column must be from derived class");
return {field};
}

#ifdef SQLITE_ORM_WITH_CPP20_ALIASES
/**
* Explicitly refer to a column.
*/
template<orm_table_reference auto table, class O, class F>
constexpr auto column(F O::*field) {
using R = std::remove_const_t<decltype(table)>;
return column<typename R::type>(field);
}

/**
* Explicitly refer to a column.
*/
template<orm_table_reference R, class O, class F>
constexpr auto operator->*(const R& /*table*/, F O::*field) {
return column<typename R::type>(field);
}

/**
* Make table reference.
*/
template<class O>
requires(!orm_recordset_alias<O>)
constexpr internal::table_reference<O> column() {
return {};
}

/**
* Make table reference.
*/
template<class O>
requires(!orm_recordset_alias<O>)
constexpr internal::table_reference<O> c() {
return {};
}
#endif
}
29 changes: 14 additions & 15 deletions dev/conditions.h
Original file line number Diff line number Diff line change
Expand Up @@ -834,10 +834,9 @@ namespace sqlite_orm {
* Explicit FROM function. Usage:
* `storage.select(&User::id, from<"a"_alias.for_<User>>());`
*/
template<orm_recordset_alias auto... tables>
template<orm_refers_to_recordset auto... recordsets>
auto from() {
static_assert(sizeof...(tables) > 0);
return internal::from_t<std::remove_const_t<decltype(tables)>...>{};
return from<internal::decay_table_reference_t<recordsets>...>();
}
#endif

Expand Down Expand Up @@ -954,12 +953,12 @@ namespace sqlite_orm {
}

template<class F, class O>
internal::using_t<O, F O::*> using_(F O::*p) {
return {p};
internal::using_t<O, F O::*> using_(F O::*field) {
return {field};
}
template<class T, class M>
internal::using_t<T, M> using_(internal::column_pointer<T, M> cp) {
return {std::move(cp)};
internal::using_t<T, M> using_(internal::column_pointer<T, M> field) {
return {std::move(field)};
}

template<class T>
Expand All @@ -983,9 +982,9 @@ namespace sqlite_orm {
}

#ifdef SQLITE_ORM_WITH_CPP20_ALIASES
template<orm_recordset_alias auto alias, class On>
template<orm_refers_to_recordset auto alias, class On>
auto left_join(On on) {
return internal::left_join_t<std::remove_const_t<decltype(alias)>, On>{std::move(on)};
return left_join<internal::decay_table_reference_t<alias>, On>(std::move(on));
}
#endif

Expand All @@ -995,9 +994,9 @@ namespace sqlite_orm {
}

#ifdef SQLITE_ORM_WITH_CPP20_ALIASES
template<orm_recordset_alias auto alias, class On>
template<orm_refers_to_recordset auto alias, class On>
auto join(On on) {
return internal::join_t<std::remove_const_t<decltype(alias)>, On>{std::move(on)};
return join<internal::decay_table_reference_t<alias>, On>(std::move(on));
}
#endif

Expand All @@ -1007,9 +1006,9 @@ namespace sqlite_orm {
}

#ifdef SQLITE_ORM_WITH_CPP20_ALIASES
template<orm_recordset_alias auto alias, class On>
template<orm_refers_to_recordset auto alias, class On>
auto left_outer_join(On on) {
return internal::left_outer_join_t<std::remove_const_t<decltype(alias)>, On>{std::move(on)};
return left_outer_join<internal::decay_table_reference_t<alias>, On>(std::move(on));
}
#endif

Expand All @@ -1019,9 +1018,9 @@ namespace sqlite_orm {
}

#ifdef SQLITE_ORM_WITH_CPP20_ALIASES
template<orm_recordset_alias auto alias, class On>
template<orm_refers_to_recordset auto alias, class On>
auto inner_join(On on) {
return internal::inner_join_t<std::remove_const_t<decltype(alias)>, On>{std::move(on)};
return inner_join<internal::decay_table_reference_t<alias>, On>(std::move(on));
}
#endif

Expand Down
11 changes: 11 additions & 0 deletions dev/core_functions.h
Original file line number Diff line number Diff line change
Expand Up @@ -1845,6 +1845,17 @@ namespace sqlite_orm {
return {};
}

#ifdef SQLITE_ORM_WITH_CPP20_ALIASES
/**
* COUNT(*) with FROM function. Specified recordset will be serialized as
* a from argument.
*/
template<orm_refers_to_recordset auto mapped>
auto count() {
return count<internal::decay_table_reference_t<mapped>>();
}
#endif

/**
* AVG(X) aggregate function.
*/
Expand Down
9 changes: 7 additions & 2 deletions dev/mapped_type_proxy.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,13 @@ namespace sqlite_orm {
template<class T, class SFINAE = void>
struct mapped_type_proxy : std::remove_const<T> {};

template<class T>
struct mapped_type_proxy<T, match_if<is_recordset_alias, T>> : std::remove_const<type_t<T>> {};
#ifdef SQLITE_ORM_WITH_CPP20_ALIASES
template<orm_table_reference R>
struct mapped_type_proxy<R, void> : R {};
#endif

template<class A>
struct mapped_type_proxy<A, match_if<is_recordset_alias, A>> : std::remove_const<type_t<A>> {};

template<class T>
using mapped_type_proxy_t = typename mapped_type_proxy<T>::type;
Expand Down
31 changes: 19 additions & 12 deletions dev/prepared_statement.h
Original file line number Diff line number Diff line change
Expand Up @@ -616,19 +616,29 @@ namespace sqlite_orm {
*/
template<class T, class... Ids>
internal::get_t<T, Ids...> get(Ids... ids) {
std::tuple<Ids...> idsTuple{std::forward<Ids>(ids)...};
return {std::move(idsTuple)};
return {{std::forward<Ids>(ids)...}};
}

#ifdef SQLITE_ORM_WITH_CPP20_ALIASES
/**
* Create a get statement.
* T is an object type mapped to a storage.
* Usage: get<User>(5);
*/
template<orm_table_reference auto als, class... Ids>
auto get(Ids... ids) {
return get<internal::mapped_type_proxy_t<decltype(als)>>(std::forward<Ids>(ids)...);
}
#endif

/**
* Create a get pointer statement.
* T is an object type mapped to a storage.
* Usage: get_pointer<User>(5);
*/
template<class T, class... Ids>
internal::get_pointer_t<T, Ids...> get_pointer(Ids... ids) {
std::tuple<Ids...> idsTuple{std::forward<Ids>(ids)...};
return {std::move(idsTuple)};
return {{std::forward<Ids>(ids)...}};
}

#ifdef SQLITE_ORM_OPTIONAL_SUPPORTED
Expand All @@ -639,8 +649,7 @@ namespace sqlite_orm {
*/
template<class T, class... Ids>
internal::get_optional_t<T, Ids...> get_optional(Ids... ids) {
std::tuple<Ids...> idsTuple{std::forward<Ids>(ids)...};
return {std::move(idsTuple)};
return {{std::forward<Ids>(ids)...}};
}
#endif // SQLITE_ORM_OPTIONAL_SUPPORTED

Expand Down Expand Up @@ -673,17 +682,15 @@ namespace sqlite_orm {
#ifdef SQLITE_ORM_WITH_CPP20_ALIASES
/**
* Create a get all statement.
* `alias` is an explicitly specified table alias of an object to be extracted.
* `als` is an explicitly specified table proxy of an object to be extracted.
* `R` is the container return type, which must have a `R::push_back(T&&)` method, and defaults to `std::vector<T>`
* Usage: storage.get_all<sqlite_schema>(...);
*/
template<orm_table_alias auto alias,
class R = std::vector<internal::mapped_type_proxy_t<decltype(alias)>>,
template<orm_refers_to_table auto als,
class R = std::vector<internal::mapped_type_proxy_t<decltype(als)>>,
class... Args>
auto get_all(Args&&... conditions) {
using expression_type = internal::get_all_t<std::remove_const_t<decltype(alias)>, R, std::decay_t<Args>...>;
internal::validate_conditions<typename expression_type::conditions_type>();
return expression_type{{std::forward<Args>(conditions)...}};
return get_all<internal::decay_table_reference_t<als>, R>(std::forward<Args>(conditions)...);
}
#endif

Expand Down
11 changes: 9 additions & 2 deletions dev/select_constraints.h
Original file line number Diff line number Diff line change
Expand Up @@ -408,9 +408,9 @@ namespace sqlite_orm {
* auto reportingTo =
* storage.select(asterisk<m>(), inner_join<m>(on(m->*&Employee::reportsTo == &Employee::employeeId)));
*/
template<orm_recordset_alias auto alias>
template<orm_refers_to_recordset auto recordset>
auto asterisk(bool definedOrder = false) {
return internal::asterisk_t<std::remove_const_t<decltype(alias)>>{definedOrder};
return asterisk<internal::decay_table_reference_t<recordset>>(definedOrder);
}
#endif

Expand All @@ -429,4 +429,11 @@ namespace sqlite_orm {
internal::object_t<T> object(bool definedOrder = false) {
return {definedOrder};
}

#ifdef SQLITE_ORM_WITH_CPP20_ALIASES
template<orm_refers_to_table auto als>
auto object(bool definedOrder = false) {
return object<internal::decay_table_reference_t<als>>(definedOrder);
}
#endif
}
Loading

0 comments on commit 2e1916b

Please sign in to comment.