General code cleanup and allow for more C++20

This commit is contained in:
rexy712 2022-05-24 14:37:41 -07:00
parent 8b6b421c52
commit 9a55d8594b
21 changed files with 775 additions and 433 deletions

View File

@ -17,7 +17,7 @@ option(BUILD_TESTS "Enable testing" OFF)
option(BUILD_HEADER_ONLY "Enable header only build" OFF) option(BUILD_HEADER_ONLY "Enable header only build" OFF)
mark_as_advanced(ENABLE_PROFILING) 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) if(BUILD_HEADER_ONLY)
set(LIBREXY_BUILT_LIBRARY_HEADERS "") set(LIBREXY_BUILT_LIBRARY_HEADERS "")

View File

@ -19,11 +19,7 @@
#ifndef REXY_ALGORITHM_HPP #ifndef REXY_ALGORITHM_HPP
#define REXY_ALGORITHM_HPP #define REXY_ALGORITHM_HPP
#include "utility.hpp" //swap
#include "rexy.hpp" #include "rexy.hpp"
#include <cstdint> //SIZE_MAX
#include <cstdlib> //size_t
#include <type_traits>
#include "detail/algorithm.hpp" #include "detail/algorithm.hpp"
@ -33,84 +29,20 @@ namespace rexy{
//right is one past the end of the list //right is one past the end of the list
template<class Iter, class Compare> template<class Iter, class Compare>
constexpr void quicksort(Iter left, Iter right, const Compare& cmp) constexpr void quicksort(Iter left, Iter right, const Compare& cmp)
noexcept(noexcept(detail::qs_partition(left, right, 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 //Requires Iterators to be LegacyRandomAccessIterators
template<class HIter, class NIter> template<class HIter, class NIter>
constexpr HIter two_way_search(const HIter& hstart, const HIter& hend, const NIter& nstart, const NIter& nend){ 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;
}
//searcher for use with generic search wrappers //searcher for use with generic search wrappers
struct two_way_searcher{ struct two_way_searcher{
template<class HIter, class NIter> template<class HIter, class NIter>
constexpr HIter operator()(const HIter& hstart, const HIter& hend, const NIter& nstart, const NIter& nend)const{ constexpr HIter operator()(const HIter& hstart, const HIter& hend, const NIter& nstart, const NIter& nend)const;
return two_way_search(hstart, hend, nstart, nend);
}
}; };
} }
#include "algorithm.tpp"
#endif #endif

108
include/rexy/algorithm.tpp Normal file
View File

@ -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 <http://www.gnu.org/licenses/>.
*/
#ifndef REXY_ALGORITHM_TPP
#define REXY_ALGORITHM_TPP
#include <cstdint> //SIZE_MAX
#include <cstdlib> //size_t
namespace rexy{
//Requires Iterators to be LegacyRandomAccessIterators
//right is one past the end of the list
template<class Iter, class Compare>
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<class HIter, class NIter>
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<class HIter, class NIter>
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

View File

@ -26,6 +26,7 @@
#include <limits> //numeric_limits #include <limits> //numeric_limits
#include "rexy.hpp" #include "rexy.hpp"
#include "compat/standard.hpp"
namespace rexy{ namespace rexy{
@ -55,38 +56,97 @@ namespace rexy{
} }
public: public:
allocator(void) = default; REXY_CPP20_CONSTEXPR allocator(void) = default;
allocator(const allocator&) = default; REXY_CPP20_CONSTEXPR allocator(const allocator&) = default;
allocator(allocator&&) = default; REXY_CPP20_CONSTEXPR allocator(allocator&&) = default;
template<class U> template<class U>
constexpr allocator(const allocator<U>&)noexcept{} constexpr allocator(const allocator<U>&)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<size_type>::max() : n*sizeof(T); size_type bytes = has_overflow(n) ? std::numeric_limits<size_type>::max() : n*sizeof(T);
if constexpr(alignof(T) <= __STDCPP_DEFAULT_NEW_ALIGNMENT__){
return reinterpret_cast<pointer>(::operator new(bytes)); #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<value_type> a;
const size_type num_items = n / sizeof(value_type);
return a.allocate(num_items);
}else{ }else{
return reinterpret_cast<pointer>(::operator new(bytes, static_cast<std::align_val_t>(alignof(T)))); if constexpr(alignof(T) <= __STDCPP_DEFAULT_NEW_ALIGNMENT__){
return static_cast<pointer>(::operator new(bytes));
}else{
return static_cast<pointer>(::operator new(bytes, static_cast<std::align_val_t>(alignof(T))));
} }
} //if consteval
} }
void deallocate(pointer p, size_type n){
#if defined(__clang__) && !defined(__cpp_sized_deallocation)
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<value_type> a;
const size_type num_items = n / sizeof(value_type);
return a.deallocate(p, num_items);
}else{
#if !defined(__cpp_sized_deallocation)
//clang does not enable ::operator delete(void* ptr, std::size_t sz) by default for some reason //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__){ if constexpr(alignof(T) <= __STDCPP_DEFAULT_NEW_ALIGNMENT__){
::operator delete(p); ::operator delete(p);
}else{ }else{
::operator delete(p, static_cast<std::align_val_t>(alignof(T))); ::operator delete(p, static_cast<std::align_val_t>(alignof(T)));
} }
#else #else //__cpp_sized_deallocation
size_type bytes = has_overflow(n) ? std::numeric_limits<size_type>::max() : n*sizeof(T); size_type bytes = has_overflow(n) ? std::numeric_limits<size_type>::max() : n*sizeof(T);
if constexpr(alignof(T) <= __STDCPP_DEFAULT_NEW_ALIGNMENT__){ if constexpr(alignof(T) <= __STDCPP_DEFAULT_NEW_ALIGNMENT__){
::operator delete(p, bytes); ::operator delete(p, bytes);
}else{ }else{
::operator delete(p, bytes, static_cast<std::align_val_t>(alignof(T))); ::operator delete(p, bytes, static_cast<std::align_val_t>(alignof(T)));
} }
#endif #endif //__cpp_sized_deallocation
} //if consteval
} }
#else //__cplusplus
REXY_CPP20_CONSTEXPR pointer allocate(size_type n){
size_type bytes = has_overflow(n) ? std::numeric_limits<size_type>::max() : n*sizeof(T);
if constexpr(alignof(T) <= __STDCPP_DEFAULT_NEW_ALIGNMENT__){
return static_cast<pointer>(::operator new(bytes));
}else{
return static_cast<pointer>(::operator new(bytes, static_cast<std::align_val_t>(alignof(T))));
}
}
REXY_CPP20_CONSTEXPR void deallocate(pointer p, size_type n){
#if !defined(__cpp_sized_deallocation)
//clang does not enable ::operator delete(void* ptr, std::size_t sz) by default for some reason
if constexpr(alignof(T) <= __STDCPP_DEFAULT_NEW_ALIGNMENT__){
::operator delete(p);
}else{
::operator delete(p, static_cast<std::align_val_t>(alignof(T)));
}
#else //__cpp_sized_deallocation
size_type bytes = has_overflow(n) ? std::numeric_limits<size_type>::max() : n*sizeof(T);
if constexpr(alignof(T) <= __STDCPP_DEFAULT_NEW_ALIGNMENT__){
::operator delete(p, bytes);
}else{
::operator delete(p, bytes, static_cast<std::align_val_t>(alignof(T)));
}
#endif //__cpp_sized_deallocation
}
#endif //__cplusplus
constexpr size_type max_size(void)const{ constexpr size_type max_size(void)const{
return std::numeric_limits<size_type>::max()/sizeof(T); return std::numeric_limits<size_type>::max()/sizeof(T);
} }
@ -99,6 +159,7 @@ namespace rexy{
constexpr bool operator!=(const allocator<T>&, const allocator<U>&){ constexpr bool operator!=(const allocator<T>&, const allocator<U>&){
return false; return false;
} }
} }
#endif #endif

