From 9a55d8594b916f7cff6d4bc9d60cdbd6852cf6ee Mon Sep 17 00:00:00 2001 From: rexy712 Date: Tue, 24 May 2022 14:37:41 -0700 Subject: [PATCH] General code cleanup and allow for more C++20 --- CMakeLists.txt | 2 +- include/rexy/algorithm.hpp | 78 +---- include/rexy/algorithm.tpp | 108 +++++++ include/rexy/allocator.hpp | 105 ++++-- include/rexy/buffer.hpp | 2 +- include/rexy/buffer.tpp | 4 +- include/rexy/compat/cpp17/string_base.hpp | 206 ++++++++++++ include/rexy/compat/cpp20/string_base.hpp | 150 +++++++++ .../compat/{constexpr.hpp => standard.hpp} | 22 +- .../rexy/cx/detail/bool_specialize_base.hpp | 10 +- include/rexy/cx/string.hpp | 6 +- include/rexy/cx/vector.hpp | 5 +- include/rexy/detail/hasallocator.hpp | 8 +- include/rexy/enum_traits.hpp | 10 + include/rexy/expression.hpp | 8 +- include/rexy/filerd.hpp | 6 +- include/rexy/storage_for.hpp | 19 +- include/rexy/storage_for.tpp | 148 ++++++++- include/rexy/string_base.hpp | 301 +----------------- include/rexy/string_base.tpp | 8 +- include/rexy/string_view.hpp | 2 +- 21 files changed, 775 insertions(+), 433 deletions(-) create mode 100644 include/rexy/algorithm.tpp create mode 100644 include/rexy/compat/cpp17/string_base.hpp create mode 100644 include/rexy/compat/cpp20/string_base.hpp rename include/rexy/compat/{constexpr.hpp => standard.hpp} (66%) diff --git a/CMakeLists.txt b/CMakeLists.txt index b8ed301..a62a974 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -17,7 +17,7 @@ option(BUILD_TESTS "Enable testing" OFF) option(BUILD_HEADER_ONLY "Enable header only build" OFF) mark_as_advanced(ENABLE_PROFILING) -set(LIBREXY_PUBLIC_HEADERS "include/rexy/rexy.hpp" "include/rexy/algorithm.hpp" "include/rexy/utility.hpp" "include/rexy/basic_string_hash.hpp" "include/rexy/hash.hpp" "include/rexy/string_view_hash.hpp" "include/rexy/string_hash.hpp" "include/rexy/mpmc_queue.hpp" "include/rexy/mpmc_queue.tpp" "include/rexy/traits.hpp" "include/rexy/steal.hpp" "include/rexy/expression.hpp" "include/rexy/string_base.hpp" "include/rexy/string.hpp" "include/rexy/string_base.tpp" "include/rexy/allocator.hpp" "include/rexy/meta.hpp" "include/rexy/buffer.hpp" "include/rexy/buffer.tpp" "include/rexy/debug_print.hpp" "include/rexy/deferred.hpp" "include/rexy/enum_traits.hpp" "include/rexy/storage_for.hpp" "include/rexy/storage_for.tpp" "include/rexy/visitor.hpp" "include/rexy/string_view.hpp" "include/rexy/string_view.tpp") +set(LIBREXY_PUBLIC_HEADERS "include/rexy/rexy.hpp" "include/rexy/algorithm.hpp" "include/rexy/algorithm.tpp" "include/rexy/utility.hpp" "include/rexy/basic_string_hash.hpp" "include/rexy/hash.hpp" "include/rexy/string_view_hash.hpp" "include/rexy/string_hash.hpp" "include/rexy/mpmc_queue.hpp" "include/rexy/mpmc_queue.tpp" "include/rexy/traits.hpp" "include/rexy/steal.hpp" "include/rexy/expression.hpp" "include/rexy/string_base.hpp" "include/rexy/string.hpp" "include/rexy/string_base.tpp" "include/rexy/allocator.hpp" "include/rexy/meta.hpp" "include/rexy/buffer.hpp" "include/rexy/buffer.tpp" "include/rexy/debug_print.hpp" "include/rexy/deferred.hpp" "include/rexy/enum_traits.hpp" "include/rexy/storage_for.hpp" "include/rexy/storage_for.tpp" "include/rexy/visitor.hpp" "include/rexy/string_view.hpp" "include/rexy/string_view.tpp") if(BUILD_HEADER_ONLY) set(LIBREXY_BUILT_LIBRARY_HEADERS "") diff --git a/include/rexy/algorithm.hpp b/include/rexy/algorithm.hpp index fb7c26d..afa32e0 100644 --- a/include/rexy/algorithm.hpp +++ b/include/rexy/algorithm.hpp @@ -19,11 +19,7 @@ #ifndef REXY_ALGORITHM_HPP #define REXY_ALGORITHM_HPP -#include "utility.hpp" //swap #include "rexy.hpp" -#include //SIZE_MAX -#include //size_t -#include #include "detail/algorithm.hpp" @@ -33,84 +29,20 @@ namespace rexy{ //right is one past the end of the list template constexpr void quicksort(Iter left, Iter right, const Compare& cmp) - noexcept(noexcept(detail::qs_partition(left, right, cmp))) - { - while(left < right){ - auto real_right = right-1; - auto pivot = detail::qs_partition(left, real_right, cmp); - quicksort(left, pivot, cmp); - left = ++pivot; - } - } + noexcept(noexcept(detail::qs_partition(left, right, cmp))); //Requires Iterators to be LegacyRandomAccessIterators template - constexpr HIter two_way_search(const HIter& hstart, const HIter& hend, const NIter& nstart, const NIter& nend){ - size_t j = 0; - size_t i = 0; - size_t nlen = nend - nstart; - size_t hlen = hend - hstart; - auto [suffix, period] = detail::critical_factorization(nstart, nend); - - if(detail::iter_compare(nstart, nstart + period, suffix)){ - size_t memory = SIZE_MAX; - while(j <= hlen - nlen){ - i = max(suffix, memory) + 1; - //right side - while(i < nlen && nstart[i] == hstart[i + j]){ - ++i; - } - if(i >= nlen){ - i = suffix; - //left side - while(i > memory && nstart[i] == hstart[i + j]){ - --i; - } - if(i <= memory){ - return hstart + j; - } - j += period; - memory = nlen - period - 1; - }else{ - j += (i - suffix); - memory = SIZE_MAX; - } - } - }else{ - period = max(suffix + 1, nlen - suffix - 1) + 1; - j = 0; - while(j <= hlen - nlen){ - i = suffix + 1; - //right side - while(i < nlen && nstart[i] == hstart[i + j]){ - ++i; - } - if(i >= nlen){ - i = suffix; - //left side - while(i != SIZE_MAX && nstart[i] == hstart[i + j]){ - --i; - } - if(i == SIZE_MAX){ - return hstart + j; - } - j += period; - }else{ - j += (i - suffix); - } - } - } - return hend; - } + constexpr HIter two_way_search(const HIter& hstart, const HIter& hend, const NIter& nstart, const NIter& nend); //searcher for use with generic search wrappers struct two_way_searcher{ template - constexpr HIter operator()(const HIter& hstart, const HIter& hend, const NIter& nstart, const NIter& nend)const{ - return two_way_search(hstart, hend, nstart, nend); - } + constexpr HIter operator()(const HIter& hstart, const HIter& hend, const NIter& nstart, const NIter& nend)const; }; } +#include "algorithm.tpp" + #endif diff --git a/include/rexy/algorithm.tpp b/include/rexy/algorithm.tpp new file mode 100644 index 0000000..9def2af --- /dev/null +++ b/include/rexy/algorithm.tpp @@ -0,0 +1,108 @@ +/** + This file is a part of rexy's general purpose library + 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 + 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_ALGORITHM_TPP +#define REXY_ALGORITHM_TPP + +#include //SIZE_MAX +#include //size_t + +namespace rexy{ + + //Requires Iterators to be LegacyRandomAccessIterators + //right is one past the end of the list + template + constexpr void quicksort(Iter left, Iter right, const Compare& cmp) + noexcept(noexcept(detail::qs_partition(left, right, cmp))) + { + while(left < right){ + auto real_right = right-1; + auto pivot = detail::qs_partition(left, real_right, cmp); + quicksort(left, pivot, cmp); + left = ++pivot; + } + } + + //Requires Iterators to be LegacyRandomAccessIterators + template + constexpr HIter two_way_search(const HIter& hstart, const HIter& hend, const NIter& nstart, const NIter& nend){ + size_t j = 0; + size_t i = 0; + size_t nlen = nend - nstart; + size_t hlen = hend - hstart; + auto [suffix, period] = detail::critical_factorization(nstart, nend); + + if(detail::iter_compare(nstart, nstart + period, suffix)){ + size_t memory = SIZE_MAX; + while(j <= hlen - nlen){ + i = max(suffix, memory) + 1; + //right side + while(i < nlen && nstart[i] == hstart[i + j]){ + ++i; + } + if(i >= nlen){ + i = suffix; + //left side + while(i > memory && nstart[i] == hstart[i + j]){ + --i; + } + if(i <= memory){ + return hstart + j; + } + j += period; + memory = nlen - period - 1; + }else{ + j += (i - suffix); + memory = SIZE_MAX; + } + } + }else{ + period = max(suffix + 1, nlen - suffix - 1) + 1; + j = 0; + while(j <= hlen - nlen){ + i = suffix + 1; + //right side + while(i < nlen && nstart[i] == hstart[i + j]){ + ++i; + } + if(i >= nlen){ + i = suffix; + //left side + while(i != SIZE_MAX && nstart[i] == hstart[i + j]){ + --i; + } + if(i == SIZE_MAX){ + return hstart + j; + } + j += period; + }else{ + j += (i - suffix); + } + } + } + return hend; + } + + template + constexpr HIter two_way_searcher::operator()(const HIter& hstart, const HIter& hend, const NIter& nstart, const NIter& nend)const{ + return two_way_search(hstart, hend, nstart, nend); + } + +} + +#endif diff --git a/include/rexy/allocator.hpp b/include/rexy/allocator.hpp index 673794c..6128b41 100644 --- a/include/rexy/allocator.hpp +++ b/include/rexy/allocator.hpp @@ -26,6 +26,7 @@ #include //numeric_limits #include "rexy.hpp" +#include "compat/standard.hpp" namespace rexy{ @@ -55,38 +56,97 @@ namespace rexy{ } public: - allocator(void) = default; - allocator(const allocator&) = default; - allocator(allocator&&) = default; + 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{} - ~allocator(void) = default; + REXY_CPP20_CONSTEXPR ~allocator(void) = default; - pointer allocate(size_type n){ + //'::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); + + #ifdef __cpp_if_consteval + if consteval{ //} //makes my braces matcher in nano not upset ;) + #else + if(std::is_constant_evaluated()){ + #endif //__cpp_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 static_cast(::operator new(bytes)); + }else{ + return static_cast(::operator new(bytes, static_cast(alignof(T)))); + } + } //if consteval + } + + + REXY_CPP20_CONSTEXPR void deallocate(pointer p, size_type n){ + #ifdef __cpp_if_consteval + if consteval{ //} //makes my braces matcher in nano not upset ;) + #else + if(std::is_constant_evaluated()){ + #endif + 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)); + return static_cast(::operator new(bytes)); }else{ - return reinterpret_cast(::operator new(bytes, static_cast(alignof(T)))); + return static_cast(::operator new(bytes, static_cast(alignof(T)))); } } - void deallocate(pointer p, size_type n){ -#if defined(__clang__) && !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 + 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 + 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); } @@ -99,6 +159,7 @@ namespace rexy{ constexpr bool operator!=(const allocator&, const allocator&){ return false; } + } #endif diff --git a/include/rexy/buffer.hpp b/include/rexy/buffer.hpp index 89ffea9..3784a5e 100644 --- a/include/rexy/buffer.hpp +++ b/include/rexy/buffer.hpp @@ -24,7 +24,7 @@ #include //size_t, ptrdiff_t #include //reverse_iterator -#include "compat/constexpr.hpp" +#include "compat/standard.hpp" namespace rexy{ diff --git a/include/rexy/buffer.tpp b/include/rexy/buffer.tpp index 31f14da..4675b3f 100644 --- a/include/rexy/buffer.tpp +++ b/include/rexy/buffer.tpp @@ -40,14 +40,14 @@ namespace rexy{ template REXY_CPP20_CONSTEXPR buffer::buffer(const Iter& start, const Iter& last){ size_type count = 0; - for(auto it = start;it != end;++it){ + for(auto it = start;it != last;++it){ ++count; } m_data = this->allocate(sizeof(value_type) * count); m_cap = count; count = 0; - for(auto it = start;it != end;++it){ + for(auto it = start;it != last;++it){ new (m_data + count) T(*it); ++count; } diff --git a/include/rexy/compat/cpp17/string_base.hpp b/include/rexy/compat/cpp17/string_base.hpp new file mode 100644 index 0000000..6f2b657 --- /dev/null +++ b/include/rexy/compat/cpp17/string_base.hpp @@ -0,0 +1,206 @@ +/** + 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_STRING_BASE_HPP +#define REXY_COMPAT_CPP17_STRING_BASE_HPP + +#include "../../string_base.hpp" + +#include //forward +#include //{false,true}_type, declval, enable_if, remove_reference, decay + +namespace rexy{ + + #define REXY_HAS_MEMFUN_WITH_RET(type, ret, fun, ...) \ + template \ + struct has_##fun##_f{ \ + static std::false_type check(...); \ + template \ + static auto check(type* u) -> std::enable_if_t().fun(__VA_ARGS__)),ret>,std::true_type>; \ + \ + static constexpr bool value = decltype(check(std::declval*>()))::value; \ + }; \ + template \ + static constexpr bool has_##fun##_f_v = has_##fun##_f::value + + #define REXY_HAS_MEMOP_WITH_RET(type, ret, opname, op, ...) \ + template \ + struct has_##opname##_f{ \ + static std::false_type check(...); \ + template \ + static auto check(type* u) -> std::enable_if_t().operator op(__VA_ARGS__)),ret>,std::true_type>; \ + \ + static constexpr bool value = decltype(check(std::declval*>()))::value; \ + }; \ + template \ + static constexpr bool has_##opname##_f_v = has_##opname##_f::value + + + REXY_HAS_MEMFUN_WITH_RET(U, typename U::size_type, length); + REXY_HAS_MEMFUN_WITH_RET(U, typename U::const_pointer, c_str); + REXY_HAS_MEMOP_WITH_RET(U, typename U::const_reference, indexop, [], 0); + REXY_HAS_MEMFUN_WITH_RET(U, typename U::const_iterator, begin); + REXY_HAS_MEMFUN_WITH_RET(U, typename U::const_iterator, end); + + #undef REXY_HAS_MEMFUN_WITH_RET + #undef REXY_HAS_MEMOP_WITH_RET + + 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; + }; + template + static constexpr bool is_string_v = is_string::value; + + template + struct are_strings{ + static constexpr bool value = (is_string::value && ...); + }; + template + static constexpr bool are_strings_v = are_strings::value; + template + struct is_string_expr{ + static constexpr bool value = rexy::is_template_type::value; + }; + template + static constexpr bool is_string_expr_v = is_string_expr::value; + template + struct are_string_expr{ + static constexpr bool value = (is_string_expr::value && ...); + }; + template + static constexpr bool are_string_expr_v = are_string_expr::value; + + //Compare + template::value,int> = 0> + constexpr bool operator==(Str1&& left, Str2&& right){ + if(left.length() != right.length()){ + return false; + } + return !detail::string_compare(std::forward(left), std::forward(right), left.length()); + } + template::value,int> = 0> + constexpr bool operator==(Str1&& left, typename std::decay_t::const_pointer right){ + if(right == nullptr){ + return false; + } + const auto rlen = detail::string_len(right); + if(rlen != left.length()){ + return false; + } + const auto minlen = min(left.length(), rlen); + return !detail::string_compare(left.c_str(), right, minlen+1); + } + template::value,int> = 0> + constexpr bool operator==(typename std::decay_t::const_pointer left, Str1&& right){ + if(left == nullptr){ + return false; + } + const auto llen = detail::string_len(left); + if(llen != right.length()){ + return false; + } + const auto minlen = min(right.length(), llen); + return !detail::string_compare(right.c_str(), left, minlen+1); + } + template::value,int> = 0> + constexpr bool operator!=(Str1&& left, Str2&& right)noexcept{ + return !(std::forward(left) == std::forward(right)); + } + template::value,int> = 0> + constexpr bool operator!=(Str1&& left, typename std::decay_t::const_pointer right)noexcept{ + return !(std::forward(left) == right); + } + template::value,int> = 0> + constexpr bool operator!=(typename std::decay_t::const_pointer left, Str1&& right)noexcept{ + return !(left == std::forward(right)); + } + + //String + string concat + template::value,int> = 0> + constexpr auto operator+(Left&& l, Right&& r) + //uses deduction guide whereas std::is_nothrow_constructible couldn't + noexcept(noexcept(::new (nullptr) string_cat_expr(std::forward(l), std::forward(r)))) + { + return string_cat_expr(std::forward(l), std::forward(r)); + } + //String + char pointer + template::value,int> = 0> + constexpr auto operator+(typename std::decay_t::const_pointer left, Right&& right) + noexcept(noexcept(::new (nullptr) string_cat_expr(rexy::basic_string_view(left), std::forward(right)))) + { + return string_cat_expr(rexy::basic_string_view(left), std::forward(right)); + } + //Char pointer + string + template::value,int> = 0> + constexpr auto operator+(Left&& left, typename std::decay_t::const_pointer right) + noexcept(noexcept(::new (nullptr) string_cat_expr(std::forward(left), rexy::basic_string_view(right)))) + { + return rexy::string_cat_expr(std::forward(left), rexy::basic_string_view(right)); + } + + + //String + expr concat + template::value && is_string_expr::value,int> = 0> + constexpr auto operator+(Left&& left, Right&& right){ + return string_cat_expr(std::forward(left), std::forward(right)); + } + //Expr + string + template::value && is_string::value,int> = 0> + constexpr auto operator+(Left&& left, Right&& right){ + return string_cat_expr(std::forward(left), std::forward(right)); + } + //Expr + expr + template::value,int> = 0> + constexpr auto operator+(Left&& left, Right&& right){ + return string_cat_expr(std::forward(left), std::forward(right)); + } + //Expr + char pointer + template::value,int> = 0> + constexpr auto operator+(Left&& left, typename std::decay_t::const_pointer right){ + return string_cat_expr(std::forward(left), rexy::basic_string_view(right)); + } + //char pointer + Expr + template::value,int> = 0> + constexpr auto operator+(typename std::decay_t::const_pointer left, Right&& right){ + return string_cat_expr(rexy::basic_string_view(left), std::forward(right)); + } + + //String concat assignment + template::value,int> = 0> + decltype(auto) operator+=(Left& l, Right&& r) + noexcept(noexcept(l + std::forward(r)) && std::is_nothrow_assignable(r))>::value) + { + return l = (l + std::forward(r)); + } + template::value && is_string_expr::value,int> = 0> + decltype(auto) operator+=(Left& l, Right&& r) + noexcept(noexcept(l + std::forward(r)) && std::is_nothrow_assignable(r))>::value) + { + return l = (l + std::forward(r)); + } + template::value, int> = 0> + decltype(auto) operator+=(Left& l, typename std::decay_t::const_pointer r) + noexcept(noexcept(l + r) && std::is_nothrow_assignable::value) + { + return l = (l + r); + } + +} + +#endif diff --git a/include/rexy/compat/cpp20/string_base.hpp b/include/rexy/compat/cpp20/string_base.hpp new file mode 100644 index 0000000..3cabaf5 --- /dev/null +++ b/include/rexy/compat/cpp20/string_base.hpp @@ -0,0 +1,150 @@ +/** + 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_STRING_BASE_HPP +#define REXY_COMPAT_CPP20_STRING_BASE_HPP + +#include "../../string_base.hpp" + +#include //convertible_to +#include //forward, as_const +#include //decay, is_nothrow_assignable + +namespace rexy{ + + template + concept BasicString = requires(const T& a){ + {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 = rexy::is_template_type::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; + + //Compare + template + constexpr bool operator==(Str1&& left, Str2&& right){ + if(left.length() != right.length()){ + return false; + } + return !detail::string_compare(std::forward(left), std::forward(right), left.length()); + } + template + constexpr bool operator==(Str1&& left, typename std::decay_t::const_pointer right){ + if(right == nullptr){ + return false; + } + const auto rlen = detail::string_len(right); + if(rlen != left.length()){ + return false; + } + const auto minlen = min(left.length(), rlen); + return !detail::string_compare(left.c_str(), right, minlen+1); + } + template + constexpr bool operator==(typename std::decay_t::const_pointer left, Str1&& right){ + if(left == nullptr){ + return false; + } + const auto llen = detail::string_len(left); + if(llen != right.length()){ + return false; + } + const auto minlen = min(right.length(), llen); + return !detail::string_compare(right.c_str(), left, minlen+1); + } + template + constexpr bool operator!=(Str1&& left, Str2&& right){ + return !(std::forward(left) == std::forward(right)); + } + template + constexpr bool operator!=(Str1&& left, typename std::decay_t::const_pointer right){ + return !(std::forward(left) == right); + } + template + constexpr bool operator!=(typename std::decay_t::const_pointer left, Str1&& right){ + return !(left == std::forward(right)); + } + + //String + string concat + template + constexpr auto operator+(Left&& l, Right&& r) + //uses deduction guide whereas std::is_nothrow_constructible couldn't + noexcept(noexcept(::new (nullptr) string_cat_expr(std::forward(l), std::forward(r)))) + { + return string_cat_expr(std::forward(l), std::forward(r)); + } + template + constexpr auto operator+(typename std::decay_t::const_pointer left, Right&& right) + noexcept(noexcept(::new (nullptr) string_cat_expr(rexy::basic_string_view(left), std::forward(right)))) + { + return string_cat_expr(rexy::basic_string_view(left), std::forward(right)); + } + template + constexpr auto operator+(Left&& left, typename std::decay_t::const_pointer right) + noexcept(noexcept(::new (nullptr) string_cat_expr(std::forward(left), rexy::basic_string_view(right)))) + { + return rexy::string_cat_expr(std::forward(left), rexy::basic_string_view(right)); + } + + //String concat assign + template + constexpr decltype(auto) operator+=(Left& l, Right&& r) + noexcept(noexcept(l + std::forward(r)) && std::is_nothrow_assignable(r))>::value) + { + return l = (l + std::forward(r)); + } + template + constexpr decltype(auto) operator+=(Left& l, typename std::decay_t::const_pointer r) + noexcept(noexcept(l + r) && std::is_nothrow_assignable::value) + { + return l = (l + r); + } + +} + +#endif diff --git a/include/rexy/compat/constexpr.hpp b/include/rexy/compat/standard.hpp similarity index 66% rename from include/rexy/compat/constexpr.hpp rename to include/rexy/compat/standard.hpp index 66786fb..616b3f4 100644 --- a/include/rexy/compat/constexpr.hpp +++ b/include/rexy/compat/standard.hpp @@ -1,6 +1,6 @@ /** This file is a part of rexy's general purpose library - Copyright (C) 2020 rexy712 + 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 @@ -16,15 +16,25 @@ along with this program. If not, see . */ -#ifndef REXY_CPP20_HPP -#define REXY_CPP20_HPP +#ifndef REXY_COMPAT_CPP20_HPP +#define REXY_COMPAT_CPP20_HPP - #if __cplusplus > 201703L + #if __cplusplus >= 202002L #define REXY_CPP20_CONSTEXPR constexpr #define REXY_CPP20_CONSTEVAL consteval - #else + #else //__cplusplus #define REXY_CPP20_CONSTEXPR #define REXY_CPP20_CONSTEVAL constexpr - #endif + #endif //__cplusplus + + #if __cplusplus >= 202300L + #define REXY_STANDARD_CPP23 + #elif __cplusplus >= 202002L + #define REXY_STANDARD_CPP20 + #elif __cplusplus >= 201703L + #define REXY_STANDARD_CPP17 + #else //__cplusplus + #error "Requires minimum C++17 standard" + #endif //__cplusplus #endif diff --git a/include/rexy/cx/detail/bool_specialize_base.hpp b/include/rexy/cx/detail/bool_specialize_base.hpp index 9441848..b191dd6 100644 --- a/include/rexy/cx/detail/bool_specialize_base.hpp +++ b/include/rexy/cx/detail/bool_specialize_base.hpp @@ -23,7 +23,7 @@ #include //move, forward, pair #include //CHAR_BIT #include "../../utility.hpp" //swap - +#include "../../compat/standard.hpp" namespace rexy::cx::detail{ class bool_specialize_base @@ -59,7 +59,7 @@ namespace rexy::cx::detail{ m_offset(offset){} constexpr boolean(const boolean&)noexcept = default; constexpr boolean(boolean&&)noexcept = default; - ~boolean(void)noexcept = default; + REXY_CPP20_CONSTEXPR ~boolean(void)noexcept = default; constexpr boolean& operator=(const boolean& b)noexcept{ return *this = static_cast(b); @@ -86,7 +86,7 @@ namespace rexy::cx::detail{ constexpr booleans(void)noexcept = default; constexpr booleans(const booleans&)noexcept = default; constexpr booleans(booleans&&)noexcept = default; - ~booleans(void)noexcept = default; + REXY_CPP20_CONSTEXPR ~booleans(void)noexcept = default; constexpr booleans& operator=(const booleans&)noexcept = default; constexpr booleans& operator=(booleans&&)noexcept = default; @@ -121,7 +121,7 @@ namespace rexy::cx::detail{ m_offset(offset){} constexpr bool_iter(const bool_iter&)noexcept = default; constexpr bool_iter(bool_iter&&)noexcept = default; - ~bool_iter(void)noexcept = default; + REXY_CPP20_CONSTEXPR ~bool_iter(void)noexcept = default; constexpr bool_iter& operator=(const bool_iter&)noexcept = default; constexpr bool_iter& operator=(bool_iter&&)noexcept = default; @@ -209,7 +209,7 @@ namespace rexy::cx::detail{ m_offset(b.m_offset){} constexpr const_bool_iter(const const_bool_iter&)noexcept = default; constexpr const_bool_iter(const_bool_iter&&)noexcept = default; - ~const_bool_iter(void)noexcept = default; + REXY_CPP20_CONSTEXPR ~const_bool_iter(void)noexcept = default; constexpr const_bool_iter& operator=(const const_bool_iter&)noexcept = default; constexpr const_bool_iter& operator=(const_bool_iter&&)noexcept = default; diff --git a/include/rexy/cx/string.hpp b/include/rexy/cx/string.hpp index 9ad85de..6029037 100644 --- a/include/rexy/cx/string.hpp +++ b/include/rexy/cx/string.hpp @@ -29,10 +29,10 @@ namespace rexy::cx{ #include #include //ptrdiff_t, size_t -#include "../compat/constexpr.hpp" +#include "../compat/standard.hpp" -//This is different from rexy::static_string in that this doesn't hold a pointer to a constant string array. -//This holds a mutable array of data which can be modified during compile time. static_string is +//This is different from rexy::string_view in that this doesn't hold a pointer to a constant string array. +//This holds a mutable array of data which can be modified during compile time. string_view is //designed to be a thin wrapper around a raw char*, this is designed to allow compile time string concatenation namespace rexy::cx{ diff --git a/include/rexy/cx/vector.hpp b/include/rexy/cx/vector.hpp index 2c86a14..d01719c 100644 --- a/include/rexy/cx/vector.hpp +++ b/include/rexy/cx/vector.hpp @@ -24,6 +24,7 @@ #include "detail/bool_specialize_base.hpp" #include "../utility.hpp" //swap +#include "../compat/standard.hpp" #include @@ -63,7 +64,7 @@ namespace rexy::cx{ m_size = min(count, max_elements); } - ~vector(void)noexcept = default; + REXY_CPP20_CONSTEXPR ~vector(void)noexcept = default; constexpr vector& operator=(const vector&)noexcept(std::is_nothrow_copy_assignable::value) = default; constexpr vector& operator=(vector&&)noexcept(std::is_nothrow_move_assignable::value) = default; @@ -283,7 +284,7 @@ namespace rexy::cx{ m_size = min(count, max_elements); } - ~vector(void)noexcept = default; + REXY_CPP20_CONSTEXPR ~vector(void)noexcept = default; constexpr vector& operator=(const vector&)noexcept = default; constexpr vector& operator=(vector&&)noexcept = default; diff --git a/include/rexy/detail/hasallocator.hpp b/include/rexy/detail/hasallocator.hpp index 0f3ce6c..4bdb123 100644 --- a/include/rexy/detail/hasallocator.hpp +++ b/include/rexy/detail/hasallocator.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,6 +19,8 @@ #ifndef REXY_DETAIL_HASALLOCATOR_HPP #define REXY_DETAIL_HASALLOCATOR_HPP +#include "../compat/standard.hpp" + namespace rexy::detail{ template @@ -26,10 +28,10 @@ namespace rexy::detail{ { Alloc m_alloc; - auto allocate(typename Alloc::size_type bytes)noexcept(noexcept(m_alloc.allocate(0))){ + REXY_CPP20_CONSTEXPR auto allocate(typename Alloc::size_type bytes)noexcept(noexcept(m_alloc.allocate(0))){ return m_alloc.allocate(bytes); } - void deallocate(typename Alloc::pointer p, typename Alloc::size_type bytes)noexcept(noexcept(m_alloc.deallocate(nullptr,0))){ + 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); } diff --git a/include/rexy/enum_traits.hpp b/include/rexy/enum_traits.hpp index 6642388..f04780d 100644 --- a/include/rexy/enum_traits.hpp +++ b/include/rexy/enum_traits.hpp @@ -21,6 +21,14 @@ #ifndef REXY_ENUM_CLASS_HPP #define REXY_ENUM_CLASS_HPP +#include "compat/standard.hpp" + +#if __cplusplus < 202002L + +#error "Cannot use enum_traits without C++20 concept support" + +#else //__cplusplus + #include template @@ -201,4 +209,6 @@ namespace rexy::enum_traits{ } } +#endif //__cplusplus + #endif diff --git a/include/rexy/expression.hpp b/include/rexy/expression.hpp index f89f4b0..a349ccc 100644 --- a/include/rexy/expression.hpp +++ b/include/rexy/expression.hpp @@ -19,7 +19,7 @@ #ifndef REXY_EXPRESSION_HPP #define REXY_EXPRESSION_HPP -#include +#include //is_void, remove_reference, conditional #include //forward #include "rexy.hpp" @@ -29,8 +29,8 @@ namespace rexy{ template class binary_expression { - static_assert(!std::is_same,void>::value, "Left value of rexy::binary_expression cannot be void!"); - static_assert(!std::is_same,void>::value, "Right value of rexy::binary_expression cannot be void!"); + static_assert(!std::is_void_v, "Left value of rexy::binary_expression cannot be void!"); + static_assert(!std::is_void_v, "Right value of rexy::binary_expression cannot be void!"); public: using left_type = std::conditional_t::value, @@ -78,7 +78,7 @@ namespace rexy{ template class unary_expression { - static_assert(!std::is_same,void>::value, "Value of rexy::unary_expression cannot be void!"); + static_assert(!std::is_void_v, "Value of rexy::unary_expression cannot be void!"); public: using left_type = std::conditional_t::value, diff --git a/include/rexy/filerd.hpp b/include/rexy/filerd.hpp index 162bbea..bd69ad0 100644 --- a/include/rexy/filerd.hpp +++ b/include/rexy/filerd.hpp @@ -77,11 +77,7 @@ namespace rexy{ #else //LIBREXY_HEADER_ONLY -namespace rexy{ - - static_assert(false, "rexy::filerd is not available when built with header only support"); - -} +#error "rexy::filerd is not available when built with header only support" #endif //LIBREXY_HEADER_ONLY diff --git a/include/rexy/storage_for.hpp b/include/rexy/storage_for.hpp index 2304917..7654acf 100644 --- a/include/rexy/storage_for.hpp +++ b/include/rexy/storage_for.hpp @@ -22,6 +22,8 @@ #include //forward, move #include //is_nothrow_constructible, etc +#include "compat/standard.hpp" + namespace rexy{ //A wrapper around raw storage. @@ -42,7 +44,19 @@ namespace rexy{ static constexpr auto size = sizeof(value_type); private: +#if __cplusplus >= 202002L + + union storage_{ + [[no_unique_address]] + struct empty_{}empty = {}; + T data; + }m_storage; + +#else //__cplusplus + alignas(align) unsigned char m_storage[size]; + +#endif //__cplusplus unsigned char m_dirty:1 = 0; public: @@ -53,7 +67,7 @@ namespace rexy{ constexpr storage_for(rvalue_reference t)noexcept(std::is_nothrow_move_constructible::value); constexpr storage_for(const storage_for& s)noexcept(std::is_nothrow_copy_constructible::value); constexpr storage_for(storage_for&& s)noexcept(std::is_nothrow_move_constructible::value); - constexpr ~storage_for(void)noexcept(std::is_nothrow_destructible::value); + REXY_CPP20_CONSTEXPR ~storage_for(void)noexcept(std::is_nothrow_destructible::value); constexpr storage_for& operator=(const storage_for& s)noexcept(std::is_nothrow_copy_assignable::value); constexpr storage_for& operator=(storage_for&& s)noexcept(std::is_nothrow_move_assignable::value); @@ -65,6 +79,9 @@ namespace rexy{ constexpr operator const_reference(void)const noexcept; constexpr operator rvalue_reference(void)&& noexcept; + constexpr reference get(void)noexcept; + constexpr const_reference get(void)const noexcept; + constexpr pointer operator->(void)noexcept; constexpr const_pointer operator->(void)const noexcept; constexpr reference operator*(void)noexcept; diff --git a/include/rexy/storage_for.tpp b/include/rexy/storage_for.tpp index 544d15e..06add8c 100644 --- a/include/rexy/storage_for.tpp +++ b/include/rexy/storage_for.tpp @@ -21,6 +21,141 @@ #include //forward, move +#if __cplusplus >= 202002L + +#include //construct_at, destroy_at + +namespace rexy{ + + template + template + constexpr storage_for::storage_for(Args&&... args)noexcept(std::is_nothrow_constructible::value){ + std::construct_at(&m_storage.data, std::forward(args)...); + m_dirty = 1; + } + template + constexpr storage_for::storage_for(const_reference t)noexcept(std::is_nothrow_copy_constructible::value){ + std::construct_at(&m_storage.data, t); + m_dirty = 1; + } + template + constexpr storage_for::storage_for(rvalue_reference t)noexcept(std::is_nothrow_move_constructible::value){ + std::construct_at(&m_storage.data, std::move(t)); + m_dirty = 1; + } + template + constexpr storage_for::storage_for(const storage_for& s)noexcept(std::is_nothrow_copy_constructible::value){ + if(s.m_dirty){ + std::construct_at(&m_storage.data, s.m_storage.data); + m_dirty = 1; + } + } + template + constexpr storage_for::storage_for(storage_for&& s)noexcept(std::is_nothrow_move_constructible::value){ + if(s.m_dirty){ + std::construct_at(&m_storage.data, std::move(s.m_storage.data)); + m_dirty = 1; + } + } + template + constexpr storage_for::~storage_for(void)noexcept(std::is_nothrow_destructible::value){ + if(m_dirty){ + destroy(); + } + } + + template + constexpr storage_for& storage_for::operator=(const storage_for& s)noexcept(std::is_nothrow_copy_assignable::value){ + return ((*this) = storage_for(s)); + } + template + constexpr storage_for& storage_for::operator=(storage_for&& s)noexcept(std::is_nothrow_move_assignable::value){ + if(s.m_dirty){ + return ((*this) = std::move(s.m_storage.data)); + }else{ + if(m_dirty){ + destroy(); + } + } + return *this; + } + template + constexpr storage_for& storage_for::operator=(const_reference t)noexcept(std::is_nothrow_copy_assignable::value){ + if(m_dirty){ + m_storage.data = t; + }else{ + std::construct_at(&m_storage.data, t); + } + m_dirty = 1; + return *this; + } + template + constexpr storage_for& storage_for::operator=(rvalue_reference t)noexcept(std::is_nothrow_move_assignable::value){ + if(m_dirty){ + m_storage.data = std::move(t); + }else{ + std::construct_at(&m_storage.data, std::move(t)); + } + m_dirty = 1; + return *this; + } + template + constexpr void storage_for::destroy(void)noexcept(std::is_nothrow_destructible::value){ + if(m_dirty){ + std::destroy_at(&m_storage.data); + m_dirty = 0; + } + } + + template + constexpr storage_for::operator reference(void)noexcept{ + return m_storage.data; + } + template + constexpr storage_for::operator const_reference(void)const noexcept{ + return m_storage.data; + } + template + constexpr storage_for::operator rvalue_reference(void)&& noexcept{ + return std::move(m_storage.data); + } + + template + constexpr auto storage_for::get(void)noexcept -> reference{ + return m_storage.data; + } + template + constexpr auto storage_for::get(void)const noexcept -> const_reference{ + return m_storage.data; + } + + template + constexpr auto storage_for::operator->(void)noexcept -> pointer{ + return &m_storage.data; + } + template + constexpr auto storage_for::operator->(void)const noexcept -> const_pointer{ + return &m_storage.data; + } + + template + constexpr auto storage_for::operator*(void)noexcept -> reference{ + return m_storage.data; + } + template + constexpr auto storage_for::operator*(void)const noexcept -> const_reference{ + return m_storage.data; + } + + template + constexpr bool storage_for::valid(void)const{ + return m_dirty; + } + +} + +#else //__cplusplus + namespace rexy{ template @@ -88,13 +223,22 @@ namespace rexy{ } template constexpr storage_for::operator const_reference(void)const noexcept{ - return m_storage; + return reinterpret_cast(m_storage); } template constexpr storage_for::operator rvalue_reference(void)&& noexcept{ return reinterpret_cast(std::move(m_storage)); } + template + constexpr auto storage_for::get(void)noexcept -> reference{ + return reinterpret_cast(m_storage); + } + template + constexpr auto storage_for::get(void)const noexcept -> const_reference{ + return reinterpret_cast(m_storage); + } + template constexpr auto storage_for::operator->(void)noexcept -> pointer{ return reinterpret_cast(&m_storage); @@ -120,4 +264,6 @@ namespace rexy{ } +#endif //__cplusplus + #endif diff --git a/include/rexy/string_base.hpp b/include/rexy/string_base.hpp index 7c50f5f..64baeb3 100644 --- a/include/rexy/string_base.hpp +++ b/include/rexy/string_base.hpp @@ -35,7 +35,7 @@ #include "detail/hasallocator.hpp" #include "rexy.hpp" -#include "compat/constexpr.hpp" +#include "compat/standard.hpp" #if __cplusplus >= 202002L #include @@ -395,305 +395,6 @@ namespace rexy{ } } -#if __cplusplus >= 202002L - - template - concept BasicString = requires(const T& a){ - {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 = rexy::is_template_type::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; - - //Compare - template - constexpr bool operator==(Str1&& left, Str2&& right){ - if(left.length() != right.length()){ - return false; - } - return !detail::string_compare(std::forward(left), std::forward(right), left.length()); - } - template - constexpr bool operator==(Str1&& left, typename std::decay_t::const_pointer right){ - if(right == nullptr){ - return false; - } - const auto rlen = detail::string_len(right); - if(rlen != left.length()){ - return false; - } - const auto minlen = min(left.length(), rlen); - return !detail::string_compare(left.c_str(), right, minlen+1); - } - template - constexpr bool operator==(typename std::decay_t::const_pointer left, Str1&& right){ - if(left == nullptr){ - return false; - } - const auto llen = detail::string_len(left); - if(llen != right.length()){ - return false; - } - const auto minlen = min(right.length(), llen); - return !detail::string_compare(right.c_str(), left, minlen+1); - } - template - constexpr bool operator!=(Str1&& left, Str2&& right){ - return !(std::forward(left) == std::forward(right)); - } - template - constexpr bool operator!=(Str1&& left, typename std::decay_t::const_pointer right){ - return !(std::forward(left) == right); - } - template - constexpr bool operator!=(typename std::decay_t::const_pointer left, Str1&& right){ - return !(left == std::forward(right)); - } - - //String + string concat - template - constexpr auto operator+(Left&& l, Right&& r) - //uses deduction guide whereas std::is_nothrow_constructible couldn't - noexcept(noexcept(::new (nullptr) string_cat_expr(std::forward(l), std::forward(r)))) - { - return string_cat_expr(std::forward(l), std::forward(r)); - } - template - constexpr auto operator+(typename std::decay_t::const_pointer left, Right&& right) - noexcept(noexcept(::new (nullptr) string_cat_expr(rexy::basic_string_view(left), std::forward(right)))) - { - return string_cat_expr(rexy::basic_string_view(left), std::forward(right)); - } - template - constexpr auto operator+(Left&& left, typename std::decay_t::const_pointer right) - noexcept(noexcept(::new (nullptr) string_cat_expr(std::forward(left), rexy::basic_string_view(right)))) - { - return rexy::string_cat_expr(std::forward(left), rexy::basic_string_view(right)); - } - - //String concat assign - template - REXY_CPP20_CONSTEXPR decltype(auto) operator+=(Left& l, Right&& r) - noexcept(noexcept(l + std::forward(r)) && std::is_nothrow_assignable(r))>::value) - { - return l = (l + std::forward(r)); - } - template - REXY_CPP20_CONSTEXPR decltype(auto) operator+=(Left& l, typename std::decay_t::const_pointer r) - noexcept(noexcept(l + r) && std::is_nothrow_assignable::value) - { - return l = (l + r); - } - -#else //__cplusplus == 202002L - - #define HAS_MEMFUN_WITH_RET(type, ret, fun, ...) \ - template \ - struct has_##fun##_f{ \ - static std::false_type check(...); \ - template \ - static auto check(type* u) -> std::enable_if_t().fun(__VA_ARGS__)),ret>,std::true_type>; \ - \ - static constexpr bool value = decltype(check(std::declval*>()))::value; \ - }; \ - template \ - static constexpr bool has_##fun##_f_v = has_##fun##_f::value - - #define HAS_MEMOP_WITH_RET(type, ret, opname, op, ...) \ - template \ - struct has_##opname##_f{ \ - static std::false_type check(...); \ - template \ - static auto check(type* u) -> std::enable_if_t().operator op(__VA_ARGS__)),ret>,std::true_type>; \ - \ - static constexpr bool value = decltype(check(std::declval*>()))::value; \ - }; \ - template \ - static constexpr bool has_##opname##_f_v = has_##opname##_f::value - - - HAS_MEMFUN_WITH_RET(U, typename U::size_type, length); - HAS_MEMFUN_WITH_RET(U, typename U::const_pointer, c_str); - HAS_MEMOP_WITH_RET(U, typename U::const_reference, indexop, [], 0); - HAS_MEMFUN_WITH_RET(U, typename U::const_iterator, begin); - HAS_MEMFUN_WITH_RET(U, typename U::const_iterator, end); - - #undef HAS_MEMFUN_WITH_RET - #undef HAS_MEMOP_WITH_RET - - 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; - }; - template - static constexpr bool is_string_v = is_string::value; - - template - struct are_strings{ - static constexpr bool value = (is_string::value && ...); - }; - template - static constexpr bool are_strings_v = are_strings::value; - template - struct is_string_expr{ - static constexpr bool value = rexy::is_template_type::value; - }; - template - static constexpr bool is_string_expr_v = is_string_expr::value; - template - struct are_string_expr{ - static constexpr bool value = (is_string_expr::value && ...); - }; - template - static constexpr bool are_string_expr_v = are_string_expr::value; - - //Compare - template::value,int> = 0> - constexpr bool operator==(Str1&& left, Str2&& right){ - if(left.length() != right.length()){ - return false; - } - return !detail::string_compare(std::forward(left), std::forward(right), left.length()); - } - template::value,int> = 0> - constexpr bool operator==(Str1&& left, typename std::decay_t::const_pointer right){ - if(right == nullptr){ - return false; - } - const auto rlen = detail::string_len(right); - if(rlen != left.length()){ - return false; - } - const auto minlen = min(left.length(), rlen); - return !detail::string_compare(left.c_str(), right, minlen+1); - } - template::value,int> = 0> - constexpr bool operator==(typename std::decay_t::const_pointer left, Str1&& right){ - if(left == nullptr){ - return false; - } - const auto llen = detail::string_len(left); - if(llen != right.length()){ - return false; - } - const auto minlen = min(right.length(), llen); - return !detail::string_compare(right.c_str(), left, minlen+1); - } - template::value,int> = 0> - constexpr bool operator!=(Str1&& left, Str2&& right)noexcept{ - return !(std::forward(left) == std::forward(right)); - } - template::value,int> = 0> - constexpr bool operator!=(Str1&& left, typename std::decay_t::const_pointer right)noexcept{ - return !(std::forward(left) == right); - } - template::value,int> = 0> - constexpr bool operator!=(typename std::decay_t::const_pointer left, Str1&& right)noexcept{ - return !(left == std::forward(right)); - } - - //String + string concat - template::value,int> = 0> - constexpr auto operator+(Left&& l, Right&& r) - //uses deduction guide whereas std::is_nothrow_constructible couldn't - noexcept(noexcept(::new (nullptr) string_cat_expr(std::forward(l), std::forward(r)))) - { - return string_cat_expr(std::forward(l), std::forward(r)); - } - //String + char pointer - template::value,int> = 0> - constexpr auto operator+(typename std::decay_t::const_pointer left, Right&& right) - noexcept(noexcept(::new (nullptr) string_cat_expr(rexy::basic_string_view(left), std::forward(right)))) - { - return string_cat_expr(rexy::basic_string_view(left), std::forward(right)); - } - //Char pointer + string - template::value,int> = 0> - constexpr auto operator+(Left&& left, typename std::decay_t::const_pointer right) - noexcept(noexcept(::new (nullptr) string_cat_expr(std::forward(left), rexy::basic_string_view(right)))) - { - return rexy::string_cat_expr(std::forward(left), rexy::basic_string_view(right)); - } - - - //String + expr concat - template::value && is_string_expr::value,int> = 0> - constexpr auto operator+(Left&& left, Right&& right){ - return string_cat_expr(std::forward(left), std::forward(right)); - } - //Expr + string - template::value && is_string::value,int> = 0> - constexpr auto operator+(Left&& left, Right&& right){ - return string_cat_expr(std::forward(left), std::forward(right)); - } - //Expr + expr - template::value,int> = 0> - constexpr auto operator+(Left&& left, Right&& right){ - return string_cat_expr(std::forward(left), std::forward(right)); - } - //Expr + char pointer - template::value,int> = 0> - constexpr auto operator+(Left&& left, typename std::decay_t::const_pointer right){ - return string_cat_expr(std::forward(left), rexy::basic_string_view(right)); - } - //char pointer + Expr - template::value,int> = 0> - constexpr auto operator+(typename std::decay_t::const_pointer left, Right&& right){ - return string_cat_expr(rexy::basic_string_view(left), std::forward(right)); - } - - //String concat assignment - template::value,int> = 0> - REXY_CPP20_CONSTEXPR decltype(auto) operator+=(Left& l, Right&& r) - noexcept(noexcept(l + std::forward(r)) && std::is_nothrow_assignable(r))>::value) - { - return l = (l + std::forward(r)); - } - template::value && is_string_expr::value,int> = 0> - REXY_CPP20_CONSTEXPR decltype(auto) operator+=(Left& l, Right&& r) - noexcept(noexcept(l + std::forward(r)) && std::is_nothrow_assignable(r))>::value) - { - return l = (l + std::forward(r)); - } - template::value, int> = 0> - REXY_CPP20_CONSTEXPR decltype(auto) operator+=(Left& l, typename std::decay_t::const_pointer r) - noexcept(noexcept(l + r) && std::is_nothrow_assignable::value) - { - return l = (l + r); - } - -#endif //__cplusplus == 202002L - } #include "string_base.tpp" diff --git a/include/rexy/string_base.tpp b/include/rexy/string_base.tpp index 0367c42..6b02efb 100644 --- a/include/rexy/string_base.tpp +++ b/include/rexy/string_base.tpp @@ -29,7 +29,11 @@ #include "compat/to_address.hpp" #include "string_view.hpp" -#define STOP_STRICT_ALIAS_WARNING(x) (x) +#if __cplusplus >= 202002L + #include "compat/cpp20/string_base.hpp" +#else + #include "compat/cpp17/string_base.hpp" +#endif namespace rexy{ @@ -385,6 +389,4 @@ namespace rexy{ } //namespace rexy -#undef STOP_STRICT_ALIAS_WARNING - #endif diff --git a/include/rexy/string_view.hpp b/include/rexy/string_view.hpp index a09622f..0cd9911 100644 --- a/include/rexy/string_view.hpp +++ b/include/rexy/string_view.hpp @@ -22,7 +22,7 @@ #include //size_t, ptrdiff_t #include //reverse_iterator -#include "compat/constexpr.hpp" +#include "compat/standard.hpp" #include "rexy.hpp" namespace rexy{