Make allocator and string_base compat easier to read and remove unneccessary concepts subdirectory

This commit is contained in:
rexy712 2022-06-26 11:55:35 -07:00
parent 5a6d7023ac
commit 7f47a4cbf8
7 changed files with 341 additions and 259 deletions

View File

@ -19,143 +19,12 @@
#ifndef REXY_DEFAULT_ALLOCATOR_HPP #ifndef REXY_DEFAULT_ALLOCATOR_HPP
#define REXY_DEFAULT_ALLOCATOR_HPP #define REXY_DEFAULT_ALLOCATOR_HPP
#include <cstddef> //ptrdiff_t, size_t #include "compat/allocator.hpp"
#include <type_traits> //true_type, false_type
#include <new>
#include <limits> //numeric_limits
#if __cplusplus >= 202002L
#include <memory> //allocator
#endif
#ifdef __cpp_concepts
#include "concepts/allocator.hpp"
#define REXY_ALLOCATOR_CONCEPT Allocator
#else
#define REXY_ALLOCATOR_CONCEPT class
#endif
#include "rexy.hpp"
#include "compat/standard.hpp"
#include "compat/if_consteval.hpp"
#include <type_traits> //declval #include <type_traits> //declval
namespace rexy{ namespace rexy{
template<class T>
struct allocator
{
using pointer = T*;
using const_pointer = const T*;
using void_pointer = void*;
using const_void_pointer = const void*;
using value_type = T;
using size_type = std::size_t;
using difference_type = ptrdiff_t;
using is_always_equal = std::true_type;
using propagate_on_container_copy_assignment = std::false_type;
using propagate_on_container_move_assignment = std::false_type;
using propagate_on_container_swap = std::false_type;
template<class U>
struct rebind{
using other = allocator<U>;
};
private:
constexpr bool has_overflow(size_type n)const{
return n > max_size();
}
public:
REXY_CPP20_CONSTEXPR allocator(void) = default;
REXY_CPP20_CONSTEXPR allocator(const allocator&) = default;
REXY_CPP20_CONSTEXPR allocator(allocator&&) = default;
template<class U>
constexpr allocator(const allocator<U>&)noexcept{}
REXY_CPP20_CONSTEXPR ~allocator(void) = default;
//'::operator new' is never a constexpr call as of C++23. However, 'std::allocator::allocate' *is* transiently constexpr as of C++20,
//even though it directly calls '::operator new' as is stated in the standard. Therefore, when evaluating this call with support for
//'if consteval', we can use the 'std::allocator::allocate' constexpr-ness when this is in a constant evaluation context.
#if __cplusplus >= 202002L
REXY_CPP20_CONSTEXPR pointer allocate(size_type n){
size_type bytes = has_overflow(n) ? std::numeric_limits<size_type>::max() : n*sizeof(T);
REXY_if_consteval{
std::allocator<value_type> a;
const size_type num_items = n / sizeof(value_type);
return a.allocate(num_items);
}else{
if constexpr(alignof(T) <= __STDCPP_DEFAULT_NEW_ALIGNMENT__){
return reinterpret_cast<pointer>(::operator new(bytes));
}else{
return reinterpret_cast<pointer>(::operator new(bytes, static_cast<std::align_val_t>(alignof(T))));
}
} //if consteval
}
REXY_CPP20_CONSTEXPR void deallocate(pointer p, size_type n){
REXY_if_consteval{
std::allocator<value_type> a;
const size_type num_items = n / sizeof(value_type);
return a.deallocate(p, num_items);
}else{
#if !defined(__cpp_sized_deallocation)
//clang does not enable ::operator delete(void* ptr, std::size_t sz) by default for some reason
if constexpr(alignof(T) <= __STDCPP_DEFAULT_NEW_ALIGNMENT__){
::operator delete(p);
}else{
::operator delete(p, static_cast<std::align_val_t>(alignof(T)));
}
#else //__cpp_sized_deallocation
size_type bytes = has_overflow(n) ? std::numeric_limits<size_type>::max() : n*sizeof(T);
if constexpr(alignof(T) <= __STDCPP_DEFAULT_NEW_ALIGNMENT__){
::operator delete(p, bytes);
}else{
::operator delete(p, bytes, static_cast<std::align_val_t>(alignof(T)));
}
#endif //__cpp_sized_deallocation
} //if consteval
}
#else //__cplusplus
REXY_CPP20_CONSTEXPR pointer allocate(size_type n){
size_type bytes = has_overflow(n) ? std::numeric_limits<size_type>::max() : n*sizeof(T);
if constexpr(alignof(T) <= __STDCPP_DEFAULT_NEW_ALIGNMENT__){
return reinterpret_cast<pointer>(::operator new(bytes));
}else{
return reinterpret_cast<pointer>(::operator new(bytes, static_cast<std::align_val_t>(alignof(T))));
}
}
REXY_CPP20_CONSTEXPR void deallocate(pointer p, size_type n){
#if !defined(__cpp_sized_deallocation)
//clang does not enable ::operator delete(void* ptr, std::size_t sz) by default for some reason
if constexpr(alignof(T) <= __STDCPP_DEFAULT_NEW_ALIGNMENT__){
::operator delete(p);
}else{
::operator delete(p, static_cast<std::align_val_t>(alignof(T)));
}
#else //__cpp_sized_deallocation
size_type bytes = has_overflow(n) ? std::numeric_limits<size_type>::max() : n*sizeof(T);
if constexpr(alignof(T) <= __STDCPP_DEFAULT_NEW_ALIGNMENT__){
::operator delete(p, bytes);
}else{
::operator delete(p, bytes, static_cast<std::align_val_t>(alignof(T)));
}
#endif //__cpp_sized_deallocation
}
#endif //__cplusplus
constexpr size_type max_size(void)const{
return std::numeric_limits<size_type>::max()/sizeof(T);
}
};
template<class T, class U> template<class T, class U>
constexpr bool operator==(const allocator<T>&, const allocator<U>&){ constexpr bool operator==(const allocator<T>&, const allocator<U>&){
return true; return true;

View File

@ -16,23 +16,8 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#ifndef REXY_CONCEPTS_ALLOCATOR_HPP #if __cplusplus >= 202002L
#define REXY_CONCEPTS_ALLOCATOR_HPP #include "cpp20/allocator.hpp"
#else
#include <concepts> //convertible_to #include "cpp17/allocator.hpp"
namespace rexy{
template<class T>
concept Allocator = requires(T a, typename T::size_type sz, typename T::pointer pa){
typename T::value_type;
typename T::size_type;
typename T::pointer;
typename T::const_pointer;
{a.allocate(sz)} -> std::convertible_to<typename T::pointer>;
{a.deallocate(pa, sz)};
};
}
#endif #endif

View File

@ -0,0 +1,100 @@
/**
This file is a part of rexy's general purpose library
Copyright (C) 2022 rexy712
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef REXY_COMPAT_CPP17_DEFAULT_ALLOCATOR_HPP
#define REXY_COMPAT_CPP17_DEFAULT_ALLOCATOR_HPP
#include <cstddef> //ptrdiff_t, size_t
#include <type_traits> //true_type, false_type
#include <new>
#include <limits> //numeric_limits
#include "../../rexy.hpp"
#define REXY_ALLOCATOR_CONCEPT class
namespace rexy{
template<class T>
struct allocator
{
using pointer = T*;
using const_pointer = const T*;
using void_pointer = void*;
using const_void_pointer = const void*;
using value_type = T;
using size_type = std::size_t;
using difference_type = ptrdiff_t;
using is_always_equal = std::true_type;
using propagate_on_container_copy_assignment = std::false_type;
using propagate_on_container_move_assignment = std::false_type;
using propagate_on_container_swap = std::false_type;
template<class U>
struct rebind{
using other = allocator<U>;
};
private:
constexpr bool has_overflow(size_type n)const{
return n > max_size();
}
public:
allocator(void) = default;
allocator(const allocator&) = default;
allocator(allocator&&) = default;
template<class U>
constexpr allocator(const allocator<U>&)noexcept{}
~allocator(void) = default;
pointer allocate(size_type n){
size_type bytes = has_overflow(n) ? std::numeric_limits<size_type>::max() : n*sizeof(T);
if constexpr(alignof(T) <= __STDCPP_DEFAULT_NEW_ALIGNMENT__){
return reinterpret_cast<pointer>(::operator new(bytes));
}else{
return reinterpret_cast<pointer>(::operator new(bytes, static_cast<std::align_val_t>(alignof(T))));
}
}
void deallocate(pointer p, size_type n){
#if !defined(__cpp_sized_deallocation)
//clang does not enable ::operator delete(void* ptr, std::size_t sz) by default for some reason
if constexpr(alignof(T) <= __STDCPP_DEFAULT_NEW_ALIGNMENT__){
::operator delete(p);
}else{
::operator delete(p, static_cast<std::align_val_t>(alignof(T)));
}
#else //__cpp_sized_deallocation
size_type bytes = has_overflow(n) ? std::numeric_limits<size_type>::max() : n*sizeof(T);
if constexpr(alignof(T) <= __STDCPP_DEFAULT_NEW_ALIGNMENT__){
::operator delete(p, bytes);
}else{
::operator delete(p, bytes, static_cast<std::align_val_t>(alignof(T)));
}
#endif //__cpp_sized_deallocation
}
constexpr size_type max_size(void)const{
return std::numeric_limits<size_type>::max()/sizeof(T);
}
};
}
#endif

View File

@ -25,9 +25,17 @@
#include <type_traits> //{false,true}_type, declval, enable_if, remove_reference, decay #include <type_traits> //{false,true}_type, declval, enable_if, remove_reference, decay
#include "../../utility.hpp" //strlen, strncmp #include "../../utility.hpp" //strlen, strncmp
#include "../../traits.hpp"
namespace rexy{ namespace rexy{
template<class Left, class Right>
class string_cat_expr;
template<class Char, REXY_ALLOCATOR_CONCEPT Alloc>
class basic_string;
template<class Char>
class basic_string_view;
#define REXY_HAS_MEMFUN_WITH_RET(type, ret, fun, ...) \ #define REXY_HAS_MEMFUN_WITH_RET(type, ret, fun, ...) \
template<class T> \ template<class T> \
struct has_##fun##_f{ \ struct has_##fun##_f{ \
@ -62,9 +70,35 @@ namespace rexy{
#undef REXY_HAS_MEMFUN_WITH_RET #undef REXY_HAS_MEMFUN_WITH_RET
#undef REXY_HAS_MEMOP_WITH_RET #undef REXY_HAS_MEMOP_WITH_RET
template<class T>
struct is_basic_string{
template<class Char, class Alloc>
static std::true_type check(const basic_string<Char,Alloc>*);
static std::false_type check(...);
static constexpr bool value = decltype(check(std::declval<rexy::remove_cvref_t<T>*>()))::value;
};
template<class T>
struct is_basic_string_view{
template<class Char>
static std::true_type check(const basic_string_view<Char>*);
static std::false_type check(...);
static constexpr bool value = decltype(check(std::declval<rexy::remove_cvref_t<T>*>()))::value;
};
template<class T>
struct is_basic_string_expr{
template<class Left, class Right>
static std::true_type check(const string_cat_expr<Left,Right>*);
static std::false_type check(...);
static constexpr bool value = decltype(check(std::declval<rexy::remove_cvref_t<T>*>()))::value;
};
template<class T> template<class T>
struct is_string{ struct is_string{
static constexpr bool value = has_length_f_v<T> && has_c_str_f_v<T> && has_indexop_f_v<T> && has_begin_f_v<T> && has_end_f_v<T>; static constexpr bool value = (is_basic_string<T>::value || is_basic_string_view<T>::value) &&
(has_length_f_v<T> && has_c_str_f_v<T> && has_indexop_f_v<T> && has_begin_f_v<T> && has_end_f_v<T>);
}; };
template<class T> template<class T>
static constexpr bool is_string_v = is_string<T>::value; static constexpr bool is_string_v = is_string<T>::value;
@ -77,7 +111,7 @@ namespace rexy{
static constexpr bool are_strings_v = are_strings<Ts...>::value; static constexpr bool are_strings_v = are_strings<Ts...>::value;
template<class T> template<class T>
struct is_string_expr{ struct is_string_expr{
static constexpr bool value = rexy::is_template_type<T,string_cat_expr>::value; static constexpr bool value = is_basic_string_expr<T>::value;
}; };
template<class T> template<class T>
static constexpr bool is_string_expr_v = is_string_expr<T>::value; static constexpr bool is_string_expr_v = is_string_expr<T>::value;

View File

@ -0,0 +1,139 @@
/**
This file is a part of rexy's general purpose library
Copyright (C) 2022 rexy712
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef REXY_COMPAT_CPP20_DEFAULT_ALLOCATOR_HPP
#define REXY_COMPAT_CPP20_DEFAULT_ALLOCATOR_HPP
#include <cstddef> //ptrdiff_t, size_t
#include <type_traits> //true_type, false_type
#include <new>
#include <limits> //numeric_limits
#include <memory> //allocator
#ifdef __cpp_concepts
#include <concepts> //convertible_to
template<class T>
concept Allocator = requires(T a, typename T::size_type sz, typename T::pointer pa){
typename T::value_type;
typename T::size_type;
typename T::pointer;
typename T::const_pointer;
{a.allocate(sz)} -> std::convertible_to<typename T::pointer>;
{a.deallocate(pa, sz)};
};
#define REXY_ALLOCATOR_CONCEPT Allocator
#else //__cpp_concepts
#define REXY_ALLOCATOR_CONCEPT class
#endif // __cpp_concepts
#include "../../rexy.hpp"
#include "../if_consteval.hpp"
namespace rexy{
template<class T>
struct allocator
{
using pointer = T*;
using const_pointer = const T*;
using void_pointer = void*;
using const_void_pointer = const void*;
using value_type = T;
using size_type = std::size_t;
using difference_type = std::ptrdiff_t;
using is_always_equal = std::true_type;
using propagate_on_container_copy_assignment = std::false_type;
using propagate_on_container_move_assignment = std::false_type;
using propagate_on_container_swap = std::false_type;
template<class U>
struct rebind{
using other = allocator<U>;
};
private:
constexpr bool has_overflow(size_type n)const{
return n > max_size();
}
public:
constexpr allocator(void) = default;
constexpr allocator(const allocator&) = default;
constexpr allocator(allocator&&) = default;
template<class U>
constexpr allocator(const allocator<U>&)noexcept{}
constexpr ~allocator(void) = default;
//'::operator new' is never a constexpr call as of C++23. However, 'std::allocator::allocate' *is* transiently constexpr as of C++20,
//even though it directly calls '::operator new' as is stated in the standard. Therefore, when evaluating this call with support for
//'if consteval', we can use the 'std::allocator::allocate' constexpr-ness when this is in a constant evaluation context.
constexpr pointer allocate(size_type n){
size_type bytes = has_overflow(n) ? std::numeric_limits<size_type>::max() : n*sizeof(T);
REXY_if_consteval{
std::allocator<value_type> a;
const size_type num_items = n / sizeof(value_type);
return a.allocate(num_items);
}else{
if constexpr(alignof(T) <= __STDCPP_DEFAULT_NEW_ALIGNMENT__){
return reinterpret_cast<pointer>(::operator new(bytes));
}else{
return reinterpret_cast<pointer>(::operator new(bytes, static_cast<std::align_val_t>(alignof(T))));
}
} //if consteval
}
constexpr void deallocate(pointer p, size_type n){
REXY_if_consteval{
std::allocator<value_type> a;
const size_type num_items = n / sizeof(value_type);
return a.deallocate(p, num_items);
}else{
#if !defined(__cpp_sized_deallocation)
//clang does not enable ::operator delete(void* ptr, std::size_t sz) by default for some reason
if constexpr(alignof(T) <= __STDCPP_DEFAULT_NEW_ALIGNMENT__){
::operator delete(p);
}else{
::operator delete(p, static_cast<std::align_val_t>(alignof(T)));
}
#else //__cpp_sized_deallocation
size_type bytes = has_overflow(n) ? std::numeric_limits<size_type>::max() : n*sizeof(T);
if constexpr(alignof(T) <= __STDCPP_DEFAULT_NEW_ALIGNMENT__){
::operator delete(p, bytes);
}else{
::operator delete(p, bytes, static_cast<std::align_val_t>(alignof(T)));
}
#endif //__cpp_sized_deallocation
} //if consteval
}
constexpr size_type max_size(void)const{
return std::numeric_limits<size_type>::max()/sizeof(T);
}
};
}
#endif

View File

@ -24,11 +24,71 @@
#include <utility> //forward #include <utility> //forward
#include <type_traits> //decay, is_nothrow_assignable #include <type_traits> //decay, is_nothrow_assignable
#include "../../concepts/string.hpp"
#include "../../utility.hpp" //strlen, strncmp #include "../../utility.hpp" //strlen, strncmp
namespace rexy{ namespace rexy{
template<class Left, class Right>
class string_cat_expr;
template<class Char, REXY_ALLOCATOR_CONCEPT Alloc>
class basic_string;
template<class Char>
class basic_string_view;
template<class T>
struct is_basic_string{
template<class Char, class Alloc>
static std::true_type check(const basic_string<Char,Alloc>*);
static std::false_type check(...);
static constexpr bool value = decltype(check(std::declval<std::remove_cvref_t<T>*>()))::value;
};
template<class T>
struct is_basic_string_view{
template<class Char>
static std::true_type check(const basic_string_view<Char>*);
static std::false_type check(...);
static constexpr bool value = decltype(check(std::declval<std::remove_cvref_t<T>*>()))::value;
};
template<class T>
struct is_basic_string_expr{
template<class Left, class Right>
static std::true_type check(const string_cat_expr<Left,Right>*);
static std::false_type check(...);
static constexpr bool value = decltype(check(std::declval<std::remove_cvref_t<T>*>()))::value;
};
template<class T>
concept BasicString = requires(const T& a){
requires(is_basic_string<T>::value || is_basic_string_view<T>::value);
{std::as_const(a).length()} -> std::convertible_to<typename std::decay_t<T>::size_type>;
{std::as_const(a).c_str()} -> std::convertible_to<typename std::decay_t<T>::const_pointer>;
{std::as_const(a)[0]} -> std::convertible_to<typename std::decay_t<T>::const_reference>;
{std::as_const(a).begin()} -> std::convertible_to<typename std::decay_t<T>::const_iterator>;
{std::as_const(a).end()} -> std::convertible_to<typename std::decay_t<T>::const_iterator>;
};
template<class T>
concept StringExpr = is_basic_string_expr<T>::value;
template<class T>
concept String = BasicString<T> || StringExpr<T>;
template<class T>
struct is_string{
static constexpr bool value = BasicString<T>;
};
template<class T>
static constexpr bool is_string_v = is_string<T>::value;
template<class T>
struct is_string_expr{
static constexpr bool value = StringExpr<T>;
};
template<class T>
static constexpr bool is_string_expr_v = is_string_expr<T>::value;
//Compare //Compare
template<BasicString Str1, BasicString Str2> template<BasicString Str1, BasicString Str2>
constexpr bool operator==(Str1&& left, Str2&& right){ constexpr bool operator==(Str1&& left, Str2&& right){

View File

@ -1,105 +0,0 @@
/**
This file is a part of rexy's general purpose library
Copyright (C) 2022 rexy712
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef REXY_CONCEPTS_STRING_HPP
#define REXY_CONCEPTS_STRING_HPP
#include <type_traits> //decay
#include <concepts> //convertible_to
#include <utility> //as_const
#include "../traits.hpp"
namespace rexy{
template<class Left, class Right>
class string_cat_expr;
template<class Char, REXY_ALLOCATOR_CONCEPT Alloc>
class basic_string;
template<class Char>
class basic_string_view;
template<class T>
struct is_basic_string{
template<class Char, class Alloc>
static std::true_type check(const basic_string<Char,Alloc>*);
static std::false_type check(...);
static constexpr bool value = decltype(check(std::declval<std::remove_cvref_t<T>*>()))::value;
};
template<class T>
struct is_basic_string_view{
template<class Char>
static std::true_type check(const basic_string_view<Char>*);
static std::false_type check(...);
static constexpr bool value = decltype(check(std::declval<std::remove_cvref_t<T>*>()))::value;
};
template<class T>
struct is_basic_string_expr{
template<class Left, class Right>
static std::true_type check(const string_cat_expr<Left,Right>*);
static std::false_type check(...);
static constexpr bool value = decltype(check(std::declval<std::remove_cvref_t<T>*>()))::value;
};
template<class T>
concept BasicString = requires(const T& a){
requires(is_basic_string<T>::value || is_basic_string_view<T>::value);
{std::as_const(a).length()} -> std::convertible_to<typename std::decay_t<T>::size_type>;
{std::as_const(a).c_str()} -> std::convertible_to<typename std::decay_t<T>::const_pointer>;
{std::as_const(a)[0]} -> std::convertible_to<typename std::decay_t<T>::const_reference>;
{std::as_const(a).begin()} -> std::convertible_to<typename std::decay_t<T>::const_iterator>;
{std::as_const(a).end()} -> std::convertible_to<typename std::decay_t<T>::const_iterator>;
};
template<class T>
concept StringExpr = is_basic_string_expr<T>::value;
template<class T>
concept String = BasicString<T> || StringExpr<T>;
template<class T>
struct is_string{
static constexpr bool value = BasicString<T>;
};
template<class T>
static constexpr bool is_string_v = is_string<T>::value;
template<class... Ts>
struct are_strings{
static constexpr bool value = (BasicString<Ts> && ...);
};
template<class... Ts>
static constexpr bool are_strings_v = are_strings<Ts...>::value;
template<class T>
struct is_string_expr{
static constexpr bool value = StringExpr<T>;
};
template<class T>
static constexpr bool is_string_expr_v = is_string_expr<T>::value;
template<class... Ts>
struct are_string_expr{
static constexpr bool value = (StringExpr<Ts> && ...);
};
template<class... Ts>
static constexpr bool are_string_expr_v = are_string_expr<Ts...>::value;
}
#endif