View File

@ -24,7 +24,7 @@
#include <cstdlib> //size_t, ptrdiff_t #include <cstdlib> //size_t, ptrdiff_t
#include <iterator> //reverse_iterator #include <iterator> //reverse_iterator
#include "compat/constexpr.hpp" #include "compat/standard.hpp"
namespace rexy{ namespace rexy{

View File

@ -40,14 +40,14 @@ namespace rexy{
template<class Iter> template<class Iter>
REXY_CPP20_CONSTEXPR buffer<T,Allocator>::buffer(const Iter& start, const Iter& last){ REXY_CPP20_CONSTEXPR buffer<T,Allocator>::buffer(const Iter& start, const Iter& last){
size_type count = 0; size_type count = 0;
for(auto it = start;it != end;++it){ for(auto it = start;it != last;++it){
++count; ++count;
} }
m_data = this->allocate(sizeof(value_type) * count); m_data = this->allocate(sizeof(value_type) * count);
m_cap = count; m_cap = count;
count = 0; count = 0;
for(auto it = start;it != end;++it){ for(auto it = start;it != last;++it){
new (m_data + count) T(*it); new (m_data + count) T(*it);
++count; ++count;
} }

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
#ifndef REXY_COMPAT_CPP17_STRING_BASE_HPP
#define REXY_COMPAT_CPP17_STRING_BASE_HPP
#include "../../string_base.hpp"
#include <utility> //forward
#include <type_traits> //{false,true}_type, declval, enable_if, remove_reference, decay
namespace rexy{
#define REXY_HAS_MEMFUN_WITH_RET(type, ret, fun, ...) \
template<class T> \
struct has_##fun##_f{ \
static std::false_type check(...); \
template<class type> \
static auto check(type* u) -> std::enable_if_t<std::is_convertible_v<decltype(std::declval<type>().fun(__VA_ARGS__)),ret>,std::true_type>; \
\
static constexpr bool value = decltype(check(std::declval<std::remove_reference_t<T>*>()))::value; \
}; \
template<class T> \
static constexpr bool has_##fun##_f_v = has_##fun##_f<T>::value
#define REXY_HAS_MEMOP_WITH_RET(type, ret, opname, op, ...) \
template<class T> \
struct has_##opname##_f{ \
static std::false_type check(...); \
template<class type> \
static auto check(type* u) -> std::enable_if_t<std::is_convertible_v<decltype(std::declval<type>().operator op(__VA_ARGS__)),ret>,std::true_type>; \
\
static constexpr bool value = decltype(check(std::declval<std::remove_reference_t<T>*>()))::value; \
}; \
template<class T> \
static constexpr bool has_##opname##_f_v = has_##opname##_f<T>::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<class T>
struct is_string{
static constexpr bool value = has_length_f_v<T> && has_c_str_f_v<T> && has_indexop_f_v<T> && has_begin_f_v<T> && has_end_f_v<T>;
};
template<class T>
static constexpr bool is_string_v = is_string<T>::value;
template<class... Ts>
struct are_strings{
static constexpr bool value = (is_string<Ts>::value && ...);
};
template<class... Ts>
static constexpr bool are_strings_v = are_strings<Ts...>::value;
template<class T>
struct is_string_expr{
static constexpr bool value = rexy::is_template_type<T,string_cat_expr>::value;
};
template<class T>
static constexpr bool is_string_expr_v = is_string_expr<T>::value;
template<class... Ts>
struct are_string_expr{
static constexpr bool value = (is_string_expr<Ts>::value && ...);
};
template<class... Ts>
static constexpr bool are_string_expr_v = are_string_expr<Ts...>::value;
//Compare
template<class Str1, class Str2, std::enable_if_t<are_strings<Str1, Str2>::value,int> = 0>
constexpr bool operator==(Str1&& left, Str2&& right){
if(left.length() != right.length()){
return false;
}
return !detail::string_compare(std::forward<Str1>(left), std::forward<Str2>(right), left.length());
}
template<class Str1, std::enable_if_t<are_strings<Str1>::value,int> = 0>
constexpr bool operator==(Str1&& left, typename std::decay_t<Str1>::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<class Str1, std::enable_if_t<are_strings<Str1>::value,int> = 0>
constexpr bool operator==(typename std::decay_t<Str1>::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<class Str1, class Str2, std::enable_if_t<are_strings<Str1, Str2>::value,int> = 0>
constexpr bool operator!=(Str1&& left, Str2&& right)noexcept{
return !(std::forward<Str1>(left) == std::forward<Str2>(right));
}
template<class Str1, std::enable_if_t<are_strings<Str1>::value,int> = 0>
constexpr bool operator!=(Str1&& left, typename std::decay_t<Str1>::const_pointer right)noexcept{
return !(std::forward<Str1>(left) == right);
}
template<class Str1, std::enable_if_t<are_strings<Str1>::value,int> = 0>
constexpr bool operator!=(typename std::decay_t<Str1>::const_pointer left, Str1&& right)noexcept{
return !(left == std::forward<Str1>(right));
}
//String + string concat
template<class Left, class Right, std::enable_if_t<are_strings<Left, Right>::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<Left>(l), std::forward<Right>(r))))
{
return string_cat_expr(std::forward<Left>(l), std::forward<Right>(r));
}
//String + char pointer
template<class Right, std::enable_if_t<is_string<Right>::value,int> = 0>
constexpr auto operator+(typename std::decay_t<Right>::const_pointer left, Right&& right)
noexcept(noexcept(::new (nullptr) string_cat_expr(rexy::basic_string_view(left), std::forward<Right>(right))))
{
return string_cat_expr(rexy::basic_string_view(left), std::forward<Right>(right));
}
//Char pointer + string
template<class Left, std::enable_if_t<is_string<Left>::value,int> = 0>
constexpr auto operator+(Left&& left, typename std::decay_t<Left>::const_pointer right)
noexcept(noexcept(::new (nullptr) string_cat_expr(std::forward<Left>(left), rexy::basic_string_view(right))))
{
return rexy::string_cat_expr(std::forward<Left>(left), rexy::basic_string_view(right));
}
//String + expr concat
template<class Left, class Right, std::enable_if_t<is_string<Left>::value && is_string_expr<Right>::value,int> = 0>
constexpr auto operator+(Left&& left, Right&& right){
return string_cat_expr(std::forward<Left>(left), std::forward<Right>(right));
}
//Expr + string
template<class Left, class Right, std::enable_if_t<is_string_expr<Left>::value && is_string<Right>::value,int> = 0>
constexpr auto operator+(Left&& left, Right&& right){
return string_cat_expr(std::forward<Left>(left), std::forward<Right>(right));
}
//Expr + expr
template<class Left, class Right, std::enable_if_t<are_string_expr<Left,Right>::value,int> = 0>
constexpr auto operator+(Left&& left, Right&& right){
return string_cat_expr(std::forward<Left>(left), std::forward<Right>(right));
}
//Expr + char pointer
template<class Left, std::enable_if_t<is_string_expr<Left>::value,int> = 0>
constexpr auto operator+(Left&& left, typename std::decay_t<Left>::const_pointer right){
return string_cat_expr(std::forward<Left>(left), rexy::basic_string_view(right));
}
//char pointer + Expr
template<class Right, std::enable_if_t<is_string_expr<Right>::value,int> = 0>
constexpr auto operator+(typename std::decay_t<Right>::const_pointer left, Right&& right){
return string_cat_expr(rexy::basic_string_view(left), std::forward<Right>(right));
}
//String concat assignment
template<class Left, class Right, std::enable_if_t<are_strings<Left,Right>::value,int> = 0>
decltype(auto) operator+=(Left& l, Right&& r)
noexcept(noexcept(l + std::forward<Right>(r)) && std::is_nothrow_assignable<Left, decltype(l + std::forward<Right>(r))>::value)
{
return l = (l + std::forward<Right>(r));
}
template<class Left, class Right, std::enable_if_t<is_string<Left>::value && is_string_expr<Right>::value,int> = 0>
decltype(auto) operator+=(Left& l, Right&& r)
noexcept(noexcept(l + std::forward<Right>(r)) && std::is_nothrow_assignable<Left, decltype(l + std::forward<Right>(r))>::value)
{
return l = (l + std::forward<Right>(r));
}
template<class Left, std::enable_if_t<is_string<Left>::value, int> = 0>
decltype(auto) operator+=(Left& l, typename std::decay_t<Left>::const_pointer r)
noexcept(noexcept(l + r) && std::is_nothrow_assignable<Left, decltype(l + r)>::value)
{
return l = (l + r);
}
}
#endif

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
#ifndef REXY_COMPAT_CPP20_STRING_BASE_HPP
#define REXY_COMPAT_CPP20_STRING_BASE_HPP
#include "../../string_base.hpp"
#include <concepts> //convertible_to
#include <utility> //forward, as_const
#include <type_traits> //decay, is_nothrow_assignable
namespace rexy{
template<class T>
concept BasicString = requires(const T& a){
{std::as_const(a).length()} -> std::convertible_to<typename std::decay_t<T>::size_type>;
{std::as_const(a).c_str()} -> std::convertible_to<typename std::decay_t<T>::const_pointer>;
{std::as_const(a)[0]} -> std::convertible_to<typename std::decay_t<T>::const_reference>;
{std::as_const(a).begin()} -> std::convertible_to<typename std::decay_t<T>::const_iterator>;
{std::as_const(a).end()} -> std::convertible_to<typename std::decay_t<T>::const_iterator>;
};
template<class T>
concept StringExpr = rexy::is_template_type<T,string_cat_expr>::value;
template<class T>
concept String = BasicString<T> || StringExpr<T>;
template<class T>
struct is_string{
static constexpr bool value = BasicString<T>;
};
template<class T>
static constexpr bool is_string_v = is_string<T>::value;
template<class... Ts>
struct are_strings{
static constexpr bool value = (BasicString<Ts> && ...);
};
template<class... Ts>
static constexpr bool are_strings_v = are_strings<Ts...>::value;
template<class T>
struct is_string_expr{
static constexpr bool value = StringExpr<T>;
};
template<class T>
static constexpr bool is_string_expr_v = is_string_expr<T>::value;
template<class... Ts>
struct are_string_expr{
static constexpr bool value = (StringExpr<Ts> && ...);
};
template<class... Ts>
static constexpr bool are_string_expr_v = are_string_expr<Ts...>::value;
//Compare
template<BasicString Str1, BasicString Str2>
constexpr bool operator==(Str1&& left, Str2&& right){
if(left.length() != right.length()){
return false;
}
return !detail::string_compare(std::forward<Str1>(left), std::forward<Str2>(right), left.length());
}
template<BasicString Str1>
constexpr bool operator==(Str1&& left, typename std::decay_t<Str1>::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<BasicString Str1>
constexpr bool operator==(typename std::decay_t<Str1>::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<BasicString Str1, BasicString Str2>
constexpr bool operator!=(Str1&& left, Str2&& right){
return !(std::forward<Str1>(left) == std::forward<Str2>(right));
}
template<BasicString Str1>
constexpr bool operator!=(Str1&& left, typename std::decay_t<Str1>::const_pointer right){
return !(std::forward<Str1>(left) == right);
}
template<BasicString Str1>
constexpr bool operator!=(typename std::decay_t<Str1>::const_pointer left, Str1&& right){
return !(left == std::forward<Str1>(right));
}
//String + string concat
template<String Left, String Right>
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<Left>(l), std::forward<Right>(r))))
{
return string_cat_expr(std::forward<Left>(l), std::forward<Right>(r));
}
template<String Right>
constexpr auto operator+(typename std::decay_t<Right>::const_pointer left, Right&& right)
noexcept(noexcept(::new (nullptr) string_cat_expr(rexy::basic_string_view(left), std::forward<Right>(right))))
{
return string_cat_expr(rexy::basic_string_view(left), std::forward<Right>(right));
}
template<String Left>
constexpr auto operator+(Left&& left, typename std::decay_t<Left>::const_pointer right)
noexcept(noexcept(::new (nullptr) string_cat_expr(std::forward<Left>(left), rexy::basic_string_view(right))))
{
return rexy::string_cat_expr(std::forward<Left>(left), rexy::basic_string_view(right));
}
//String concat assign
template<BasicString Left, String Right>
constexpr decltype(auto) operator+=(Left& l, Right&& r)
noexcept(noexcept(l + std::forward<Right>(r)) && std::is_nothrow_assignable<Left, decltype(l + std::forward<Right>(r))>::value)
{
return l = (l + std::forward<Right>(r));
}
template<BasicString Left>
constexpr decltype(auto) operator+=(Left& l, typename std::decay_t<Left>::const_pointer r)
noexcept(noexcept(l + r) && std::is_nothrow_assignable<Left, decltype(l + r)>::value)
{
return l = (l + r);
}
}
#endif

View File

@ -1,6 +1,6 @@
/** /**
This file is a part of rexy's general purpose library 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 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 it under the terms of the GNU General Public License as published by
@ -16,15 +16,25 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#ifndef REXY_CPP20_HPP #ifndef REXY_COMPAT_CPP20_HPP
#define REXY_CPP20_HPP #define REXY_COMPAT_CPP20_HPP
#if __cplusplus > 201703L #if __cplusplus >= 202002L
#define REXY_CPP20_CONSTEXPR constexpr #define REXY_CPP20_CONSTEXPR constexpr
#define REXY_CPP20_CONSTEVAL consteval #define REXY_CPP20_CONSTEVAL consteval
#else #else //__cplusplus
#define REXY_CPP20_CONSTEXPR #define REXY_CPP20_CONSTEXPR
#define REXY_CPP20_CONSTEVAL 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 #endif

View File

@ -23,7 +23,7 @@
#include <utility> //move, forward, pair #include <utility> //move, forward, pair
#include <climits> //CHAR_BIT #include <climits> //CHAR_BIT
#include "../../utility.hpp" //swap #include "../../utility.hpp" //swap
#include "../../compat/standard.hpp"
namespace rexy::cx::detail{ namespace rexy::cx::detail{
class bool_specialize_base class bool_specialize_base
@ -59,7 +59,7 @@ namespace rexy::cx::detail{
m_offset(offset){} m_offset(offset){}
constexpr boolean(const boolean&)noexcept = default; constexpr boolean(const boolean&)noexcept = default;
constexpr boolean(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{ constexpr boolean& operator=(const boolean& b)noexcept{
return *this = static_cast<bool>(b); return *this = static_cast<bool>(b);
@ -86,7 +86,7 @@ namespace rexy::cx::detail{
constexpr booleans(void)noexcept = default; constexpr booleans(void)noexcept = default;
constexpr booleans(const booleans&)noexcept = default; constexpr booleans(const booleans&)noexcept = default;
constexpr booleans(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=(const booleans&)noexcept = default;
constexpr booleans& operator=(booleans&&)noexcept = default; constexpr booleans& operator=(booleans&&)noexcept = default;
@ -121,7 +121,7 @@ namespace rexy::cx::detail{
m_offset(offset){} m_offset(offset){}
constexpr bool_iter(const bool_iter&)noexcept = default; constexpr bool_iter(const bool_iter&)noexcept = default;
constexpr bool_iter(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=(const bool_iter&)noexcept = default;
constexpr bool_iter& operator=(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){} m_offset(b.m_offset){}
constexpr const_bool_iter(const const_bool_iter&)noexcept = default; constexpr const_bool_iter(const const_bool_iter&)noexcept = default;
constexpr const_bool_iter(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 const_bool_iter&)noexcept = default;
constexpr const_bool_iter& operator=(const_bool_iter&&)noexcept = default; constexpr const_bool_iter& operator=(const_bool_iter&&)noexcept = default;

View File

@ -29,10 +29,10 @@ namespace rexy::cx{
#include <type_traits> #include <type_traits>
#include <cstddef> //ptrdiff_t, size_t #include <cstddef> //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 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. static_string is //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 //designed to be a thin wrapper around a raw char*, this is designed to allow compile time string concatenation
namespace rexy::cx{ namespace rexy::cx{

View File

@ -24,6 +24,7 @@
#include "detail/bool_specialize_base.hpp" #include "detail/bool_specialize_base.hpp"
#include "../utility.hpp" //swap #include "../utility.hpp" //swap
#include "../compat/standard.hpp"
#include <type_traits> #include <type_traits>
@ -63,7 +64,7 @@ namespace rexy::cx{
m_size = min(count, max_elements); 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<T>::value) = default; constexpr vector& operator=(const vector&)noexcept(std::is_nothrow_copy_assignable<T>::value) = default;
constexpr vector& operator=(vector&&)noexcept(std::is_nothrow_move_assignable<T>::value) = default; constexpr vector& operator=(vector&&)noexcept(std::is_nothrow_move_assignable<T>::value) = default;
@ -283,7 +284,7 @@ namespace rexy::cx{
m_size = min(count, max_elements); 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=(const vector&)noexcept = default;
constexpr vector& operator=(vector&&)noexcept = default; constexpr vector& operator=(vector&&)noexcept = default;

View File

@ -1,6 +1,6 @@
/** /**
This file is a part of rexy's general purpose library 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 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 it under the terms of the GNU General Public License as published by
@ -19,6 +19,8 @@
#ifndef REXY_DETAIL_HASALLOCATOR_HPP #ifndef REXY_DETAIL_HASALLOCATOR_HPP
#define REXY_DETAIL_HASALLOCATOR_HPP #define REXY_DETAIL_HASALLOCATOR_HPP
#include "../compat/standard.hpp"
namespace rexy::detail{ namespace rexy::detail{
template<class Alloc> template<class Alloc>
@ -26,10 +28,10 @@ namespace rexy::detail{
{ {
Alloc m_alloc; 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); 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); m_alloc.deallocate(p, bytes);
} }

View File

@ -21,6 +21,14 @@
#ifndef REXY_ENUM_CLASS_HPP #ifndef REXY_ENUM_CLASS_HPP
#define 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 <type_traits> #include <type_traits>
template<class T> template<class T>
@ -201,4 +209,6 @@ namespace rexy::enum_traits{
} }
} }
#endif //__cplusplus
#endif #endif

View File

@ -19,7 +19,7 @@
#ifndef REXY_EXPRESSION_HPP #ifndef REXY_EXPRESSION_HPP
#define REXY_EXPRESSION_HPP #define REXY_EXPRESSION_HPP
#include <type_traits> #include <type_traits> //is_void, remove_reference, conditional
#include <utility> //forward #include <utility> //forward
#include "rexy.hpp" #include "rexy.hpp"
@ -29,8 +29,8 @@ namespace rexy{
template<class L, class R> template<class L, class R>
class binary_expression class binary_expression
{ {
static_assert(!std::is_same<std::decay_t<L>,void>::value, "Left value of rexy::binary_expression cannot be void!"); static_assert(!std::is_void_v<L>, "Left value of rexy::binary_expression cannot be void!");
static_assert(!std::is_same<std::decay_t<R>,void>::value, "Right value of rexy::binary_expression cannot be void!"); static_assert(!std::is_void_v<R>, "Right value of rexy::binary_expression cannot be void!");
public: public:
using left_type = std::conditional_t<std::is_rvalue_reference<L>::value, using left_type = std::conditional_t<std::is_rvalue_reference<L>::value,
@ -78,7 +78,7 @@ namespace rexy{
template<class L> template<class L>
class unary_expression class unary_expression
{ {
static_assert(!std::is_same<std::decay_t<L>,void>::value, "Value of rexy::unary_expression cannot be void!"); static_assert(!std::is_void_v<L>, "Value of rexy::unary_expression cannot be void!");
public: public:
using left_type = std::conditional_t<std::is_rvalue_reference<L>::value, using left_type = std::conditional_t<std::is_rvalue_reference<L>::value,

View File

@ -77,11 +77,7 @@ namespace rexy{
#else //LIBREXY_HEADER_ONLY #else //LIBREXY_HEADER_ONLY
namespace rexy{ #error "rexy::filerd is not available when built with header only support"
static_assert(false, "rexy::filerd is not available when built with header only support");
}
#endif //LIBREXY_HEADER_ONLY #endif //LIBREXY_HEADER_ONLY

View File

@ -22,6 +22,8 @@
#include <utility> //forward, move #include <utility> //forward, move
#include <type_traits> //is_nothrow_constructible, etc #include <type_traits> //is_nothrow_constructible, etc
#include "compat/standard.hpp"
namespace rexy{ namespace rexy{
//A wrapper around raw storage. //A wrapper around raw storage.
@ -42,7 +44,19 @@ namespace rexy{
static constexpr auto size = sizeof(value_type); static constexpr auto size = sizeof(value_type);
private: 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]; alignas(align) unsigned char m_storage[size];
#endif //__cplusplus
unsigned char m_dirty:1 = 0; unsigned char m_dirty:1 = 0;
public: public:
@ -53,7 +67,7 @@ namespace rexy{
constexpr storage_for(rvalue_reference t)noexcept(std::is_nothrow_move_constructible<value_type>::value); constexpr storage_for(rvalue_reference t)noexcept(std::is_nothrow_move_constructible<value_type>::value);
constexpr storage_for(const storage_for& s)noexcept(std::is_nothrow_copy_constructible<value_type>::value); constexpr storage_for(const storage_for& s)noexcept(std::is_nothrow_copy_constructible<value_type>::value);
constexpr storage_for(storage_for&& s)noexcept(std::is_nothrow_move_constructible<value_type>::value); constexpr storage_for(storage_for&& s)noexcept(std::is_nothrow_move_constructible<value_type>::value);
constexpr ~storage_for(void)noexcept(std::is_nothrow_destructible<value_type>::value); REXY_CPP20_CONSTEXPR ~storage_for(void)noexcept(std::is_nothrow_destructible<value_type>::value);
constexpr storage_for& operator=(const storage_for& s)noexcept(std::is_nothrow_copy_assignable<value_type>::value); constexpr storage_for& operator=(const storage_for& s)noexcept(std::is_nothrow_copy_assignable<value_type>::value);
constexpr storage_for& operator=(storage_for&& s)noexcept(std::is_nothrow_move_assignable<value_type>::value); constexpr storage_for& operator=(storage_for&& s)noexcept(std::is_nothrow_move_assignable<value_type>::value);
@ -65,6 +79,9 @@ namespace rexy{
constexpr operator const_reference(void)const noexcept; constexpr operator const_reference(void)const noexcept;
constexpr operator rvalue_reference(void)&& 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 pointer operator->(void)noexcept;
constexpr const_pointer operator->(void)const noexcept; constexpr const_pointer operator->(void)const noexcept;
constexpr reference operator*(void)noexcept; constexpr reference operator*(void)noexcept;

View File

@ -21,6 +21,141 @@
#include <utility> //forward, move #include <utility> //forward, move
#if __cplusplus >= 202002L
#include <memory> //construct_at, destroy_at
namespace rexy{
template<class T>
template<class... Args>
constexpr storage_for<T>::storage_for(Args&&... args)noexcept(std::is_nothrow_constructible<value_type,Args...>::value){
std::construct_at(&m_storage.data, std::forward<Args>(args)...);
m_dirty = 1;
}
template<class T>
constexpr storage_for<T>::storage_for(const_reference t)noexcept(std::is_nothrow_copy_constructible<value_type>::value){
std::construct_at(&m_storage.data, t);
m_dirty = 1;
}
template<class T>
constexpr storage_for<T>::storage_for(rvalue_reference t)noexcept(std::is_nothrow_move_constructible<value_type>::value){
std::construct_at(&m_storage.data, std::move(t));
m_dirty = 1;
}
template<class T>
constexpr storage_for<T>::storage_for(const storage_for& s)noexcept(std::is_nothrow_copy_constructible<value_type>::value){
if(s.m_dirty){
std::construct_at(&m_storage.data, s.m_storage.data);
m_dirty = 1;
}
}
template<class T>
constexpr storage_for<T>::storage_for(storage_for&& s)noexcept(std::is_nothrow_move_constructible<value_type>::value){
if(s.m_dirty){
std::construct_at(&m_storage.data, std::move(s.m_storage.data));
m_dirty = 1;
}
}
template<class T>
constexpr storage_for<T>::~storage_for(void)noexcept(std::is_nothrow_destructible<value_type>::value){
if(m_dirty){
destroy();
}
}
template<class T>
constexpr storage_for<T>& storage_for<T>::operator=(const storage_for& s)noexcept(std::is_nothrow_copy_assignable<value_type>::value){
return ((*this) = storage_for(s));
}
template<class T>
constexpr storage_for<T>& storage_for<T>::operator=(storage_for&& s)noexcept(std::is_nothrow_move_assignable<value_type>::value){
if(s.m_dirty){
return ((*this) = std::move(s.m_storage.data));
}else{
if(m_dirty){
destroy();
}
}
return *this;
}
template<class T>
constexpr storage_for<T>& storage_for<T>::operator=(const_reference t)noexcept(std::is_nothrow_copy_assignable<value_type>::value){
if(m_dirty){
m_storage.data = t;
}else{
std::construct_at(&m_storage.data, t);
}
m_dirty = 1;
return *this;
}
template<class T>
constexpr storage_for<T>& storage_for<T>::operator=(rvalue_reference t)noexcept(std::is_nothrow_move_assignable<value_type>::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<class T>
constexpr void storage_for<T>::destroy(void)noexcept(std::is_nothrow_destructible<value_type>::value){
if(m_dirty){
std::destroy_at(&m_storage.data);
m_dirty = 0;
}
}
template<class T>
constexpr storage_for<T>::operator reference(void)noexcept{
return m_storage.data;
}
template<class T>
constexpr storage_for<T>::operator const_reference(void)const noexcept{
return m_storage.data;
}
template<class T>
constexpr storage_for<T>::operator rvalue_reference(void)&& noexcept{
return std::move(m_storage.data);
}
template<class T>
constexpr auto storage_for<T>::get(void)noexcept -> reference{
return m_storage.data;
}
template<class T>
constexpr auto storage_for<T>::get(void)const noexcept -> const_reference{
return m_storage.data;
}
template<class T>
constexpr auto storage_for<T>::operator->(void)noexcept -> pointer{
return &m_storage.data;
}
template<class T>
constexpr auto storage_for<T>::operator->(void)const noexcept -> const_pointer{
return &m_storage.data;
}
template<class T>
constexpr auto storage_for<T>::operator*(void)noexcept -> reference{
return m_storage.data;
}
template<class T>
constexpr auto storage_for<T>::operator*(void)const noexcept -> const_reference{
return m_storage.data;
}
template<class T>
constexpr bool storage_for<T>::valid(void)const{
return m_dirty;
}
}
#else //__cplusplus
namespace rexy{ namespace rexy{
template<class T> template<class T>
@ -88,13 +223,22 @@ namespace rexy{
} }
template<class T> template<class T>
constexpr storage_for<T>::operator const_reference(void)const noexcept{ constexpr storage_for<T>::operator const_reference(void)const noexcept{
return m_storage; return reinterpret_cast<const_reference>(m_storage);
} }
template<class T> template<class T>
constexpr storage_for<T>::operator rvalue_reference(void)&& noexcept{ constexpr storage_for<T>::operator rvalue_reference(void)&& noexcept{
return reinterpret_cast<rvalue_reference>(std::move(m_storage)); return reinterpret_cast<rvalue_reference>(std::move(m_storage));
} }
template<class T>
constexpr auto storage_for<T>::get(void)noexcept -> reference{
return reinterpret_cast<reference>(m_storage);
}
template<class T>
constexpr auto storage_for<T>::get(void)const noexcept -> const_reference{
return reinterpret_cast<const_reference>(m_storage);
}
template<class T> template<class T>
constexpr auto storage_for<T>::operator->(void)noexcept -> pointer{ constexpr auto storage_for<T>::operator->(void)noexcept -> pointer{
return reinterpret_cast<T*>(&m_storage); return reinterpret_cast<T*>(&m_storage);
@ -120,4 +264,6 @@ namespace rexy{
} }
#endif //__cplusplus
#endif #endif

View File

@ -35,7 +35,7 @@
#include "detail/hasallocator.hpp" #include "detail/hasallocator.hpp"
#include "rexy.hpp" #include "rexy.hpp"
#include "compat/constexpr.hpp" #include "compat/standard.hpp"
#if __cplusplus >= 202002L #if __cplusplus >= 202002L
#include <concepts> #include <concepts>
@ -395,305 +395,6 @@ namespace rexy{
} }
} }
#if __cplusplus >= 202002L
template<class T>
concept BasicString = requires(const T& a){
{std::as_const(a).length()} -> std::convertible_to<typename std::decay_t<T>::size_type>;
{std::as_const(a).c_str()} -> std::convertible_to<typename std::decay_t<T>::const_pointer>;
{std::as_const(a)[0]} -> std::convertible_to<typename std::decay_t<T>::const_reference>;
{std::as_const(a).begin()} -> std::convertible_to<typename std::decay_t<T>::const_iterator>;
{std::as_const(a).end()} -> std::convertible_to<typename std::decay_t<T>::const_iterator>;
};
template<class T>
concept StringExpr = rexy::is_template_type<T,string_cat_expr>::value;
template<class T>
concept String = BasicString<T> || StringExpr<T>;
template<class T>
struct is_string{
static constexpr bool value = BasicString<T>;
};
template<class T>
static constexpr bool is_string_v = is_string<T>::value;
template<class... Ts>
struct are_strings{
static constexpr bool value = (BasicString<Ts> && ...);
};
template<class... Ts>
static constexpr bool are_strings_v = are_strings<Ts...>::value;
template<class T>
struct is_string_expr{
static constexpr bool value = StringExpr<T>;
};
template<class T>
static constexpr bool is_string_expr_v = is_string_expr<T>::value;
template<class... Ts>
struct are_string_expr{
static constexpr bool value = (StringExpr<Ts> && ...);
};
template<class... Ts>
static constexpr bool are_string_expr_v = are_string_expr<Ts...>::value;
//Compare
template<BasicString Str1, BasicString Str2>
constexpr bool operator==(Str1&& left, Str2&& right){
if(left.length() != right.length()){
return false;
}
return !detail::string_compare(std::forward<Str1>(left), std::forward<Str2>(right), left.length());
}
template<BasicString Str1>
constexpr bool operator==(Str1&& left, typename std::decay_t<Str1>::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<BasicString Str1>
constexpr bool operator==(typename std::decay_t<Str1>::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<BasicString Str1, BasicString Str2>
constexpr bool operator!=(Str1&& left, Str2&& right){
return !(std::forward<Str1>(left) == std::forward<Str2>(right));
}
template<BasicString Str1>
constexpr bool operator!=(Str1&& left, typename std::decay_t<Str1>::const_pointer right){
return !(std::forward<Str1>(left) == right);
}
template<BasicString Str1>
constexpr bool operator!=(typename std::decay_t<Str1>::const_pointer left, Str1&& right){
return !(left == std::forward<Str1>(right));
}
//String + string concat
template<String Left, String Right>
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<Left>(l), std::forward<Right>(r))))
{
return string_cat_expr(std::forward<Left>(l), std::forward<Right>(r));
}
template<String Right>
constexpr auto operator+(typename std::decay_t<Right>::const_pointer left, Right&& right)
noexcept(noexcept(::new (nullptr) string_cat_expr(rexy::basic_string_view(left), std::forward<Right>(right))))
{
return string_cat_expr(rexy::basic_string_view(left), std::forward<Right>(right));
}
template<String Left>
constexpr auto operator+(Left&& left, typename std::decay_t<Left>::const_pointer right)
noexcept(noexcept(::new (nullptr) string_cat_expr(std::forward<Left>(left), rexy::basic_string_view(right))))
{
return rexy::string_cat_expr(std::forward<Left>(left), rexy::basic_string_view(right));
}
//String concat assign
template<BasicString Left, String Right>
REXY_CPP20_CONSTEXPR decltype(auto) operator+=(Left& l, Right&& r)
noexcept(noexcept(l + std::forward<Right>(r)) && std::is_nothrow_assignable<Left, decltype(l + std::forward<Right>(r))>::value)
{
return l = (l + std::forward<Right>(r));
}
template<BasicString Left>
REXY_CPP20_CONSTEXPR decltype(auto) operator+=(Left& l, typename std::decay_t<Left>::const_pointer r)
noexcept(noexcept(l + r) && std::is_nothrow_assignable<Left, decltype(l + r)>::value)
{
return l = (l + r);
}
#else //__cplusplus == 202002L
#define HAS_MEMFUN_WITH_RET(type, ret, fun, ...) \
template<class T> \
struct has_##fun##_f{ \
static std::false_type check(...); \
template<class type> \
static auto check(type* u) -> std::enable_if_t<std::is_convertible_v<decltype(std::declval<type>().fun(__VA_ARGS__)),ret>,std::true_type>; \
\
static constexpr bool value = decltype(check(std::declval<std::remove_reference_t<T>*>()))::value; \
}; \
template<class T> \
static constexpr bool has_##fun##_f_v = has_##fun##_f<T>::value
#define HAS_MEMOP_WITH_RET(type, ret, opname, op, ...) \
template<class T> \
struct has_##opname##_f{ \
static std::false_type check(...); \
template<class type> \
static auto check(type* u) -> std::enable_if_t<std::is_convertible_v<decltype(std::declval<type>().operator op(__VA_ARGS__)),ret>,std::true_type>; \
\
static constexpr bool value = decltype(check(std::declval<std::remove_reference_t<T>*>()))::value; \
}; \
template<class T> \
static constexpr bool has_##opname##_f_v = has_##opname##_f<T>::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<class T>
struct is_string{
static constexpr bool value = has_length_f_v<T> && has_c_str_f_v<T> && has_indexop_f_v<T> && has_begin_f_v<T> && has_end_f_v<T>;
};
template<class T>
static constexpr bool is_string_v = is_string<T>::value;
template<class... Ts>
struct are_strings{
static constexpr bool value = (is_string<Ts>::value && ...);
};
template<class... Ts>
static constexpr bool are_strings_v = are_strings<Ts...>::value;
template<class T>
struct is_string_expr{
static constexpr bool value = rexy::is_template_type<T,string_cat_expr>::value;
};
template<class T>
static constexpr bool is_string_expr_v = is_string_expr<T>::value;
template<class... Ts>
struct are_string_expr{
static constexpr bool value = (is_string_expr<Ts>::value && ...);
};
template<class... Ts>
static constexpr bool are_string_expr_v = are_string_expr<Ts...>::value;
//Compare
template<class Str1, class Str2, std::enable_if_t<are_strings<Str1, Str2>::value,int> = 0>
constexpr bool operator==(Str1&& left, Str2&& right){
if(left.length() != right.length()){
return false;
}
return !detail::string_compare(std::forward<Str1>(left), std::forward<Str2>(right), left.length());
}
template<class Str1, std::enable_if_t<are_strings<Str1>::value,int> = 0>
constexpr bool operator==(Str1&& left, typename std::decay_t<Str1>::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<class Str1, std::enable_if_t<are_strings<Str1>::value,int> = 0>
constexpr bool operator==(typename std::decay_t<Str1>::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<class Str1, class Str2, std::enable_if_t<are_strings<Str1, Str2>::value,int> = 0>
constexpr bool operator!=(Str1&& left, Str2&& right)noexcept{
return !(std::forward<Str1>(left) == std::forward<Str2>(right));
}
template<class Str1, std::enable_if_t<are_strings<Str1>::value,int> = 0>
constexpr bool operator!=(Str1&& left, typename std::decay_t<Str1>::const_pointer right)noexcept{
return !(std::forward<Str1>(left) == right);
}
template<class Str1, std::enable_if_t<are_strings<Str1>::value,int> = 0>
constexpr bool operator!=(typename std::decay_t<Str1>::const_pointer left, Str1&& right)noexcept{
return !(left == std::forward<Str1>(right));
}
//String + string concat
template<class Left, class Right, std::enable_if_t<are_strings<Left, Right>::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<Left>(l), std::forward<Right>(r))))
{
return string_cat_expr(std::forward<Left>(l), std::forward<Right>(r));
}
//String + char pointer
template<class Right, std::enable_if_t<is_string<Right>::value,int> = 0>
constexpr auto operator+(typename std::decay_t<Right>::const_pointer left, Right&& right)
noexcept(noexcept(::new (nullptr) string_cat_expr(rexy::basic_string_view(left), std::forward<Right>(right))))
{
return string_cat_expr(rexy::basic_string_view(left), std::forward<Right>(right));
}
//Char pointer + string
template<class Left, std::enable_if_t<is_string<Left>::value,int> = 0>
constexpr auto operator+(Left&& left, typename std::decay_t<Left>::const_pointer right)
noexcept(noexcept(::new (nullptr) string_cat_expr(std::forward<Left>(left), rexy::basic_string_view(right))))
{
return rexy::string_cat_expr(std::forward<Left>(left), rexy::basic_string_view(right));
}
//String + expr concat
template<class Left, class Right, std::enable_if_t<is_string<Left>::value && is_string_expr<Right>::value,int> = 0>
constexpr auto operator+(Left&& left, Right&& right){
return string_cat_expr(std::forward<Left>(left), std::forward<Right>(right));
}
//Expr + string
template<class Left, class Right, std::enable_if_t<is_string_expr<Left>::value && is_string<Right>::value,int> = 0>
constexpr auto operator+(Left&& left, Right&& right){
return string_cat_expr(std::forward<Left>(left), std::forward<Right>(right));
}
//Expr + expr
template<class Left, class Right, std::enable_if_t<are_string_expr<Left,Right>::value,int> = 0>
constexpr auto operator+(Left&& left, Right&& right){
return string_cat_expr(std::forward<Left>(left), std::forward<Right>(right));
}
//Expr + char pointer
template<class Left, std::enable_if_t<is_string_expr<Left>::value,int> = 0>
constexpr auto operator+(Left&& left, typename std::decay_t<Left>::const_pointer right){
return string_cat_expr(std::forward<Left>(left), rexy::basic_string_view(right));
}
//char pointer + Expr
template<class Right, std::enable_if_t<is_string_expr<Right>::value,int> = 0>
constexpr auto operator+(typename std::decay_t<Right>::const_pointer left, Right&& right){
return string_cat_expr(rexy::basic_string_view(left), std::forward<Right>(right));
}
//String concat assignment
template<class Left, class Right, std::enable_if_t<are_strings<Left,Right>::value,int> = 0>
REXY_CPP20_CONSTEXPR decltype(auto) operator+=(Left& l, Right&& r)
noexcept(noexcept(l + std::forward<Right>(r)) && std::is_nothrow_assignable<Left, decltype(l + std::forward<Right>(r))>::value)
{
return l = (l + std::forward<Right>(r));
}
template<class Left, class Right, std::enable_if_t<is_string<Left>::value && is_string_expr<Right>::value,int> = 0>
REXY_CPP20_CONSTEXPR decltype(auto) operator+=(Left& l, Right&& r)
noexcept(noexcept(l + std::forward<Right>(r)) && std::is_nothrow_assignable<Left, decltype(l + std::forward<Right>(r))>::value)
{
return l = (l + std::forward<Right>(r));
}
template<class Left, std::enable_if_t<is_string<Left>::value, int> = 0>
REXY_CPP20_CONSTEXPR decltype(auto) operator+=(Left& l, typename std::decay_t<Left>::const_pointer r)
noexcept(noexcept(l + r) && std::is_nothrow_assignable<Left, decltype(l + r)>::value)
{
return l = (l + r);
}
#endif //__cplusplus == 202002L
} }
#include "string_base.tpp" #include "string_base.tpp"

View File

@ -29,7 +29,11 @@
#include "compat/to_address.hpp" #include "compat/to_address.hpp"
#include "string_view.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{ namespace rexy{
@ -385,6 +389,4 @@ namespace rexy{
} //namespace rexy } //namespace rexy
#undef STOP_STRICT_ALIAS_WARNING
#endif #endif

View File

@ -22,7 +22,7 @@
#include <cstddef> //size_t, ptrdiff_t #include <cstddef> //size_t, ptrdiff_t
#include <iterator> //reverse_iterator #include <iterator> //reverse_iterator
#include "compat/constexpr.hpp" #include "compat/standard.hpp"
#include "rexy.hpp" #include "rexy.hpp"
namespace rexy{ namespace rexy{