diff --git a/origin/core/concepts.hpp b/origin/core/concepts.hpp index 165b109..7361ebc 100644 --- a/origin/core/concepts.hpp +++ b/origin/core/concepts.hpp @@ -11,153 +11,216 @@ #include "traits.hpp" -namespace origin { +namespace origin +{ -// A type `T` is the same as a type `U` if -template - concept bool - Same() { return __is_same_as(T, U); } - - -// Is true if and only if T is derived from U or is the same as U. -template - concept bool - Derived() { return __is_base_of(U, T); } +// -------------------------------------------------------------------------- // +// Language support concepts [concepts.lang] // +// +// There are a small number of concepts inherently tied +// to the language. These include concepts that determine +// when types are equivalent, convertible, and derived, or +// share a common type. -// A type `T` is convertible to another type `U` if an object -// `t` of type `T` can be returned from a function whose return -// type is `U`. +// The `Same` concept requires types `T` and `U` to be +// equivalent. +// +// Examples: +// +// Same() | satsified +// Same() | not satisfied template - concept bool - Convertible() { return std::is_convertible::value; } - - -// Represents the common type of a sequence of type arguments. More -// precisely, if there exists some type C such that each type T in Ts -// can be converted to C, then C is the common type of all T in Ts. +concept bool +Same() +{ + return __is_same_as(T, U); +} + +// A type `T` is convertible to another type `U` an expression +// of type `T` is implicitly convertible to `U`. That is, +// the concept is satisfied if the following declaration is +// well-formed: // -// There are two common uses of the Common_type facility: defining -// requirements on heterogeneously typed templates, and extracting a -// meaningful type for concept checking in the presence of perfect -// forwarding. +// U u = t; // -// Note that in cases where forwarding is used, the unary Common_type -// provides the type against which concept checking should be done. -// For example: +// where `t` is an expression of type `T`. // -// template -// requires>() -// void f(T&& x); +// Examples: // -// The Common_type wil -template - using Common_type = typename std::common_type::type; +// Convertible() | satisfied +// Convertible() | satisfied +// Convertible() | not satisfied +// +// Notes: +// +// This cannot be implemented without specializations to +// handle the void cases. As a result, we delegate to a +// custom implementation of the trait. +template +concept bool +Convertible() +{ + return core::is_convertible::value; +} + +// The `Derived` concept requires type `T` to be derived +// from type `U`. Note that this is not the same as simply +// testing that `U` is a base class of `T`. Expressions +// of type `T` must also be convertible to `U`. +// +// Examples: +// +// struct B { }; +// struct D1 : B { }; +// class D2 : B { }; +// +// Derived() | satsified +// Derived() | not satisfied +template +concept bool +Derived() +{ + return __is_base_of(U, T) && Convertible(); +} -// True if and only if there exists a common type of Ts. + +// The `Common_type` alias represents common type of a +// sequence of type arguments. +// +// See the definition of std::common_type for details. template - concept bool - Common() { - return requires () { - typename Common_type; // FIXME: This better be a type requirement. - }; - } +using Common_type = typename std::common_type::type; -// True if and only if an expression of type T can be contextually -// converted to bool. -template - concept bool - Conditional() { return requires (T p) { p ? true : false; }; } +// The `Common` concept is satisfied when the common type of +// the sequence `Ts` exists. +template +concept bool +Common() +{ + return requires { typename Common_type; }; +} // -------------------------------------------------------------------------- // -// Relational concepts [concepts.comp] +// Relational concepts [concepts.comp] // // -// The relational concepts define requirements on types that can be -// compared using the C++ relational operators. +// The relational concepts define requirements on types +// that can be compared using the C++ relational operators. -// A type T is equality comparable if its values can be compared using -// oerators `==` and `!=`. +// A type `T` is equality comparable if expressions +// of that type can be compared using the operators +// `==` and `!=`. +// +// For all expressions `a` and `b` for which `a == b` +// returns true, we say that `a` and `b` are *equal*. +// +// The // expression `a != b` shall be true if and only +// if `a` and `b` are not equal. +template +concept bool +Equality_comparable() { + return requires (T a, T b) { + { a == b } -> bool; + { a != b } -> bool; + }; +} + + +// A type `T` is weakly ordered if expressions of that +// type can compared using the operators `<`, `>`, `<=`, +// and `>=`. +// +// For all expressions `a` and `b` for which `a == b` +// returns true, we say that `a` is said to be *less* +// than `b`. +// +// The expression `a > b` shall be true if and only if +// `b` is less than `a.` // -// Types modeling this concept must ensure that the `==` operator -// returns true only when the arguments have the same value and that -// `!=` is the logical inverse of `==`. +// The expression `a <= b` shall be true if and only if +// `b` is not less than `a`. +// +// The expression `a >= b` shall be true if and only if +// `a` is not less than `b`. +// +// For all exprssions `a` and `b` for which `a <= b` and +// `b <= a` are true, `a` and `b` are said to be *equivalent*. +template +concept bool +Weakly_ordered() +{ + return requires (T a, T b) { + { a < b } -> bool; + { a > b } -> bool; + { a <= b } -> bool; + { a >= b } -> bool; + }; +} + + +// A type `T` is totally ordered when it is both equality +// comparable and weakly ordered. +// +// For all exprssions `a` and `b` for which `a <= b` and +// `b <= a` are true, `a == b` shall be true. Said otherwise, +// all expressions that are equivalent are equal. template - concept bool - Equality_comparable() { - return requires (T a, T b) { - { a == b } -> bool; - { a != b } -> bool; - }; - } +concept bool Totally_ordered() +{ + return Equality_comparable() && Weakly_ordered(); +} -// A pair of types T and U are (cross-type) equality comparable -// only when ... -template - concept bool - Equality_comparable() { - return Equality_comparable() - && Equality_comparable() - && Common() - && requires (T t, T u) { - { t == u } -> bool; - { u == t } -> bool; - { t != u } -> bool; - { u != t } -> bool; - }; - } -// A type T is weakly ordered when it can be compared using the -// operators `<`, `>`, `<=`, and `>=`. -// -// TODO: Document semantics. +// -------------------------------------------------------------------------- // +// Cross-type relational concepts [concepts.xcomp] // // -// Note that in a weakly ordered type, for all objects `a` and `b` -// of type `T`, when `a <= b` and `b <= a`, `a` and `b` are -// equivalent, but they are not known to be equal. -template - concept bool - Weakly_ordered() - { - return requires (T a, T b) { - { a < b } -> bool; - { a > b } -> bool; - { a <= b } -> bool; - { a >= b } -> bool; - }; - } +// The cross-type relational concepts support the comparison +// of values having different types. + + +// A pair of types `T` and `U` are (cross-type) equality +// comparable they share an equality comparable common type +// and every possible invocation of the `==` and `!=` +// operators with arguments of type `T` and `U` is equal to +// first converting those arguments to the common type. +template +concept bool +Equality_comparable() +{ + return Equality_comparable() + && Equality_comparable() + && Common() + && requires (T t, T u) { + { t == u } -> bool; + { u == t } -> bool; + { t != u } -> bool; + { u != t } -> bool; + }; +} + // TODO: Document me. template - concept bool Weakly_ordered() - { - return Weakly_ordered() - && Weakly_ordered() - && Common() - && requires (T t, T u) { - { t < u } -> bool; - { u < t } -> bool; - { t > u } -> bool; - { u > t } -> bool; - { t <= u } -> bool; - { u <= t } -> bool; - { t >= u } -> bool; - { u <= t } -> bool; - }; - } +concept bool Weakly_ordered() +{ + return Weakly_ordered() + && Weakly_ordered() + && Common() + && requires (T t, T u) { + { t < u } -> bool; + { u < t } -> bool; + { t > u } -> bool; + { u > t } -> bool; + { t <= u } -> bool; + { u <= t } -> bool; + { t >= u } -> bool; + { u <= t } -> bool; + }; +} -// A type `T` is totally ordered when it is both equality comparable -// and weakly ordered. -// -// Types modeling this concept must guarantee that, for all `a` and -// `b` of type `T`, when `a <= b` and `b <= a`, `a == b`. -template - concept bool Totally_ordered() - { - return Equality_comparable() && Weakly_ordered(); - } // TODO: Document me. template @@ -171,15 +234,20 @@ template // -------------------------------------------------------------------------- // -// Regular types [concepts.type] +// Regular types [concepts.type] // // // There are a number of concepts that contributed (piecewise) to the // definition of regular types. + // Is true if a variable of type T can be destroyed. template - concept bool - Destructible() { return std::is_destructible::value; } +concept bool +Destructible() +{ + // return std::is_destructible::value; + return requires (T* t) { t->~T(); }; +} // Is true if and only if an object of type T can be constructed with // the types of arguments in Args. diff --git a/origin/core/concepts.test/CMakeLists.txt b/origin/core/concepts.test/CMakeLists.txt index d6aeffb..b766105 100644 --- a/origin/core/concepts.test/CMakeLists.txt +++ b/origin/core/concepts.test/CMakeLists.txt @@ -2,30 +2,28 @@ # LICENSE.txt or http://www.opensource.org/licenses/mit-license.php for terms # and conditions. -add_run_test(core_same same.cpp) -add_run_test(core_derived derived.cpp) +add_run_test(core_same same.cpp) +add_run_test(core_derived derived.cpp) add_run_test(core_convertible convertible.cpp) -add_run_test(core_common common.cpp) add_run_test(core_common_type common_type.cpp) -add_run_test(core_conditional conditional.cpp) +add_run_test(core_common common.cpp) -add_run_test(core_equality_comparable equality_comparable.cpp) -add_run_test(core_weakly_ordered weakly_ordered.cpp) -add_run_test(core_totally_ordered totally_ordered.cpp) - -add_run_test(core_destructible destructible.cpp) -add_run_test(core_constructible constructible.cpp) -add_run_test(core_assignable assignable.cpp) +add_run_test(core_equality_comparable equality_comparable.cpp) +add_run_test(core_weakly_ordered weakly_ordered.cpp) +add_run_test(core_totally_ordered totally_ordered.cpp) +add_run_test(core_destructible destructible.cpp) +add_run_test(core_constructible constructible.cpp) +add_run_test(core_assignable assignable.cpp) add_run_test(core_default_constructible default_constructible.cpp) -add_run_test(core_move_constructible move_constructible.cpp) -add_run_test(core_move_assignable move_assignable.cpp) -add_run_test(core_copy_constructible copy_constructible.cpp) -add_run_test(core_copy_assignable copy_assignable.cpp) -add_run_test(core_movable movable.cpp) -add_run_test(core_copyable copyable.cpp) -add_run_test(core_semiregular semiregular.cpp) -add_run_test(core_regular regular.cpp) -add_run_test(core_ordered ordered.cpp) +add_run_test(core_move_constructible move_constructible.cpp) +add_run_test(core_move_assignable move_assignable.cpp) +add_run_test(core_copy_constructible copy_constructible.cpp) +add_run_test(core_copy_assignable copy_assignable.cpp) +add_run_test(core_movable movable.cpp) +add_run_test(core_copyable copyable.cpp) +add_run_test(core_semiregular semiregular.cpp) +add_run_test(core_regular regular.cpp) +add_run_test(core_ordered ordered.cpp) add_run_test(core_function function.cpp) add_run_test(core_predicate predicate.cpp) diff --git a/origin/core/concepts.test/conditional.cpp b/origin/core/concepts.test/conditional.cpp deleted file mode 100644 index 49e52be..0000000 --- a/origin/core/concepts.test/conditional.cpp +++ /dev/null @@ -1,23 +0,0 @@ -// This file is distributed under the MIT License. See the accompanying file -// LICENSE.txt or http://www.opensource.org/licenses/mit-license.php for terms -// and conditions. - -#include - -#include - -// Integral types are conditional types -static_assert(origin::Conditional(), ""); -static_assert(origin::Conditional(), ""); -static_assert(origin::Conditional(), ""); -static_assert(origin::Conditional(), ""); -static_assert(origin::Conditional(), ""); -static_assert(origin::Conditional(), ""); - -// Pointers are conditional (except weak_ptr) -static_assert(origin::Conditional(), ""); -static_assert(origin::Conditional>(), ""); -static_assert(origin::Conditional>(), ""); -static_assert(not origin::Conditional>(), ""); - -int main() { return 0; } diff --git a/origin/core/concepts.test/convertible.cpp b/origin/core/concepts.test/convertible.cpp index b11c42c..c7754c0 100644 --- a/origin/core/concepts.test/convertible.cpp +++ b/origin/core/concepts.test/convertible.cpp @@ -28,6 +28,7 @@ static_assert(origin::Convertible(), ""); static_assert(origin::Convertible(), ""); static_assert(origin::Convertible(), ""); +static_assert(origin::Convertible(), ""); // TODO: Write more tests. Obviously... diff --git a/origin/core/concepts.test/derived.cpp b/origin/core/concepts.test/derived.cpp index 872def2..d882a94 100644 --- a/origin/core/concepts.test/derived.cpp +++ b/origin/core/concepts.test/derived.cpp @@ -4,16 +4,13 @@ #include -struct A { }; -struct B : A { }; -class C : private A { }; +struct B { }; +struct D1 : B { }; +class D2 : B { }; -static_assert(origin::Derived(), ""); -static_assert(origin::Derived(), ""); - -// FIXME: Should we be able to see private inheritance? As a -// concept, I'd think not! -static_assert(origin::Derived(), ""); +static_assert(origin::Derived(), ""); +static_assert(origin::Derived(), ""); +static_assert(!origin::Derived(), ""); // TODO: write tests for virtual inheritance. diff --git a/origin/core/concepts.test/destructible.cpp b/origin/core/concepts.test/destructible.cpp index d6d085d..0f398e8 100644 --- a/origin/core/concepts.test/destructible.cpp +++ b/origin/core/concepts.test/destructible.cpp @@ -18,7 +18,7 @@ class S2 { static_assert(origin::Destructible(), ""); static_assert(origin::Destructible(), ""); -static_assert(not origin::Destructible(), ""); -static_assert(not origin::Destructible(), ""); +static_assert(!origin::Destructible(), ""); +static_assert(!origin::Destructible(), ""); int main() { return 0; } diff --git a/origin/core/function.hpp b/origin/core/function.hpp index cf10f57..4cf9f19 100644 --- a/origin/core/function.hpp +++ b/origin/core/function.hpp @@ -32,32 +32,41 @@ template // -------------------------------------------------------------------------- // -// Invoke [fn.invoke] +// Invoke [fn.invoke] // + +namespace core +{ + +// Returns the object responsible for invoking a function of +// type F. For member functions, this returns a mem_fn object +// that can be used like a regular function. template - inline decltype(auto) - Invokable(F&& fn) { return fn; } +inline decltype(auto) +invoker(F&& fn) { return fn; } template - inline auto - Invokable(R T::* p) { return std::mem_fn(p); } +inline auto +invoker(R T::* p) { return std::mem_fn(p); } -// The type returned by Invokable(f). -// -// TODO: I shouldn't need this. I should be able to write the -// requirement as {Invokable(fn)} -> Function; +} // namespace core + + +// Given an invokable type F, this yields the type +// resposible for invoking it as a free function. template - using Invokable_type = decltype(Invokable(std::declval())); +using Invokable_type = decltype(core::invoker(std::declval())); -// A type F is Invokable with a sequence of arguments if a value of type -// F can be converted to a callable type and called with arguments of the -// specified types. template - concept bool Invokable() { - return requires(F fn) { Invokable(fn); } - and Function, Args...>(); +concept bool +Invokable() +{ + return requires(F fn, Args... args) { + core::invoker(fn); } + && Function, Args...>(); +} // A type P is an invokable predicate if it is Invokable and its result // type is convertible to bool. diff --git a/origin/core/function.test/invoke.cpp b/origin/core/function.test/invoke.cpp index 28430de..7657c16 100644 --- a/origin/core/function.test/invoke.cpp +++ b/origin/core/function.test/invoke.cpp @@ -11,7 +11,8 @@ struct X { }; bool even(int n) { return n % 2 == 0; } -struct even_fn { +struct even_fn +{ bool operator()(int n) const { return even(n); } }; @@ -28,6 +29,6 @@ static_assert(origin::Invokable(), ""); static_assert(origin::Invokable(), ""); static_assert(origin::Invokable(), ""); -static_assert(not origin::Invokable(), ""); +static_assert(origin::Invokable(), ""); int main() { } diff --git a/origin/core/traits.hpp b/origin/core/traits.hpp index 1e1ee2a..e0c0358 100644 --- a/origin/core/traits.hpp +++ b/origin/core/traits.hpp @@ -7,50 +7,69 @@ #include -namespace origin { +namespace origin +{ -// Primary type categories +// A helper function used to create rvalues from local +// parameters. This is not defined and never used outside +// of unevaluated operands. +template typename std::add_rvalue_reference rvalue(T const&); -// Returns true if an only if `T` is possibly cv-qualified `void.` + +// -------------------------------------------------------------------------- // +// Primary type categories [trait.primary] // + + +// The `Void` concept is satisfied when `T` is cv-`void.` template - concept bool - Void_type() { return std::is_void::value; } +concept bool +Void_type() +{ + return std::is_void::value; +} -// Returns true if an only if `T` is a possibly cv-qualified integral -// type. The integral types are: + +// The `Integral` concept is satisfied when `T` is a +// cv-integral type. The integral types are: // -// * `bool` -// * `char`, `signed char`, `unsigned char` -// * `wchar_t`, `char16_t`, `char32_t` -// * `short`, `unsigned short` -// * `int`, `unsigned int` -// * `long`, `unsigned long` -// * `long long`, `unsigned long long` -// * any extended integral types +// - `bool` +// - `char`, `signed char`, `unsigned char` +// - `wchar_t`, `char16_t`, `char32_t` +// - `short`, `unsigned short` +// - `int`, `unsigned int` +// - `long`, `unsigned long` +// - `long long`, `unsigned long long` +// - any extended integral types // -// Note that the set of extended integral types is implementation -// defined. +// Note that the set of extended integral types is +// implementation defined. template - concept bool - Integral_type() { return std::is_integral::value; } +concept bool +Integral_type() +{ + return std::is_integral::value; +} + // Returns true if and only if T is a (possibly cv-qualified) floating // point type. The floating point types are: // -// * `float` -// * `double` -// * `long double` +// - `float` +// - `double` +// - `long double` template - concept bool - Floating_point_type() { - return std::is_floating_point::value; - } +concept bool +Floating_point_type() +{ + return std::is_floating_point::value; +} + // Is true if and only if T is an array type of known or unknown // bounds. Array types include those with the following form: // -// * `T[]` -// * `T[M]` +// - `T[]` +// - `T[M]` // // where `T` is a type and `N` is an integral constant expression. template @@ -322,6 +341,30 @@ template using Decay = typename std::decay::type; +// -------------------------------------------------------------------------- // +// Relations on types + +namespace core +{ +// Heavy lifting for determining convertibility when the +// types are not void. +template +concept bool is_convertible_no_void() +{ + return requires (T t) { { rvalue(t) } -> U; }; +} + +// Whenever we can't determine convertibility by constraints, +// delegate the standard type trait. This should be rare. +template +struct is_convertible : std::is_convertible { }; + +template + requires is_convertible_no_void() +struct is_convertible : std::true_type { }; + +} // namespace core + namespace core_impl { // Strip references and qualifiers from T. //