Skip to content

Commit

Permalink
Uniform syntax for column pointer expressions through table references
Browse files Browse the repository at this point in the history
  • Loading branch information
trueqbit committed Nov 12, 2023
1 parent f6e1c97 commit 6b0613d
Show file tree
Hide file tree
Showing 21 changed files with 772 additions and 292 deletions.
4 changes: 2 additions & 2 deletions dev/alias.h
Original file line number Diff line number Diff line change
Expand Up @@ -329,8 +329,8 @@ namespace sqlite_orm {
*/
template<orm_table_alias A, class C>
requires(orm_cte_moniker<internal::type_t<A>>)
constexpr auto operator->*(const A& /*tableAlias*/, C field) {
return alias_column<A>(std::move(field));
constexpr auto operator->*(const A& /*tableAlias*/, C c) {
return alias_column<A>(std::move(c));
}

/**
Expand Down
253 changes: 144 additions & 109 deletions dev/alias_traits.h
Original file line number Diff line number Diff line change
@@ -1,109 +1,144 @@
#pragma once

#include <type_traits> // std::remove_const, std::is_base_of, std::is_same
#ifdef SQLITE_ORM_WITH_CPP20_ALIASES
#include <concepts>
#endif

#include "functional/cxx_universal.h"
#include "functional/cxx_type_traits_polyfill.h"
#include "type_traits.h"

namespace sqlite_orm {

/** @short Base class for a custom table alias, column alias or expression alias.
*/
struct alias_tag {};

namespace internal {

template<class A>
SQLITE_ORM_INLINE_VAR constexpr bool is_alias_v = std::is_base_of<alias_tag, A>::value;

template<class A>
using is_alias = polyfill::bool_constant<is_alias_v<A>>;

/** @short Alias of a column in a record set, see `orm_column_alias`.
*/
template<class A>
SQLITE_ORM_INLINE_VAR constexpr bool is_column_alias_v =
polyfill::conjunction_v<is_alias<A>, polyfill::negation<polyfill::is_detected<type_t, A>>>;

template<class A>
using is_column_alias = is_alias<A>;

/** @short Alias of any type of record set, see `orm_recordset_alias`.
*/
template<class A>
SQLITE_ORM_INLINE_VAR constexpr bool is_recordset_alias_v =
polyfill::conjunction_v<is_alias<A>, polyfill::is_detected<type_t, A>>;

template<class A>
using is_recordset_alias = polyfill::bool_constant<is_recordset_alias_v<A>>;

/** @short Alias of a concrete table, see `orm_table_alias`.
*/
template<class A>
SQLITE_ORM_INLINE_VAR constexpr bool is_table_alias_v = polyfill::conjunction_v<
is_recordset_alias<A>,
polyfill::negation<std::is_same<polyfill::detected_t<type_t, A>, std::remove_const_t<A>>>>;

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

/** @short Alias of a CTE, see `orm_cte_moniker`.
*/
template<class A>
SQLITE_ORM_INLINE_VAR constexpr bool is_cte_moniker_v =
#ifdef SQLITE_ORM_WITH_CTE
polyfill::conjunction_v<is_recordset_alias<A>,
std::is_same<polyfill::detected_t<type_t, A>, std::remove_const_t<A>>>;
#else
false;
#endif

template<class A>
using is_cte_moniker = polyfill::bool_constant<is_cte_moniker_v<A>>;
}

#ifdef SQLITE_ORM_WITH_CPP20_ALIASES
template<class A>
concept orm_alias = std::derived_from<A, alias_tag>;

/** @short Alias of a column in a record set.
*
* A column alias has the following traits:
* - is derived from `alias_tag`
* - must not have a nested `type` typename
*/
template<class A>
concept orm_column_alias = (orm_alias<A> && !orm_names_type<A>);

/** @short Alias of any type of record set.
*
* A record set alias has the following traits:
* - is derived from `alias_tag`.
* - has a nested `type` typename, which refers to a mapped object.
*/
template<class A>
concept orm_recordset_alias = (orm_alias<A> && orm_names_type<A>);

/** @short Alias of a concrete table.
*
* A concrete table alias has the following traits:
* - is derived from `alias_tag`.
* - has a `type` typename, which refers to another mapped object (i.e. doesn't refer to itself).
*/
template<class A>
concept orm_table_alias = (orm_recordset_alias<A> && !std::same_as<typename A::type, std::remove_const_t<A>>);

/** @short Moniker of a CTE.
*
* A CTE moniker has the following traits:
* - is derived from `alias_tag`.
* - has a `type` typename, which refers to itself.
*/
template<class A>
concept orm_cte_moniker = (orm_recordset_alias<A> && std::same_as<typename A::type, std::remove_const_t<A>>);
#endif
}
#pragma once

#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

#include "functional/cxx_universal.h"
#include "functional/cxx_type_traits_polyfill.h"
#include "type_traits.h"

namespace sqlite_orm {

/** @short Base class for a custom table alias, column alias or expression alias.
*/
struct alias_tag {};

namespace internal {

template<class A>
SQLITE_ORM_INLINE_VAR constexpr bool is_alias_v = std::is_base_of<alias_tag, A>::value;

template<class A>
using is_alias = polyfill::bool_constant<is_alias_v<A>>;

/** @short Alias of a column in a record set, see `orm_column_alias`.
*/
template<class A>
SQLITE_ORM_INLINE_VAR constexpr bool is_column_alias_v =
polyfill::conjunction_v<is_alias<A>, polyfill::negation<polyfill::is_detected<type_t, A>>>;

template<class A>
using is_column_alias = is_alias<A>;

/** @short Alias of any type of record set, see `orm_recordset_alias`.
*/
template<class A>
SQLITE_ORM_INLINE_VAR constexpr bool is_recordset_alias_v =
polyfill::conjunction_v<is_alias<A>, polyfill::is_detected<type_t, A>>;

template<class A>
using is_recordset_alias = polyfill::bool_constant<is_recordset_alias_v<A>>;

/** @short Alias of a concrete table, see `orm_table_alias`.
*/
template<class A>
SQLITE_ORM_INLINE_VAR constexpr bool is_table_alias_v = polyfill::conjunction_v<
is_recordset_alias<A>,
polyfill::negation<std::is_same<polyfill::detected_t<type_t, A>, std::remove_const_t<A>>>>;

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

/** @short Moniker of a CTE, see `orm_cte_moniker`.
*/
template<class A>
SQLITE_ORM_INLINE_VAR constexpr bool is_cte_moniker_v =
#ifdef SQLITE_ORM_WITH_CTE
polyfill::conjunction_v<is_recordset_alias<A>,
std::is_same<polyfill::detected_t<type_t, A>, std::remove_const_t<A>>>;
#else
false;
#endif

template<class A>
using is_cte_moniker = polyfill::bool_constant<is_cte_moniker_v<A>>;
}

#ifdef SQLITE_ORM_WITH_CPP20_ALIASES
template<class A>
concept orm_alias = std::derived_from<A, alias_tag>;

/** @short Alias of a column in a record set.
*
* A column alias has the following traits:
* - is derived from `alias_tag`
* - must not have a nested `type` typename
*/
template<class A>
concept orm_column_alias = (orm_alias<A> && !orm_names_type<A>);

/** @short Alias of any type of record set.
*
* A record set alias has the following traits:
* - is derived from `alias_tag`.
* - has a nested `type` typename, which refers to a mapped object.
*/
template<class A>
concept orm_recordset_alias = (orm_alias<A> && orm_names_type<A>);

/** @short Alias of a concrete table.
*
* A concrete table alias has the following traits:
* - is derived from `alias_tag`.
* - has a `type` typename, which refers to another mapped object (i.e. doesn't refer to itself).
*/
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>;

/** @short Moniker of a CTE.
*
* A CTE moniker has the following traits:
* - is derived from `alias_tag`.
* - has a `type` typename, which refers to itself.
*/
template<class A>
concept orm_cte_moniker = (orm_recordset_alias<A> && std::same_as<typename A::type, std::remove_const_t<A>>);

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> || orm_cte_moniker<T>);
#endif
}
44 changes: 41 additions & 3 deletions dev/column_pointer.h
Original file line number Diff line number Diff line change
Expand Up @@ -47,11 +47,49 @@ 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

#ifdef SQLITE_ORM_WITH_CTE
/**
* Explicitly refer to a column alias mapped into a CTE or subquery.
Expand Down
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
Loading

0 comments on commit 6b0613d

Please sign in to comment.