General code cleanup and allow for more C++20
This commit is contained in:
parent
8b6b421c52
commit
9a55d8594b
@ -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 "")
|
||||
|
||||
@ -19,11 +19,7 @@
|
||||
#ifndef REXY_ALGORITHM_HPP
|
||||
#define REXY_ALGORITHM_HPP
|
||||
|
||||
#include "utility.hpp" //swap
|
||||
#include "rexy.hpp"
|
||||
#include <cstdint> //SIZE_MAX
|
||||
#include <cstdlib> //size_t
|
||||
#include <type_traits>
|
||||
|
||||
#include "detail/algorithm.hpp"
|
||||
|
||||
@ -33,84 +29,20 @@ namespace rexy{
|
||||
//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;
|
||||
}
|
||||
}
|
||||
noexcept(noexcept(detail::qs_partition(left, right, cmp)));
|
||||
|
||||
//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;
|
||||
}
|
||||
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<class HIter, class NIter>
|
||||
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
|
||||
|
||||
108
include/rexy/algorithm.tpp
Normal file
108
include/rexy/algorithm.tpp
Normal 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
|
||||
@ -26,6 +26,7 @@
|
||||
#include <limits> //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<class U>
|
||||
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);
|
||||
|
||||
#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{
|
||||
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
|
||||
}
|
||||
|
||||
|
||||
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
|
||||
if constexpr(alignof(T) <= __STDCPP_DEFAULT_NEW_ALIGNMENT__){
|
||||
::operator delete(p);
|
||||
}else{
|
||||
::operator delete(p, static_cast<std::align_val_t>(alignof(T)));
|
||||
}
|
||||
#else //__cpp_sized_deallocation
|
||||
size_type bytes = has_overflow(n) ? std::numeric_limits<size_type>::max() : n*sizeof(T);
|
||||
if constexpr(alignof(T) <= __STDCPP_DEFAULT_NEW_ALIGNMENT__){
|
||||
::operator delete(p, bytes);
|
||||
}else{
|
||||
::operator delete(p, bytes, static_cast<std::align_val_t>(alignof(T)));
|
||||
}
|
||||
#endif //__cpp_sized_deallocation
|
||||
|
||||
} //if consteval
|
||||
|
||||
}
|
||||
#else //__cplusplus
|
||||
REXY_CPP20_CONSTEXPR pointer allocate(size_type n){
|
||||
size_type bytes = has_overflow(n) ? std::numeric_limits<size_type>::max() : n*sizeof(T);
|
||||
|
||||
if constexpr(alignof(T) <= __STDCPP_DEFAULT_NEW_ALIGNMENT__){
|
||||
return reinterpret_cast<pointer>(::operator new(bytes));
|
||||
return static_cast<pointer>(::operator new(bytes));
|
||||
}else{
|
||||
return reinterpret_cast<pointer>(::operator new(bytes, static_cast<std::align_val_t>(alignof(T))));
|
||||
return static_cast<pointer>(::operator new(bytes, static_cast<std::align_val_t>(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<std::align_val_t>(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<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
|
||||
if constexpr(alignof(T) <= __STDCPP_DEFAULT_NEW_ALIGNMENT__){
|
||||
::operator delete(p, bytes);
|
||||
}else{
|
||||
::operator delete(p, bytes, static_cast<std::align_val_t>(alignof(T)));
|
||||
}
|
||||
#endif //__cpp_sized_deallocation
|
||||
|
||||
}
|
||||
#endif //__cplusplus
|
||||
constexpr size_type max_size(void)const{
|
||||
return std::numeric_limits<size_type>::max()/sizeof(T);
|
||||
}
|
||||
@ -99,6 +159,7 @@ namespace rexy{
|
||||
constexpr bool operator!=(const allocator<T>&, const allocator<U>&){
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@ -24,7 +24,7 @@
|
||||
#include <cstdlib> //size_t, ptrdiff_t
|
||||
#include <iterator> //reverse_iterator
|
||||
|
||||
#include "compat/constexpr.hpp"
|
||||
#include "compat/standard.hpp"
|
||||
|
||||
namespace rexy{
|
||||
|
||||
|
||||
@ -40,14 +40,14 @@ namespace rexy{
|
||||
template<class Iter>
|
||||
REXY_CPP20_CONSTEXPR buffer<T,Allocator>::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;
|
||||
}
|
||||
|
||||
206
include/rexy/compat/cpp17/string_base.hpp
Normal file
206
include/rexy/compat/cpp17/string_base.hpp
Normal 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
|
||||
150
include/rexy/compat/cpp20/string_base.hpp
Normal file
150
include/rexy/compat/cpp20/string_base.hpp
Normal 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
|
||||
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#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
|
||||
@ -23,7 +23,7 @@
|
||||
#include <utility> //move, forward, pair
|
||||
#include <climits> //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<bool>(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;
|
||||
|
||||
@ -29,10 +29,10 @@ namespace rexy::cx{
|
||||
#include <type_traits>
|
||||
#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 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{
|
||||
|
||||
@ -24,6 +24,7 @@
|
||||
|
||||
#include "detail/bool_specialize_base.hpp"
|
||||
#include "../utility.hpp" //swap
|
||||
#include "../compat/standard.hpp"
|
||||
|
||||
#include <type_traits>
|
||||
|
||||
@ -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<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);
|
||||
}
|
||||
|
||||
~vector(void)noexcept = default;
|
||||
REXY_CPP20_CONSTEXPR ~vector(void)noexcept = default;
|
||||
|
||||
constexpr vector& operator=(const vector&)noexcept = default;
|
||||
constexpr vector& operator=(vector&&)noexcept = default;
|
||||
|
||||
@ -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<class Alloc>
|
||||
@ -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);
|
||||
}
|
||||
|
||||
|
||||
@ -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 <type_traits>
|
||||
|
||||
template<class T>
|
||||
@ -201,4 +209,6 @@ namespace rexy::enum_traits{
|
||||
}
|
||||
}
|
||||
|
||||
#endif //__cplusplus
|
||||
|
||||
#endif
|
||||
|
||||
@ -19,7 +19,7 @@
|
||||
#ifndef REXY_EXPRESSION_HPP
|
||||
#define REXY_EXPRESSION_HPP
|
||||
|
||||
#include <type_traits>
|
||||
#include <type_traits> //is_void, remove_reference, conditional
|
||||
#include <utility> //forward
|
||||
|
||||
#include "rexy.hpp"
|
||||
@ -29,8 +29,8 @@ namespace rexy{
|
||||
template<class L, class R>
|
||||
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_same<std::decay_t<R>,void>::value, "Right 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_void_v<R>, "Right value of rexy::binary_expression cannot be void!");
|
||||
|
||||
public:
|
||||
using left_type = std::conditional_t<std::is_rvalue_reference<L>::value,
|
||||
@ -78,7 +78,7 @@ namespace rexy{
|
||||
template<class L>
|
||||
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:
|
||||
using left_type = std::conditional_t<std::is_rvalue_reference<L>::value,
|
||||
|
||||
@ -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
|
||||
|
||||
|
||||
@ -22,6 +22,8 @@
|
||||
#include <utility> //forward, move
|
||||
#include <type_traits> //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_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(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=(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 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;
|
||||
|
||||
@ -21,6 +21,141 @@
|
||||
|
||||
#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{
|
||||
|
||||
template<class T>
|
||||
@ -88,13 +223,22 @@ namespace rexy{
|
||||
}
|
||||
template<class T>
|
||||
constexpr storage_for<T>::operator const_reference(void)const noexcept{
|
||||
return m_storage;
|
||||
return reinterpret_cast<const_reference>(m_storage);
|
||||
}
|
||||
template<class T>
|
||||
constexpr storage_for<T>::operator rvalue_reference(void)&& noexcept{
|
||||
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>
|
||||
constexpr auto storage_for<T>::operator->(void)noexcept -> pointer{
|
||||
return reinterpret_cast<T*>(&m_storage);
|
||||
@ -120,4 +264,6 @@ namespace rexy{
|
||||
|
||||
}
|
||||
|
||||
#endif //__cplusplus
|
||||
|
||||
#endif
|
||||
|
||||
@ -35,7 +35,7 @@
|
||||
#include "detail/hasallocator.hpp"
|
||||
#include "rexy.hpp"
|
||||
|
||||
#include "compat/constexpr.hpp"
|
||||
#include "compat/standard.hpp"
|
||||
|
||||
#if __cplusplus >= 202002L
|
||||
#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"
|
||||
|
||||
@ -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
|
||||
|
||||
@ -22,7 +22,7 @@
|
||||
#include <cstddef> //size_t, ptrdiff_t
|
||||
#include <iterator> //reverse_iterator
|
||||
|
||||
#include "compat/constexpr.hpp"
|
||||
#include "compat/standard.hpp"
|
||||
#include "rexy.hpp"
|
||||
|
||||
namespace rexy{
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user