Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Uniform syntax for column pointer expressions through table references #1244

Merged
merged 5 commits into from
Nov 12, 2023
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
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