292 lines
11 KiB
C++
292 lines
11 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_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
|
|
#include <algorithm> //lexicographically_compare
|
|
|
|
#include "../../utility.hpp" //strlen, strncmp
|
|
#include "../../traits.hpp"
|
|
|
|
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;
|
|
|
|
#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_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<rexy::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<rexy::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<rexy::remove_cvref_t<T>*>()))::value;
|
|
};
|
|
|
|
template<class T>
|
|
struct is_string{
|
|
static constexpr bool value = (is_basic_string<T>::value || is_basic_string_view<T>::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 = is_basic_string_expr<T>::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==(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<class Str1, std::enable_if_t<are_strings<Str1>::value,int> = 0>
|
|
constexpr bool operator==(const Str1& left, typename std::decay_t<Str1>::const_pointer right){
|
|
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<class Str1, std::enable_if_t<are_strings<Str1>::value,int> = 0>
|
|
constexpr bool operator==(typename std::decay_t<Str1>::const_pointer left, const Str1& right){
|
|
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<class Str1, class Str2, std::enable_if_t<are_strings<Str1, Str2>::value,int> = 0>
|
|
constexpr bool operator!=(const Str1& left, const Str2& right)noexcept{
|
|
return !(left == right);
|
|
}
|
|
template<class Str1, std::enable_if_t<are_strings<Str1>::value,int> = 0>
|
|
constexpr bool operator!=(const Str1& left, typename std::decay_t<Str1>::const_pointer right)noexcept{
|
|
return !(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, const Str1& right)noexcept{
|
|
return !(left == right);
|
|
}
|
|
template<class Str1, class Str2, std::enable_if_t<are_string<Str1,Str2>::value,int> = 0>
|
|
constexpr bool operator<(const Str1& left, const Str2& right)noexcept{
|
|
return std::lexicographical_compare(left.begin(), left.end(), right.begin(), right.end());
|
|
}
|
|
template<class Str1, class Str2, std::enable_if_t<are_string<Str1,Str2>::value,int> = 0>
|
|
constexpr bool operator<=(const Str1& left, const Str2& right)noexcept{
|
|
return !(right < left);
|
|
}
|
|
template<class Str1, class Str2, std::enable_if_t<are_string<Str1,Str2>::value,int> = 0>
|
|
constexpr bool operator>(const Str1& left, const Str2& right)noexcept{
|
|
return (right < left);
|
|
}
|
|
template<class Str1, class Str2, std::enable_if_t<are_string<Str1,Str2>::value,int> = 0>
|
|
constexpr bool operator>=(const Str1& left, const Str2& right)noexcept{
|
|
return !(left < 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);
|
|
}
|
|
|
|
template<class T, class Char, std::enable_if_t<is_string<T>::value, int> = 0>
|
|
constexpr std::size_t find_first_of(T&& t, const Char* c, std::size_t start, std::size_t size){
|
|
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<class T, class Char, std::enable_if_t<is_string<T>::value, int> = 0>
|
|
constexpr std::size_t find_last_of(T&& t, const Char* c, std::size_t start, std::size_t size){
|
|
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;
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|