Skip to content

Commit

Permalink
Add concept for standard container requirements (#314)
Browse files Browse the repository at this point in the history
* Add concept for standard container requirements

* Archtypes for container and allocator aware types, with tests.

* std::vector<bool> sepcialisation of get_allocator is not noexcept

* Support sequence containers

* Remove duplication

* Include ranges

* Correct namespace

* Update archtype for from_range

* Range methods fix

* Patch up the sequence concept as best we can

* Contiguous concept with tests

* Concept for reversible container requirements and partial concepts for associativ and unordered

* Complete associative concept

* Try compound requirement for visual studio

* Complete unorder container concepts

* Check for all basic string instantiations
  • Loading branch information
Twon authored Jun 26, 2024
1 parent 006ead0 commit b9cd166
Show file tree
Hide file tree
Showing 42 changed files with 1,318 additions and 12 deletions.
3 changes: 2 additions & 1 deletion libraries/core/src/morpheus/core/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,9 @@ target_sources(MorpheusCore
)

add_subdirectory(base)
add_subdirectory(conformance)
add_subdirectory(concurrency)
add_subdirectory(conformance)
add_subdirectory(containers)
add_subdirectory(functional)
add_subdirectory(memory)
add_subdirectory(meta)
Expand Down
1 change: 1 addition & 0 deletions libraries/core/src/morpheus/core/containers/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
add_subdirectory(concepts)
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
add_subdirectory(detail)

target_sources(MorpheusCore
PUBLIC
allocator_aware.hpp
associative.hpp
container.hpp
contiguous.hpp
reversible.hpp
sequence.hpp
unordered.hpp
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
#pragma once

#include "morpheus/core/containers/concepts/container.hpp"

#include <concepts>
#include <utility>

namespace morpheus::containers::concepts
{

/// \concept AllocatorAware
/// Concept capturing the requirements for an allocator aware container as outline in the standard at
/// <a href="https://eel.is/c++draft/container.alloc.reqmts">[container.alloc.reqmts]</a>, details at
/// <a href="https://en.cppreference.com/w/cpp/named_req/AllocatorAwareContainer">AllocatorAwareContainer</a>.
template <typename X>
concept AllocatorAware = Container<X> && requires(X x, X&& rx, typename X::allocator_type a){
requires std::same_as<typename X::allocator_type::value_type, typename X::value_type>;
requires std::default_initializable<X>;
{ X(a) };
{ X(std::as_const(x), a) };
{ X(std::move(rx)) };
{ X(std::move(rx), a) };
{ std::as_const(x).get_allocator() } -> std::same_as<typename X::allocator_type>;
{ x = x } -> std::same_as<X&>;
{ x = rx } -> std::same_as<X&>;
{ x.swap(x) } -> std::same_as<void>;
};

} // namespace morpheus::containers::concepts
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
#pragma once

#include "morpheus/core/containers/concepts/detail/return_types.hpp"
#include "morpheus/core/containers/concepts/allocator_aware.hpp"

#include <concepts>
#include <initializer_list>

namespace morpheus::containers::concepts
{

/// \concept Associative
/// Concept capturing the requirements for an associative container as outline in the standard at
/// <a href="https://eel.is/c++draft/container.requirements#associative.reqmts">[associative.reqmts]</a>, details at
/// <a href="https://en.cppreference.com/w/cpp/named_req/AssociativeContainer">AssociativeContainer</a>.
template <typename T>
concept Associative = AllocatorAware<T> && requires(T t, typename T::value_type v, typename T::key_type k, typename T::size_type s, typename T::iterator i,
typename T::const_iterator ci, typename T::key_compare c, typename T::node_type n,
std::initializer_list<typename T::value_type> il)
{
requires (std::default_initializable<typename T::key_compare>) and (std::copy_constructible<typename T::key_compare>);
{ T(c) };
{ T(i, i, c) };
{ T(i, i) };
#if (__cpp_lib_containers_ranges >= 202202L)
{ T(std::from_range, ranges::subrange<typename T::iterator>{}, c) };
{ T(std::from_range, ranges::subrange<typename T::iterator>{}) };
#endif // (__cpp_lib_containers_ranges >= 202202L)
{ T(il, c) };
{ T(il) };
{ t = il };
{ std::as_const(t).key_comp() } -> std::same_as<typename T::key_compare>;
{ std::as_const(t).value_comp() } -> std::same_as<typename T::value_compare>;
{ t.emplace() } -> detail::InsertReturnType<T>;
{ t.emplace_hint(i) } -> std::same_as<typename T::iterator>;
{ t.insert(v) } -> detail::InsertReturnType<T>;
{ t.insert(ci, v) } -> std::same_as<typename T::iterator>;
{ t.insert(i, i) } -> std::same_as<void>;
#if (__cpp_lib_containers_ranges >= 202202L)
{ t.insert_range(ranges::subrange<typename T::iterator>{}) } -> std::same_as<void>;
#endif // (__cpp_lib_containers_ranges >= 202202L)
{ t.insert(il) } -> std::same_as<void>;
{ t.insert(std::move(n)) } -> detail::InsertNodeHandleReturnType<T>;
{ t.insert(ci, std::move(n)) } -> std::same_as<typename T::iterator>;
{ t.extract(k) } -> std::same_as<typename T::node_type>;
{ t.extract(ci) } -> std::same_as<typename T::node_type>;
{ t.merge(t) } -> std::same_as<void>;
{ t.erase(i) } -> std::same_as<typename T::iterator>;
{ t.erase(ci) } -> std::same_as<typename T::iterator>;
{ t.erase(i, i) } -> std::same_as<typename T::iterator>;
{ t.erase(ci, ci) } -> std::same_as<typename T::iterator>;
{ t.erase(k) } -> std::same_as<typename T::size_type>;
{ t.clear() } -> std::same_as<void>;
{ t.find(k) } -> std::same_as<typename T::iterator>;
{ std::as_const(t).find(k) } -> std::same_as<typename T::const_iterator>;
{ std::as_const(t).count(k) } -> std::same_as<typename T::size_type>;
{ std::as_const(t).contains(k) } -> std::same_as<bool>;
{ t.lower_bound(k) } -> detail::BoundReturnType<T>;
{ std::as_const(t).lower_bound(k) } -> detail::BoundConstReturnType<T>;
{ t.equal_range(k) } -> detail::BoundReturnType<T>;
{ std::as_const(t).equal_range(k) } -> detail::BoundConstReturnType<T>;
{ t.upper_bound(k) } -> detail::BoundReturnType<T>;
{ std::as_const(t).upper_bound(k) } -> detail::BoundConstReturnType<T>;
};

} // namespace morpheus::containers::concepts
34 changes: 34 additions & 0 deletions libraries/core/src/morpheus/core/containers/concepts/container.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
#pragma once

#include <concepts>
#include <iterator>
#include <utility>

namespace morpheus::containers::concepts
{

/// \concept Container
/// Concept capturing the requirements for a container as outline in the standard at
/// <a href="https://eel.is/c++draft/container.requirements">[container.requirements]</a>, details at
/// <a href="https://en.cppreference.com/w/cpp/named_req/Container">Container</a>.
template<typename C>
concept Container = requires(C c) {
typename C::value_type;
typename C::reference;
typename C::const_reference;
requires std::forward_iterator<typename C::iterator>;
requires std::forward_iterator<typename C::const_iterator>;
requires std::signed_integral<typename C::difference_type>;
requires std::unsigned_integral<typename C::size_type>;
requires std::convertible_to<typename C::iterator, typename C::const_iterator>;
requires std::regular<C>;
{ begin(c) } -> std::same_as<typename C::iterator>;
{ end(c) } -> std::same_as<typename C::iterator>;
{ cbegin(std::as_const(c)) } -> std::same_as<typename C::const_iterator>;
{ cend(std::as_const(c)) } -> std::same_as<typename C::const_iterator>;
requires (not std::bidirectional_iterator<typename C::iterator>) or requires {{ size(c) } -> std::same_as<typename C::size_type>; };
{ c.max_size() } -> std::same_as<typename C::size_type>;
{ empty(c) } -> std::same_as<bool>;
};

} // namespace morpheus::containers::concepts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
#pragma once

#include "morpheus/core/containers/concepts/sequence.hpp"

#include <concepts>
#include <iterator>

namespace morpheus::containers::concepts
{

/// \concept Contiguous
/// Concept capturing the requirements for a contiguous container, a named requirement outlined in the standard at
/// <a href="https://eel.is/c++draft/container.requirements.general">[container.requirements.general]</a>, details at
/// <a href="https://en.cppreference.com/w/cpp/named_req/ContiguousContainer">ContiguousContainer</a>.
template <typename T>
concept Contiguous = StrictSequence<T> && requires(T t){
requires std::contiguous_iterator<typename T::iterator>;
requires std::contiguous_iterator<typename T::const_iterator>;
};

} // namespace morpheus::containers::concepts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
target_sources(MorpheusCore
PUBLIC
return_types.hpp
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
#pragma once

#include <concepts>
#include <utility>

namespace morpheus::containers::concepts::detail
{

template <typename I, typename T>
concept InsertReturnType = requires
{
requires std::same_as<I, typename T::iterator> or std::same_as<I, std::pair<typename T::iterator, bool>>;
};

template <typename I, typename T>
concept InsertNodeHandleReturnType = requires
{
requires requires { std::same_as<I, typename T::iterator>; } or requires { std::same_as<I, typename T::insert_return_type>; };
};

template <typename I, typename T>
concept BoundReturnType = requires
{
requires std::same_as<I, typename T::iterator> or std::same_as<I, std::pair<typename T::iterator, typename T::iterator>>;
};

template <typename I, typename T>
concept BoundConstReturnType = requires
{
requires std::same_as<I, typename T::const_iterator> or std::same_as<I, std::pair<typename T::const_iterator, typename T::const_iterator>>;
};

} // namespace morpheus::containers::concepts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
#pragma once

#include "morpheus/core/containers/concepts/container.hpp"

#include <concepts>
#include <iterator>
#include <utility>

namespace morpheus::containers::concepts
{

/// \concept Reversible
/// Concept capturing the requirements for a reversible container, a named requirement outlined in the standard at
/// <a href="https://eel.is/c++draft/container.rev.reqmts">[container.rev.reqmts]</a>, details at
/// <a href="https://en.cppreference.com/w/cpp/named_req/ReversibleContainer">ReversibleContainer</a>.
template <typename T>
concept Reversible = Container<T> && requires(T t){
typename T::reverse_iterator;
typename T::const_reverse_iterator;
requires std::same_as<typename T::reverse_iterator, std::reverse_iterator<typename T::iterator>>;
requires std::same_as<typename T::const_reverse_iterator, std::reverse_iterator<typename T::const_iterator>>;
{ rbegin(t) } -> std::same_as<typename T::reverse_iterator>;
{ rend(t) } -> std::same_as<typename T::reverse_iterator>;
{ crbegin(std::as_const(t)) } -> std::same_as<typename T::const_reverse_iterator>;
{ crend(std::as_const(t)) } -> std::same_as<typename T::const_reverse_iterator>;
};

} // namespace morpheus::containers::concepts
67 changes: 67 additions & 0 deletions libraries/core/src/morpheus/core/containers/concepts/sequence.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
#pragma once

#include "morpheus/core/containers/concepts/container.hpp"
#include "morpheus/core/conformance/ranges.hpp"
#include "morpheus/core/meta/is_array.hpp"
#include "morpheus/core/meta/is_string.hpp"

#include <concepts>
#include <initializer_list>
#include <forward_list>
#include <string>
#include <version>

namespace morpheus::containers::concepts
{

/// \concept Sequence
/// Concept capturing the requirements for a sequence container as outline in the standard at
/// <a href="https://eel.is/c++draft/sequence.reqmts">[sequence.requirements]</a>, details at
/// <a href="https://en.cppreference.com/w/cpp/named_req/SequenceContainer">SequenceContainer</a>.
template <typename T>
concept Sequence = Container<T> && requires(T t, typename T::value_type v, typename T::size_type s, typename T::iterator i, std::initializer_list<typename T::value_type> il){
{ T(s, v) };
{ T(i, i) };
#if (__cpp_lib_containers_ranges >= 202202L)
{ T(std::from_range, ranges::subrange<typename T::iterator>{}) };
#endif // (__cpp_lib_containers_ranges >= 202202L)
{ T(il) };
{ t = il };
{ t.insert(i, v) } -> std::same_as<typename T::iterator>;
{ t.insert(i, std::move(v)) } -> std::same_as<typename T::iterator>;
{ t.insert(i, s, v) } -> std::same_as<typename T::iterator>;
{ t.insert(i, i, i) } -> std::same_as<typename T::iterator>;
#if (__cpp_lib_containers_ranges >= 202202L)
{ t.insert_range(i, ranges::subrange<typename T::iterator>{}) } -> std::same_as<typename T::iterator>;
#endif // (__cpp_lib_containers_ranges >= 202202L)
{ t.insert(i, il) } -> std::same_as<typename T::iterator>;
{ t.erase(i) } -> std::same_as<typename T::iterator>;
{ t.erase(i, i) } -> std::same_as<typename T::iterator>;
{ t.clear() } -> std::same_as<void>;
{ t.assign(i, i) } -> std::same_as<void>;
#if (__cpp_lib_containers_ranges >= 202202L)
{ t.assign_range(ranges::subrange<typename T::iterator>{}) } -> std::same_as<void>;
#endif // (__cpp_lib_containers_ranges >= 202202L)
{ t.assign(il) } -> std::same_as<void>;
{ t.assign(s, v) } -> std::same_as<void>;
};

/// \concept StrictSequence
/// Concept strictly capturing the requirements for a sequence container as outline in the standard at
/// <a href="https://eel.is/c++draft/sequence.reqmts">[sequence.requirements]</a>, details at
/// <a href="https://en.cppreference.com/w/cpp/named_req/SequenceContainer">SequenceContainer</a>.
/// The requirements <a href="https://eel.is/c++draft/sequence.reqmts">[sequence.requirements]</a> don't
/// actually match numerous types listed as sequence containers. This includes:
/// - std::string
/// - std::forward_list
/// - std::array
/// Hoever they are listed as as Sequence types while having interfaces that differ from the Sequence
/// container requirements so this extension concept exactly mirrors that requirement.
template <typename T>
concept StrictSequence = meta::is_array_v<T> ||
meta::is_string_v<T> ||
std::same_as<T, std::forward_list<typename T::value_type>> ||
Sequence<T>;


} // namespace morpheus::containers::concepts
Loading

0 comments on commit b9cd166

Please sign in to comment.