From 7f47a4cbf84f705cc5329f549879b2720c1c1edf Mon Sep 17 00:00:00 2001 From: rexy712 Date: Sun, 26 Jun 2022 11:55:35 -0700 Subject: [PATCH] Make allocator and string_base compat easier to read and remove unneccessary concepts subdirectory --- include/rexy/allocator.hpp | 133 +---------------- .../rexy/{concepts => compat}/allocator.hpp | 23 +-- include/rexy/compat/cpp17/allocator.hpp | 100 +++++++++++++ include/rexy/compat/cpp17/string_base.hpp | 38 ++++- include/rexy/compat/cpp20/allocator.hpp | 139 ++++++++++++++++++ include/rexy/compat/cpp20/string_base.hpp | 62 +++++++- include/rexy/concepts/string.hpp | 105 ------------- 7 files changed, 341 insertions(+), 259 deletions(-) rename include/rexy/{concepts => compat}/allocator.hpp (62%) create mode 100644 include/rexy/compat/cpp17/allocator.hpp create mode 100644 include/rexy/compat/cpp20/allocator.hpp delete mode 100644 include/rexy/concepts/string.hpp diff --git a/include/rexy/allocator.hpp b/include/rexy/allocator.hpp index 35adb86..fcdf3df 100644 --- a/include/rexy/allocator.hpp +++ b/include/rexy/allocator.hpp @@ -19,143 +19,12 @@ #ifndef REXY_DEFAULT_ALLOCATOR_HPP #define REXY_DEFAULT_ALLOCATOR_HPP -#include //ptrdiff_t, size_t -#include //true_type, false_type -#include -#include //numeric_limits - -#if __cplusplus >= 202002L - #include //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 "compat/allocator.hpp" #include //declval namespace rexy{ - template - 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 - struct rebind{ - using other = allocator; - }; - - 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 - constexpr allocator(const allocator&)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::max() : n*sizeof(T); - - REXY_if_consteval{ - std::allocator 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(::operator new(bytes)); - }else{ - return reinterpret_cast(::operator new(bytes, static_cast(alignof(T)))); - } - } //if consteval - } - - - REXY_CPP20_CONSTEXPR void deallocate(pointer p, size_type n){ - REXY_if_consteval{ - std::allocator 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(alignof(T))); - } - #else //__cpp_sized_deallocation - size_type bytes = has_overflow(n) ? std::numeric_limits::max() : n*sizeof(T); - if constexpr(alignof(T) <= __STDCPP_DEFAULT_NEW_ALIGNMENT__){ - ::operator delete(p, bytes); - }else{ - ::operator delete(p, bytes, static_cast(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::max() : n*sizeof(T); - - if constexpr(alignof(T) <= __STDCPP_DEFAULT_NEW_ALIGNMENT__){ - return reinterpret_cast(::operator new(bytes)); - }else{ - return reinterpret_cast(::operator new(bytes, static_cast(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(alignof(T))); - } - #else //__cpp_sized_deallocation - size_type bytes = has_overflow(n) ? std::numeric_limits::max() : n*sizeof(T); - if constexpr(alignof(T) <= __STDCPP_DEFAULT_NEW_ALIGNMENT__){ - ::operator delete(p, bytes); - }else{ - ::operator delete(p, bytes, static_cast(alignof(T))); - } - #endif //__cpp_sized_deallocation - - } -#endif //__cplusplus - constexpr size_type max_size(void)const{ - return std::numeric_limits::max()/sizeof(T); - } - }; template constexpr bool operator==(const allocator&, const allocator&){ return true; diff --git a/include/rexy/concepts/allocator.hpp b/include/rexy/compat/allocator.hpp similarity index 62% rename from include/rexy/concepts/allocator.hpp rename to include/rexy/compat/allocator.hpp index da5aa73..d76df2c 100644 --- a/include/rexy/concepts/allocator.hpp +++ b/include/rexy/compat/allocator.hpp @@ -16,23 +16,8 @@ along with this program. If not, see . */ -#ifndef REXY_CONCEPTS_ALLOCATOR_HPP -#define REXY_CONCEPTS_ALLOCATOR_HPP - -#include //convertible_to - -namespace rexy{ - - template - 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; - {a.deallocate(pa, sz)}; - }; - -} - +#if __cplusplus >= 202002L + #include "cpp20/allocator.hpp" +#else + #include "cpp17/allocator.hpp" #endif diff --git a/include/rexy/compat/cpp17/allocator.hpp b/include/rexy/compat/cpp17/allocator.hpp new file mode 100644 index 0000000..23d556d --- /dev/null +++ b/include/rexy/compat/cpp17/allocator.hpp @@ -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 . +*/ + +#ifndef REXY_COMPAT_CPP17_DEFAULT_ALLOCATOR_HPP +#define REXY_COMPAT_CPP17_DEFAULT_ALLOCATOR_HPP + +#include //ptrdiff_t, size_t +#include //true_type, false_type +#include +#include //numeric_limits + +#include "../../rexy.hpp" + +#define REXY_ALLOCATOR_CONCEPT class + +namespace rexy{ + + template + 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 + struct rebind{ + using other = allocator; + }; + + 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 + constexpr allocator(const allocator&)noexcept{} + ~allocator(void) = default; + + pointer allocate(size_type n){ + size_type bytes = has_overflow(n) ? std::numeric_limits::max() : n*sizeof(T); + + if constexpr(alignof(T) <= __STDCPP_DEFAULT_NEW_ALIGNMENT__){ + return reinterpret_cast(::operator new(bytes)); + }else{ + return reinterpret_cast(::operator new(bytes, static_cast(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(alignof(T))); + } + #else //__cpp_sized_deallocation + size_type bytes = has_overflow(n) ? std::numeric_limits::max() : n*sizeof(T); + if constexpr(alignof(T) <= __STDCPP_DEFAULT_NEW_ALIGNMENT__){ + ::operator delete(p, bytes); + }else{ + ::operator delete(p, bytes, static_cast(alignof(T))); + } + #endif //__cpp_sized_deallocation + + } + constexpr size_type max_size(void)const{ + return std::numeric_limits::max()/sizeof(T); + } + }; + +} + +#endif diff --git a/include/rexy/compat/cpp17/string_base.hpp b/include/rexy/compat/cpp17/string_base.hpp index 3db23cd..ffced5e 100644 --- a/include/rexy/compat/cpp17/string_base.hpp +++ b/include/rexy/compat/cpp17/string_base.hpp @@ -25,9 +25,17 @@ #include //{false,true}_type, declval, enable_if, remove_reference, decay #include "../../utility.hpp" //strlen, strncmp +#include "../../traits.hpp" namespace rexy{ + template + class string_cat_expr; + template + class basic_string; + template + class basic_string_view; + #define REXY_HAS_MEMFUN_WITH_RET(type, ret, fun, ...) \ template \ struct has_##fun##_f{ \ @@ -62,9 +70,35 @@ namespace rexy{ #undef REXY_HAS_MEMFUN_WITH_RET #undef REXY_HAS_MEMOP_WITH_RET + template + struct is_basic_string{ + template + static std::true_type check(const basic_string*); + static std::false_type check(...); + + static constexpr bool value = decltype(check(std::declval*>()))::value; + }; + template + struct is_basic_string_view{ + template + static std::true_type check(const basic_string_view*); + static std::false_type check(...); + + static constexpr bool value = decltype(check(std::declval*>()))::value; + }; + template + struct is_basic_string_expr{ + template + static std::true_type check(const string_cat_expr*); + static std::false_type check(...); + + static constexpr bool value = decltype(check(std::declval*>()))::value; + }; + template struct is_string{ - static constexpr bool value = has_length_f_v && has_c_str_f_v && has_indexop_f_v && has_begin_f_v && has_end_f_v; + static constexpr bool value = (is_basic_string::value || is_basic_string_view::value) && + (has_length_f_v && has_c_str_f_v && has_indexop_f_v && has_begin_f_v && has_end_f_v); }; template static constexpr bool is_string_v = is_string::value; @@ -77,7 +111,7 @@ namespace rexy{ static constexpr bool are_strings_v = are_strings::value; template struct is_string_expr{ - static constexpr bool value = rexy::is_template_type::value; + static constexpr bool value = is_basic_string_expr::value; }; template static constexpr bool is_string_expr_v = is_string_expr::value; diff --git a/include/rexy/compat/cpp20/allocator.hpp b/include/rexy/compat/cpp20/allocator.hpp new file mode 100644 index 0000000..32297dc --- /dev/null +++ b/include/rexy/compat/cpp20/allocator.hpp @@ -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 . +*/ + +#ifndef REXY_COMPAT_CPP20_DEFAULT_ALLOCATOR_HPP +#define REXY_COMPAT_CPP20_DEFAULT_ALLOCATOR_HPP + +#include //ptrdiff_t, size_t +#include //true_type, false_type +#include +#include //numeric_limits +#include //allocator + +#ifdef __cpp_concepts + + #include //convertible_to + template + 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; + {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 + 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 + struct rebind{ + using other = allocator; + }; + + 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 + constexpr allocator(const allocator&)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::max() : n*sizeof(T); + + REXY_if_consteval{ + std::allocator 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(::operator new(bytes)); + }else{ + return reinterpret_cast(::operator new(bytes, static_cast(alignof(T)))); + } + } //if consteval + } + + + constexpr void deallocate(pointer p, size_type n){ + REXY_if_consteval{ + std::allocator 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(alignof(T))); + } + #else //__cpp_sized_deallocation + size_type bytes = has_overflow(n) ? std::numeric_limits::max() : n*sizeof(T); + if constexpr(alignof(T) <= __STDCPP_DEFAULT_NEW_ALIGNMENT__){ + ::operator delete(p, bytes); + }else{ + ::operator delete(p, bytes, static_cast(alignof(T))); + } + #endif //__cpp_sized_deallocation + + } //if consteval + + } + constexpr size_type max_size(void)const{ + return std::numeric_limits::max()/sizeof(T); + } + }; + +} + +#endif diff --git a/include/rexy/compat/cpp20/string_base.hpp b/include/rexy/compat/cpp20/string_base.hpp index ce95b08..9745cf4 100644 --- a/include/rexy/compat/cpp20/string_base.hpp +++ b/include/rexy/compat/cpp20/string_base.hpp @@ -24,11 +24,71 @@ #include //forward #include //decay, is_nothrow_assignable -#include "../../concepts/string.hpp" #include "../../utility.hpp" //strlen, strncmp namespace rexy{ + template + class string_cat_expr; + template + class basic_string; + template + class basic_string_view; + + template + struct is_basic_string{ + template + static std::true_type check(const basic_string*); + static std::false_type check(...); + + static constexpr bool value = decltype(check(std::declval*>()))::value; + }; + + template + struct is_basic_string_view{ + template + static std::true_type check(const basic_string_view*); + static std::false_type check(...); + + static constexpr bool value = decltype(check(std::declval*>()))::value; + }; + + template + struct is_basic_string_expr{ + template + static std::true_type check(const string_cat_expr*); + static std::false_type check(...); + + static constexpr bool value = decltype(check(std::declval*>()))::value; + }; + + template + concept BasicString = requires(const T& a){ + requires(is_basic_string::value || is_basic_string_view::value); + {std::as_const(a).length()} -> std::convertible_to::size_type>; + {std::as_const(a).c_str()} -> std::convertible_to::const_pointer>; + {std::as_const(a)[0]} -> std::convertible_to::const_reference>; + {std::as_const(a).begin()} -> std::convertible_to::const_iterator>; + {std::as_const(a).end()} -> std::convertible_to::const_iterator>; + }; + template + concept StringExpr = is_basic_string_expr::value; + template + concept String = BasicString || StringExpr; + + template + struct is_string{ + static constexpr bool value = BasicString; + }; + template + static constexpr bool is_string_v = is_string::value; + template + struct is_string_expr{ + static constexpr bool value = StringExpr; + }; + template + static constexpr bool is_string_expr_v = is_string_expr::value; + //Compare template constexpr bool operator==(Str1&& left, Str2&& right){ diff --git a/include/rexy/concepts/string.hpp b/include/rexy/concepts/string.hpp deleted file mode 100644 index 503d90d..0000000 --- a/include/rexy/concepts/string.hpp +++ /dev/null @@ -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 . -*/ - -#ifndef REXY_CONCEPTS_STRING_HPP -#define REXY_CONCEPTS_STRING_HPP - -#include //decay -#include //convertible_to -#include //as_const - -#include "../traits.hpp" - -namespace rexy{ - - template - class string_cat_expr; - template - class basic_string; - template - class basic_string_view; - - template - struct is_basic_string{ - template - static std::true_type check(const basic_string*); - static std::false_type check(...); - - static constexpr bool value = decltype(check(std::declval*>()))::value; - }; - - template - struct is_basic_string_view{ - template - static std::true_type check(const basic_string_view*); - static std::false_type check(...); - - static constexpr bool value = decltype(check(std::declval*>()))::value; - }; - - template - struct is_basic_string_expr{ - template - static std::true_type check(const string_cat_expr*); - static std::false_type check(...); - - static constexpr bool value = decltype(check(std::declval*>()))::value; - }; - - template - concept BasicString = requires(const T& a){ - requires(is_basic_string::value || is_basic_string_view::value); - {std::as_const(a).length()} -> std::convertible_to::size_type>; - {std::as_const(a).c_str()} -> std::convertible_to::const_pointer>; - {std::as_const(a)[0]} -> std::convertible_to::const_reference>; - {std::as_const(a).begin()} -> std::convertible_to::const_iterator>; - {std::as_const(a).end()} -> std::convertible_to::const_iterator>; - }; - template - concept StringExpr = is_basic_string_expr::value; - template - concept String = BasicString || StringExpr; - - template - struct is_string{ - static constexpr bool value = BasicString; - }; - template - static constexpr bool is_string_v = is_string::value; - template - struct are_strings{ - static constexpr bool value = (BasicString && ...); - }; - template - static constexpr bool are_strings_v = are_strings::value; - template - struct is_string_expr{ - static constexpr bool value = StringExpr; - }; - template - static constexpr bool is_string_expr_v = is_string_expr::value; - template - struct are_string_expr{ - static constexpr bool value = (StringExpr && ...); - }; - template - static constexpr bool are_string_expr_v = are_string_expr::value; - -} - -#endif