From 2578895b4006135125023039c6a60f427f13368a Mon Sep 17 00:00:00 2001 From: rexy712 Date: Tue, 24 May 2022 17:24:55 -0700 Subject: [PATCH] More general cleanup of conditional C++20 support. Add Allocator concept and apply it to strings. --- include/rexy/allocator.hpp | 26 +++- include/rexy/compat/cpp17/source_location.hpp | 2 +- include/rexy/compat/standard.hpp | 6 +- include/rexy/concepts/allocator.hpp | 38 +++++ include/rexy/detail/hasallocator.hpp | 26 +++- include/rexy/enum_traits.hpp | 7 +- include/rexy/storage_for.hpp | 2 +- include/rexy/storage_for.tpp | 2 +- include/rexy/string_base.hpp | 24 ++- include/rexy/string_base.tpp | 141 +++++++++--------- tests/CMakeLists.txt | 2 +- tests/basic_string.cpp | 1 + 12 files changed, 174 insertions(+), 103 deletions(-) create mode 100644 include/rexy/concepts/allocator.hpp diff --git a/include/rexy/allocator.hpp b/include/rexy/allocator.hpp index 6128b41..2bfda3b 100644 --- a/include/rexy/allocator.hpp +++ b/include/rexy/allocator.hpp @@ -1,6 +1,6 @@ /** This file is a part of rexy's general purpose library - Copyright (C) 2020 rexy712 + Copyright (C) 2020-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 @@ -19,12 +19,22 @@ #ifndef REXY_DEFAULT_ALLOCATOR_HPP #define REXY_DEFAULT_ALLOCATOR_HPP -#include //size_t -#include //ptrdiff_t -#include //true_type, false_type +#include //ptrdiff_t, size_t +#include //true_type, false_type, is_constant_evaluated #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" @@ -80,9 +90,9 @@ namespace rexy{ return a.allocate(num_items); }else{ if constexpr(alignof(T) <= __STDCPP_DEFAULT_NEW_ALIGNMENT__){ - return static_cast(::operator new(bytes)); + return reinterpret_cast(::operator new(bytes)); }else{ - return static_cast(::operator new(bytes, static_cast(alignof(T)))); + return reinterpret_cast(::operator new(bytes, static_cast(alignof(T)))); } } //if consteval } @@ -123,9 +133,9 @@ namespace rexy{ size_type bytes = has_overflow(n) ? std::numeric_limits::max() : n*sizeof(T); if constexpr(alignof(T) <= __STDCPP_DEFAULT_NEW_ALIGNMENT__){ - return static_cast(::operator new(bytes)); + return reinterpret_cast(::operator new(bytes)); }else{ - return static_cast(::operator new(bytes, static_cast(alignof(T)))); + return reinterpret_cast(::operator new(bytes, static_cast(alignof(T)))); } } REXY_CPP20_CONSTEXPR void deallocate(pointer p, size_type n){ diff --git a/include/rexy/compat/cpp17/source_location.hpp b/include/rexy/compat/cpp17/source_location.hpp index 9965db8..ae74dee 100644 --- a/include/rexy/compat/cpp17/source_location.hpp +++ b/include/rexy/compat/cpp17/source_location.hpp @@ -22,7 +22,7 @@ #include //uint_least32_t //clang bug: https://bugs.llvm.org/show_bug.cgi?id=48886 -#if __cplusplus >= 202002L && !defined(__clang__) +#if defined(__cpp_consteval) && !defined(__clang__) #define CONSTEVAL consteval #else #define CONSTEVAL constexpr diff --git a/include/rexy/compat/standard.hpp b/include/rexy/compat/standard.hpp index 616b3f4..0082b85 100644 --- a/include/rexy/compat/standard.hpp +++ b/include/rexy/compat/standard.hpp @@ -19,13 +19,13 @@ #ifndef REXY_COMPAT_CPP20_HPP #define REXY_COMPAT_CPP20_HPP - #if __cplusplus >= 202002L + #ifdef __cpp_consteval #define REXY_CPP20_CONSTEXPR constexpr #define REXY_CPP20_CONSTEVAL consteval - #else //__cplusplus + #else //__cpp_consteval #define REXY_CPP20_CONSTEXPR #define REXY_CPP20_CONSTEVAL constexpr - #endif //__cplusplus + #endif //__cpp_consteval #if __cplusplus >= 202300L #define REXY_STANDARD_CPP23 diff --git a/include/rexy/concepts/allocator.hpp b/include/rexy/concepts/allocator.hpp new file mode 100644 index 0000000..da5aa73 --- /dev/null +++ b/include/rexy/concepts/allocator.hpp @@ -0,0 +1,38 @@ +/** + 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_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)}; + }; + +} + +#endif diff --git a/include/rexy/detail/hasallocator.hpp b/include/rexy/detail/hasallocator.hpp index 4bdb123..34a7731 100644 --- a/include/rexy/detail/hasallocator.hpp +++ b/include/rexy/detail/hasallocator.hpp @@ -20,10 +20,13 @@ #define REXY_DETAIL_HASALLOCATOR_HPP #include "../compat/standard.hpp" +#include "../allocator.hpp" + +#include //is_empty namespace rexy::detail{ - template + template> struct hasallocator { Alloc m_alloc; @@ -42,7 +45,28 @@ namespace rexy::detail{ return m_alloc; } }; +#if __has_cpp_attribute(no_unique_address) + template + struct hasallocator + { + [[no_unique_address]] + Alloc m_alloc; + REXY_CPP20_CONSTEXPR auto allocate(typename Alloc::size_type bytes)noexcept(noexcept(m_alloc.allocate(0))){ + return m_alloc.allocate(bytes); + } + REXY_CPP20_CONSTEXPR void deallocate(typename Alloc::pointer p, typename Alloc::size_type bytes)noexcept(noexcept(m_alloc.deallocate(nullptr,0))){ + m_alloc.deallocate(p, bytes); + } + + Alloc& allocator(void){ + return m_alloc; + } + const Alloc& allocator(void)const{ + return m_alloc; + } + }; +#endif } #endif diff --git a/include/rexy/enum_traits.hpp b/include/rexy/enum_traits.hpp index f04780d..d57f2df 100644 --- a/include/rexy/enum_traits.hpp +++ b/include/rexy/enum_traits.hpp @@ -23,11 +23,12 @@ #include "compat/standard.hpp" -#if __cplusplus < 202002L +#ifndef __cpp_concepts #error "Cannot use enum_traits without C++20 concept support" -#else //__cplusplus +#else //__cpp_concepts + #include @@ -209,6 +210,6 @@ namespace rexy::enum_traits{ } } -#endif //__cplusplus +#endif //__cpp_concepts #endif diff --git a/include/rexy/storage_for.hpp b/include/rexy/storage_for.hpp index 7654acf..d12a8fd 100644 --- a/include/rexy/storage_for.hpp +++ b/include/rexy/storage_for.hpp @@ -44,7 +44,7 @@ namespace rexy{ static constexpr auto size = sizeof(value_type); private: -#if __cplusplus >= 202002L +#if __cplusplus >= 202002L && __has_cpp_attribute(no_unique_address) && __cpp_constexpr >= 202002L union storage_{ [[no_unique_address]] diff --git a/include/rexy/storage_for.tpp b/include/rexy/storage_for.tpp index 06add8c..0ea755a 100644 --- a/include/rexy/storage_for.tpp +++ b/include/rexy/storage_for.tpp @@ -21,7 +21,7 @@ #include //forward, move -#if __cplusplus >= 202002L +#if __cplusplus >= 202002L && __has_cpp_attribute(no_unique_address) && __cpp_constexpr >= 202002L #include //construct_at, destroy_at diff --git a/include/rexy/string_base.hpp b/include/rexy/string_base.hpp index 64baeb3..3d7627f 100644 --- a/include/rexy/string_base.hpp +++ b/include/rexy/string_base.hpp @@ -37,10 +37,6 @@ #include "compat/standard.hpp" -#if __cplusplus >= 202002L - #include -#endif - namespace rexy{ template @@ -72,7 +68,7 @@ namespace rexy{ size_type capacity:(CHAR_BIT*sizeof(size_type)-1); //take away last bit from capacity for islong size_type length; //length of string excluding null terminator constexpr ldata(void)noexcept: - islong(0), + islong(1), capacity(0), length(0){} }; @@ -88,12 +84,12 @@ namespace rexy{ length(0), data{}{} }; - //union of short and long string representations. Default to long representation for wstring_view's use case. + //union of short and long string representations. union combine_data{ ldata l; sdata s; constexpr combine_data(void)noexcept: - l(){} + s(){} }m_data; //direct access to current string data regardless of representation. Increases access speed. @@ -245,8 +241,8 @@ namespace rexy{ //Supplies all functions that string_base can't implement - template - class basic_string : protected detail::hasallocator, public string_base + template + class basic_string : protected detail::hasallocator, public string_base { public: using value_type = typename string_base::value_type; @@ -260,7 +256,7 @@ namespace rexy{ using const_iterator = typename string_base::const_iterator; using reverse_iterator = typename string_base::reverse_iterator; using const_reverse_iterator = typename string_base::const_reverse_iterator; - using allocator_type = Allocator; + using allocator_type = Alloc; private: REXY_CPP20_CONSTEXPR void _copy_construct_string(const_pointer data, size_type len, size_type cap) @@ -325,11 +321,11 @@ namespace rexy{ noexcept(noexcept(this->allocate(0)) && noexcept(this->deallocate(nullptr,0))); - template - REXY_CPP20_CONSTEXPR basic_string substring(size_type start, size_type end)const; + template + REXY_CPP20_CONSTEXPR basic_string substring(size_type start, size_type end)const; REXY_CPP20_CONSTEXPR pointer release(void)noexcept(noexcept(this->allocate(0))); - using detail::hasallocator::allocator; + using detail::hasallocator::allocator; constexpr basic_string_view create_view(void)const noexcept; constexpr basic_string_view create_view(const_iterator start, const_iterator fin)const noexcept; @@ -361,7 +357,7 @@ namespace rexy{ constexpr string_cat_expr(string_cat_expr&&) = default; constexpr size_type length(void)const noexcept; - template + template REXY_CPP20_CONSTEXPR operator basic_string(void) noexcept(std::is_nothrow_constructible, typename basic_string::size_type>::value && std::is_nothrow_invocable>,decltype(*this)>::value); diff --git a/include/rexy/string_base.tpp b/include/rexy/string_base.tpp index 6b02efb..cea097e 100644 --- a/include/rexy/string_base.tpp +++ b/include/rexy/string_base.tpp @@ -29,7 +29,7 @@ #include "compat/to_address.hpp" #include "string_view.hpp" -#if __cplusplus >= 202002L +#ifdef __cpp_concepts #include "compat/cpp20/string_base.hpp" #else #include "compat/cpp17/string_base.hpp" @@ -80,8 +80,8 @@ namespace rexy{ //allocate string if longer than small string capacity, copy otherwise - template - REXY_CPP20_CONSTEXPR void basic_string::_copy_construct_string(const_pointer data, size_type len, size_type cap) + template + REXY_CPP20_CONSTEXPR void basic_string::_copy_construct_string(const_pointer data, size_type len, size_type cap) noexcept(noexcept(this->allocate(0))) { if(cap > this->get_short_capacity()){ @@ -103,51 +103,51 @@ namespace rexy{ } } - template - constexpr basic_string::basic_string(void)noexcept{} - template - constexpr basic_string::basic_string(rexy::steal data)noexcept: + template + constexpr basic_string::basic_string(void)noexcept{} + template + constexpr basic_string::basic_string(rexy::steal data)noexcept: basic_string(data.value(), data.value() ? strlen(data.value()) : 0){} - template - constexpr basic_string::basic_string(rexy::steal data, size_type len)noexcept: + template + constexpr basic_string::basic_string(rexy::steal data, size_type len)noexcept: string_base(data.value(), len, len){} - template - constexpr basic_string::basic_string(rexy::steal data, size_type len, size_type cap)noexcept: + template + constexpr basic_string::basic_string(rexy::steal data, size_type len, size_type cap)noexcept: string_base(data.value(), len, cap){} - template - REXY_CPP20_CONSTEXPR basic_string::basic_string(const_pointer data, size_type len, size_type cap) + template + REXY_CPP20_CONSTEXPR basic_string::basic_string(const_pointer data, size_type len, size_type cap) noexcept(noexcept(this->allocate(0))) { _copy_construct_string(data, len, cap); } - template - REXY_CPP20_CONSTEXPR basic_string::basic_string(const_pointer data, size_type len) + template + REXY_CPP20_CONSTEXPR basic_string::basic_string(const_pointer data, size_type len) noexcept(noexcept(this->allocate(0))): basic_string(data, len, len){} - template - REXY_CPP20_CONSTEXPR basic_string::basic_string(const_pointer data) + template + REXY_CPP20_CONSTEXPR basic_string::basic_string(const_pointer data) noexcept(noexcept(this->allocate(0))): basic_string(data, data ? strlen(data) : 0){} - template - REXY_CPP20_CONSTEXPR basic_string::basic_string(size_type cap) + template + REXY_CPP20_CONSTEXPR basic_string::basic_string(size_type cap) noexcept(noexcept(this->allocate(0))): basic_string(size_type{0}, cap){} - template - REXY_CPP20_CONSTEXPR basic_string::basic_string(size_type len, size_type cap) + template + REXY_CPP20_CONSTEXPR basic_string::basic_string(size_type len, size_type cap) noexcept(noexcept(this->allocate(0))) { _copy_construct_string(nullptr, len, cap); } - template - REXY_CPP20_CONSTEXPR basic_string::basic_string(const basic_string_view& sv) + template + REXY_CPP20_CONSTEXPR basic_string::basic_string(const basic_string_view& sv) noexcept(noexcept(this->allocate(0))) { _copy_construct_string(sv.c_str(), sv.length(), sv.length()); } - template + template template - REXY_CPP20_CONSTEXPR basic_string::basic_string(InputIt start, InputIt fin) + REXY_CPP20_CONSTEXPR basic_string::basic_string(InputIt start, InputIt fin) noexcept(noexcept(this->allocate(0))): basic_string(nullptr, size_type{fin - start}) { @@ -159,35 +159,36 @@ namespace rexy{ raw[i] = 0; } //normal copy and move ctors - template - REXY_CPP20_CONSTEXPR basic_string::basic_string(const basic_string& b) + template + REXY_CPP20_CONSTEXPR basic_string::basic_string(const basic_string& b) noexcept(noexcept(this->allocate(0))): - detail::hasallocator(b) + detail::hasallocator(b) { _copy_construct_string(b.get(), b.length(), b.capacity()); } - template - constexpr basic_string::basic_string(basic_string&& s)noexcept: - detail::hasallocator(std::move(s)), + template + constexpr basic_string::basic_string(basic_string&& s)noexcept: + detail::hasallocator(std::move(s)), string_base(std::move(s)){} - template - REXY_CPP20_CONSTEXPR basic_string::basic_string(const string_base& b) + template + REXY_CPP20_CONSTEXPR basic_string::basic_string(const string_base& b) noexcept(noexcept(this->allocate(0))) { _copy_construct_string(b.get(), b.length(), b.capacity()); } //dtor - template - REXY_CPP20_CONSTEXPR basic_string::~basic_string(void) + template + REXY_CPP20_CONSTEXPR basic_string::~basic_string(void) noexcept(noexcept(this->deallocate(nullptr, 0))) { - if(this->islong()) + if(this->islong()){ this->deallocate(this->get_pointer(), sizeof(value_type)*(this->get_long_capacity()+1)); + } } - template - REXY_CPP20_CONSTEXPR basic_string& basic_string::operator=(const basic_string& s) + template + REXY_CPP20_CONSTEXPR basic_string& basic_string::operator=(const basic_string& s) noexcept(noexcept(this->allocate(0)) && noexcept(this->deallocate(nullptr, 0))) { @@ -199,29 +200,29 @@ namespace rexy{ basic_string tmp(s); return (*this = std::move(tmp)); } - template - constexpr basic_string& basic_string::operator=(basic_string&& s)noexcept{ + template + constexpr basic_string& basic_string::operator=(basic_string&& s)noexcept{ string_base::operator=(std::move(s)); return *this; } - template - REXY_CPP20_CONSTEXPR basic_string& basic_string::operator=(const string_base& s) + template + REXY_CPP20_CONSTEXPR basic_string& basic_string::operator=(const string_base& s) noexcept(noexcept(this->allocate(0)) && noexcept(this->deallocate(nullptr,0))) { return (*this = basic_string(s)); } //Copy from c string - template - REXY_CPP20_CONSTEXPR basic_string& basic_string::operator=(const basic_string_view& sv) + template + REXY_CPP20_CONSTEXPR basic_string& basic_string::operator=(const basic_string_view& sv) noexcept(noexcept(this->allocate(0)) && noexcept(this->deallocate(nullptr,0))) { return _copy_string(sv.c_str(), sv.length()); } - template - REXY_CPP20_CONSTEXPR basic_string& basic_string::operator=(const_pointer c) + template + REXY_CPP20_CONSTEXPR basic_string& basic_string::operator=(const_pointer c) noexcept(noexcept(this->allocate(0)) && noexcept(this->deallocate(nullptr,0))) { @@ -229,14 +230,14 @@ namespace rexy{ } //Replace managed pointer. Frees existing value - template - REXY_CPP20_CONSTEXPR void basic_string::reset(pointer val) + template + REXY_CPP20_CONSTEXPR void basic_string::reset(pointer val) noexcept(noexcept(this->deallocate(nullptr,0))) { reset(val, val ? strlen(val) : 0); } - template - REXY_CPP20_CONSTEXPR void basic_string::reset(pointer val, size_type len) + template + REXY_CPP20_CONSTEXPR void basic_string::reset(pointer val, size_type len) noexcept(noexcept(this->deallocate(nullptr,0))) { if(this->islong()) @@ -246,8 +247,8 @@ namespace rexy{ this->set_long_length(len); this->set_long_capacity(len); } - template - REXY_CPP20_CONSTEXPR bool basic_string::resize(size_type newsize) + template + REXY_CPP20_CONSTEXPR bool basic_string::resize(size_type newsize) noexcept(noexcept(this->allocate(0)) && noexcept(this->deallocate(nullptr,0))) { @@ -257,8 +258,8 @@ namespace rexy{ return false; return (*this = basic_string(this->get_pointer(), newsize)); } - template - REXY_CPP20_CONSTEXPR void basic_string::append(const_pointer data, size_type len) + template + REXY_CPP20_CONSTEXPR void basic_string::append(const_pointer data, size_type len) noexcept(noexcept(this->allocate(0)) && noexcept(this->deallocate(nullptr,0))) { @@ -278,17 +279,17 @@ namespace rexy{ *this = std::move(tmp); } } - template - REXY_CPP20_CONSTEXPR void basic_string::append(const_pointer data) + template + REXY_CPP20_CONSTEXPR void basic_string::append(const_pointer data) noexcept(noexcept(this->allocate(0)) && noexcept(this->deallocate(nullptr,0))) { if(data) append(data, strlen(data)); } - template + template template - REXY_CPP20_CONSTEXPR void basic_string::append(InputIt start, InputIt fin) + REXY_CPP20_CONSTEXPR void basic_string::append(InputIt start, InputIt fin) noexcept(noexcept(this->allocate(0)) && noexcept(this->deallocate(nullptr,0))) { @@ -313,9 +314,9 @@ namespace rexy{ } - template - template - REXY_CPP20_CONSTEXPR auto basic_string::substring(size_type start, size_type end)const -> basic_string{ + template + template + REXY_CPP20_CONSTEXPR auto basic_string::substring(size_type start, size_type end)const -> basic_string{ if(start > end || end > this->length()) return {}; const size_type newlen = end - start; @@ -323,8 +324,8 @@ namespace rexy{ tmp.append(this->get() + start, newlen); return tmp; } - template - REXY_CPP20_CONSTEXPR auto basic_string::release(void)noexcept(noexcept(this->allocate(0))) -> pointer{ + template + REXY_CPP20_CONSTEXPR auto basic_string::release(void)noexcept(noexcept(this->allocate(0))) -> pointer{ if(this->islong()){ pointer raw = this->get_long_ptr(); this->set_islong_flag(false); @@ -341,18 +342,18 @@ namespace rexy{ this->set_short_length(0); return retval; } - template - constexpr auto basic_string::create_view(void)const noexcept -> basic_string_view{ + template + constexpr auto basic_string::create_view(void)const noexcept -> basic_string_view{ const auto ptr = this->get_pointer(); return basic_string_view(ptr, ptr + this->length()); } - template - constexpr auto basic_string::create_view(const_iterator start, const_iterator fin)const noexcept -> basic_string_view{ + template + constexpr auto basic_string::create_view(const_iterator start, const_iterator fin)const noexcept -> basic_string_view{ return basic_string_view(start, fin); } - template - REXY_CPP20_CONSTEXPR basic_string& basic_string::_copy_string(const_pointer s, size_type len) + template + REXY_CPP20_CONSTEXPR basic_string& basic_string::_copy_string(const_pointer s, size_type len) noexcept(noexcept(this->allocate(0)) && noexcept(this->deallocate(nullptr,0))) { @@ -374,7 +375,7 @@ namespace rexy{ return this->m_l.length() + this->m_r.length(); } template - template + template REXY_CPP20_CONSTEXPR string_cat_expr::operator basic_string(void) noexcept(std::is_nothrow_constructible, typename basic_string::size_type>::value && std::is_nothrow_invocable>,decltype(*this)>::value) diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index d290f34..991b52f 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.0.2) project(rexylib_tests) set(INCLUDE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/../include) include_directories("${INCLUDE_PATH}") -add_compile_options(-Wall -Wextra -pedantic -std=c++17) +add_compile_options(-Wall -Wextra -pedantic -std=c++20 -Wno-free-nonheap-object) link_libraries(rexy) if(ENABLE_PROFILING) diff --git a/tests/basic_string.cpp b/tests/basic_string.cpp index 21c821d..50a3431 100644 --- a/tests/basic_string.cpp +++ b/tests/basic_string.cpp @@ -5,6 +5,7 @@ #include #include #include +#include [[noreturn]] void error(const char* str){ fprintf(stderr, "%s", str);