Last active
May 17, 2023 04:36
-
-
Save airglow923/424c112543f984a7657e85af4c93aa46 to your computer and use it in GitHub Desktop.
User-defined concepts for container and allocator types using C++20 concepts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <utility> | |
#include <memory> | |
#include <iterator> | |
#include <type_traits> | |
#include <concepts> | |
namespace phd { | |
template<typename T> | |
concept copy_assignable = std::is_copy_assignable_v<T>; | |
template<typename T> | |
concept move_assignable = std::is_move_assignable_v<T>; | |
template <typename T> | |
concept boolean_testable_impl = std::convertible_to<T, bool>; | |
template <typename T> | |
concept boolean_testable = | |
boolean_testable_impl<T> && | |
requires (T&& t) { | |
{ !std::forward<T>(t) } -> boolean_testable_impl; | |
}; | |
template<typename T> | |
concept nullable_pointer = | |
std::equality_comparable<T> && | |
std::default_initializable<T> && | |
std::copy_constructible<T> && | |
copy_assignable<T> && | |
std::destructible<T> && | |
requires (T p, T q) { | |
{ T(nullptr) } -> std::same_as<T>; | |
{ p = nullptr } -> std::same_as<T&>; | |
{ p != q } -> boolean_testable; | |
{ p == nullptr } -> boolean_testable; | |
{ nullptr == p } -> boolean_testable; | |
{ p != nullptr } -> boolean_testable; | |
{ nullptr != p } -> boolean_testable; | |
}; | |
template< | |
typename Alloc, | |
typename T = Alloc::value_type, | |
typename pointer = std::allocator_traits<Alloc>::pointer, | |
typename const_pointer = std::allocator_traits<Alloc>::const_pointer, | |
typename void_pointer = std::allocator_traits<Alloc>::void_pointer, | |
typename const_void_pointer = | |
std::allocator_traits<Alloc>::const_void_pointer, | |
typename size_type = std::allocator_traits<Alloc>::size_type, | |
typename difference_type = std::allocator_traits<Alloc>::difference_type | |
> | |
concept allocator = | |
nullable_pointer<pointer> && | |
std::random_access_iterator<pointer> && | |
std::contiguous_iterator<pointer> && | |
nullable_pointer<const_pointer> && | |
std::random_access_iterator<const_pointer> && | |
std::contiguous_iterator<const_pointer> && | |
std::convertible_to<pointer, const_pointer> && | |
nullable_pointer<void_pointer> && | |
std::convertible_to<pointer, void_pointer> && | |
nullable_pointer<const_void_pointer> && | |
std::convertible_to<pointer, const_void_pointer> && | |
std::convertible_to<const_pointer, const_void_pointer> && | |
std::convertible_to<void_pointer, const_void_pointer> && | |
std::unsigned_integral<size_type> && | |
std::signed_integral<difference_type> && | |
std::copy_constructible<Alloc> && | |
std::move_constructible<Alloc> && | |
copy_assignable<Alloc> && | |
move_assignable<Alloc> && | |
std::equality_comparable<Alloc> && | |
requires(Alloc a) { | |
typename T; | |
// *p | |
{*std::declval<pointer>()} -> std::same_as<T&>; | |
// *cp | |
{*std::declval<const_pointer>()} -> std::same_as<const T&>; | |
// static_cast<Alloc::pointer>(vp) | |
requires | |
std::same_as< | |
decltype(static_cast<T*>(std::declval<void_pointer>())), | |
pointer | |
>; | |
// static_cast<Alloc::const_pointer>(cvp) | |
requires | |
std::same_as< | |
decltype(static_cast<const_pointer>( | |
std::declval<const_void_pointer>())), | |
const_pointer | |
>; | |
// std::pointer_traits<Alloc::pointer>::pointer_to(r) | |
{std::pointer_traits<pointer>::pointer_to(*std::declval<pointer>())} | |
-> std::same_as<pointer>; | |
{a.allocate(std::declval<size_type>())} -> std::same_as<pointer>; | |
{a.deallocate(std::declval<pointer>(), std::declval<size_type>())} | |
-> std::same_as<void>; | |
}; | |
template<typename T, typename Alloc> | |
concept erasable = requires(Alloc m, T* p) { | |
{std::allocator_traits<Alloc>::destroy(m, p)} -> std::same_as<void>; | |
}; | |
template<typename T, typename Alloc> | |
concept move_insertable = requires(Alloc m, T* p, T&& rv) { | |
{std::allocator_traits<Alloc>::construct(m, p, rv)} -> std::same_as<void>; | |
}; | |
template<typename T, typename Alloc> | |
concept copy_insertable = | |
move_insertable<T, Alloc> && | |
requires(Alloc m, T* p, const T& v) { | |
{std::allocator_traits<Alloc>::construct(m, p, v)} | |
-> std::same_as<void>; | |
}; | |
template<typename Iter, typename Container> | |
concept container_iterator = | |
std::same_as<Iter, typename Container::iterator> || | |
std::same_as<Iter, typename Container::const_iterator>; | |
template< | |
typename Container, | |
typename C = Container, | |
typename T = C::value_type, | |
typename Alloc = C::allocator_type, | |
typename value_type = T, | |
typename reference = C::reference, | |
typename const_reference = C::const_reference, | |
typename iterator = C::iterator, | |
typename const_iterator = C::const_iterator, | |
typename difference_type = C::difference_type, | |
typename size_type = C::size_type | |
> | |
concept container = | |
erasable<T, Alloc> && | |
requires() { | |
typename reference; | |
typename const_reference; | |
} && | |
std::forward_iterator<iterator> && | |
std::convertible_to<iterator, const_iterator> && | |
std::forward_iterator<const_iterator> && | |
std::signed_integral<difference_type> && | |
std::same_as<difference_type, | |
typename std::iterator_traits<iterator>::difference_type> && | |
std::same_as<difference_type, | |
typename std::iterator_traits<const_iterator>::difference_type> && | |
std::unsigned_integral<size_type> && | |
std::default_initializable<C> && | |
std::copy_constructible<C> && | |
std::equality_comparable<C> && | |
std::swappable<C> && | |
copy_insertable<T, Alloc> && | |
std::equality_comparable<T> && | |
std::destructible<T> && | |
requires (C a) { | |
{ a.~C() } -> std::same_as<void>; | |
{ a.begin() } -> container_iterator<C>; | |
{ a.end() } -> container_iterator<C>; | |
{ a.cbegin() } -> std::same_as<const_iterator>; | |
{ a.cend() } -> std::same_as<const_iterator>; | |
{ a.size() } -> std::same_as<size_type>; | |
{ a.max_size() } -> std::same_as<size_type>; | |
{ a.empty() } -> std::convertible_to<bool>; | |
}; | |
} |
Two things I notice off the bat:
- Operations with
std::nullptr_t
onnullable_pointer
also need to check the const qualifiedconst std::nullptr_t
. std::convertible_to<bool>
is not the same asboolean-testable
. You would defineboolean-testable
as,
template <class T>
concept boolean_testable =
std::convertible_to<T, bool> &&
requires (T &&t) { { !std::forward<T>(t) } -> std::convertible_to<bool>; };
Hi @HunterKohler, thanks for telling me what I missed. I will fix the errors
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Usage:
Example: