rexylib/include/rexy/compat/cpp20/string_base.hpp

258 lines
8.0 KiB
C++

/**
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 <utility> //forward
#include <type_traits> //decay, is_nothrow_assignable
#include <algorithm> //lexicographically_compare_three_way
#include "../../utility.hpp" //strlen, strncmp
namespace rexy{
template<class Left, class Right>
class string_cat_expr;
template<class Char, REXY_ALLOCATOR_CONCEPT Alloc>
class basic_string;
template<class Char>
class basic_string_view;
template<class T>
struct is_basic_string{
template<class Char, class Alloc>
static std::true_type check(const basic_string<Char,Alloc>*);
static std::false_type check(...);
static constexpr bool value = decltype(check(std::declval<std::remove_cvref_t<T>*>()))::value;
};
template<class T>
struct is_basic_string_view{
template<class Char>
static std::true_type check(const basic_string_view<Char>*);
static std::false_type check(...);
static constexpr bool value = decltype(check(std::declval<std::remove_cvref_t<T>*>()))::value;
};
template<class T>
struct is_basic_string_expr{
template<class Left, class Right>
static std::true_type check(const string_cat_expr<Left,Right>*);
static std::false_type check(...);
static constexpr bool value = decltype(check(std::declval<std::remove_cvref_t<T>*>()))::value;
};
template<class T>
concept BasicString = requires(const T& a){
requires(is_basic_string<T>::value || is_basic_string_view<T>::value);
{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 = is_basic_string_expr<T>::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 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;
//Compare
template<BasicString Str1, BasicString Str2>
constexpr bool operator==(const Str1& left, const Str2& right){
if(left.length() != right.length()){
return false;
}
return !rexy::strncmp(left.c_str(), right.c_str(), left.length()+1);
}
template<BasicString Str1>
constexpr bool operator==(const Str1& left, typename std::decay_t<Str1>::const_pointer right)noexcept{
if(right == nullptr){
return false;
}
const rexy::basic_string_view rstr(right);
if(rstr.length() != left.length()){
return false;
}
return !rexy::strncmp(left.c_str(), rstr.c_str(), left.length());
}
template<BasicString Str1>
constexpr bool operator==(typename std::decay_t<Str1>::const_pointer left, const Str1& right)noexcept{
if(left == nullptr){
return false;
}
const rexy::basic_string_view lstr(left);
if(lstr.length() != right.length()){
return false;
}
return !rexy::strncmp(lstr.c_str(), right.c_str(), right.length());
}
template<BasicString Str1, BasicString Str2>
constexpr bool operator!=(const Str1& left, const Str2& right)noexcept{
return !(left == right);
}
template<BasicString Str1>
constexpr bool operator!=(const Str1& left, typename std::decay_t<Str1>::const_pointer right)noexcept{
return !(left == right);
}
template<BasicString Str1>
constexpr bool operator!=(typename std::decay_t<Str1>::const_pointer left, const Str1& right)noexcept{
return !(left == right);
}
template<BasicString Str1, BasicString Str2>
constexpr auto operator<=>(const Str1& left, const Str2& right)noexcept{
return std::lexicographical_compare_three_way(left.begin(), left.end(), right.begin(), right.end());
}
//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);
}
template<BasicString T, class Char>
constexpr std::size_t find_first_of(T&& t, const Char* c, std::size_t start, std::size_t size)noexcept{
if(start > t.length()){
return rexy::npos;
}
for(auto it = t.cbegin() + start;it != t.cend();++it){
for(std::size_t i = 0;i < size;++i){
if(*it == c[i]){
return it - t.cbegin();
}
}
}
return rexy::npos;
}
template<BasicString T, class Char>
constexpr std::size_t find_first_not_of(T&& t, const Char* c, std::size_t start, std::size_t size)noexcept{
if(start > t.length()){
return rexy::npos;
}
for(auto it = t.cbegin() + start;it != t.cend();++it){
bool found = false;
for(std::size_t i = 0;i < size;++i){
if(*it == c[i]){
found = true;
break;
}
}
if(!found){
return it - t.cbegin();
}
}
return rexy::npos;
}
template<BasicString T, class Char>
constexpr std::size_t find_last_of(T&& t, const Char* c, std::size_t start, std::size_t size)noexcept{
if(start > t.length()){
return rexy::npos;
}
const auto b = t.cend() - 1;
const auto e = t.cbegin() - 1 - start;
for(auto it = b;it != e;--it){
for(std::size_t i = 0;i < size;++i){
if(*it == c[i]){
return it - t.cbegin();
}
}
}
return rexy::npos;
}
template<BasicString T, class Char>
constexpr std::size_t find_last_not_of(T&& t, const Char* c, std::size_t start, std::size_t size)noexcept{
if(start > t.length()){
return rexy::npos;
}
const auto b = t.cend() - 1;
const auto e = t.cbegin() - 1 - start;
for(auto it = b;it != e;--it){
bool found = false;
for(std::size_t i = 0;i < size;++i){
if(*it == c[i]){
found = true;
break;
}
}
if(!found){
return it - t.cbegin();
}
}
return rexy::npos;
}
}
#endif