Compare commits

..

No commits in common. "master" and "v0.2.1" have entirely different histories.

91 changed files with 1332 additions and 10110 deletions

View File

@ -1,9 +1,6 @@
project(librexy) project(librexy)
cmake_minimum_required(VERSION 3.0.2) cmake_minimum_required(VERSION 3.0.2)
include(GNUInstallDirs) include(GNUInstallDirs)
include(CMakeDependentOption)
set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS 1)
set(librexy_VERSION_STRING "000020000L") set(librexy_VERSION_STRING "000020000L")
set(librexy_VERSION_MAJOR 0) set(librexy_VERSION_MAJOR 0)
@ -12,82 +9,38 @@ set(librexy_VERSION_REVISION 0)
set(INCLUDE_PATH ${CMAKE_SOURCE_DIR}/include) set(INCLUDE_PATH ${CMAKE_SOURCE_DIR}/include)
include_directories(BEFORE SYSTEM "${INCLUDE_PATH}") include_directories(BEFORE SYSTEM "${INCLUDE_PATH}")
#find_program(CMAKE_CXX_CPPCHECK NAMES cppcheck)
#if(CMAKE_CXX_CPPCHECK)
# list(APPEND CMAKE_CXX_CPPCHECK "--enable=warning" "--inconclusive" "--force" "--inline-suppr")
#endif()
cmake_dependent_option(ENABLE_SHARED "Build shared library" ON "NOT BUILD_HEADER_ONLY" OFF) option(ENABLE_SHARED "Build shared library" ON)
cmake_dependent_option(ENABLE_SSO "Use small string optimization" ON "NOT BUILD_HEADER_ONLY" ON)
option(ENABLE_PROFILING "Enable asan" OFF) option(ENABLE_PROFILING "Enable asan" OFF)
option(BUILD_TESTS "Enable testing" OFF) option(BUILD_TESTS "Enable testing" OFF)
option(BUILD_HEADER_ONLY "Enable header only build" OFF)
mark_as_advanced(ENABLE_PROFILING) mark_as_advanced(ENABLE_PROFILING)
if(MSVC) set(SOURCE_LIST "src/filerd.cpp" "src/string.cpp" "src/string_view.cpp" "src/threadpool.cpp" "src/demangle.cpp")
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/out CACHE STRING "") add_library(ensure OBJECT "src/ensure.cpp")
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/out CACHE STRING "") target_compile_options(ensure PRIVATE -Wall -Wextra -pedantic -std=c++20)
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/out CACHE STRING "") if(ENABLE_SHARED)
endif() add_library(rexy SHARED ${SOURCE_LIST})
set_target_properties(rexy PROPERTIES SOVERSION "${librexy_VERSION_MAJOR}.${librexy_VERSION_MINOR}")
set(LIBREXY_LIBFLAGS "-lrexy")
set(CMAKE_CXX_STANDARD 20) target_link_libraries(rexy "-lpthread")
set(LIBREXY_PUBLIC_HEADERS "include/rexy/rexy.hpp" "include/rexy/algorithm.hpp" "include/rexy/algorithm.tpp" "include/rexy/utility.hpp" "include/rexy/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" "include/rexy/format.hpp" "include/rexy/format.tpp" "include/rexy/list.hpp" "include/rexy/list.tpp")
if(BUILD_HEADER_ONLY)
set(LIBREXY_BUILT_LIBRARY_HEADERS "")
set(librexy_HEADER_ONLY_BUILD 1)
add_library(rexy INTERFACE)
set(LIBREXY_LIBFLAGS "")
else() else()
set(LIBREXY_BUILT_LIBRARY_HEADERS "include/rexy/filerd.hpp" "include/rexy/threadpool.hpp" "include/rexy/demangle.hpp") add_library(rexy STATIC ${SOURCE_LIST})
set(librexy_HEADER_ONLY_BUILD 0) set(LIBREXY_LIBFLAGS "-lrexy -lpthread")
set(SOURCE_LIST "src/filerd.cpp" "src/string.cpp" "src/string_view.cpp" "src/threadpool.cpp" "src/demangle.cpp") target_link_libraries(rexy "-lpthread")
if(ENABLE_SHARED)
add_library(rexy SHARED ${SOURCE_LIST})
set_target_properties(rexy PROPERTIES SOVERSION "${librexy_VERSION_MAJOR}.${librexy_VERSION_MINOR}")
set(LIBREXY_LIBFLAGS "-lrexy")
target_link_libraries(rexy "-lpthread")
else()
add_library(rexy STATIC ${SOURCE_LIST})
set(LIBREXY_LIBFLAGS "-lrexy -lpthread")
target_link_libraries(rexy "-lpthread")
endif()
if(MSVC)
#make msvc update the __cplusplus macro to actually comply to c++ standard
target_compile_options(rexy PRIVATE "/Zc:__cplusplus")
else()
target_compile_options(rexy PRIVATE "-Wall" "-Wextra" "-pedantic")
endif()
set_target_properties(rexy PROPERTIES VERSION "${librexy_VERSION_MAJOR}.${librexy_VERSION_MINOR}.${librexy_VERSION_REVISION}")
if(ENABLE_SSO)
set(librexy_ENABLE_SSO 1)
else()
set(librexy_ENABLE_SSO 0)
endif()
if(ENABLE_PROFILING)
target_compile_options(rexy PRIVATE -fsanitize=address -fno-omit-frame-pointer -fno-optimize-sibling-calls)
target_link_options(rexy PRIVATE -fsanitize=address -fno-omit-frame-pointer -fno-optimize-sibling-calls)
endif()
endif() endif()
set_target_properties(rexy PROPERTIES VERSION "${librexy_VERSION_MAJOR}.${librexy_VERSION_MINOR}.${librexy_VERSION_REVISION}")
if(ENABLE_PROFILING)
target_compile_options(rexy PRIVATE -fsanitize=address -fno-omit-frame-pointer -fno-optimize-sibling-calls)
target_link_options(rexy PRIVATE -fsanitize=address -fno-omit-frame-pointer -fno-optimize-sibling-calls)
endif()
if(BUILD_TESTS) if(BUILD_TESTS)
add_library(ensure OBJECT "src/ensure.cpp")
if(MSVC)
target_compile_options(ensure PRIVATE "/Zc:__cplusplus")
else()
target_compile_options(ensure PRIVATE "-Wall" "-Wextra" "-pedantic")
endif()
enable_testing() enable_testing()
add_subdirectory(tests) add_subdirectory(tests)
endif() endif()
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/filerd.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/demangle.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")
target_compile_options(rexy PRIVATE -Wall -Wextra -pedantic -std=c++20)
install(TARGETS rexy install(TARGETS rexy
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
@ -97,11 +50,9 @@ install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/pc/librexy.pc"
DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig" DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig"
) )
install(FILES ${LIBREXY_PUBLIC_HEADERS} DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/rexy/") install(FILES ${LIBREXY_PUBLIC_HEADERS} DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/rexy/")
install(FILES ${LIBREXY_BUILT_LIBRARY_HEADERS} DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/rexy/")
install(DIRECTORY "include/rexy/detail" DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/rexy" FILES_MATCHING PATTERN "*.hpp" PATTERN "*.tpp") install(DIRECTORY "include/rexy/detail" DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/rexy" FILES_MATCHING PATTERN "*.hpp" PATTERN "*.tpp")
install(DIRECTORY "include/rexy/cx" DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/rexy" FILES_MATCHING PATTERN "*.hpp" PATTERN "*.tpp") install(DIRECTORY "include/rexy/cx" DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/rexy" FILES_MATCHING PATTERN "*.hpp" PATTERN "*.tpp")
install(DIRECTORY "include/rexy/compat" DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/rexy" FILES_MATCHING PATTERN "*.hpp" PATTERN "*.tpp") install(DIRECTORY "include/rexy/compat" DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/rexy" FILES_MATCHING PATTERN "*.hpp" PATTERN "*.tpp")
install(DIRECTORY "include/rexy/concepts" DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/rexy" FILES_MATCHING PATTERN "*.hpp" PATTERN "*.tpp")
configure_file( configure_file(
"${CMAKE_CURRENT_SOURCE_DIR}/pc/librexy.pc.cmake.in" "${CMAKE_CURRENT_SOURCE_DIR}/pc/librexy.pc.cmake.in"

View File

@ -19,7 +19,11 @@
#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"
@ -29,20 +33,84 @@ 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(HIter hstart, HIter hend, NIter nstart, 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()(HIter hstart, HIter hend, NIter nstart, 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

View File

@ -1,108 +0,0 @@
/**
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 <cstddef> //size_t
#include <limits> //numeric_limits
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(HIter hstart, HIter hend, NIter nstart, NIter nend){
std::size_t j = 0;
std::size_t i = 0;
std::size_t nlen = nend - nstart;
std::size_t hlen = hend - hstart;
auto [suffix, period] = detail::critical_factorization(nstart, nend);
if(detail::iter_compare(nstart, nstart + period, suffix)){
std::size_t memory = std::numeric_limits<std::size_t>::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 = std::numeric_limits<std::size_t>::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 != std::numeric_limits<std::size_t>::max() && nstart[i] == hstart[i + j]){
--i;
}
if(i == std::numeric_limits<std::size_t>::max()){
return hstart + j;
}
j += period;
}else{
j += (i - suffix);
}
}
}
return hend;
}
template<class HIter, class NIter>
constexpr HIter two_way_searcher::operator()(HIter hstart, HIter hend, NIter nstart, NIter nend)const{
return two_way_search(hstart, hend, nstart, nend);
}
}
#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-2022 rexy712 Copyright (C) 2020 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,12 +19,78 @@
#ifndef REXY_DEFAULT_ALLOCATOR_HPP #ifndef REXY_DEFAULT_ALLOCATOR_HPP
#define REXY_DEFAULT_ALLOCATOR_HPP #define REXY_DEFAULT_ALLOCATOR_HPP
#include "compat/allocator.hpp" #include <cstdlib> //size_t
#include <cstddef> //ptrdiff_t
#include <type_traits> //true_type, false_type
#include <new>
#include <limits> //numeric_limits
#include <type_traits> //declval #include "rexy.hpp"
namespace rexy{ namespace rexy{
template<class T>
struct allocator
{
using pointer = T*;
using const_pointer = const T*;
using void_pointer = void*;
using const_void_pointer = const void*;
using value_type = T;
using size_type = size_t;
using difference_type = ptrdiff_t;
using is_always_equal = std::true_type;
using propagate_on_container_copy_assignment = std::false_type;
using propagate_on_container_move_assignment = std::false_type;
using propagate_on_container_swap = std::false_type;
template<class U>
struct rebind{
using other = allocator<U>;
};
private:
constexpr bool has_overflow(size_type n)const{
return n > max_size();
}
public:
allocator(void) = default;
allocator(const allocator&) = default;
allocator(allocator&&) = default;
template<class U>
constexpr allocator(const allocator<U>&)noexcept{}
~allocator(void) = default;
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));
}else{
return reinterpret_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
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
}
constexpr size_type max_size(void)const{
return std::numeric_limits<size_type>::max()/sizeof(T);
}
};
template<class T, class U> template<class T, class U>
constexpr bool operator==(const allocator<T>&, const allocator<U>&){ constexpr bool operator==(const allocator<T>&, const allocator<U>&){
return true; return true;
@ -33,14 +99,6 @@ namespace rexy{
constexpr bool operator!=(const allocator<T>&, const allocator<U>&){ constexpr bool operator!=(const allocator<T>&, const allocator<U>&){
return false; return false;
} }
template<REXY_ALLOCATOR_CONCEPT T>
struct is_nothrow_allocator{
static constexpr bool value = noexcept(std::declval<T>().allocate(0)) && noexcept(std::declval<T>().deallocate(nullptr, 0));
};
template<REXY_ALLOCATOR_CONCEPT T>
static constexpr bool is_nothrow_allocator_v = is_nothrow_allocator<T>::value;
} }
#endif #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) 2022 rexy712 Copyright (C) 2020 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,14 +16,19 @@
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_COMPAT_TRAITS_HPP #ifndef REXY_BASIC_STRING_HASH_HPP
#define REXY_COMPAT_TRAITS_HPP #define REXY_BASIC_STRING_HASH_HPP
#ifdef REXY_STANDARD_CPP20 #include "string_hash.hpp"
#include "cpp20/traits.hpp" #include "string.hpp"
#else // REXY_STANDARD_CPP26
#include "cpp17/traits.hpp"
#endif // REXY_STANDARD_CPP26
#include "rexy.hpp"
namespace rexy{
template<>
struct hash<rexy::string> : public string_hash<rexy::string>{};
}
#endif #endif

View File

@ -21,12 +21,10 @@
#include "allocator.hpp" #include "allocator.hpp"
#include "detail/hasallocator.hpp" #include "detail/hasallocator.hpp"
#include <cstddef> //size_t, ptrdiff_t #include <cstdlib> //size_t, ptrdiff_t
#include <iterator> //reverse_iterator #include <iterator> //reverse_iterator
#include "storage_for.hpp" #include "compat/constexpr.hpp"
#include "compat/standard.hpp"
namespace rexy{ namespace rexy{
@ -35,7 +33,7 @@ namespace rexy{
{ {
public: public:
using value_type = T; using value_type = T;
using size_type = std::size_t; using size_type = size_t;
using difference_type = ptrdiff_t; using difference_type = ptrdiff_t;
using pointer = T*; using pointer = T*;
using const_pointer = const T*; using const_pointer = const T*;
@ -54,16 +52,17 @@ namespace rexy{
public: public:
constexpr buffer(void); constexpr buffer(void);
REXY_CPP20_CONSTEXPR buffer(const_pointer data, size_type length)noexcept(is_nothrow_allocator_v<Allocator>); REXY_CPP20_CONSTEXPR buffer(const_pointer data, size_type length)noexcept(noexcept(this->allocate(0)));
template<class Iter> template<class Iter>
REXY_CPP20_CONSTEXPR buffer(const Iter& start, const Iter& last); REXY_CPP20_CONSTEXPR buffer(const Iter& start, const Iter& last);
REXY_CPP20_CONSTEXPR buffer(size_type cap)noexcept(is_nothrow_allocator_v<Allocator>); REXY_CPP20_CONSTEXPR buffer(size_type cap)noexcept(noexcept(this->allocate(0)));
REXY_CPP20_CONSTEXPR buffer(const buffer& b)noexcept(is_nothrow_allocator_v<Allocator>); REXY_CPP20_CONSTEXPR buffer(const buffer& b)noexcept(noexcept(this->allocate(0)));
constexpr buffer(buffer&& b)noexcept; constexpr buffer(buffer&& b)noexcept;
REXY_CPP20_CONSTEXPR ~buffer(void)noexcept(is_nothrow_allocator_v<Allocator>); REXY_CPP20_CONSTEXPR ~buffer(void)noexcept(noexcept(this->deallocate(nullptr, 0)));
REXY_CPP20_CONSTEXPR buffer& operator=(const buffer& b) REXY_CPP20_CONSTEXPR buffer& operator=(const buffer& b)
noexcept(is_nothrow_allocator_v<Allocator>); noexcept(noexcept(this->allocate(0)) &&
noexcept(this->deallocate(nullptr, 0)));
constexpr buffer& operator=(buffer&& b)noexcept; constexpr buffer& operator=(buffer&& b)noexcept;
constexpr pointer data(void); constexpr pointer data(void);
@ -94,8 +93,6 @@ namespace rexy{
constexpr const_reverse_iterator crbegin(void)const; constexpr const_reverse_iterator crbegin(void)const;
constexpr const_reverse_iterator crend(void)const; constexpr const_reverse_iterator crend(void)const;
REXY_CPP20_CONSTEXPR void clear(void);
REXY_CPP20_CONSTEXPR void append(const_pointer p, size_type len); REXY_CPP20_CONSTEXPR void append(const_pointer p, size_type len);
}; };

View File

@ -21,52 +21,51 @@
#include <utility> //exchange, swap #include <utility> //exchange, swap
#include <algorithm> //max #include <algorithm> //max
#include <memory> //construct_at
namespace rexy{ namespace rexy{
template<class T, class Allocator> template<class T, class Allocator>
constexpr buffer<T,Allocator>::buffer(void){} constexpr buffer<T,Allocator>::buffer(void){}
template<class T, class Allocator> template<class T, class Allocator>
REXY_CPP20_CONSTEXPR buffer<T,Allocator>::buffer(const_pointer data, size_type length)noexcept(is_nothrow_allocator_v<Allocator>): REXY_CPP20_CONSTEXPR buffer<T,Allocator>::buffer(const_pointer data, size_type length)noexcept(noexcept(this->allocate(0))):
m_data(this->allocate(sizeof(value_type) * length)), m_data(this->allocate(sizeof(value_type) * length)),
m_cap(length), m_cap(length),
m_size(length) m_size(length)
{ {
for(size_type i = 0;i < length;++i){ for(size_type i = 0;i < length;++i){
std::construct_at(m_data + i, data[i]); new (m_data + i) T(data[i]);
} }
} }
template<class T, class Allocator> template<class T, class Allocator>
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 != last;++it){ for(auto it = start;it != end;++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 != last;++it){ for(auto it = start;it != end;++it){
std::construct_at(m_data + count, *it); new (m_data + count) T(*it);
++count; ++count;
} }
m_size = count; m_size = count;
} }
template<class T, class Allocator> template<class T, class Allocator>
REXY_CPP20_CONSTEXPR buffer<T,Allocator>::buffer(size_type cap)noexcept(is_nothrow_allocator_v<Allocator>): REXY_CPP20_CONSTEXPR buffer<T,Allocator>::buffer(size_type cap)noexcept(noexcept(this->allocate(0))):
m_data(this->allocate(sizeof(value_type) * cap)), m_data(this->allocate(sizeof(value_type) * cap)),
m_cap(cap), m_cap(cap),
m_size(0){} m_size(0){}
template<class T, class Allocator> template<class T, class Allocator>
REXY_CPP20_CONSTEXPR buffer<T,Allocator>::buffer(const buffer& b)noexcept(is_nothrow_allocator_v<Allocator>): REXY_CPP20_CONSTEXPR buffer<T,Allocator>::buffer(const buffer& b)noexcept(noexcept(this->allocate(0))):
m_data(this->allocate(sizeof(value_type) * b.m_cap)), m_data(this->allocate(sizeof(value_type) * b.m_cap)),
m_cap(b.m_cap), m_cap(b.m_cap),
m_size(b.m_size) m_size(b.m_size)
{ {
for(size_type i = 0;i < b.m_size;++i){ for(size_type i = 0;i < b.m_size;++i){
std::construct_at(m_data + i, b.m_data[i]); new (m_data + i) T(b.m_data[i]);
} }
} }
template<class T, class Allocator> template<class T, class Allocator>
@ -75,15 +74,16 @@ namespace rexy{
m_cap(b.m_cap), m_cap(b.m_cap),
m_size(b.m_size){} m_size(b.m_size){}
template<class T, class Allocator> template<class T, class Allocator>
REXY_CPP20_CONSTEXPR buffer<T,Allocator>::~buffer(void)noexcept(is_nothrow_allocator_v<Allocator>){ REXY_CPP20_CONSTEXPR buffer<T,Allocator>::~buffer(void)noexcept(noexcept(this->deallocate(nullptr, 0))){
for(size_type i = 0;i < m_size;++i){ for(size_type i = 0;i < m_size;++i){
std::destroy_at(m_data + i); m_data[i].~T();
} }
this->deallocate(m_data, m_cap * sizeof(value_type)); this->deallocate(m_data, m_cap * sizeof(value_type));
} }
template<class T, class Allocator> template<class T, class Allocator>
REXY_CPP20_CONSTEXPR buffer<T,Allocator>& buffer<T,Allocator>::operator=(const buffer& b) REXY_CPP20_CONSTEXPR buffer<T,Allocator>& buffer<T,Allocator>::operator=(const buffer& b)
noexcept(is_nothrow_allocator_v<Allocator>) noexcept(noexcept(this->allocate(0)) &&
noexcept(this->deallocate(nullptr, 0)))
{ {
return (*this = buffer(b)); return (*this = buffer(b));
} }
@ -107,7 +107,7 @@ namespace rexy{
if(new_cap > m_cap){ if(new_cap > m_cap){
buffer tmp(new_cap); buffer tmp(new_cap);
for(size_type i = 0;i < m_size;++i){ for(size_type i = 0;i < m_size;++i){
std::construct_at(tmp.m_data + i, std::move(m_data[i])); new (tmp.m_data + i) T(std::move(m_data[i]));
} }
std::swap(tmp.m_data, m_data); std::swap(tmp.m_data, m_data);
} }
@ -203,16 +203,12 @@ namespace rexy{
template<class T, class Allocator> template<class T, class Allocator>
REXY_CPP20_CONSTEXPR void buffer<T,Allocator>::append(const_pointer p, size_type len){ REXY_CPP20_CONSTEXPR void buffer<T,Allocator>::append(const_pointer p, size_type len){
if(len + m_size > m_cap){ if(len + m_size > m_cap){
buffer b(std::max(m_cap * 2, len + m_size)); resize(std::max(m_cap * 2, len + m_size));
b.append(m_data, m_size);
b.append(p, len);
*this = std::move(b);
}else{
for(size_type i = 0;i < len;++i){
std::construct_at(m_data + (m_size + i), p[i]);
}
m_size += len;
} }
for(size_type i = 0;i < len;++i){
new (m_data + (m_size + i)) T(p[i]);
}
m_size += len;
} }
} }

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) 2022 rexy712 Copyright (C) 2020 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,11 +16,15 @@
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
#define REXY_CPP20_HPP
#ifndef LIBREXY_ENABLE_DEBUG_LEVEL #if __cplusplus > 201703L
#define LIBREXY_ENABLE_DEBUG_LEVEL 0 #define REXY_CPP20_CONSTEXPR constexpr
#endif #define REXY_CPP20_CONSTEVAL consteval
#else
#define REXY_CPP20_CONSTEXPR
#define REXY_CPP20_CONSTEVAL constexpr
#endif
#ifndef LIBREXY_ENABLE_COLOR_DEBUG
#define LIBREXY_ENABLE_COLOR_DEBUG 1
#endif #endif

View File

@ -1,100 +0,0 @@
/**
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_DEFAULT_ALLOCATOR_HPP
#define REXY_COMPAT_CPP17_DEFAULT_ALLOCATOR_HPP
#include <cstddef> //ptrdiff_t, size_t
#include <type_traits> //true_type, false_type
#include <new>
#include <limits> //numeric_limits
#include "../../rexy.hpp"
#define REXY_ALLOCATOR_CONCEPT class
namespace rexy{
template<class T>
struct allocator
{
using pointer = T*;
using const_pointer = const T*;
using void_pointer = void*;
using const_void_pointer = const void*;
using value_type = T;
using size_type = std::size_t;
using difference_type = ptrdiff_t;
using is_always_equal = std::true_type;
using propagate_on_container_copy_assignment = std::false_type;
using propagate_on_container_move_assignment = std::false_type;
using propagate_on_container_swap = std::false_type;
template<class U>
struct rebind{
using other = allocator<U>;
};
private:
constexpr bool has_overflow(size_type n)const{
return n > max_size();
}
public:
allocator(void) = default;
allocator(const allocator&) = default;
allocator(allocator&&) = default;
template<class U>
constexpr allocator(const allocator<U>&)noexcept{}
~allocator(void) = default;
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));
}else{
return reinterpret_cast<pointer>(::operator new(bytes, static_cast<std::align_val_t>(alignof(T))));
}
}
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
}
constexpr size_type max_size(void)const{
return std::numeric_limits<size_type>::max()/sizeof(T);
}
};
}
#endif

View File

@ -1,44 +0,0 @@
/**
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_CX_STRING_HPP
#define REXY_COMPAT_CPP17_CX_STRING_HPP
#include "../../../string_base.hpp" //string_cat_expr
#include "../../../string_view.hpp" //string_view
#include <utility> //forward
#include <type_traits> //enable_if
namespace rexy::cx{
template<class Str1, class Str2, std::enable_if_t<is_cx_string<Str1,Str2>::value,int> = 0>
constexpr auto operator+(Str1&& l, Str2&& r)noexcept{
return string_cat_expr(std::forward<Str1>(l), std::forward<Str2>(r));
}
template<class Str1, std::enable_if_t<is_cx_string<Str1>::value,int> = 0>
constexpr auto operator+(Str1&& l, const char* r)noexcept{
return string_cat_expr(std::forward<Str1>(l), rexy::basic_string_view<char>(r));
}
template<class Str1, std::enable_if_t<is_cx_string<Str1>::value,int> = 0>
constexpr auto operator+(const char* l, Str1&& r)noexcept{
return string_cat_expr(rexy::basic_string_view<char>(l), std::forward<Str1>(r));
}
}
#endif

View File

@ -22,7 +22,7 @@
#include <cstdint> //uint_least32_t #include <cstdint> //uint_least32_t
//clang bug: https://bugs.llvm.org/show_bug.cgi?id=48886 //clang bug: https://bugs.llvm.org/show_bug.cgi?id=48886
#if defined(__cpp_consteval) && !defined(__clang__) #if __cplusplus >= 202002L && !defined(__clang__)
#define CONSTEVAL consteval #define CONSTEVAL consteval
#else #else
#define CONSTEVAL constexpr #define CONSTEVAL constexpr

View File

@ -1,336 +0,0 @@
/**
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)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<class T, class Char, std::enable_if_t<is_string<T>::value, int> = 0>
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<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)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<class T, class Char, std::enable_if_t<is_string<T>::value, int> = 0>
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

View File

@ -1,73 +0,0 @@
/**
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_TRAITS_HPP
#define REXY_COMPAT_CPP17_TRAITS_HPP
namespace rexy{
template<class T>
struct remove_volatile{
using type = T;
};
template<class T>
struct remove_volatile<volatile T>{
using type = T;
};
template<class T>
using remove_volatile_t = typename remove_volatile<T>::type;
template<class T>
struct remove_const{
using type = T;
};
template<class T>
struct remove_const<const T>{
using type = T;
};
template<class T>
using remove_const_t = typename remove_const<T>::type;
template<class T>
struct remove_cv{
using type = remove_volatile_t<remove_const_t<T>>;
};
template<class T>
using remove_cv_t = typename remove_cv<T>::type;
template<class T>
struct remove_reference{
using type = T;
};
template<class T>
struct remove_reference<T&>{
using type = T;
};
template<class T>
struct remove_reference<T&&>{
using type = T;
};
template<class T>
using remove_reference_t = typename remove_reference<T>::type;
template<class T>
struct remove_cvref{
using type = remove_cv_t<remove_reference_t<T>>;
};
template<class T>
using remove_cvref_t = typename remove_cvref<T>::type;
}
#endif

View File

@ -1,139 +0,0 @@
/**
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_DEFAULT_ALLOCATOR_HPP
#define REXY_COMPAT_CPP20_DEFAULT_ALLOCATOR_HPP
#include <cstddef> //ptrdiff_t, size_t
#include <type_traits> //true_type, false_type
#include <new>
#include <limits> //numeric_limits
#include <memory> //allocator
#ifdef __cpp_concepts
#include <concepts> //convertible_to
template<class T>
concept Allocator = requires(T a, typename T::size_type sz, typename T::pointer pa){
typename T::value_type;
typename T::size_type;
typename T::pointer;
typename T::const_pointer;
{a.allocate(sz)} -> std::convertible_to<typename T::pointer>;
{a.deallocate(pa, sz)};
};
#define REXY_ALLOCATOR_CONCEPT Allocator
#else //__cpp_concepts
#define REXY_ALLOCATOR_CONCEPT class
#endif // __cpp_concepts
#include "../../rexy.hpp"
#include "../if_consteval.hpp"
namespace rexy{
template<class T>
struct allocator
{
using pointer = T*;
using const_pointer = const T*;
using void_pointer = void*;
using const_void_pointer = const void*;
using value_type = T;
using size_type = std::size_t;
using difference_type = std::ptrdiff_t;
using is_always_equal = std::true_type;
using propagate_on_container_copy_assignment = std::false_type;
using propagate_on_container_move_assignment = std::false_type;
using propagate_on_container_swap = std::false_type;
template<class U>
struct rebind{
using other = allocator<U>;
};
private:
constexpr bool has_overflow(size_type n)const{
return n > max_size();
}
public:
constexpr allocator(void) = default;
constexpr allocator(const allocator&) = default;
constexpr allocator(allocator&&) = default;
template<class U>
constexpr allocator(const allocator<U>&)noexcept{}
constexpr ~allocator(void) = default;
//'::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.
constexpr pointer allocate(size_type n){
size_type bytes = has_overflow(n) ? std::numeric_limits<size_type>::max() : n*sizeof(T);
REXY_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 reinterpret_cast<pointer>(::operator new(bytes));
}else{
return reinterpret_cast<pointer>(::operator new(bytes, static_cast<std::align_val_t>(alignof(T))));
}
} //if consteval
}
constexpr void deallocate(pointer p, size_type n){
REXY_if_consteval{
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
}
constexpr size_type max_size(void)const{
return std::numeric_limits<size_type>::max()/sizeof(T);
}
};
}
#endif

View File

@ -1,46 +0,0 @@
/**
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_CX_STRING_HPP
#define REXY_COMPAT_CPP20_CX_STRING_HPP
#include "../../../string_base.hpp" //string_cat_expr
#include "../../../string_view.hpp" //string_view
#include <utility> //forward
namespace rexy{
template<class T>
concept CxString = cx::is_cx_string<T>::value;
}
namespace rexy::cx{
template<CxString Str1, CxString Str2>
constexpr auto operator+(Str1&& l, Str2&& r)noexcept{
return string_cat_expr(std::forward<Str1>(l), std::forward<Str2>(r));
}
template<CxString Str1>
constexpr auto operator+(Str1&& l, const char* r)noexcept{
return string_cat_expr(std::forward<Str1>(l), rexy::basic_string_view<char>(r));
}
template<CxString Str1>
constexpr auto operator+(const char* l, Str1&& r)noexcept{
return string_cat_expr(rexy::basic_string_view<char>(l), std::forward<Str1>(r));
}
}
#endif

View File

@ -1,257 +0,0 @@
/**
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

View File

@ -1,30 +0,0 @@
/**
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_if_consteval
#if defined(__cpp_if_consteval)
#define REXY_if_consteval if consteval
#define REXY_if_not_consteval if not consteval
#elif defined(__cpp_lib_is_constant_evaluated)
#include <type_traits> //is_constant_evaluated
#define REXY_if_consteval if(std::is_constant_evaluated())
#define REXY_if_not_consteval if(!std::is_constant_evaluated())
#endif //__cpp_if_consteval
#endif

View File

@ -1,45 +0,0 @@
/**
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_HPP
#define REXY_COMPAT_CPP20_HPP
#define REXY_REQUIRES_CPP20 static_assert(__cplusplus >= 202002L, "C++20 is required to use this file.")
#if __cplusplus >= 202002L
#define REXY_CPP20_CONSTEXPR constexpr
#define REXY_CPP20_CONSTEVAL consteval
#else //__cpp_consteval
#define REXY_CPP20_CONSTEXPR
#define REXY_CPP20_CONSTEVAL constexpr
#endif //__cpp_consteval
#if __cplusplus >= 202300L
#define REXY_STANDARD_CPP23
#endif
#if __cplusplus >= 202002L
#define REXY_STANDARD_CPP20
#endif
#if __cplusplus >= 201703L
#define REXY_STANDARD_CPP17
#endif
#if __cplusplus < 201703L
#error "Requires minimum C++17 standard"
#endif //__cplusplus
#endif

View File

@ -27,12 +27,12 @@
namespace rexy::cx{ namespace rexy::cx{
template<class T, std::size_t N> template<class T, size_t N>
class array class array
{ {
public: public:
using value_type = T; using value_type = T;
using size_type = std::size_t; using size_type = size_t;
using difference_type = ptrdiff_t; using difference_type = ptrdiff_t;
using reference = T&; using reference = T&;
using const_reference = const T&; using const_reference = const T&;
@ -125,7 +125,7 @@ namespace rexy::cx{
} }
}; };
template<std::size_t N> template<size_t N>
class array<bool,N> : public detail::bool_specialize_base class array<bool,N> : public detail::bool_specialize_base
{ {
public: public:

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) 2022 rexy712 Copyright (C) 2020 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,8 +16,17 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#if __cplusplus >= 202002L #ifndef REXY_CX_CX_STRING_HASH_HPP
#include "cpp20/allocator.hpp" #define REXY_CX_CX_STRING_HASH_HPP
#else
#include "cpp17/allocator.hpp" #include "../string_hash.hpp"
#include "string.hpp"
namespace rexy::cx{
template<size_t N>
struct hash<rexy::cx::string<N>> : public string_hash<rexy::cx::string<N>>{};
}
#endif #endif

View File

@ -23,13 +23,13 @@
#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
{ {
protected: protected:
using size_type = std::size_t; using size_type = size_t;
using difference_type = ptrdiff_t; using difference_type = ptrdiff_t;
static constexpr size_type bits_per_byte = CHAR_BIT; static constexpr size_type bits_per_byte = CHAR_BIT;
@ -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;
REXY_CPP20_CONSTEXPR ~boolean(void)noexcept = default; ~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,15 +86,15 @@ 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;
REXY_CPP20_CONSTEXPR ~booleans(void)noexcept = default; ~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;
constexpr boolean operator[](std::size_t i){ constexpr boolean operator[](size_t i){
return boolean{m_value, i}; return boolean{m_value, i};
} }
constexpr boolean operator[](std::size_t i)const{ constexpr boolean operator[](size_t i)const{
return boolean{const_cast<uchar&>(m_value), i}; return boolean{const_cast<uchar&>(m_value), i};
} }
constexpr uchar& data(void){ constexpr uchar& data(void){
@ -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;
REXY_CPP20_CONSTEXPR ~bool_iter(void)noexcept = default; ~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;
REXY_CPP20_CONSTEXPR ~const_bool_iter(void)noexcept = default; ~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

@ -25,7 +25,7 @@
#include "../hash.hpp" #include "../hash.hpp"
#include <climits> //CHAR_BIT #include <climits> //CHAR_BIT
#include <cstddef> //std::size_t, ptrdiff_t #include <cstddef> //size_t, ptrdiff_t
#include <type_traits> //decay #include <type_traits> //decay
#include <initializer_list> #include <initializer_list>
@ -42,14 +42,14 @@ namespace rexy::cx{
template<class Key, class Value> template<class Key, class Value>
element(Key,Value) -> element<Key,Value>; element(Key,Value) -> element<Key,Value>;
template<class Key, class Value, std::size_t N, class Hash = hash<Key>> template<class Key, class Value, size_t N, class Hash = hash<Key>>
class hashmap class hashmap
{ {
public: public:
using key_type = Key; using key_type = Key;
using mapped_type = Value; using mapped_type = Value;
using value_type = element<Key,Value>; using value_type = element<Key,Value>;
using size_type = std::size_t; using size_type = size_t;
using difference_type = ptrdiff_t; using difference_type = ptrdiff_t;
using hasher = Hash; using hasher = Hash;
using reference = mapped_type&; using reference = mapped_type&;
@ -72,7 +72,7 @@ namespace rexy::cx{
noexcept(std::is_nothrow_default_constructible<value_type>::value && noexcept(std::is_nothrow_default_constructible<value_type>::value &&
std::is_nothrow_copy_constructible<value_type>::value && std::is_nothrow_copy_constructible<value_type>::value &&
std::is_nothrow_move_assignable<mapped_type>::value && std::is_nothrow_move_assignable<mapped_type>::value &&
std::is_nothrow_invocable<Hash,Key,std::size_t>::value); std::is_nothrow_invocable<Hash,Key,size_t>::value);
//no key checks. give a correct key or get a random answer :) //no key checks. give a correct key or get a random answer :)
template<class U, class UHash = hash<std::decay_t<U>>> template<class U, class UHash = hash<std::decay_t<U>>>
@ -89,4 +89,8 @@ namespace rexy::cx{
#include "hashmap.tpp" #include "hashmap.tpp"
#ifdef REXY_STRING_BASE_HPP
#include "../string_hash.hpp"
#endif
#endif #endif

View File

@ -23,12 +23,12 @@
namespace rexy::cx{ namespace rexy::cx{
template<class Key, class Value, std::size_t N, class Hash> template<class Key, class Value, size_t N, class Hash>
constexpr hashmap<Key,Value,N,Hash>::hashmap(const value_type(&elements)[N]) constexpr hashmap<Key,Value,N,Hash>::hashmap(const value_type(&elements)[N])
noexcept(std::is_nothrow_default_constructible<value_type>::value && noexcept(std::is_nothrow_default_constructible<value_type>::value &&
std::is_nothrow_copy_constructible<value_type>::value && std::is_nothrow_copy_constructible<value_type>::value &&
std::is_nothrow_move_assignable<mapped_type>::value && std::is_nothrow_move_assignable<mapped_type>::value &&
std::is_nothrow_invocable<Hash,Key,std::size_t>::value) std::is_nothrow_invocable<Hash,Key,size_t>::value)
{ {
array<vector<value_type,N>,N> buckets; array<vector<value_type,N>,N> buckets;
array<size_type,N> key_hashes; //full hash values for keys to verify good index values array<size_type,N> key_hashes; //full hash values for keys to verify good index values
@ -101,7 +101,7 @@ namespace rexy::cx{
} }
//no key checks. give a correct key or get a random answer :) //no key checks. give a correct key or get a random answer :)
template<class Key, class Value, std::size_t N, class Hash> template<class Key, class Value, size_t N, class Hash>
template<class U, class UHash> template<class U, class UHash>
constexpr auto hashmap<Key,Value,N,Hash>::operator[](U&& key)noexcept -> reference{ constexpr auto hashmap<Key,Value,N,Hash>::operator[](U&& key)noexcept -> reference{
auto d = m_g[UHash{}(std::forward<U>(key), 0) % max_size]; auto d = m_g[UHash{}(std::forward<U>(key), 0) % max_size];
@ -109,7 +109,7 @@ namespace rexy::cx{
return m_elements[d & ~single_bucket_bit].value; return m_elements[d & ~single_bucket_bit].value;
return m_elements[UHash{}(std::forward<U>(key), d) % max_size].value; return m_elements[UHash{}(std::forward<U>(key), d) % max_size].value;
} }
template<class Key, class Value, std::size_t N, class Hash> template<class Key, class Value, size_t N, class Hash>
template<class U, class UHash> template<class U, class UHash>
constexpr auto hashmap<Key,Value,N,Hash>::operator[](U&& key)const noexcept -> const_reference{ constexpr auto hashmap<Key,Value,N,Hash>::operator[](U&& key)const noexcept -> const_reference{
auto d = m_g[UHash{}(std::forward<U>(key), 0) % max_size]; auto d = m_g[UHash{}(std::forward<U>(key), 0) % max_size];
@ -118,7 +118,7 @@ namespace rexy::cx{
return m_elements[UHash{}(std::forward<U>(key), d) % max_size].value; return m_elements[UHash{}(std::forward<U>(key), d) % max_size].value;
} }
template<class Key, class Value, std::size_t N, class Hash> template<class Key, class Value, size_t N, class Hash>
template<class U, class UHash> template<class U, class UHash>
constexpr bool hashmap<Key,Value,N,Hash>::contains(U&& key)const noexcept{ constexpr bool hashmap<Key,Value,N,Hash>::contains(U&& key)const noexcept{
const auto hashval = UHash{}(std::forward<U>(key), 0); const auto hashval = UHash{}(std::forward<U>(key), 0);
@ -129,7 +129,7 @@ namespace rexy::cx{
return m_elements[UHash{}(std::forward<U>(key), d) % max_size].key == std::forward<U>(key); return m_elements[UHash{}(std::forward<U>(key), d) % max_size].key == std::forward<U>(key);
} }
template<class Key, class Value, std::size_t N, class Hash = hash<Key>> template<class Key, class Value, size_t N, class Hash = hash<Key>>
constexpr auto make_hashmap(const typename hashmap<Key,Value,N,Hash>::value_type(&list)[N]){ constexpr auto make_hashmap(const typename hashmap<Key,Value,N,Hash>::value_type(&list)[N]){
return hashmap<Key,Value,N,Hash>(list); return hashmap<Key,Value,N,Hash>(list);
} }

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-2022 rexy712 Copyright (C) 2020 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,69 +19,57 @@
#ifndef REXY_CX_STRING_HPP #ifndef REXY_CX_STRING_HPP
#define REXY_CX_STRING_HPP #define REXY_CX_STRING_HPP
#include <cstddef> //ptrdiff_t, size_t
namespace rexy::cx{ namespace rexy::cx{
template<std::size_t N, class Char> template<size_t N, class Char>
class string; class string;
} }
#include "../string_base.hpp" //string_cat_expr #include "../string_base.hpp"
#include "../string_view.hpp" #include "../utility.hpp"
#include "../utility.hpp" //strlen, strcmp #include <type_traits>
#include "../detail/string_appender.hpp" #include <cstddef> //ptrdiff_t, size_t
#include "../traits.hpp" //remove_cvref
#include <type_traits> //nothrow_invocable, integral_constant, declval
#include <iterator> //reverse_iterator
#include "../compat/standard.hpp" #include "../compat/constexpr.hpp"
//This is different from rexy::string_view in that this doesn't hold a pointer to a constant string array. //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. string_view is //This holds a mutable array of data which can be modified during compile time. static_string 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{
template<std::size_t N, class Char = char> template<size_t N, class Char = char>
class string class string
{ {
public: public:
using value_type = Char; using value_type = Char;
using size_type = std::size_t; using size_type = size_t;
using difference_type = std::ptrdiff_t; using difference_type = ptrdiff_t;
using pointer = value_type*; using pointer = value_type*;
using const_pointer = const value_type*; using const_pointer = const value_type*;
using reference = value_type&; using reference = value_type&;
using const_reference = const value_type&; using const_reference = const value_type&;
using iterator = pointer; using iterator = pointer;
using const_iterator = const_pointer; using const_iterator = const_pointer;
using reverse_iterator = std::reverse_iterator<iterator>;
using const_reverse_iterator = std::reverse_iterator<const_iterator>;
public:
static constexpr size_type max_size = N;
static constexpr size_type npos = size_type(-1);
public: public:
static constexpr size_t max_size = N;
private:
value_type m_data[N] = {}; value_type m_data[N] = {};
size_type m_length = 0; size_type m_length = 0;
public: public:
constexpr string(void) = default; constexpr string(void) = default;
template<size_type M> template<size_type M>
constexpr string(const value_type(&data)[M])noexcept: constexpr string(const char(&data)[M])noexcept:
m_length(M) m_length(M)
{ {
static_assert(M <= N); static_assert(M <= N);
for(size_type i = 0;i < M;++i){ for(size_type i = 0;i < M;++i){
m_data[i] = data[i]; m_data[i] = data[i];
} }
if(m_data[m_length - 1] == 0){
--m_length; //exclude null terminator in length
}
} }
constexpr string(const_pointer data)noexcept: constexpr string(const_pointer data)noexcept:
m_length(rexy::strlen(data)) m_length(strlen(data))
{ {
for(size_type i = 0;i < m_length;++i){ for(size_type i = 0;i < m_length;++i){
m_data[i] = data[i]; m_data[i] = data[i];
@ -104,7 +92,7 @@ namespace rexy::cx{
} }
template<class Left, class Right> template<class Left, class Right>
constexpr string(const rexy::string_cat_expr<Left,Right>& expr) constexpr string(const rexy::string_cat_expr<Left,Right>& expr)
noexcept(std::is_nothrow_invocable<rexy::detail::string_appender<cx::string<N,Char>>, decltype(expr)>::value) noexcept(std::is_nothrow_invocable<rexy::detail::string_appender<cx::string<N>>, decltype(expr)>::value)
{ {
rexy::detail::string_appender<cx::string<N,value_type>> append(*this); rexy::detail::string_appender<cx::string<N,value_type>> append(*this);
append(expr); append(expr);
@ -115,7 +103,7 @@ namespace rexy::cx{
REXY_CPP20_CONSTEXPR ~string(void)noexcept = default; REXY_CPP20_CONSTEXPR ~string(void)noexcept = default;
constexpr string& operator=(const_pointer c)noexcept{ constexpr string& operator=(const_pointer c)noexcept{
m_length = rexy::strlen(c); m_length = strlen(c);
for(size_type i = 0;i < m_length;++i){ for(size_type i = 0;i < m_length;++i){
m_data[i] = c[i]; m_data[i] = c[i];
} }
@ -124,64 +112,45 @@ namespace rexy::cx{
constexpr string& operator=(const string&)noexcept = default; constexpr string& operator=(const string&)noexcept = default;
constexpr string& operator=(string&&)noexcept = default; constexpr string& operator=(string&&)noexcept = default;
constexpr bool operator==(const string& s)noexcept{return !rexy::strcmp(m_data, s.m_data);}
constexpr bool operator!=(const string& s)noexcept{return rexy::strcmp(m_data, s.m_data);}
constexpr size_type length(void)const noexcept{return m_length;} constexpr size_type length(void)const noexcept{
constexpr size_type size(void)const noexcept{return m_length;} return m_length;
constexpr size_type capacity(void)const noexcept{return max_size;}
constexpr bool empty(void)const noexcept{return m_length == 0;}
constexpr pointer c_str(void)noexcept{return m_data;}
constexpr const_pointer c_str(void)const noexcept{return m_data;}
constexpr pointer get(void)noexcept{return m_data;}
constexpr const_pointer get(void)const noexcept{return m_data;}
constexpr operator pointer(void)noexcept{return m_data;}
constexpr operator const_pointer(void)const noexcept{return m_data;}
constexpr reference operator[](size_type i)noexcept{return m_data[i];}
constexpr const_reference operator[](size_type i)const noexcept{return m_data[i];}
constexpr reference at(size_type i)noexcept{return m_data[i];}
constexpr const_reference at(size_type i)const noexcept{return m_data[i];}
constexpr const_reference front(size_type i)const noexcept{return m_data[0];}
constexpr const_reference back(size_type i)const noexcept{return m_data[m_length-1];}
constexpr const_iterator search(const basic_string_view<value_type>& s)const{
return two_way_search(cbegin(), cend(), s.cbegin(), s.cend());
} }
constexpr const_iterator search(const_pointer c)const{ constexpr size_type capacity(void)const noexcept{
return search(basic_string_view<value_type>{c}); return max_size;
} }
template<class Searcher> constexpr pointer c_str(void)noexcept{
constexpr const_iterator search(const basic_string_view<value_type>& s, Searcher&& searcher)const{ return m_data;
return searcher(cbegin(), cend(), s.cbegin(), s.cend());
} }
template<class Searcher> constexpr const_pointer c_str(void)const noexcept{
constexpr const_iterator search(const_pointer c, Searcher&& searcher)const{ return m_data;
return search(basic_string_view<value_type>{c}, searcher); }
constexpr pointer get(void)noexcept{
return m_data;
}
constexpr const_pointer get(void)const noexcept{
return m_data;
}
constexpr operator pointer(void)noexcept{
return m_data;
}
constexpr operator const_pointer(void)const noexcept{
return m_data;
} }
constexpr iterator begin(void)noexcept{return m_data;} constexpr bool valid(void)const noexcept{
constexpr const_iterator begin(void)const noexcept{return m_data;} return m_length > 0;
constexpr const_iterator cbegin(void)const noexcept{return m_data;} }
constexpr iterator end(void)noexcept{return m_data+m_length;} constexpr reference operator[](size_type i)noexcept{
constexpr const_iterator end(void)const noexcept{return m_data+m_length;} return m_data[i];
constexpr const_iterator cend(void)const noexcept{return m_data+m_length;} }
constexpr const_reference operator[](size_type i)const noexcept{
constexpr const_reverse_iterator rbegin(void)const{return const_reverse_iterator(m_data+m_length);} return m_data[i];
constexpr const_reverse_iterator rend(void)const{return const_reverse_iterator(m_data-1);} }
constexpr const_reverse_iterator crbegin(void)const{return rbegin();}
constexpr const_reverse_iterator crend(void)const{return rend();}
constexpr bool valid(void)const noexcept{return m_length > 0;}
constexpr bool compare(const string& s)const{return *this == s;}
constexpr bool compare(const_pointer c)const{return *this == c;}
constexpr bool resize(size_type i)noexcept{ constexpr bool resize(size_type i)noexcept{
if(i >= capacity()){ if(i >= capacity())
return false; return false;
}
m_length = i; m_length = i;
m_data[m_length] = 0; m_data[m_length] = 0;
return true; return true;
@ -192,48 +161,48 @@ namespace rexy::cx{
m_data[m_length++] = data[i]; m_data[m_length++] = data[i];
} }
} }
constexpr void append(const_pointer data)noexcept{append(data, rexy::strlen(data));} constexpr void append(const_pointer data)noexcept{
constexpr void append(const string& s)noexcept{append(s.get(), s.length());} append(data, strlen(data));
constexpr void append(const rexy::basic_string_view<value_type>& s)noexcept{append(s.get(), s.length());}
constexpr size_type find_first_of(value_type v, size_type start = 0)const{
return rexy::find_first_of(*this, &v, start, 1);
} }
constexpr size_type find_first_of(const_pointer c, size_type pos = 0)const{ constexpr void append(const string& s)noexcept{
return rexy::find_first_of(*this, c, pos, rexy::strlen(c)); append(s.get(), s.length());
} }
constexpr size_type find_first_of(const_pointer c, size_type start, size_type size)const{ constexpr void append(const rexy::basic_string_view<value_type>& s)noexcept{
return rexy::find_first_of(*this, c, start, size); append(s.get(), s.length());
} }
constexpr size_type find_last_of(value_type v, size_type start = 0)const{
return rexy::find_last_of(*this, &v, start, 1);
}
constexpr size_type find_last_of(const_pointer c, size_type start = 0)const{
return rexy::find_last_of(*this, c, start, rexy::strlen(c));
}
constexpr size_type find_last_of(const_pointer c, size_type start, size_type size)const{
return rexy::find_last_of(*this, c, start, size);
}
};
template<class Char, std::size_t N>
string(const Char(&data)[N]) -> string<N, Char>;
template<class... Ts>
struct is_cx_string{
template<class Char, std::size_t N>
static std::true_type check(cx::string<N,Char>);
static std::false_type check(...);
static constexpr bool value = (decltype(check(std::declval<rexy::remove_cvref_t<Ts>>()))::value && ...);
}; };
namespace detail{
template<class T>
struct is_cx_string_helper{
static constexpr bool value = false;
};
template<size_t N, class Char>
struct is_cx_string_helper<cx::string<N,Char>>{
static constexpr bool value = true;
};
template<class... Ts>
struct is_cx_string{
static constexpr bool value = (is_cx_string_helper<std::decay_t<Ts>>::value && ...);
};
}
template<class Str1, class Str2, std::enable_if_t<detail::is_cx_string<Str1,Str2>::value,int> = 0>
constexpr auto operator+(Str1&& l, Str2&& r)noexcept{
return string_cat_expr(std::forward<Str1>(l), std::forward<Str2>(r));
}
template<class Str1, std::enable_if_t<detail::is_cx_string<Str1>::value,int> = 0>
constexpr auto operator+(Str1&& l, const char* r)noexcept{
return string_cat_expr(std::forward<Str1>(l), rexy::basic_string_view<char>(r));
}
template<class Str1, std::enable_if_t<detail::is_cx_string<Str1>::value,int> = 0>
constexpr auto operator+(const char* l, Str1&& r)noexcept{
return string_cat_expr(rexy::basic_string_view<char>(l), std::forward<Str1>(r));
}
} }
#ifdef __cpp_concepts #ifdef REXY_CX_HASH_HPP
#include "../compat/cpp20/cx/string.hpp" #include "cx_string_hash.hpp"
#else //__cpp_concepts #endif
#include "../compat/cpp17/cx/string.hpp"
#endif //__cpp_concepts
#endif #endif

View File

@ -24,18 +24,17 @@
#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>
namespace rexy::cx{ namespace rexy::cx{
template<class T, std::size_t N> template<class T, size_t N>
class vector class vector
{ {
public: public:
using value_type = T; using value_type = T;
using size_type = std::size_t; using size_type = size_t;
using difference_type = ptrdiff_t; using difference_type = ptrdiff_t;
using iterator = T*; using iterator = T*;
using const_iterator = const T*; using const_iterator = const T*;
@ -64,7 +63,7 @@ namespace rexy::cx{
m_size = min(count, max_elements); m_size = min(count, max_elements);
} }
REXY_CPP20_CONSTEXPR ~vector(void)noexcept = default; ~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;
@ -244,7 +243,7 @@ namespace rexy::cx{
} }
}; };
template<std::size_t N> template<size_t N>
class vector<bool,N> : public detail::bool_specialize_base class vector<bool,N> : public detail::bool_specialize_base
{ {
public: public:
@ -284,7 +283,7 @@ namespace rexy::cx{
m_size = min(count, max_elements); m_size = min(count, max_elements);
} }
REXY_CPP20_CONSTEXPR ~vector(void)noexcept = default; ~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

@ -19,18 +19,24 @@
#ifndef REXY_DEBUG_PRINT_HPP #ifndef REXY_DEBUG_PRINT_HPP
#define REXY_DEBUG_PRINT_HPP #define REXY_DEBUG_PRINT_HPP
#include "detail/debug_config.hpp" #ifndef REXY_ENABLE_DEBUG_LEVEL
#define REXY_ENABLE_DEBUG_LEVEL 0
#endif
#ifndef REXY_ENABLE_COLOR_DEBUG
#define REXY_ENABLE_COLOR_DEBUG 1
#endif
//Debug output section //Debug output section
#if LIBREXY_ENABLE_DEBUG_LEVEL > 0 #if REXY_ENABLE_DEBUG_LEVEL > 0
#define LIBREXY_ENABLE_DEBUG_OUTPUT #define REXY_ENABLE_DEBUG_OUTPUT
#endif #endif
#if LIBREXY_ENABLE_DEBUG_LEVEL > 2 #if REXY_ENABLE_DEBUG_LEVEL > 2
#define LIBREXY_ENABLE_DEBUG_VERBOSE_OUTPUT #define REXY_ENABLE_DEBUG_VERBOSE_OUTPUT
#endif #endif
#ifdef LIBREXY_ENABLE_DEBUG_OUTPUT #ifdef REXY_ENABLE_DEBUG_OUTPUT
#include <cstdio> //fprintf, vfprintf #include <cstdio> //fprintf, vfprintf
#include <utility> //forward #include <utility> //forward
@ -58,7 +64,7 @@
}; };
template<class... Args> template<class... Args>
print(Args&&...) -> print<Args&&...>; print(Args&&...) -> print<Args&&...>;
#ifdef LIBREXY_ENABLE_COLOR_DEBUG #ifdef REXY_ENABLE_COLOR_DEBUG
template<class... Args> template<class... Args>
struct print_succ{ struct print_succ{
explicit print_succ(Args&&... args, const rexy::compat::source_location& loc = rexy::compat::source_location::current()){ explicit print_succ(Args&&... args, const rexy::compat::source_location& loc = rexy::compat::source_location::current()){
@ -132,7 +138,7 @@
public: public:
using print<Args...>::print; using print<Args...>::print;
}; };
#endif //LIBREXY_ENABLE_COLOR_DEBUG #endif //REXY_ENABLE_COLOR_DEBUG
template<class... Args> template<class... Args>
print_warn(Args&&...) -> print_warn<Args&&...>; print_warn(Args&&...) -> print_warn<Args&&...>;
template<class... Args> template<class... Args>
@ -142,7 +148,7 @@
template<class... Args> template<class... Args>
print_succ(Args&&...) -> print_succ<Args&&...>; print_succ(Args&&...) -> print_succ<Args&&...>;
#ifdef LIBREXY_ENABLE_DEBUG_VERBOSE_OUTPUT #ifdef REXY_ENABLE_DEBUG_VERBOSE_OUTPUT
namespace verbose{ namespace verbose{
template<class... Args> template<class... Args>
class print : public ::rexy::debug::print<Args...>{ class print : public ::rexy::debug::print<Args...>{
@ -190,7 +196,7 @@
static constexpr inline void print_warn(...){} static constexpr inline void print_warn(...){}
static constexpr inline void print_error(...){} static constexpr inline void print_error(...){}
} }
#endif //LIBREXY_ENABLE_DEBUG_VERBOSE_OUTPUT #endif //REXY_ENABLE_DEBUG_VERBOSE_OUTPUT
} }
#else #else
namespace rexy::debug{ namespace rexy::debug{
@ -207,6 +213,6 @@
static constexpr inline void print_error(...){} static constexpr inline void print_error(...){}
} }
} }
#endif //LIBREXY_ENABLE_DEBUG_OUTPUT #endif //REXY_ENABLE_DEBUG_OUTPUT
#endif #endif

View File

@ -21,9 +21,6 @@
#include <tuple> #include <tuple>
#include <utility> //forward, index_sequence #include <utility> //forward, index_sequence
#include <cstddef> //size_t
#include "compat/standard.hpp"
namespace rexy{ namespace rexy{
template<class T, class... Args> template<class T, class... Args>
@ -39,17 +36,17 @@ namespace rexy{
template<class... FArgs> template<class... FArgs>
constexpr deferred(FArgs&&... args); constexpr deferred(FArgs&&... args);
REXY_CPP20_CONSTEXPR deferred(const deferred&) = default; deferred(const deferred&) = default;
REXY_CPP20_CONSTEXPR deferred(deferred&&) = default; deferred(deferred&&) = default;
REXY_CPP20_CONSTEXPR ~deferred(void) = default; ~deferred(void) = default;
REXY_CPP20_CONSTEXPR deferred& operator=(const deferred&) = default; deferred& operator=(const deferred&) = default;
REXY_CPP20_CONSTEXPR deferred& operator=(deferred&&) = default; deferred& operator=(deferred&&) = default;
constexpr value_type value(void); constexpr value_type value(void);
constexpr operator value_type(void); constexpr operator value_type(void);
private: private:
template<std::size_t... Is> template<size_t... Is>
constexpr value_type get_value_(std::index_sequence<Is...>); constexpr value_type get_value_(std::index_sequence<Is...>);
}; };
@ -73,7 +70,7 @@ namespace rexy{
constexpr operator ret_t(void); constexpr operator ret_t(void);
private: private:
template<std::size_t... Is> template<size_t... Is>
constexpr decltype(auto) get_value_(std::index_sequence<Is...>); constexpr decltype(auto) get_value_(std::index_sequence<Is...>);
}; };
@ -90,7 +87,7 @@ namespace rexy{
return get_value_(std::make_index_sequence<sizeof...(Args)>()); return get_value_(std::make_index_sequence<sizeof...(Args)>());
} }
template<class T, class... Args> template<class T, class... Args>
template<std::size_t... Is> template<size_t... Is>
constexpr auto deferred<T,Args...>::get_value_(std::index_sequence<Is...>) -> value_type{ constexpr auto deferred<T,Args...>::get_value_(std::index_sequence<Is...>) -> value_type{
return T{std::forward<std::tuple_element_t<Is,decltype(m_args)>>(std::get<Is>(m_args))...}; return T{std::forward<std::tuple_element_t<Is,decltype(m_args)>>(std::get<Is>(m_args))...};
} }
@ -115,7 +112,7 @@ namespace rexy{
return get_value_(std::make_index_sequence<sizeof...(Args)>()); return get_value_(std::make_index_sequence<sizeof...(Args)>());
} }
template<class Fn, class... Args> template<class Fn, class... Args>
template<std::size_t... Is> template<size_t... Is>
constexpr decltype(auto) deferred_function<Fn,Args...>::get_value_(std::index_sequence<Is...>){ constexpr decltype(auto) deferred_function<Fn,Args...>::get_value_(std::index_sequence<Is...>){
return std::forward<Fn>(m_fn)(std::forward<std::tuple_element_t<Is,decltype(m_args)>>(std::get<Is>(m_args))...); return std::forward<Fn>(m_fn)(std::forward<std::tuple_element_t<Is,decltype(m_args)>>(std::get<Is>(m_args))...);
} }

View File

@ -22,9 +22,10 @@
#include "../utility.hpp" //swap #include "../utility.hpp" //swap
#include <type_traits> //nothrow_invocable #include <type_traits> //nothrow_invocable
#include <utility> //pair, forward #include <functional> //less, greater
#include <utility> //pair
#include <iterator> //iterator_traits #include <iterator> //iterator_traits
#include <cstddef> //size_t #include <cstdlib> //size_t
namespace rexy::detail{ namespace rexy::detail{
@ -50,12 +51,12 @@ namespace rexy::detail{
return left; return left;
} }
template<class Iter, class Op> template<class Iter, class Op>
constexpr std::pair<std::size_t,size_t> max_suffix(const Iter& needle, std::size_t nlen, const Op& op = Op()){ constexpr std::pair<size_t,size_t> max_suffix(const Iter& needle, size_t nlen, const Op& op = Op()){
using value_type = typename std::iterator_traits<Iter>::value_type; using value_type = typename std::iterator_traits<Iter>::value_type;
std::size_t max_suffix = -1; size_t max_suffix = -1;
std::size_t j = 0; size_t j = 0;
std::size_t k = 1; size_t k = 1;
std::size_t period = 1; size_t period = 1;
value_type a; value_type a;
value_type b; value_type b;
@ -80,23 +81,18 @@ namespace rexy::detail{
} }
return {max_suffix, period}; return {max_suffix, period};
} }
template<class Iter> template<class Iter>
constexpr std::pair<std::size_t,size_t> critical_factorization(const Iter& nstart, const Iter& nend){ constexpr std::pair<size_t,size_t> critical_factorization(const Iter& nstart, const Iter& nend){
auto msuffix = max_suffix(nstart, nend - nstart, []<class T, class U>(T&& left, U&& right) constexpr{ auto msuffix = max_suffix(nstart, nend - nstart, std::less{});
return std::forward<T>(left) < std::forward<U>(right); auto msuffix_rev = max_suffix(nstart, nend - nstart, std::greater{});
});
auto msuffix_rev = max_suffix(nstart, nend - nstart, []<class T, class U>(T&& left, U&& right) constexpr{
return std::forward<T>(left) > std::forward<U>(right);
});
if(msuffix.first < msuffix_rev.first){ if(msuffix.first < msuffix_rev.first){
return msuffix_rev; return msuffix_rev;
} }
return msuffix; return msuffix;
} }
template<class Iter> template<class Iter>
constexpr bool iter_compare(const Iter& left, const Iter& right, std::size_t length){ constexpr bool iter_compare(const Iter& left, const Iter& right, size_t length){
for(std::size_t i = 0;i < length;++i){ for(size_t i = 0;i < length;++i){
if(left[i] != right[i]) if(left[i] != right[i])
return false; return false;
} }

View File

@ -1,105 +0,0 @@
/**
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_DETAIL_FORMAT_ARG_STORE_HPP
#define REXY_DETAIL_FORMAT_ARG_STORE_HPP
#include <cstddef> //size_t
#include "basic_types.hpp" //arg_info
#include "storage.hpp" //stored_type
#include "named_args.hpp" //count_named_args
#include "../../string_view.hpp"
namespace rexy::fmt::detail{
//Implementation of argument storage, erasing the need of passing around a parameter pack. Used in
//constructor of basic_format_args
template<class Context, class... Args>
class basic_format_arg_store
{
public:
using char_type = typename Context::char_type;
public:
static constexpr std::size_t num_args = sizeof...(Args);
static constexpr std::size_t num_named_args = count_named_args_v<Args...>;
struct format_data
{
friend class basic_format_arg_store;
private:
unsigned char m_data[
(sizeof(arg_info) * num_args) +
(sizeof(basic_string_view<char_type>) * num_named_args) +
(sizeof(stored_type_t<Args,Context>) + ...)
] = {};
arg_info* info(void){
return reinterpret_cast<arg_info*>(raw_info());
}
constexpr unsigned char* data(void){
return raw_info() + (sizeof(arg_info) * num_args);
}
constexpr unsigned char* raw_info(void){
return m_data;
}
constexpr const unsigned char* raw_info(void)const{
return m_data;
}
public:
const arg_info* info(void)const{
return reinterpret_cast<const arg_info*>(raw_info());
}
const unsigned char* data(void)const{
return raw_info() + (sizeof(arg_info) * num_args);
}
const unsigned char* raw(void)const{
return m_data;
}
}packed_data;
basic_format_arg_store(Args&&... args);
private:
template<class Arg>
void store_arg(std::size_t argnum, Arg&& arg);
template<NamedArg Arg>
void store_arg(std::size_t argnum, Arg&& arg);
template<class... SArgs>
void store_args(SArgs&&... args);
};
//Specialized for empty parameter pack
template<class Context>
class basic_format_arg_store<Context>
{
public:
static constexpr std::size_t num_args = 0;
};
}
#endif

View File

@ -1,89 +0,0 @@
/**
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_DETAIL_FORMAT_ARG_STORE_TPP
#define REXY_DETAIL_FORMAT_ARG_STORE_TPP
#include "arg_store.hpp"
#include "storage.hpp"
#include "basic_types.hpp"
#include "../../utility.hpp" //memcpy
#include <utility> //forward
#include <memory> //addressof
#include <cstddef> //size_t
namespace rexy::fmt::detail{
///////////////////////basic_format_arg_store////////////////////////
template<class Context, class... Args>
basic_format_arg_store<Context,Args...>::basic_format_arg_store(Args&&... args){
store_args(std::forward<Args>(args)...);
}
template<class Context, class... Args>
template<class Arg>
void basic_format_arg_store<Context,Args...>::store_arg(std::size_t argnum, Arg&& arg){
using stored_type = stored_type_t<Arg,Context>;
arg_info& ai = packed_data.info()[argnum];
ai.type = map_to_storage_enum_v<stored_type,typename Context::char_type>;
//convert to one of the storable types
stored_type st{std::forward<Arg>(arg)};
//save to the array of data
rexy::memcpy(packed_data.data()+ai.offset, std::addressof(st), sizeof(st));
//setup next entry's offset
if(argnum+1 < num_args){
packed_data.info()[argnum+1].offset = ai.offset + sizeof(st);
}
}
template<class Context, class... Args>
template<NamedArg Arg>
void basic_format_arg_store<Context,Args...>::store_arg(std::size_t argnum, Arg&& arg){
using stored_type = stored_type_t<Arg,Context>;
arg_info& ai = packed_data.info()[argnum];
ai.type = map_to_storage_enum_v<stored_type,typename Context::char_type>;
ai.named = true;
const std::size_t name_size = sizeof(format_string_view<char_type>);
//convert to one of the storable types
stored_type st{std::forward<Arg>(arg).value};
format_string_view<char_type> name{arg.name.c_str(), arg.name.length()};
//save to the array of data
rexy::memcpy(packed_data.data()+ai.offset, std::addressof(name), name_size);
rexy::memcpy(packed_data.data()+ai.offset+name_size, std::addressof(st), sizeof(st));
//setup next entry's offset
if(argnum+1 < num_args){
packed_data.info()[argnum+1].offset = ai.offset + sizeof(st) + name_size;
}
}
template<class Context, class... Args>
template<class... SArgs>
void basic_format_arg_store<Context,Args...>::store_args(SArgs&&... args){
std::size_t argnum = 0;
(store_arg(argnum++, std::forward<SArgs>(args)), ...);
}
}
#endif

View File

@ -1,100 +0,0 @@
/**
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_DETAIL_FORMAT_BASIC_TYPES_HPP
#define REXY_DETAIL_FORMAT_BASIC_TYPES_HPP
#include <cstddef> //size_t
#include "storage.hpp"
namespace rexy::fmt::detail{
static constexpr std::size_t invalid_arg_index = std::size_t(-1);
template<class Char>
struct format_string_view{
const Char* name;
std::size_t length;
};
struct arg_info{
storage_type type = storage_type::none_t;
std::size_t offset = 0;
bool named = false;
};
enum class alignment : int{
none = 0,
left = '<',
right = '>',
center = '^'
};
enum class presentation{
default_t,
int_t,
char_t,
float_t,
string_t,
ptr_t
};
struct format_specs{
int width = 0;
int precision = 0;
int type = 0;
alignment align = alignment::none;
presentation present = presentation::default_t;
int align_char = ' ';
int sign = 0;
bool locale = false;
bool alt_form = false;
bool zero_fill = false;
};
enum class dyn_type{
none = 0,
index,
string
};
template<class Char>
struct dynamic_format_specs : public format_specs{
union dyn_val{
std::size_t index = invalid_arg_index;
format_string_view<Char> name;
}dyn_width, dyn_precision;
dyn_type width_type = dyn_type::index;
dyn_type precision_type = dyn_type::index;
};
template<class FormatCtx>
constexpr void normalize_dynamic_format_specs(FormatCtx& ctx, dynamic_format_specs<typename FormatCtx::char_type>& specs);
}
namespace rexy{
template<class OutIt>
struct format_to_n_result{
OutIt out;
std::size_t size;
};
}
#endif

View File

@ -1,55 +0,0 @@
/**
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_DETAIL_FORMAT_BASIC_TYPES_TPP
#define REXY_DETAIL_FORMAT_BASIC_TYPES_TPP
#include "basic_types.hpp"
#include "format_args.hpp"
#include <cstddef> //size_t
namespace rexy::fmt::detail{
template<class FormatCtx>
constexpr void normalize_dynamic_format_specs(FormatCtx& ctx, dynamic_format_specs<typename FormatCtx::char_type>& specs){
std::size_t index = 0;
if(specs.width_type == dyn_type::index){
index = specs.dyn_width.index;
}else{
const auto& name = specs.dyn_width.name;
index = ctx.arg_index(name.name, name.name + name.length);
}
specs.width = ((index != invalid_arg_index)
? visit_format_arg(parse::dynamic_integer_retriever{}, ctx.arg(index))
: specs.width);
if(specs.precision_type == dyn_type::index){
index = specs.dyn_precision.index;
}else{
const auto& name = specs.dyn_precision.name;
index = ctx.arg_index(name.name, name.name + name.length);
}
specs.precision = ((index != invalid_arg_index)
? visit_format_arg(parse::dynamic_integer_retriever{}, ctx.arg(index))
: specs.precision);
}
}
#endif

View File

@ -1,97 +0,0 @@
/**
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_DETAIL_FORMAT_CONTEXT_HANDLER_HPP
#define REXY_DETAIL_FORMAT_CONTEXT_HANDLER_HPP
#include <cstddef> //size_t
#include "standard_types.hpp"
#include "internal_types.hpp"
#include "format_context.hpp"
#include "parse_context.hpp"
#include "format_args.hpp"
#include "../../string_view.hpp"
#include <locale> //locale
namespace rexy::fmt::detail{
//Holds the format and parse contexts and performs parse+format calls for replacement fields
template<class Char>
struct format_handler
{
public:
using char_type = Char;
using fmt_ctx_t = fmt_context_t<char_type>;
using parse_ctx_t = parse_context_t<char_type>;
using out_it_t = output_iterator_t<char_type>;
using fmt_it_t = typename parse_ctx_t::iterator;
using handle_t = typename basic_format_arg<fmt_ctx_t>::handle;
template<class T>
using fmter_t = typename fmt_ctx_t::template formatter_type<T>;
public:
fmt_ctx_t fmt_ctx;
parse_ctx_t parse_ctx;
public:
format_handler(out_it_t output, basic_string_view<char_type> fmt, basic_format_args<fmt_ctx_t> args);
format_handler(out_it_t output, basic_string_view<char_type> fmt, basic_format_args<fmt_ctx_t> args, const std::locale& l);
constexpr fmt_it_t do_raw(fmt_it_t start, fmt_it_t fin);
constexpr fmt_it_t do_format_spec(fmt_it_t start, fmt_it_t fin, std::size_t id);
constexpr fmt_it_t do_empty_format(fmt_it_t start, fmt_it_t last, std::size_t id);
constexpr std::size_t on_arg_id(void);
constexpr std::size_t on_arg_id(std::size_t i);
constexpr std::size_t on_arg_id(fmt_it_t start, fmt_it_t last);
};
template<class Char>
format_handler(output_iterator_t<Char>, basic_string_view<Char>, basic_format_args<fmt_context_t<Char>>) -> format_handler<Char>;
//Constant-evaluated version of format_handler. Does not perform formatting but will complete a parsing
//pass during compile
template<class Char, class... Args>
class format_checker
{
public:
using char_type = Char;
using parse_ctx_t = parse_context_t<char_type>;
using fmt_it_t = typename parse_ctx_t::iterator;
parse_ctx_t parse_ctx;
consteval format_checker(void) = default;
consteval format_checker(basic_string_view<char_type> fmt, std::size_t numargs = 0);
constexpr fmt_it_t do_raw(fmt_it_t, fmt_it_t last);
constexpr fmt_it_t do_format_spec(fmt_it_t start, fmt_it_t last, std::size_t id);
constexpr void do_empty_format(fmt_it_t, fmt_it_t, std::size_t);
constexpr std::size_t on_arg_id(void);
constexpr std::size_t on_arg_id(std::size_t i);
constexpr std::size_t on_arg_id(fmt_it_t start, fmt_it_t last);
private:
template<std::size_t I, class FArg, class... FArgs>
constexpr fmt_it_t do_format_spec_impl(fmt_it_t start, fmt_it_t last, std::size_t id);
};
}
#endif

View File

@ -1,162 +0,0 @@
/**
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_DETAIL_FORMAT_CONTEXT_HANDLER_TPP
#define REXY_DETAIL_FORMAT_CONTEXT_HANDLER_TPP
#include "context_handler.hpp"
#include "parse_context.hpp"
#include "format_context.hpp"
#include "formatter.hpp"
#include "format_args.hpp"
#include "named_args.hpp"
#include "format_error.hpp"
#include "../../string_view.hpp"
#include <locale> //locale
#include <cstddef> //size_t
#include <type_traits> //remove_cvref
namespace rexy::fmt::detail{
///////////////////////////////format_handler//////////////////////////////////
template<class Char>
format_handler<Char>::format_handler(out_it_t output, basic_string_view<char_type> fmt, basic_format_args<fmt_ctx_t> args):
fmt_ctx(args, output),
parse_ctx(fmt){}
template<class Char>
format_handler<Char>::format_handler(out_it_t output, basic_string_view<char_type> fmt, basic_format_args<fmt_ctx_t> args, const std::locale& l):
fmt_ctx(args, output, l),
parse_ctx(fmt){}
template<class Char>
constexpr auto format_handler<Char>::do_raw(fmt_it_t start, fmt_it_t fin) -> fmt_it_t{
for(auto it = start;it != fin;++it){
fmt_ctx.out() = *it;
}
return fin;
}
template<class Char>
constexpr auto format_handler<Char>::do_format_spec(fmt_it_t start, fmt_it_t fin, std::size_t id) -> fmt_it_t{
parse_ctx.advance_to(start);
const auto arg = fmt_ctx.arg(id);
format_specs specs;
visit_format_arg(format::arg_formatter<fmt_ctx_t,parse_ctx_t,format_specs>{fmt_ctx, parse_ctx, specs}, arg);
return parse_ctx.begin();
}
template<class Char>
constexpr auto format_handler<Char>::do_empty_format(fmt_it_t start, fmt_it_t last, std::size_t id) -> fmt_it_t{
parse_ctx.advance_to(start);
const auto arg = fmt_ctx.arg(id);
visit_format_arg(format::empty_formatter<fmt_ctx_t,parse_ctx_t>{fmt_ctx, parse_ctx}, arg);
return parse_ctx.begin();
}
template<class Char>
constexpr std::size_t format_handler<Char>::on_arg_id(void){
return parse_ctx.next_arg_id();
}
template<class Char>
constexpr std::size_t format_handler<Char>::on_arg_id(std::size_t i){
parse_ctx.check_arg_id(i);
return i;
}
template<class Char>
constexpr std::size_t format_handler<Char>::on_arg_id(fmt_it_t start, fmt_it_t last){
const auto id = fmt_ctx.arg_index(start, last);
parse_ctx.check_arg_id(id);
return id;
}
///////////////////////////////format_checker//////////////////////////////////
template<class Char, class... Args>
consteval format_checker<Char,Args...>::format_checker(basic_string_view<char_type> fmt, std::size_t numargs):
parse_ctx(fmt, numargs){}
template<class Char, class... Args>
constexpr auto format_checker<Char,Args...>::do_raw(fmt_it_t, fmt_it_t last) -> fmt_it_t{
return last;
}
template<class Char, class... Args>
constexpr auto format_checker<Char,Args...>::do_format_spec(fmt_it_t start, fmt_it_t last, std::size_t id) -> fmt_it_t{
parse_ctx.advance_to(start);
if constexpr(sizeof...(Args)){
return do_format_spec_impl<0,Args...>(start, last, id);
}else{
REXY_THROW_FORMAT_ERROR("Missing argument");
return start;
}
}
template<class Char, class... Args>
constexpr void format_checker<Char,Args...>::do_empty_format(fmt_it_t, fmt_it_t, std::size_t){/*nothing to parse and the checker doesn't format so just return*/}
template<class Char, class... Args>
template<std::size_t I, class FArg, class... FArgs>
constexpr auto format_checker<Char,Args...>::do_format_spec_impl(fmt_it_t start, fmt_it_t last, std::size_t id) -> fmt_it_t{
if(I == id){
using fmt_ctx_t = fmt_context_t<Char>;
using base_type = std::remove_cvref_t<FArg>;
using stored_type = stored_type_t<base_type,fmt_ctx_t>;
if constexpr(Handle<stored_type,fmt_ctx_t>){
using fmter_t = typename fmt_ctx_t::template formatter_type<base_type>;
fmter_t fmter{};
return fmter.parse(parse_ctx);
}else{
dynamic_format_specs<Char> specs{};
format_specs_checker<cx_format_specs_handler<parse_ctx_t,Args...>> handler{
cx_format_specs_handler<parse_ctx_t,Args...>{parse_ctx, specs},
detail::map_to_storage_enum_v<stored_type,Char>
};
return parse::perform_standard_parse(start, last, handler);
}
}else{
if constexpr(sizeof...(FArgs) > 0){
return do_format_spec_impl<I+1,FArgs...>(start, last, id);
}
}
REXY_THROW_FORMAT_ERROR("Missing argument");
}
template<class Char, class... Args>
constexpr std::size_t format_checker<Char,Args...>::on_arg_id(void){
return parse_ctx.next_arg_id();
}
template<class Char, class... Args>
constexpr std::size_t format_checker<Char,Args...>::on_arg_id(std::size_t i){
if(i > sizeof...(Args)){
REXY_THROW_FORMAT_ERROR("Arg index out of bounds");
}
parse_ctx.check_arg_id(i);
return i;
}
template<class Char, class... Args>
constexpr std::size_t format_checker<Char,Args...>::on_arg_id(fmt_it_t start, fmt_it_t last){
const std::size_t id = find_static_named_arg_id<fmt_it_t, Args...>(start, last);
if(id == invalid_arg_index){
REXY_THROW_FORMAT_ERROR("No such named arg");
}
return on_arg_id(id);
}
}
#endif

View File

@ -1,130 +0,0 @@
/**
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_DETAIL_FORMAT_FORMAT_ARGS_HPP
#define REXY_DETAIL_FORMAT_FORMAT_ARGS_HPP
#include <cstddef> //size_t
#include <variant> //monostate
#include "storage.hpp" //storage_type
#include "basic_types.hpp" //arg_info
#include "arg_store.hpp"
#include "traits.hpp"
#include "../../string_view.hpp"
namespace rexy::fmt{
//Type erasure of single argument.
//Context is an instantiation of 'basic_format_context'
template<class Context>
class basic_format_arg
{
public:
using char_type = typename Context::char_type;
public:
//Handles call to formatter for a user defined type
//type erase the user defined values
class handle
{
public:
using format_ptr_t = void(handle::*)(basic_format_parse_context<char_type>&, Context&)const;
public:
const void* m_data;
format_ptr_t m_format_impl;
public:
template<class T>
explicit handle(T&& t);
template<class T>
void format_impl(basic_format_parse_context<char_type>& parse_ctx, Context& format_ctx)const;
void format(basic_format_parse_context<char_type>& parse_ctx, Context& format_ctx)const;
};
union{
std::monostate none_v = {};
bool bool_v;
char_type char_v;
int int_v;
unsigned int uint_v;
long long long_long_v;
unsigned long long ulong_long_v;
float float_v;
double double_v;
long double long_double_v;
const char_type* char_ptr_v;
basic_string_view<char_type> string_v;
const void* ptr_v;
handle custom_v;
};
detail::storage_type active_member = detail::storage_type::none_t;
//initialize std::variant equivalent to std::monostate
basic_format_arg(void)noexcept{}
explicit basic_format_arg(bool b)noexcept;
explicit basic_format_arg(char_type b)noexcept;
explicit basic_format_arg(int b)noexcept;
explicit basic_format_arg(unsigned int b)noexcept;
explicit basic_format_arg(long long b)noexcept;
explicit basic_format_arg(unsigned long long b)noexcept;
explicit basic_format_arg(float b)noexcept;
explicit basic_format_arg(double b)noexcept;
explicit basic_format_arg(long double b)noexcept;
explicit basic_format_arg(const char_type* b)noexcept;
explicit basic_format_arg(basic_string_view<char_type> b)noexcept;
explicit basic_format_arg(const void* b)noexcept;
explicit basic_format_arg(const handle b)noexcept;
detail::storage_type type(void)const;
explicit operator bool(void)const noexcept;
};
//Holds list of arguments, erasing concrete types.
//Context is an instantiation of 'basic_format_context'
template<class Context>
class basic_format_args
{
public:
using char_type = detail::extract_char_type_from_context_t<Context>;
private:
//TODO less pointers
const detail::arg_info* m_info = nullptr;
const unsigned char* m_data = nullptr;
const std::size_t m_num_args = 0;
public:
basic_format_args(void)noexcept = default;
constexpr basic_format_args(const detail::basic_format_arg_store<Context>& store)noexcept;
template<class... Args>
basic_format_args(const detail::basic_format_arg_store<Context,Args...>& store)noexcept;
basic_format_arg<Context> get(std::size_t i)const noexcept;
basic_format_arg<Context> get(const char_type* first, const char_type* last)const;
std::size_t get_index(const char_type* first, const char_type* last)const;
};
//Visitor for basic_format_arg
template<class Fun, class Context>
decltype(auto) visit_format_arg(Fun&& fun, const basic_format_arg<Context>& arg);
}
#endif

View File

@ -1,236 +0,0 @@
/**
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_DETAIL_FORMAT_FORMAT_ARGS_TPP
#define REXY_DETAIL_FORMAT_FORMAT_ARGS_TPP
#include "format_args.hpp"
#include "basic_types.hpp" //format_string_view
#include "standard_types.hpp"
#include "parse_context.hpp"
#include "format_context.hpp"
#include "storage.hpp"
#include "arg_store.hpp"
#include "format_error.hpp"
#include "../../string_view.hpp"
#include <utility> //forward
#include <type_traits> //remove_cvref, add_lvalue_reference, add_pointer, add_const, conditional
#include <cstddef> //size_t
#include <memory> //addressof
namespace rexy::fmt{
/////////////////////////////basic_format_arg::handle//////////////////////////////
template<class Context>
template<class T>
basic_format_arg<Context>::handle::handle(T&& t):
m_data(std::addressof(t)),
m_format_impl(&handle::format_impl<std::remove_cvref_t<T>>){}
template<class Context>
template<class T>
void basic_format_arg<Context>::handle::format_impl(basic_format_parse_context<char_type>& parse_ctx, Context& format_ctx)const{
using value_type = std::remove_cvref_t<T>;
using formatter_t = typename Context::template formatter_type<value_type>;
using qualified_type = std::conditional_t<detail::has_const_formatter<value_type,Context>,
std::add_const_t<value_type>,
value_type>;
using reference_type = std::add_lvalue_reference_t<qualified_type>;
using pointer_type = std::add_pointer_t<qualified_type>;
using const_pointer_type = std::add_pointer_t<std::add_const_t<qualified_type>>;
reference_type ref = *const_cast<pointer_type>(reinterpret_cast<const_pointer_type>(m_data));
formatter_t fmt;
parse_ctx.advance_to(fmt.parse(parse_ctx));
format_ctx.advance_to(fmt.format(ref, format_ctx));
}
template<class Context>
void basic_format_arg<Context>::handle::format(basic_format_parse_context<char_type>& parse_ctx, Context& format_ctx)const{
//Pretty epic gamer moment here
(this->*m_format_impl)(parse_ctx, format_ctx);
}
///////////////////////////////basic_format_arg////////////////////////////////
template<class Context>
basic_format_arg<Context>::basic_format_arg(bool b)noexcept:
bool_v(b),
active_member(detail::storage_type::bool_t){}
template<class Context>
basic_format_arg<Context>::basic_format_arg(char_type b)noexcept:
char_v(b),
active_member(detail::storage_type::char_t){}
template<class Context>
basic_format_arg<Context>::basic_format_arg(int b)noexcept:
int_v(b),
active_member(detail::storage_type::int_t){}
template<class Context>
basic_format_arg<Context>::basic_format_arg(unsigned int b)noexcept:
uint_v(b),
active_member(detail::storage_type::uint_t){}
template<class Context>
basic_format_arg<Context>::basic_format_arg(long long b)noexcept:
long_long_v(b),
active_member(detail::storage_type::long_long_t){}
template<class Context>
basic_format_arg<Context>::basic_format_arg(unsigned long long b)noexcept:
ulong_long_v(b),
active_member(detail::storage_type::ulong_long_t){}
template<class Context>
basic_format_arg<Context>::basic_format_arg(float b)noexcept:
float_v(b),
active_member(detail::storage_type::float_t){}
template<class Context>
basic_format_arg<Context>::basic_format_arg(double b)noexcept:
double_v(b),
active_member(detail::storage_type::double_t){}
template<class Context>
basic_format_arg<Context>::basic_format_arg(long double b)noexcept:
long_double_v(b),
active_member(detail::storage_type::long_double_t){}
template<class Context>
basic_format_arg<Context>::basic_format_arg(const char_type* b)noexcept:
char_ptr_v(b),
active_member(detail::storage_type::char_ptr_t){}
template<class Context>
basic_format_arg<Context>::basic_format_arg(basic_string_view<char_type> b)noexcept:
string_v(b),
active_member(detail::storage_type::string_t){}
template<class Context>
basic_format_arg<Context>::basic_format_arg(const void* b)noexcept:
ptr_v(b),
active_member(detail::storage_type::ptr_t){}
template<class Context>
basic_format_arg<Context>::basic_format_arg(const handle b)noexcept:
custom_v(b),
active_member(detail::storage_type::custom_t){}
template<class Context>
detail::storage_type basic_format_arg<Context>::type(void)const{
return active_member;
}
template<class Context>
basic_format_arg<Context>::operator bool(void)const noexcept{
return active_member != detail::storage_type::none_t;
}
///////////////////////////////basic_format_args////////////////////////////////
template<class Context>
constexpr basic_format_args<Context>::basic_format_args(const detail::basic_format_arg_store<Context>&)noexcept{}
template<class Context>
template<class... Args>
basic_format_args<Context>::basic_format_args(const detail::basic_format_arg_store<Context,Args...>& store)noexcept:
m_info(store.packed_data.info()),
m_data(store.packed_data.data()),
m_num_args(sizeof...(Args)){}
template<class Context>
basic_format_arg<Context> basic_format_args<Context>::get(std::size_t i)const noexcept{
const bool named = m_info[i].named;
const std::size_t offset = m_info[i].offset + (named ? sizeof(detail::format_string_view<char_type>) : 0);
const unsigned char* const data = m_data + offset;
switch(m_info[i].type){
case detail::storage_type::bool_t:
return basic_format_arg<Context>{*reinterpret_cast<const bool*>(data)};
case detail::storage_type::char_t:
return basic_format_arg<Context>{*reinterpret_cast<const char_type*>(data)};
case detail::storage_type::int_t:
return basic_format_arg<Context>{*reinterpret_cast<const int*>(data)};
case detail::storage_type::uint_t:
return basic_format_arg<Context>{*reinterpret_cast<const unsigned int*>(data)};
case detail::storage_type::long_long_t:
return basic_format_arg<Context>{*reinterpret_cast<const long long*>(data)};
case detail::storage_type::ulong_long_t:
return basic_format_arg<Context>{*reinterpret_cast<const unsigned long long*>(data)};
case detail::storage_type::float_t:
return basic_format_arg<Context>{*reinterpret_cast<const float*>(data)};
case detail::storage_type::double_t:
return basic_format_arg<Context>{*reinterpret_cast<const double*>(data)};
case detail::storage_type::long_double_t:
return basic_format_arg<Context>{*reinterpret_cast<const long double*>(data)};
case detail::storage_type::char_ptr_t:
return basic_format_arg<Context>{*reinterpret_cast<const char_type* const*>(data)};
case detail::storage_type::string_t:
return basic_format_arg<Context>{*reinterpret_cast<const basic_string_view<char_type>*>(data)};
case detail::storage_type::ptr_t:
return basic_format_arg<Context>{reinterpret_cast<const void*>(*reinterpret_cast<const char* const*>(data))};
case detail::storage_type::custom_t:
return basic_format_arg<Context>{*reinterpret_cast<const typename basic_format_arg<Context>::handle*>(data)};
default:
return basic_format_arg<Context>{};
};
}
template<class Context>
basic_format_arg<Context> basic_format_args<Context>::get(const char_type* first, const char_type* last)const{
return get(get_index(first, last));
}
template<class Context>
std::size_t basic_format_args<Context>::get_index(const char_type* first, const char_type* last)const{
for(std::size_t i = 0;i < m_num_args;++i){
if(m_info[i].named){
auto data = reinterpret_cast<const detail::format_string_view<char_type>* const>(m_data + m_info[i].offset);
const std::size_t len = last - first;
if(len == data->length && !rexy::strncmp(data->name, first, len)){
return i;
}
}
}
REXY_THROW_FORMAT_ERROR("Requested named arg does not exist");
}
template<class Fun, class Context>
decltype(auto) visit_format_arg(Fun&& fun, const basic_format_arg<Context>& arg){
switch(arg.type()){
case detail::storage_type::bool_t:
return std::forward<Fun>(fun)(arg.bool_v);
case detail::storage_type::char_t:
return std::forward<Fun>(fun)(arg.char_v);
case detail::storage_type::int_t:
return std::forward<Fun>(fun)(arg.int_v);
case detail::storage_type::uint_t:
return std::forward<Fun>(fun)(arg.uint_v);
case detail::storage_type::long_long_t:
return std::forward<Fun>(fun)(arg.long_long_v);
case detail::storage_type::ulong_long_t:
return std::forward<Fun>(fun)(arg.ulong_long_v);
case detail::storage_type::float_t:
return std::forward<Fun>(fun)(arg.float_v);
case detail::storage_type::double_t:
return std::forward<Fun>(fun)(arg.double_v);
case detail::storage_type::long_double_t:
return std::forward<Fun>(fun)(arg.long_double_v);
case detail::storage_type::char_ptr_t:
return std::forward<Fun>(fun)(arg.char_ptr_v);
case detail::storage_type::string_t:
return std::forward<Fun>(fun)(arg.string_v);
case detail::storage_type::ptr_t:
return std::forward<Fun>(fun)(arg.ptr_v);
case detail::storage_type::custom_t:
return std::forward<Fun>(fun)(arg.custom_v);
default:
REXY_THROW_FORMAT_ERROR("Invalid format_arg storage_type");
};
}
}
#endif

View File

@ -1,77 +0,0 @@
/**
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_DETAIL_FORMAT_FORMAT_CONTEXT_HPP
#define REXY_DETAIL_FORMAT_FORMAT_CONTEXT_HPP
#include <type_traits> //remove_cvref, declval, integral_constant, void_t
#include <cstddef> //size_t
#include <locale> //locale
#include "standard_types.hpp" //basic_format_args, formatter
namespace rexy::fmt{
namespace detail{
//type trait used for properly cv-qualifying the argument to a call to formatter<type>::format
template<class T, class Context, class = void>
struct is_formatter_const : public std::false_type{};
template<class T, class Context>
struct is_formatter_const<T,Context,std::void_t<
decltype(std::declval<typename Context::template formatter_type<std::remove_cvref_t<T>>>().format(std::declval<const T&>(), std::declval<Context>()))
>> : public std::true_type{};
template<class T, class Context>
concept has_formatter = requires(typename Context::template formatter_type<std::remove_cvref_t<T>> fmter, T& t, Context& ctx){
fmter.format(t, ctx);
};
template<class T, class Context>
concept has_const_formatter = has_formatter<const std::remove_cvref_t<T>, Context>;
}
//Holds the arguments passed to calls to 'format' or 'vformat' in type erasing structure.
//Additionally holds the iterator where output is written.
template<class OutIt, class Char>
class basic_format_context
{
public:
using iterator = OutIt;
using char_type = Char;
template<class T>
using formatter_type = formatter<T, Char>;
private:
basic_format_args<basic_format_context> m_fmt_args;
iterator m_outit;
std::locale m_locale;
public:
basic_format_context(basic_format_args<basic_format_context> fmt_args, iterator outit);
basic_format_context(basic_format_args<basic_format_context> fmt_args, iterator outit, const std::locale& l);
basic_format_arg<basic_format_context> arg(std::size_t id)const;
basic_format_arg<basic_format_context> arg(const char_type* first, const char_type* last)const;
std::size_t arg_index(const char_type* first, const char_type* last)const;
std::locale locale(void);
iterator out(void);
void advance_to(iterator it);
};
}
#endif

View File

@ -1,70 +0,0 @@
/**
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_DETAIL_FORMAT_FORMAT_CONTEXT_TPP
#define REXY_DETAIL_FORMAT_FORMAT_CONTEXT_TPP
#include "format_context.hpp"
#include "format_args.hpp"
#include <utility> //move
#include <cstddef> //size_t
#include <locale> //locale
namespace rexy::fmt{
///////////////////////////////basic_format_context////////////////////////////////
template<class OutIt, class Char>
basic_format_context<OutIt,Char>::basic_format_context(basic_format_args<basic_format_context> fmt_args, iterator outit):
m_fmt_args(fmt_args),
m_outit(outit){}
template<class OutIt, class Char>
basic_format_context<OutIt,Char>::basic_format_context(basic_format_args<basic_format_context> fmt_args, iterator outit, const std::locale& l):
m_fmt_args(fmt_args),
m_outit(outit),
m_locale(l){}
template<class OutIt, class Char>
auto basic_format_context<OutIt,Char>::arg(std::size_t id)const -> basic_format_arg<basic_format_context>{
return m_fmt_args.get(id);
}
template<class OutIt, class Char>
auto basic_format_context<OutIt,Char>::arg(const char_type* first, const char_type* last)const -> basic_format_arg<basic_format_context>{
return m_fmt_args.get(first, last);
}
template<class OutIt, class Char>
std::size_t basic_format_context<OutIt,Char>::arg_index(const char_type* first, const char_type* last)const{
return m_fmt_args.get_index(first, last);
}
template<class OutIt, class Char>
std::locale basic_format_context<OutIt,Char>::locale(void){
return m_locale;
}
template<class OutIt, class Char>
auto basic_format_context<OutIt,Char>::out(void) -> iterator{
return std::move(m_outit);
}
template<class OutIt, class Char>
void basic_format_context<OutIt,Char>::advance_to(iterator it){
m_outit = std::move(it);
}
}
#endif

View File

@ -1,47 +0,0 @@
/**
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_DETAIL_FORMAT_FORMAT_ERROR_HPP
#define REXY_DETAIL_FORMAT_FORMAT_ERROR_HPP
#include "../../string_view.hpp"
#include "../../detail/debug_config.hpp"
#define REXY_FORMAT_REAL_STRINGIFY(str) #str
#define REXY_FORMAT_STRINGIFY(str) REXY_FORMAT_REAL_STRINGIFY(str)
#if LIBREXY_ENABLE_DEBUG_LEVEL > 0
#define REXY_THROW_FORMAT_ERROR(errstring) throw format_error{__FILE__ ":" REXY_FORMAT_STRINGIFY(__LINE__) ": " errstring}
#else
#define REXY_THROW_FORMAT_ERROR(errstring) throw format_error{errstring}
#endif
namespace rexy::fmt{
struct format_error{
string_view errstr;
constexpr const char* what(void)const{
return errstr.c_str();
}
};
}
#endif

View File

@ -1,40 +0,0 @@
/**
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_DETAIL_FORMAT_FORMAT_STRING_HPP
#define REXY_DETAIL_FORMAT_FORMAT_STRING_HPP
#include "../../string_view.hpp"
namespace rexy::fmt::detail{
//Class accepted as an argument to 'format' calls. Will call a constant-evaluated parse of the format string
//to check for correctness.
template<class Char, class... Args>
class basic_format_string
{
public:
basic_string_view<Char> str;
consteval basic_format_string(const Char* f);
consteval basic_format_string(basic_string_view<Char> f);
};
}
#endif

View File

@ -1,43 +0,0 @@
/**
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_DETAIL_FORMAT_FORMAT_STRING_TPP
#define REXY_DETAIL_FORMAT_FORMAT_STRING_TPP
#include "format_string.hpp"
#include "context_handler.hpp"
#include "parse.hpp"
#include "../../string_view.hpp"
namespace rexy::fmt::detail{
template<class Char, class... Args>
consteval basic_format_string<Char,Args...>::basic_format_string(const Char* f):
basic_format_string(basic_string_view<Char>{f}){}
template<class Char, class... Args>
consteval basic_format_string<Char,Args...>::basic_format_string(basic_string_view<Char> f):
str(f)
{
parse::perform_parse(format_checker<Char, Args...>{f, sizeof...(Args)}, str);
}
}
#endif

View File

@ -1,178 +0,0 @@
/**
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_DETAIL_FORMAT_FORMATTER_HPP
#define REXY_DETAIL_FORMAT_FORMATTER_HPP
#include <cstddef> //std::size_t, nullptr_t
#include <variant> //monostate
#include <locale> //locale
#include <type_traits> //remove_cvref
#include <utility> //forward
#include "basic_types.hpp" //format_specs
#include "internal_types.hpp"
#include "standard_types.hpp" //basic_parse_context
#include "specs_handler.hpp" //format_specs_checker
#include "traits.hpp"
#include "../../string.hpp"
#include "../../string_view.hpp"
#include "../../allocator.hpp"
namespace rexy::fmt{
namespace detail{
namespace format{
template<class Char, class OutIt>
constexpr OutIt perform_standard_format(const void* t, OutIt out, const format_specs& specs, const std::locale& loc);
template<class Char, class OutIt>
constexpr OutIt perform_standard_format(const void* t, OutIt out);
template<class Char, class OutIt>
requires(!UTF8_String<Char>)
constexpr OutIt perform_standard_format(const Char* c, OutIt out, const format_specs& specs, const std::locale& loc);
template<UTF8_String Char, class OutIt>
requires(UTF8_String<Char>)
constexpr OutIt perform_standard_format(const Char* c, OutIt out, const format_specs& specs, const std::locale& loc);
template<class Char, class OutIt>
constexpr OutIt perform_standard_format(const Char* c, OutIt out);
template<class Char, class OutIt>
constexpr OutIt perform_standard_format(basic_string_view<std::type_identity_t<Char>> str, OutIt out, const format_specs& specs, const std::locale& loc);
template<class Char, class OutIt>
constexpr OutIt perform_standard_format(basic_string_view<std::type_identity_t<Char>> str, OutIt out);
template<class Char, class OutIt>
constexpr OutIt perform_standard_format(Char b, OutIt out, const format_specs& specs, const std::locale& loc);
template<class Char, class OutIt>
constexpr OutIt perform_standard_format(Char b, OutIt out);
template<class Char, class OutIt>
constexpr OutIt perform_standard_format(bool b, OutIt out, const format_specs& specs, const std::locale& loc);
template<class Char, class OutIt>
constexpr OutIt perform_standard_format(bool b, OutIt out);
template<class Char, class OutIt, Integral T>
constexpr OutIt perform_standard_format(T b, OutIt out, const format_specs& specs, const std::locale& loc);
template<class Char, class OutIt, Arithmetic T>
constexpr OutIt perform_standard_format(T b, OutIt out);
template<class Char, class OutIt, Floating T>
constexpr OutIt perform_standard_format(T f, OutIt out, const format_specs& specs, const std::locale& loc);
template<class Char, class OutIt>
constexpr OutIt perform_standard_format(std::monostate, OutIt out, const format_specs&, const std::locale&);
template<class Char, class OutIt>
constexpr OutIt perform_standard_format(std::monostate, OutIt out);
template<class FmtCtx, class ParseCtx, class Specs>
struct arg_formatter{
FmtCtx& fmt_ctx;
ParseCtx& parse_ctx;
const Specs& specs;
template<Handle<FmtCtx> T>
constexpr void operator()(T&& t);
template<class T>
constexpr void operator()(T&& t);
};
template<class FmtCtx, class ParseCtx>
struct empty_formatter{
FmtCtx& fmt_ctx;
ParseCtx& parse_ctx;
template<Handle<FmtCtx> T>
constexpr void operator()(T&& t);
template<class T>
constexpr void operator()(T&& t);
};
}
template<class T, class Char>
class formatter_base
{
public:
using char_type = Char;
using parse_ctx_t = basic_format_parse_context<char_type>;
using fmt_ctx_t = detail::fmt_context_t<char_type>;
using format_spec_t = detail::dynamic_format_specs<char_type>;
using format_spec_handler_t = detail::format_specs_checker<detail::dynamic_format_specs_handler<parse_ctx_t>>;
protected:
format_spec_t specs;
public:
constexpr auto parse(basic_format_parse_context<Char>& ctx) -> decltype(ctx.begin());
template<class U, class FormatContext>
auto format(U&& t, FormatContext& ctx) -> decltype(ctx.out());
};
}
}
namespace rexy{
template<class T, class Char = char>
class formatter;
#define IMPLEMENT_STANDARD_FORMATTER(type) \
template<fmt::detail::Formatter_Char C> \
class formatter<type, C> : public fmt::detail::formatter_base<fmt::detail::stored_type_t<type,fmt::detail::fmt_context_t<C>>, C>{}
IMPLEMENT_STANDARD_FORMATTER(int);
IMPLEMENT_STANDARD_FORMATTER(unsigned int);
IMPLEMENT_STANDARD_FORMATTER(long long);
IMPLEMENT_STANDARD_FORMATTER(unsigned long long);
IMPLEMENT_STANDARD_FORMATTER(bool);
IMPLEMENT_STANDARD_FORMATTER(float);
IMPLEMENT_STANDARD_FORMATTER(double);
IMPLEMENT_STANDARD_FORMATTER(long double);
IMPLEMENT_STANDARD_FORMATTER(std::nullptr_t);
IMPLEMENT_STANDARD_FORMATTER(short);
IMPLEMENT_STANDARD_FORMATTER(unsigned short);
IMPLEMENT_STANDARD_FORMATTER(long);
IMPLEMENT_STANDARD_FORMATTER(unsigned long);
IMPLEMENT_STANDARD_FORMATTER(char);
IMPLEMENT_STANDARD_FORMATTER(unsigned char);
IMPLEMENT_STANDARD_FORMATTER(signed char);
template<fmt::detail::Formatter_Char C>
class formatter<C*,C> : public fmt::detail::formatter_base<const C*,C>{};
template<fmt::detail::Formatter_Char C>
class formatter<const C*,C> : public fmt::detail::formatter_base<const C*,C>{};
template<fmt::detail::Formatter_Char C, std::size_t I>
class formatter<const C[I],C> : public fmt::detail::formatter_base<const C*,C>{};
template<fmt::detail::Formatter_Char C, REXY_ALLOCATOR_CONCEPT A>
class formatter<basic_string<C,A>,C> : public fmt::detail::formatter_base<basic_string_view<C>,C>{};
template<fmt::detail::Formatter_Char C>
class formatter<basic_string_view<C>,C> : public fmt::detail::formatter_base<basic_string_view<C>,C>{};
template<class T, fmt::detail::Formatter_Char C>
class formatter<T*,C> : public fmt::detail::formatter_base<const void*,C>{};
template<class T, fmt::detail::Formatter_Char C>
class formatter<const T*,C> : public fmt::detail::formatter_base<const void*,C>{};
#undef IMPLEMENT_STANDARD_FORMATTER
}
#endif

View File

@ -1,665 +0,0 @@
/**
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_DETAIL_FORMAT_FORMATTER_TPP
#define REXY_DETAIL_FORMAT_FORMATTER_TPP
#include "formatter.hpp"
#include "basic_types.hpp"
#include "internal_types.hpp"
#include "standard_types.hpp"
#include "parse_context.hpp"
#include "format_context.hpp"
#include "format_args.hpp"
#include "parse.hpp"
#include "utf_iterator.hpp"
#include "../../utility.hpp" //abs, memcpy
#include <type_traits> //remove_cvref
#include <utility> //forward, move
#include <cstddef> //size_t
#include <algorithm> //max
#include <cmath> //signbit
#include <charconv> //to_chars
#include <cctype> //toupper
#include <cstdint> //uintptr_t
#include <locale> //locale
#include <variant> //monostate
#include <limits>
namespace rexy::fmt::detail{
template<class T, class Char>
constexpr auto formatter_base<T,Char>::parse(basic_format_parse_context<Char>& ctx) -> decltype(ctx.begin()){
using stored_type = detail::stored_type_t<std::remove_cvref_t<T>,fmt_ctx_t>;
return detail::parse::perform_standard_parse(
ctx.begin(),
ctx.end(),
format_spec_handler_t{
detail::dynamic_format_specs_handler<parse_ctx_t>{ctx, specs},
detail::map_to_storage_enum_v<stored_type,char_type>
}
);
}
template<class T, class Char>
template<class U, class FormatContext>
auto formatter_base<T,Char>::format(U&& t, FormatContext& ctx) -> decltype(ctx.out()){
normalize_dynamic_format_specs(ctx, specs);
return detail::format::perform_standard_format<Char>(std::forward<U>(t), ctx.out(), specs);
}
namespace format{
template<class Char, class OutIt>
constexpr OutIt perform_format_write(const Char* c, OutIt out, std::size_t length){
for(std::size_t i = 0;i < length;++i){
*out++ = c[i];
}
return std::move(out);
}
template<class OutIt, class Outputer>
constexpr OutIt perform_format_write_aligned(OutIt out, int width, const format_specs& specs, alignment default_align, Outputer&& outputer){
const int fill_amount = std::max(specs.width - width, 0);
int left_fill_amount = fill_amount;
int right_fill_amount = fill_amount;
const alignment align = specs.align == alignment::none ? default_align : specs.align;
switch(align){
case alignment::center:
left_fill_amount /= 2;
[[fallthrough]];
case alignment::right:
for(std::size_t i = 0;i < left_fill_amount;++i){
*out++ = specs.align_char;
}
right_fill_amount -= left_fill_amount;
[[fallthrough]];
case alignment::left:
out = outputer(std::move(out));
for(std::size_t i = 0;i < right_fill_amount;++i){
*out++ = specs.align_char;
}
break;
default:
REXY_THROW_FORMAT_ERROR("Invalid alignment state");
};
return std::move(out);
}
template<class T>
consteval T cxlog10(T t){
return t < 10 ? 1 : 1 + cxlog10(t / 10);
}
template<class OutIt, class T>
constexpr OutIt perform_format_write_sign(OutIt out, int sign, T val){
if(val < 0){
*out++ = '-';
}else if(sign == '+'){
*out++ = '+';
}else if(sign == ' '){
*out++ = ' ';
}
return std::move(out);
}
template<class OutIt, Floating T>
constexpr OutIt perform_format_write_sign(OutIt out, int sign, T val){
if(std::signbit(val)){
*out++ = '-';
}else if(sign == '+'){
*out++ = '+';
}else if(sign == ' '){
*out++ = ' ';
}
return std::move(out);
}
template<class Char, class OutIt>
constexpr OutIt perform_standard_format(const void* t, OutIt out, const format_specs& specs, const std::locale& loc){
//2 hexidecimal digits per octet
constexpr std::size_t maxbufflen = sizeof(std::uintptr_t) * (CHAR_BIT / 4.0) + 3; //+2 for '0x', +1 for float rounding truncation
char buff[maxbufflen] = {};
buff[0] = '0';
buff[1] = 'x';
char* buffstart = buff + 2;
char* buffend = buff + maxbufflen;
auto result = std::to_chars(buffstart, buffend, reinterpret_cast<std::uintptr_t>(t), 16);
if(result.ec != std::errc{}){
REXY_THROW_FORMAT_ERROR("Unable to convert pointer type");
}
return perform_standard_format<Char>(buff, out, specs, loc);
}
template<class Char, class OutIt>
constexpr OutIt perform_standard_format(const void* t, OutIt out){
//2 hexidecimal digits per octet
constexpr std::size_t maxbufflen = sizeof(std::uintptr_t) * (CHAR_BIT / 4.0) + 4; //+2 for '0x', +1 for float rounding truncation
char buff[maxbufflen] = {};
buff[0] = '0';
buff[1] = 'x';
char* buffstart = buff + 2;
char* buffend = buff + maxbufflen;
auto result = std::to_chars(buffstart, buffend, reinterpret_cast<std::uintptr_t>(t), 16);
if(result.ec != std::errc{}){
REXY_THROW_FORMAT_ERROR("Unable to convert pointer type");
}
const auto bufflen = result.ptr - buff;
return perform_format_write(buff, out, bufflen);
}
static constexpr unsigned int codepoint_fields[] = {
0x1100u, 0x115Fu,
0x2329u, 0x232Au,
0x2E80u, 0x303Eu,
0x3040u, 0xA4CFu,
0xAC00u, 0xD7A3u,
0xF900u, 0xFAFFu,
0xFE10u, 0xFE19u,
0xFE30u, 0xFE6Fu,
0xFF00u, 0xFF60u,
0xFFE0u, 0xFFE6u,
0x1F300u, 0x1F64Fu,
0x1F900u, 0x1F9FFu,
0x20000u, 0x2FFFDu,
0x30000u, 0x3FFFDu
};
constexpr unsigned int estimate_unicode_width(const char32_t c){
unsigned int width = 1;
for(auto field : codepoint_fields){
if(c < field){
return width;
}
//flip between 1 and 2
width ^= 3u;
}
return width;
}
constexpr unsigned int estimate_unicode_string_width(const char* first, const char* last){
unsigned int width = 0;
for(utf8_iterator<char> it{first, last};it.valid();++it){
const auto codepoint = *it;
width += estimate_unicode_width(codepoint);
}
return width;
}
template<class Char, class OutIt>
requires(!UTF8_String<Char>)
constexpr OutIt perform_standard_format(const Char* c, OutIt out, const format_specs& specs, const std::locale& loc){
const basic_string_view<Char> str{c};
std::size_t est_width = 0;
std::size_t print_cnt = 0;
if(specs.precision > 0){
est_width = std::min<std::size_t>(str.length(), specs.precision);
print_cnt = est_width;
}else{
est_width = str.length();
print_cnt = str.length();
}
const auto outputter = [&](OutIt o){
for(std::size_t i = 0;i < print_cnt;++i){
*o++ = str[i];
}
return std::move(o);
};
return perform_format_write_aligned(std::move(out), est_width, specs, alignment::left, outputter);
}
template<UTF8_String Char, class OutIt>
requires(UTF8_String<Char>)
constexpr OutIt perform_standard_format(const Char* c, OutIt out, const format_specs& specs, const std::locale& loc){
const basic_string_view<Char> str{c};
std::size_t est_width = 0;
std::size_t print_cnt = 0;
if(specs.precision > 0){
for(utf8_iterator<Char> it{str.begin(), str.end()};it.valid();++it){
const auto ch_width = estimate_unicode_width(*it);
if(ch_width + est_width > specs.precision){
break;
}
print_cnt += it.byte_count();
est_width += ch_width;
}
}else{
est_width = estimate_unicode_string_width(str.cbegin(), str.cend());
print_cnt = str.length();
}
const auto outputter = [&](OutIt o){
for(std::size_t i = 0;i < print_cnt;++i){
*o++ = str[i];
}
return std::move(o);
};
return perform_format_write_aligned(std::move(out), est_width, specs, alignment::left, outputter);
}
template<class Char, class OutIt>
constexpr OutIt perform_standard_format(const Char* c, OutIt out){
for(const Char* i = c;*i;++i){
*out++ = *i;
}
return std::move(out);
}
template<class Char, class OutIt>
constexpr OutIt perform_standard_format(basic_string_view<std::type_identity_t<Char>> str, OutIt out, const format_specs& specs, const std::locale& loc){
return perform_standard_format<Char>(str.c_str(), std::move(out), specs, loc);
}
template<class Char, class OutIt>
constexpr OutIt perform_standard_format(basic_string_view<std::type_identity_t<Char>> str, OutIt out){
return perform_standard_format(str.c_str(), std::move(out));
}
template<class Char, class OutIt>
constexpr OutIt perform_standard_format(Char b, OutIt out, const format_specs& specs, const std::locale& loc){
if(specs.present == presentation::int_t){
return perform_standard_format<Char>(static_cast<int>(b), std::move(out), specs, loc);
}
const auto outputter = [=](OutIt o){
*o++ = b;
return std::move(o);
};
return perform_format_write_aligned(std::move(out), 1, specs, alignment::left, outputter);
}
template<class Char, class OutIt>
constexpr OutIt perform_standard_format(Char b, OutIt out){
*out++ = b;
return std::move(out);
}
template<class Char, class OutIt>
constexpr OutIt perform_standard_format(bool b, OutIt out, const format_specs& specs, const std::locale& loc){
switch(specs.present){
case presentation::default_t:
case presentation::string_t:
break;
case presentation::int_t:
case presentation::char_t:
return perform_standard_format<Char>(static_cast<unsigned char>(b), std::move(out), specs, loc);
default:
REXY_THROW_FORMAT_ERROR("Invalid type argument for bool");
};
if(specs.locale){
const auto& facet = std::use_facet<std::numpunct<Char>>(loc);
const auto word = b ? facet.truename() : facet.falsename();
format_specs copy_specs = specs;
copy_specs.locale = false;
return perform_standard_format<Char>(
word.c_str(),
std::move(out),
copy_specs,
loc
);
}
return perform_standard_format<Char>(b ? "true" : "false", std::move(out), specs, loc);
}
template<class Char, class OutIt>
constexpr OutIt perform_standard_format(bool b, OutIt out){
return perform_standard_format<Char>(b ? "true" : "false", std::move(out));
}
template<class Char, class OutIt>
constexpr OutIt perform_localized_integer_write(OutIt out, const char* start, const char* last, const std::locale& loc){
const auto& facet = std::use_facet<std::numpunct<Char>>(loc);
const int group_size = int(facet.grouping()[0]);
const Char sep = facet.thousands_sep();
const int len = int(last - start);
if(group_size != 0){
int write_count = (len-1) % group_size;
*out++ = *start++;
while(true){
for(;write_count > 0;--write_count){
*out++ = *start++;
}
if(start == last){
break;
}
*out++ = sep;
write_count = std::min(group_size, int(last - start));
}
return std::move(out);
}
return perform_format_write(start, std::move(out), len);
}
template<class Char, class OutIt, Integral T>
constexpr OutIt perform_standard_format(T b, OutIt out, const format_specs& specs, const std::locale& loc){
constexpr std::size_t maxbufflen = rexy::max(
std::numeric_limits<long long>::digits + 3, //add 1 for sign bit, 2 for prefix
std::numeric_limits<unsigned long long>::digits + 3
);
if(specs.present == presentation::char_t){
return perform_standard_format<Char>(static_cast<Char>(b), std::move(out), specs, loc);
};
int base = 10;
int total_width = 0;
const bool should_zero_fill = specs.zero_fill && specs.align == alignment::none;
bool to_upper = false;
char buff[maxbufflen] = {};
char* buffstart = buff;
char* buffend = buff + maxbufflen;
string_view prefix = "";
switch(specs.type){
case 'b':
prefix = "0b";
base = 2;
break;
case 'B':
prefix = "0B";
base = 2;
break;
case 'o':
prefix = b == 0 ? "" : "0";
base = 8;
break;
case 'x':
prefix = "0x";
base = 16;
break;
case 'X':
prefix = "0X";
to_upper = true;
base = 16;
break;
default:
break;
};
total_width += prefix.length();
auto result = std::to_chars(buffstart, buffend, b, base);
if(result.ec != std::errc{}){
REXY_THROW_FORMAT_ERROR("Unable to convert integral type");
}
buffend = result.ptr;
if(b < 0){
++buffstart;
total_width += 1;
}else if(specs.sign != 0){
total_width += 1;
}
const auto bufflen = buffend - buffstart;
total_width += bufflen;
if(to_upper){
for(auto it = buffstart;it != buffend;++it){
*it = std::toupper(*it);
}
}
const auto outputter = [&](OutIt o) -> OutIt{
if(specs.alt_form){
o = perform_format_write_sign(std::move(o), specs.sign, b);
o = perform_format_write(prefix.data(), std::move(o), prefix.length());
}else{
o = perform_format_write(prefix.data(), std::move(out), prefix.length());
o = perform_format_write_sign(std::move(o), specs.sign, b);
}
if(should_zero_fill && specs.width > total_width){
const int fill_width = specs.width - total_width;
for(int i = 0;i < fill_width;++i){
*o++ = '0';
}
}
if(specs.locale){
const auto& facet = std::use_facet<std::numpunct<Char>>(loc);
const int group_size = facet.grouping()[0];
const Char sep = facet.thousands_sep();
return perform_localized_integer_write<Char>(std::move(o), buffstart, buffend, loc);
}
return perform_format_write(buffstart, std::move(o), bufflen);
};
if(should_zero_fill){
return outputter(std::move(out));
}
return perform_format_write_aligned(std::move(out), total_width, specs, alignment::right, outputter);
}
//////////////////////////////////////////////DONE///////////////////////////////////////////////
template<class Char, class OutIt, Arithmetic T>
constexpr OutIt perform_standard_format(T b, OutIt out){
using limits = std::numeric_limits<long double>;
constexpr auto maxexp = limits::max_exponent10; //this is a constant expression but using limits::max_exponent10 directly isn't?
//long double will be the longest type possible, so operate on that.
//+4 for ones' place digit, decimal point, and 'e+' in scientific mode
//maximum buffer length is the maximum significant digits plus maximum length of exponent
constexpr std::size_t maxbufflen = 4 + limits::max_digits10 + cxlog10(maxexp);
char buff[maxbufflen] = {};
char* buffend = buff + maxbufflen;
auto result = std::to_chars(buff, buffend, b);
if(result.ec != std::errc{}){
REXY_THROW_FORMAT_ERROR("Unable to convert arithmetic type");
}
const auto bufflen = result.ptr - buff;
return perform_format_write(buff, std::move(out), bufflen);
}
//TODO
template<class Char, class OutIt, Floating T>
constexpr OutIt perform_standard_format(T f, OutIt out, const format_specs& specs, const std::locale& loc){
using limits = std::numeric_limits<T>;
//max number of post-decimal digits is same as the inverted smallest radix exponent
constexpr int max_precision = rexy::abs(limits::min_exponent);
//max number of leading digits is same as biggest decimal exponent
constexpr int max_significants = limits::max_exponent10;
//+4 for ones' place digit, decimal point, and 'e+' in scientific mode
//maximum buffer length is the maximum significant digits plus maximum precision because the
//user can request any precision. So you can take the longest number with no decimal and add on
//the longest decimal trail allowed.
constexpr int maxbufflen = max_precision + max_significants + 4;
char buff[maxbufflen] = {};
char* buffstart = buff;
char* buffend = buff + maxbufflen;
const bool supplied_precision = specs.precision > 0;
const bool is_infinity = f == std::numeric_limits<T>::infinity() || f == -std::numeric_limits<T>::infinity();
const bool is_nan = (f != f);
const bool is_integer_representable = (static_cast<T>(static_cast<long long>(f)) == f) && !is_infinity && !is_nan;
const bool should_zero_fill = specs.zero_fill && specs.align == alignment::none;
std::chars_format fmt = std::chars_format::general;
bool to_upper = false;
bool manual_precision = supplied_precision;
bool trailing_dot = false;
std::size_t total_width = 0;
//TODO handle any other modes and formatting options
std::to_chars_result result{};
switch(specs.type){
case 'A':
to_upper = true;
[[fallthrough]];
case 'a':
fmt = std::chars_format::hex;
break;
case 'E':
to_upper = true;
[[fallthrough]];
case 'e':
fmt = std::chars_format::scientific;
manual_precision = true;
break;
case 'F':
to_upper = true;
[[fallthrough]];
case 'f':
fmt = std::chars_format::fixed;
manual_precision = true;
break;
case 'G':
to_upper = true;
[[fallthrough]];
case 'g':
manual_precision = true;
if(specs.alt_form){
//keep trailing zeros
fmt = std::chars_format::fixed;
}
break;
default:
trailing_dot = is_integer_representable && !supplied_precision && specs.alt_form;
break;
};
//manually handle nan and inf since some compilers (msvc) output something other than the desired values in 'to_chars'
if(is_nan){
result.ptr = buffstart + 3;
rexy::memcpy(buffstart, "nan", 3);
}else if(is_infinity){
result.ptr = buffstart + 3;
rexy::memcpy(buffstart, "inf", 3);
}else if(manual_precision){
const int precision = supplied_precision ? specs.precision : 6;
result = std::to_chars(buffstart, buffend, f, fmt, precision);
}else{
result = std::to_chars(buffstart, buffend, f, fmt);
}
if(result.ec != std::errc{}){
REXY_THROW_FORMAT_ERROR("Unable to convert floating type");
}
buffend = result.ptr;
//exclude negative sign automatically put there
if(buffstart[0] == '-'){
++buffstart;
++total_width;
}else if(specs.sign != 0){
++total_width;
}
if(trailing_dot){
++total_width;
}
const auto bufflen = buffend - buffstart;
total_width += bufflen;
if(to_upper){
for(auto it = buffstart;it != buffend;++it){
*it = std::toupper(*it);
}
}
const auto outputter = [&](OutIt o){
Char radix_char = '.';
o = perform_format_write_sign(std::move(o), specs.sign, f);
if(should_zero_fill && specs.width > total_width && !(is_infinity || is_nan)){
const int fill_width = specs.width - total_width;
for(int i = 0;i < fill_width;++i){
*o++ = '0';
}
}
if(specs.locale){
const auto& facet = std::use_facet<std::numpunct<Char>>(loc);
const int group_size = facet.grouping()[0];
radix_char = facet.decimal_point();
string_view buff_view{buffstart, buffend};
const auto radix_pos = buff_view.find_first_of('.');
if(radix_pos != string_view::npos){
buff[radix_pos] = radix_char;
o = perform_localized_integer_write<Char>(std::move(o), buffstart, buffstart + radix_pos, loc);
buffstart += radix_pos;
}
}
o = perform_format_write(buffstart, std::move(o), bufflen);
if(trailing_dot){
*o++ = radix_char;
}
return std::move(o);
};
if(should_zero_fill){
return outputter(std::move(out));
}
return perform_format_write_aligned(std::move(out), total_width, specs, alignment::right, outputter);
}
template<class Char, class OutIt>
constexpr OutIt perform_standard_format(std::monostate, OutIt out, const format_specs&, const std::locale&){
return std::move(out);
}
template<class Char, class OutIt>
constexpr OutIt perform_standard_format(std::monostate, OutIt out){
return std::move(out);
}
template<class FmtCtx, class ParseCtx, class Specs>
template<Handle<FmtCtx> T>
constexpr void arg_formatter<FmtCtx,ParseCtx,Specs>::operator()(T&& t){
t.format(parse_ctx, fmt_ctx);
}
template<class FmtCtx, class ParseCtx, class Specs>
template<class T>
constexpr void arg_formatter<FmtCtx,ParseCtx,Specs>::operator()(T&& t){
using handler_t = format_specs_checker<dynamic_format_specs_handler<ParseCtx>>;
using specs_t = dynamic_format_specs<typename FmtCtx::char_type>;
specs_t specs;
parse_ctx.advance_to(parse::perform_standard_parse(
parse_ctx.begin(),
parse_ctx.end(),
handler_t{
dynamic_format_specs_handler<ParseCtx>{
parse_ctx,
specs
},
map_to_storage_enum_v<T,typename FmtCtx::char_type>
}
));
normalize_dynamic_format_specs(fmt_ctx, specs);
fmt_ctx.advance_to(perform_standard_format<typename FmtCtx::char_type>(t, fmt_ctx.out(), specs, fmt_ctx.locale()));
}
template<class FmtCtx, class ParseCtx>
template<Handle<FmtCtx> T>
constexpr void empty_formatter<FmtCtx,ParseCtx>::operator()(T&& t){
t.format(parse_ctx, fmt_ctx);
}
template<class FmtCtx, class ParseCtx>
template<class T>
constexpr void empty_formatter<FmtCtx,ParseCtx>::operator()(T&& t){
perform_standard_format<typename FmtCtx::char_type>(t, fmt_ctx.out());
}
} //namespace format
}
#endif

View File

@ -1,83 +0,0 @@
/**
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_DETAIL_FORMAT_INTERNAL_TYPES_HPP
#define REXY_DETAIL_FORMAT_INTERNAL_TYPES_HPP
#include "../../cx/string.hpp"
#include "standard_types.hpp"
#include <iterator> //back_insert_iterator
#include <type_traits> //type_identity
namespace rexy::fmt::detail{
//forward declare implementation types
template<class Char, class... Args>
class basic_format_string;
template<class Context, class... Args>
class basic_format_arg_store;
template<class Char>
class format_output_buffer_base;
template<class Char, class OutIt>
class basic_format_output_buffer;
struct format_specs;
template<class Char>
struct dynamic_format_specs;
template<class ParseCtx, class... Args>
struct checker_format_specs_handler;
template<class ParseCtx, class FmtCtx>
struct format_specs_handler;
template<class ParseCtx>
struct dynamic_format_specs_handler;
template<class T, class Char>
struct runtime_arg;
template<class T, rexy::cx::string Name>
struct static_arg;
//aliases for easier access to both implementation and standard defined types
template<class Char>
using fmt_buffer_t = format_output_buffer_base<Char>;
template<class Char>
using output_iterator_t = std::back_insert_iterator<fmt_buffer_t<Char>>;
template<class Char>
using fmt_context_t = basic_format_context<output_iterator_t<Char>,Char>;
template<class Char>
using parse_context_t = basic_format_parse_context<Char>;
template<class... Args>
using format_arg_store = basic_format_arg_store<fmt_context_t<char>, Args...>;
template<class... Args>
using wformat_arg_store = basic_format_arg_store<fmt_context_t<wchar_t>, Args...>;
template<class... Args>
using format_string = basic_format_string<char,std::type_identity_t<Args>...>;
template<class... Args>
using wformat_string = basic_format_string<wchar_t, std::type_identity_t<Args>...>;
template<class OutIt>
using format_output_buffer = basic_format_output_buffer<char,OutIt>;
template<class OutIt>
using wformat_output_buffer = basic_format_output_buffer<wchar_t,OutIt>;
template<class Char>
using impl_format_args = basic_format_args<fmt_context_t<Char>>;
template<class Char>
using format_arg = basic_format_arg<fmt_context_t<Char>>;
}
#endif

View File

@ -1,117 +0,0 @@
/**
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_DETAIL_FORMAT_NAMED_ARGS_HPP
#define REXY_DETAIL_FORMAT_NAMED_ARGS_HPP
#include <type_traits> //remove_cvref, declval, integral_constant
#include <utility> //forward
#include <cstddef> //size_t
#include "../../string_view.hpp"
namespace rexy::fmt{
namespace detail{
template<class T, class Char>
struct arg_base{
const T& value;
const basic_string_view<Char> name;
};
//class used to store name and value for runtime analysis of named arguments
//the type T is the actual formatted type and name is used only for lookup
//during runtime formatting
template<class T, class Char>
struct runtime_arg : public arg_base<T,Char>{
using char_type = Char;
using value_type = std::remove_cvref_t<T>;
};
//class used to store name and value for compile time analysis of named arguments
//the type T is the actual formatted type and Name is used for name lookup in
//format string checking
template<class T, rexy::cx::string Name>
struct static_arg : public arg_base<T,typename decltype(Name)::value_type>{
using char_type = typename decltype(Name)::value_type;
using value_type = std::remove_cvref_t<T>;
static constexpr auto static_name = Name;
constexpr static_arg(const T& v):
arg_base<T,char_type>{v, {Name.c_str(), Name.length()}}{}
static_arg(const static_arg&) = delete;
constexpr static_arg(static_arg&&) = default;
};
//temporary object to store Name to pass off to a compile time argument during assignment
template<rexy::cx::string Name>
struct arg_literal_result{
template<class T>
constexpr auto operator=(T&& t){
return static_arg<std::remove_cvref_t<T>, Name>{std::forward<T>(t)};
}
};
template<class T>
struct is_runtime_named_arg{
template<class U, class Char>
static auto check(runtime_arg<U,Char>) -> std::true_type;
static auto check(...) -> std::false_type;
static constexpr bool value = decltype(check(std::declval<std::remove_cvref_t<T>>()))::value;
};
template<class T>
static constexpr bool is_runtime_named_arg_v = is_runtime_named_arg<T>::value;
template<class T>
struct is_static_named_arg{
template<class U, rexy::cx::string Name>
static auto check(static_arg<U,Name>) -> std::true_type;
static auto check(...) -> std::false_type;
static constexpr bool value = decltype(check(std::declval<std::remove_cvref_t<T>>()))::value;
};
template<class T>
static constexpr bool is_static_named_arg_v = is_static_named_arg<T>::value;
template<class T>
concept RuntimeNamedArg = is_runtime_named_arg_v<T>;
template<class T>
concept StaticNamedArg = is_static_named_arg_v<T>;
template<class T>
concept NamedArg = StaticNamedArg<T> || RuntimeNamedArg<T>;
template<class It, std::size_t I, class Arg, class... Args>
constexpr std::size_t find_static_named_arg_id_impl(It first, It last);
template<class It, class... Args>
constexpr std::size_t find_static_named_arg_id(It first, It last);
template<class... Args>
struct count_named_args{
static constexpr std::size_t value = (std::size_t{NamedArg<Args>} + ...);
};
template<class... Args>
static constexpr std::size_t count_named_args_v = count_named_args<Args...>::value;
}
}
#endif

View File

@ -1,57 +0,0 @@
/**
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_DETAIL_FORMAT_NAMED_ARGS_TPP
#define REXY_DETAIL_FORMAT_NAMED_ARGS_TPP
#include "named_args.hpp"
#include "basic_types.hpp"
#include "../../utility.hpp" //strcmp
namespace rexy::fmt::detail{
template<class It, std::size_t I, class Arg, class... Args>
constexpr std::size_t find_static_named_arg_id_impl(It first, It last){
if constexpr(StaticNamedArg<Arg>){
const auto len = last - first;
const auto arg_len = Arg::static_name.length();
if(len == arg_len){
if(!rexy::strncmp(first, Arg::static_name.c_str(), arg_len)){
return I;
}
}
}
if constexpr(sizeof...(Args) > 0){
return find_static_named_arg_id_impl<It,I+1,Args...>(first, last);
}else{
return invalid_arg_index;
}
}
template<class It, class... Args>
constexpr std::size_t find_static_named_arg_id(It first, It last){
if constexpr(sizeof...(Args) == 0){
return invalid_arg_index;
}else{
return find_static_named_arg_id_impl<It, 0, Args...>(first, last);
}
}
}
#endif

View File

@ -1,123 +0,0 @@
/**
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_DETAIL_FORMAT_OUTPUT_BUFFER_HPP
#define REXY_DETAIL_FORMAT_OUTPUT_BUFFER_HPP
#include <cstddef> //size_t
#include <cstdio> //fputc, FILE
#include <cwchar> //fputwc
#include "format_error.hpp"
namespace rexy::fmt::detail{
//polymorphic buffer of which a reference can be passed around to prevent
//passing around multiple output iterator types
template<class Char>
class format_output_buffer_base
{
public:
using value_type = Char;
protected:
static constexpr std::size_t s_bufsize = 256;
protected:
Char m_data[s_bufsize];
std::size_t m_size = 0;
std::size_t m_written_count = 0;
public:
format_output_buffer_base(void) = default;
virtual ~format_output_buffer_base(void) = default;
virtual std::size_t write_out(void) = 0;
void clear(void);
void push_back(Char c);
constexpr std::size_t count(void)const;
};
//Used with the 'formatter' to output a type
//OutIt is an iterator where the output is written at the end.
template<class Char, class OutIt>
class basic_format_output_buffer : public format_output_buffer_base<Char>
{
private:
using base = format_output_buffer_base<Char>;
private:
OutIt m_out;
public:
basic_format_output_buffer(OutIt out);
~basic_format_output_buffer(void)override;
std::size_t write_out(void)override;
constexpr OutIt out(void);
private:
std::size_t write_out_simple(void);
};
template<class Char, class OutIt>
class basic_format_output_n_buffer : public format_output_buffer_base<Char>
{
private:
using base = format_output_buffer_base<Char>;
private:
OutIt m_out;
std::size_t m_max_write = 0;
public:
basic_format_output_n_buffer(OutIt out, std::size_t max);
~basic_format_output_n_buffer(void)override;
std::size_t write_out(void)override;
constexpr OutIt out(void);
};
template<class Char>
class basic_format_size_buffer : public format_output_buffer_base<Char>
{
public:
constexpr basic_format_size_buffer(void) = default;
constexpr ~basic_format_size_buffer(void)override;
std::size_t write_out(void)override;
};
template<class Char>
struct print_format_buffer : public format_output_buffer_base<Char>
{
private:
FILE* m_stream;
public:
constexpr print_format_buffer(FILE* stream):
m_stream(stream){}
constexpr ~print_format_buffer(void){
write_out();
}
std::size_t write_out(void)override{
const auto written = fwrite(this->m_data, sizeof(this->m_data[0]), this->m_size, m_stream);
if(written != this->m_size){
REXY_THROW_FORMAT_ERROR("Failed to print data");
}
return written;
}
};
}
#endif

View File

@ -1,153 +0,0 @@
/**
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_DETAIL_FORMAT_OUTPUT_BUFFER_TPP
#define REXY_DETAIL_FORMAT_OUTPUT_BUFFER_TPP
#include "output_buffer.hpp"
#include <cstddef> //size_t
#include <utility> //move
#include <type_traits> //is_pointer, remove_cvref, integral_constant
#include <algorithm> //min
namespace rexy::fmt::detail{
///////////////////////format_output_buffer_base////////////////////////
template<class Char>
void format_output_buffer_base<Char>::clear(void){
m_size = 0;
}
template<class Char>
void format_output_buffer_base<Char>::push_back(Char c){
if(m_size == s_bufsize){
m_written_count += write_out();
clear();
}
m_data[m_size++] = c;
}
template<class Char>
constexpr std::size_t format_output_buffer_base<Char>::count(void)const{
//include data that will be written during next call to write_out
return m_written_count + m_size;
}
///////////////////////basic_format_output_buffer////////////////////////
template<class Char, class OutIt>
basic_format_output_buffer<Char,OutIt>::basic_format_output_buffer(OutIt out):
m_out(out){}
template<class Char, class OutIt>
basic_format_output_buffer<Char,OutIt>::~basic_format_output_buffer(void){
write_out();
}
template<class T>
concept HasContainerAlias = requires{
typename T::container_type;
};
template<class T, class It>
concept HasInsertMethod = requires(T t, It i){
{t.insert(1, i, i)}; //don't use 0 index because pointer conversion causes ambiguous call
{t.size()};
};
template<class It>
struct something : public It{
template<class U = something, class C = decltype(U::container)>
static std::true_type check(int);
static std::false_type check(...);
static constexpr bool value = decltype(check(0))::value;
};
template<class Char, class OutIt>
OutIt real_write_out(const Char* start, std::size_t write_count, OutIt out){
static constexpr bool has_container = something<OutIt>::value;//decltype(container_traits<OutIt>::check(0))::value;
//optimize for types where direct access to the underlying container is available
//check for a container member and a 'container_type' typedef in OutIt
//reason for the type check is that without 'container_type' it might not be safe to access the 'container'
if constexpr(has_container && HasContainerAlias<OutIt>){
struct container_access : public OutIt{
using OutIt::container;
};
container_access ca{out};
auto& container = ca.container;
if constexpr(HasInsertMethod<typename OutIt::container_type,const Char*>){
if constexpr(std::is_pointer_v<std::remove_cvref_t<decltype(container)>>){
container->insert(container->size(), start, start + write_count);
}else{
container.insert(container.size(), start, start + write_count);
}
return std::move(out);
}
}
for(std::size_t i = 0;i < write_count;++i){
out = start[i];
}
return std::move(out);
}
template<class Char, class OutIt>
std::size_t basic_format_output_buffer<Char,OutIt>::write_out(void){
m_out = real_write_out(this->m_data, this->m_size, std::move(m_out));
return this->m_size;
}
template<class Char, class OutIt>
constexpr OutIt basic_format_output_buffer<Char,OutIt>::out(void){
return std::move(m_out);
}
///////////////////////basic_format_output_n_buffer////////////////////////
template<class Char, class OutIt>
basic_format_output_n_buffer<Char,OutIt>::basic_format_output_n_buffer(OutIt out, std::size_t max):
m_out(out),
m_max_write(max){}
template<class Char, class OutIt>
basic_format_output_n_buffer<Char,OutIt>::~basic_format_output_n_buffer(void){
write_out();
}
template<class Char, class OutIt>
std::size_t basic_format_output_n_buffer<Char,OutIt>::write_out(void){
const auto to_write = std::min(this->size, m_max_write);
m_out = real_write_out(this->m_data, to_write, std::move(m_out));
m_max_write -= to_write;
return to_write;
}
template<class Char, class OutIt>
constexpr OutIt basic_format_output_n_buffer<Char,OutIt>::out(void){
return std::move(m_out);
}
/////////////////////////basic_format_size_buffer//////////////////////////
template<class Char>
constexpr basic_format_size_buffer<Char>::~basic_format_size_buffer(void){
write_out();
}
template<class Char>
std::size_t basic_format_size_buffer<Char>::write_out(void){
return this->m_size;
}
}
#endif

View File

@ -1,116 +0,0 @@
/**
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_DETAIL_FORMAT_PARSE_HPP
#define REXY_DETAIL_FORMAT_PARSE_HPP
#include "../../string_view.hpp"
#include <cstddef> //size_t
namespace rexy::fmt::detail::parse{
//A few basic string checking functions
template<class Char>
constexpr auto find_first_of_it(Char v,
typename basic_string_view<Char>::const_iterator start_it,
typename basic_string_view<Char>::const_iterator last_it);
template<class Char>
constexpr auto find_first_of_it(const basic_string_view<Char>& str, Char v);
template<class It>
constexpr bool check_duplicate_char(It start, It end);
template<class Char>
constexpr bool is_a_number(Char c);
template<class It>
constexpr int to_integer(It start, It end);
template<class It>
constexpr It find_valid_index(It start, It end);
//Used with 'visit_format_arg' as the 'fun' arg. Gets the value of the argument as a 'long long'
//if the argument is of integral type. Throws an error otherwise
struct dynamic_integer_retriever{
template<class T>
constexpr long long operator()(T&& t);
};
//Used during call to 'perform_standard_nested_replacement_field' as the 'Adapter' type.
//Calls 'on_dynamic_width' of 'specs'
template<class Specs>
struct dynamic_width_adapter{
Specs& specs;
constexpr decltype(auto) operator()(void);
constexpr decltype(auto) operator()(int i);
template<class It>
constexpr decltype(auto) operator()(It start, It last);
};
//Used during call to 'perform_standard_nested_replacement_field' as the 'Adapter' type.
//Calls 'on_dynamic_precision' of 'specs'
template<class Specs>
struct dynamic_precision_adapter{
Specs& specs;
constexpr decltype(auto) operator()(void);
constexpr decltype(auto) operator()(int i);
template<class It>
constexpr decltype(auto) operator()(It start, It last);
};
template<class Handler>
struct dynamic_index_adapter{
Handler& handler;
std::size_t& id;
constexpr decltype(auto) operator()(void);
constexpr decltype(auto) operator()(int i);
template<class It>
constexpr decltype(auto) operator()(It start, It last);
};
//Standalone functions for parsing standard format fields
template<class It, class FormatSpecs>
constexpr It perform_standard_fill_align_option(It start, It last, FormatSpecs&& specs);
template<class It, class FormatSpecs>
constexpr It perform_standard_sign_option(It start, It last, FormatSpecs&& specs);
template<class It, class FormatSpecs>
constexpr It perform_standard_alt_form_option(It start, It last, FormatSpecs&& specs);
template<class It, class FormatSpecs>
constexpr It perform_standard_zero_fill_option(It start, It last, FormatSpecs&& specs);
template<class It, class Adapter>
constexpr It perform_standard_nested_replacement_field(It start, It last, Adapter&& func);
template<class It, class FormatSpecs>
constexpr It perform_standard_width_option(It start, It last, FormatSpecs&& specs);
template<class It, class FormatSpecs>
constexpr It perform_standard_precision_option(It start, It last, FormatSpecs&& specs);
template<class It, class FormatSpecs>
constexpr It perform_standard_locale_option(It start, It last, FormatSpecs&& specs);
template<class It, class FormatSpecs>
constexpr It perform_standard_type_option(It start, It last, FormatSpecs&& specs);
template<class It, class FormatSpecs>
constexpr It perform_standard_parse(It start, It last, FormatSpecs&& specs);
//Standalone functions for parsing the entire format string
template<class Handler, class It>
constexpr It perform_format_index_spec(Handler&& handler, It start, It last, std::size_t& id);
template<class Handler, class It>
constexpr It perform_empty_format_field_parse(Handler&& handler, It start, It last);
template<class Handler, class It>
constexpr It perform_format_field_parse(Handler&& handler, It start, It last);
template<class Handler, class Char>
constexpr void perform_parse(Handler&& handler, basic_string_view<Char> fmt);
}
#endif

View File

@ -1,354 +0,0 @@
/**
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_DETAIL_FORMAT_PARSE_TPP
#define REXY_DETAIL_FORMAT_PARSE_TPP
#include "parse.hpp"
#include "format_error.hpp"
#include "../../string_view.hpp"
#include <cstddef> //size_t
#include <type_traits> //is_integral, remove_cvref, add_lvalue_reference, remove_reference
#include <algorithm> //min
namespace rexy::fmt::detail::parse{
template<class Char>
constexpr auto find_first_of_it(Char v,
typename basic_string_view<Char>::const_iterator start_it,
typename basic_string_view<Char>::const_iterator last_it)
{
const basic_string_view<Char> view{start_it, last_it};
const std::size_t range_size = last_it - start_it;
return start_it + std::min(view.find_first_of(v), range_size);
}
template<class Char>
constexpr auto find_first_of_it(const basic_string_view<Char>& str, Char v){
return find_first_of_it(v, str.begin(), str.end());
}
template<class It>
constexpr bool check_duplicate_char(It start, It end){
if(start == end){
return false;
}
const auto next = start + 1;
return *start == *next;
}
template<class Char>
constexpr bool is_a_number(Char c){
return (c >= '0' && c <= '9');
}
template<class Char>
constexpr bool is_valid_name_char(Char c){
return (c >= 'a' && c <= 'z') ||
(c >= 'A' && c <= 'Z') ||
(c == '_');
}
template<class It>
constexpr int to_integer(It start, It end){
int retval = 0;
std::size_t mul = 1;
for(auto it = end;it != start;--it){
const auto cur = it-1;
retval += ((*cur - '0') * mul);
mul *= 10;
}
return retval;
}
template<class T>
constexpr long long dynamic_integer_retriever::operator()(T&& t){
if constexpr(std::is_integral_v<std::remove_cvref_t<T>>){
return static_cast<long long>(t);
}else{
REXY_THROW_FORMAT_ERROR("Invalid dynamic specifier");
}
return {};
}
template<class Specs>
constexpr decltype(auto) dynamic_width_adapter<Specs>::operator()(void){
return specs.on_dynamic_width();
}
template<class Specs>
constexpr decltype(auto) dynamic_width_adapter<Specs>::operator()(int i){
return specs.on_dynamic_width(i);
}
template<class Specs>
template<class It>
constexpr decltype(auto) dynamic_width_adapter<Specs>::operator()(It start, It last){
return specs.on_dynamic_width(start, last);
}
template<class Specs>
constexpr decltype(auto) dynamic_precision_adapter<Specs>::operator()(void){
return specs.on_dynamic_precision();
}
template<class Specs>
constexpr decltype(auto) dynamic_precision_adapter<Specs>::operator()(int i){
return specs.on_dynamic_precision(i);
}
template<class Specs>
template<class It>
constexpr decltype(auto) dynamic_precision_adapter<Specs>::operator()(It start, It last){
return specs.on_dynamic_precision(start, last);
}
template<class Handler>
constexpr decltype(auto) dynamic_index_adapter<Handler>::operator()(void){
return (id = handler.on_arg_id());
}
template<class Handler>
constexpr decltype(auto) dynamic_index_adapter<Handler>::operator()(int i){
id = i;
return handler.on_arg_id(i);
}
template<class Handler>
template<class It>
constexpr decltype(auto) dynamic_index_adapter<Handler>::operator()(It start, It last){
id = handler.on_arg_id(start, last);
return id;
}
template<class It, class FormatSpecs>
constexpr It perform_standard_fill_align_option(It start, It last, FormatSpecs&& specs){
char fill_val = ' ';
It it = start;
for(int i = 0;i < 2 && it != last;++i){
if(*it == '>' || *it == '<' || *it == '^'){
specs.on_align(*it, fill_val);
return ++it;
}
fill_val = *it;
++it;
}
return start;
}
template<class It, class FormatSpecs>
constexpr It perform_standard_sign_option(It start, It last, FormatSpecs&& specs){
if(*start == '+' || *start == '-' || *start == ' '){
specs.on_sign(*start);
return ++start;
}
return start;
}
template<class It, class FormatSpecs>
constexpr It perform_standard_alt_form_option(It start, It last, FormatSpecs&& specs){
if(*start == '#'){
specs.on_alt_form();
++start;
}
return start;
}
template<class It, class FormatSpecs>
constexpr It perform_standard_zero_fill_option(It start, It last, FormatSpecs&& specs){
if(*start == '0'){
specs.on_zero_fill();
++start;
}
return start;
}
template<class It, class Adapter>
constexpr It perform_index_retrieve(It start, It last, Adapter&& func){
if(*start == '0'){
func(0);
return start+1;
}
if(*start == '}' || *start == ':'){
func();
return start;
}
if(!is_a_number(*start)){
auto it = start;
for(;it != last;++it){
if(!is_valid_name_char(*it)){
func(start, it);
return it;
}
}
return last;
}
for(auto it = start+1;it != last;++it){
if(!is_a_number(*it)){
func(to_integer(start, it));
return it;
}
}
return last;
}
template<class It, class Adapter>
constexpr It perform_standard_nested_replacement_field(It start, It last, Adapter&& func){
auto it = perform_index_retrieve(start, last, func);
if(*it != '}'){
REXY_THROW_FORMAT_ERROR("Dynamic index invalid spec");
}
return it+1;
}
template<class It, class FormatSpecs>
constexpr It perform_standard_width_option(It start, It last, FormatSpecs&& specs){
using specs_t = std::add_lvalue_reference_t<std::remove_reference_t<FormatSpecs>>;
if(*start == '{'){
return perform_standard_nested_replacement_field(start+1, last, dynamic_width_adapter<specs_t>{specs});
}
if(*start == '0'){
REXY_THROW_FORMAT_ERROR("Field invalid spec");
}
It it = start;
for(;is_a_number(*it) && it != last;++it){}
if(it == start){
return start;
}
specs.on_width(to_integer(start, it));
return it;
}
template<class It, class FormatSpecs>
constexpr It perform_standard_precision_option(It start, It last, FormatSpecs&& specs){
using specs_t = std::add_lvalue_reference_t<std::remove_reference_t<FormatSpecs>>;
if(*start != '.'){
return start;
}
++start;
if(*start == '{'){
return perform_standard_nested_replacement_field(start+1, last, dynamic_precision_adapter<specs_t>{specs});
}
if(*start == '0'){
REXY_THROW_FORMAT_ERROR("Field invalid spec");
}
It it = start;
for(;is_a_number(*it) && it != last;++it){}
if(it == start){
REXY_THROW_FORMAT_ERROR("Field invalid spec");
}
specs.on_precision(to_integer(start, it));
return it;
}
template<class It, class FormatSpecs>
constexpr It perform_standard_locale_option(It start, It last, FormatSpecs&& specs){
if(*start == 'L'){
specs.on_locale();
return ++start;
}
return start;
}
template<class It, class FormatSpecs>
constexpr It perform_standard_type_option(It start, It last, FormatSpecs&& specs){
if(*start == '}'){
specs.on_type_option();
return start;
}
specs.on_type_option(*start);
return ++start;
}
template<class It, class FormatSpecs>
constexpr It perform_standard_parse(It start, It last, FormatSpecs&& specs){
using specs_t = std::add_lvalue_reference_t<std::remove_reference_t<FormatSpecs>>;
using std_fmt_fn = It(*)(It,It,specs_t);
constexpr std_fmt_fn fns[] = {
perform_standard_fill_align_option<It,specs_t>,
perform_standard_sign_option<It,specs_t>,
perform_standard_alt_form_option<It,specs_t>,
perform_standard_zero_fill_option<It,specs_t>,
perform_standard_width_option<It,specs_t>,
perform_standard_precision_option<It,specs_t>,
perform_standard_locale_option<It,specs_t>,
perform_standard_type_option<It,specs_t>
};
constexpr int num_std_fns = sizeof(fns) / sizeof(fns[0]);
for(int i = 0;i < num_std_fns;++i){
start = fns[i](start, last, specs);
if(start == last){
break;
}
}
if(*start != '}'){
REXY_THROW_FORMAT_ERROR("Missing closing brace for format field");
}
return start;
}
template<class Handler, class It>
constexpr It perform_format_index_spec(Handler&& handler, It start, It last, std::size_t& id){
dynamic_index_adapter<Handler> adapter{handler, id};
const auto it = perform_index_retrieve(start, last, adapter);
if(*it != '}' && *it != ':'){
REXY_THROW_FORMAT_ERROR("Invalid index spec");
}
return it;
}
template<class Handler, class It>
constexpr It perform_format_field_parse(Handler&& handler, It start, It last){
if(start == last){
REXY_THROW_FORMAT_ERROR("Unmatched brace");
}
std::size_t index = 0;
start = perform_format_index_spec(handler, start, last, index);
if(*start == ':'){
return handler.do_format_spec(start+1, last, index);
}
handler.do_empty_format(start, last, index);
return start;
}
template<class Handler, class Char>
constexpr void perform_parse(Handler&& handler, basic_string_view<Char> fmt){
using char_type = Char;
auto work_it = fmt.begin();
auto open_brace_it = fmt.begin();
auto close_brace_it = fmt.begin();
while(true){
open_brace_it = find_first_of_it(char_type{'{'}, work_it, fmt.end());
while(true){
close_brace_it = find_first_of_it(char_type{'}'}, work_it, open_brace_it);
if(close_brace_it == open_brace_it){
//don't go past open brace because we still need to parse it
handler.do_raw(work_it, close_brace_it);
work_it = close_brace_it;
break;
}
if(check_duplicate_char(close_brace_it, fmt.end())){
++close_brace_it;
handler.do_raw(work_it, close_brace_it);
//go past closing brace to not double parse it
work_it = close_brace_it + 1;
}else{
REXY_THROW_FORMAT_ERROR("Unmatched brace");
}
}
if(open_brace_it == fmt.end()){
break;
}
if(check_duplicate_char(open_brace_it, fmt.end())){
++open_brace_it;
handler.do_raw(work_it, open_brace_it);
work_it = open_brace_it + 1;
continue;
}
work_it = perform_format_field_parse(handler, open_brace_it+1, fmt.end()) + 1;
}
return;
}
}
#endif

View File

@ -1,61 +0,0 @@
/**
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_DETAIL_FORMAT_PARSE_CONTEXT_HPP
#define REXY_DETAIL_FORMAT_PARSE_CONTEXT_HPP
#include <cstddef> //size_t
#include "../../string_view.hpp"
namespace rexy::fmt{
//Holds the format string for use in calls to 'formatter<T>::parse'
//Additionally keeps tally of the current argument index for automatic replacement indexing or
//switches to manual indexing mode when encountering a manually indexed field. Throws error on mode switch.
template<class Char>
class basic_format_parse_context
{
public:
using char_type = Char;
using iterator = typename basic_string_view<Char>::const_iterator;
using const_iterator = typename basic_string_view<Char>::const_iterator;
public:
basic_string_view<char_type> format;
std::size_t arg_count;
long long arg_index = 0;
public:
//fmt is the format specifier, num_args is 'sizeof...(Args)' where 'Args' is the parameter pack of the format call
constexpr explicit basic_format_parse_context(basic_string_view<char_type> fmt, std::size_t num_args = 0)noexcept;
basic_format_parse_context(const basic_format_parse_context&) = delete;
basic_format_parse_context(basic_format_parse_context&&) = delete;
basic_format_parse_context& operator=(basic_format_parse_context&&) = delete;
basic_format_parse_context& operator=(const basic_format_parse_context&) = delete;
constexpr const_iterator begin(void)const noexcept;
constexpr const_iterator end(void)const noexcept;
constexpr void advance_to(const_iterator it);
constexpr std::size_t next_arg_id(void);
constexpr void check_arg_id(std::size_t id);
};
}
#endif

View File

@ -1,80 +0,0 @@
/**
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_DETAIL_FORMAT_PARSE_CONTEXT_TPP
#define REXY_DETAIL_FORMAT_PARSE_CONTEXT_TPP
#include "../../compat/if_consteval.hpp"
#include "parse_context.hpp"
#include "format_error.hpp"
#include <cstddef> //size_t
namespace rexy::fmt{
/////////////////////////////basic_format_parse_context//////////////////////////////
template<class Char>
constexpr basic_format_parse_context<Char>::basic_format_parse_context(basic_string_view<char_type> fmt, std::size_t num_args)noexcept:
format(fmt),
arg_count(num_args){}
template<class Char>
constexpr auto basic_format_parse_context<Char>::begin(void)const noexcept -> const_iterator{
return format.begin();
}
template<class Char>
constexpr auto basic_format_parse_context<Char>::end(void)const noexcept -> const_iterator{
return format.end();
}
template<class Char>
constexpr void basic_format_parse_context<Char>::advance_to(const_iterator it){
const std::size_t diff = it - format.begin();
format.remove_prefix(diff);
}
template<class Char>
constexpr std::size_t basic_format_parse_context<Char>::next_arg_id(void){
if(arg_index < 0){
//error, already in manual mode
REXY_THROW_FORMAT_ERROR("Invalid indexing mode switch");
}
REXY_if_consteval{
if(arg_index >= arg_count){
REXY_THROW_FORMAT_ERROR("Missing argument");
}
}
return arg_index++;
}
template<class Char>
constexpr void basic_format_parse_context<Char>::check_arg_id(std::size_t id){
if(arg_index > 0){
//error, already in automatic mode
REXY_THROW_FORMAT_ERROR("Invalid indexing mode switch");
}
REXY_if_consteval{
if(id >= arg_count){
REXY_THROW_FORMAT_ERROR("Missing argument");
}
}
arg_index = detail::invalid_arg_index;
}
}
#endif

View File

@ -1,129 +0,0 @@
/**
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_DETAIL_FORMAT_SPECS_HANDLER_HPP
#define REXY_DETAIL_FORMAT_SPECS_HANDLER_HPP
#include "../../compat/if_consteval.hpp"
namespace rexy::fmt::detail{
//Stores standard format-spec information to use later in formatting
template<class ParseCtx, class FmtCtx>
struct format_specs_handler{
ParseCtx& parse_ctx;
FmtCtx& fmt_ctx;
format_specs& specs;
constexpr format_specs_handler(ParseCtx& pc, FmtCtx& fc, format_specs& s):
parse_ctx(pc),
fmt_ctx(fc),
specs(s){}
constexpr void on_width(int w);
constexpr void on_dynamic_width(void);
constexpr void on_dynamic_width(int index);
template<class It>
constexpr void on_dynamic_width(It first, It last);
constexpr void on_precision(int p);
constexpr void on_dynamic_precision(void);
constexpr void on_dynamic_precision(int index);
template<class It>
constexpr void on_dynamic_precision(It first, It last);
constexpr void on_locale(void);
constexpr void on_type_option(int o = 0);
constexpr void on_align(int al, int val);
constexpr void on_sign(int s);
constexpr void on_alt_form(void);
constexpr void on_zero_fill(void);
};
//Stores standard format-spec information to use later in formatting. Stores indices of dynamic specifiers
//to be retrieved later.
template<class ParseCtx>
struct dynamic_format_specs_handler{
using specs_type = dynamic_format_specs<typename ParseCtx::char_type>;
ParseCtx& parse_ctx;
specs_type& specs;
constexpr dynamic_format_specs_handler(ParseCtx& pc, specs_type& s);
constexpr void on_width(int w);
constexpr void on_dynamic_width(void);
constexpr void on_dynamic_width(int index);
template<class It>
constexpr void on_dynamic_width(It first, It last);
constexpr void on_precision(int p);
constexpr void on_dynamic_precision(void);
constexpr void on_dynamic_precision(int index);
template<class It>
constexpr void on_dynamic_precision(It first, It last);
constexpr void on_locale(void);
constexpr void on_type_option(int o = 0);
constexpr void on_align(int al, int val);
constexpr void on_sign(int s);
constexpr void on_alt_form(void);
constexpr void on_zero_fill(void);
};
//Used during compile time only. Checks dynamic specifiers given types pack as template parameters.
template<class ParseCtx, class... Args>
struct cx_format_specs_handler : public dynamic_format_specs_handler<ParseCtx>{
using char_type = typename ParseCtx::char_type;
constexpr cx_format_specs_handler(ParseCtx& pc, dynamic_format_specs<char_type>& s):
dynamic_format_specs_handler<ParseCtx>(pc, s){
REXY_if_not_consteval{
throw 0;
}
}
constexpr void on_dynamic_width(void);
constexpr void on_dynamic_width(int index);
template<class It>
constexpr void on_dynamic_width(It first, It last);
constexpr void on_dynamic_precision(void);
constexpr void on_dynamic_precision(int index);
template<class It>
constexpr void on_dynamic_precision(It first, It last);
};
template<class Handler>
struct format_specs_checker : public Handler{
storage_type arg_type = storage_type::none_t;
bool requires_arithmetic_presentation = false;
constexpr format_specs_checker(const Handler& h, storage_type type);
constexpr void on_precision(int p);
constexpr void on_dynamic_precision(void);
constexpr void on_dynamic_precision(int index);
template<class It>
constexpr void on_dynamic_precision(It first, It last);
constexpr void on_locale(void);
constexpr void on_sign(int s);
constexpr void on_alt_form(void);
constexpr void on_zero_fill(void);
constexpr void on_type_option(int type = 0);
private:
constexpr void check_precision(void);
constexpr void check_arithmetic_type(void);
};
}
#endif

View File

@ -1,389 +0,0 @@
/**
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_DETAIL_FORMAT_SPECS_HANDLER_TPP
#define REXY_DETAIL_FORMAT_SPECS_HANDLER_TPP
#include "specs_handler.hpp"
#include "format_error.hpp"
#include "traits.hpp"
#include "named_args.hpp"
#include "format_args.hpp"
#include "internal_types.hpp"
#include "standard_types.hpp"
#include "basic_types.hpp"
namespace rexy::fmt::detail{
/////////////////////////////format_specs_handler//////////////////////////////
template<class ParseCtx, class FmtCtx>
constexpr void format_specs_handler<ParseCtx,FmtCtx>::on_width(int w){
specs.width = w;
}
template<class ParseCtx, class FmtCtx>
constexpr void format_specs_handler<ParseCtx,FmtCtx>::on_dynamic_width(void){
const auto index = parse_ctx.next_arg_id();
const auto arg = fmt_ctx.arg(index);
const auto value = visit_format_arg(parse::dynamic_integer_retriever{}, arg);
on_width(value);
}
template<class ParseCtx, class FmtCtx>
constexpr void format_specs_handler<ParseCtx,FmtCtx>::on_dynamic_width(int index){
parse_ctx.check_arg_id(index);
const auto arg = fmt_ctx.arg(index);
const auto value = visit_format_arg(parse::dynamic_integer_retriever{}, arg);
on_width(value);
}
template<class ParseCtx, class FmtCtx>
template<class It>
constexpr void format_specs_handler<ParseCtx,FmtCtx>::on_dynamic_width(It first, It last){
on_dynamic_width(fmt_ctx.arg_index(first, last));
}
template<class ParseCtx, class FmtCtx>
constexpr void format_specs_handler<ParseCtx,FmtCtx>::on_precision(int p){
specs.precision = p;
}
template<class ParseCtx, class FmtCtx>
constexpr void format_specs_handler<ParseCtx,FmtCtx>::on_dynamic_precision(void){
const auto index = parse_ctx.next_arg_id();
const auto arg = fmt_ctx.arg(index);
const auto value = visit_format_arg(parse::dynamic_integer_retriever{}, arg);
on_precision(value);
}
template<class ParseCtx, class FmtCtx>
constexpr void format_specs_handler<ParseCtx,FmtCtx>::on_dynamic_precision(int index){
parse_ctx.check_arg_id(index);
const auto arg = fmt_ctx.arg(index);
const auto value = visit_format_arg(parse::dynamic_integer_retriever{}, arg);
on_precision(value);
}
template<class ParseCtx, class FmtCtx>
template<class It>
constexpr void format_specs_handler<ParseCtx,FmtCtx>::on_dynamic_precision(It first, It last){
on_dynamic_precision(fmt_ctx.arg_index(first, last));
}
template<class ParseCtx, class FmtCtx>
constexpr void format_specs_handler<ParseCtx,FmtCtx>::on_locale(void){
specs.locale = true;
}
template<class ParseCtx, class FmtCtx>
constexpr void format_specs_handler<ParseCtx,FmtCtx>::on_type_option(int o){
specs.type = o;
}
template<class ParseCtx, class FmtCtx>
constexpr void format_specs_handler<ParseCtx,FmtCtx>::on_align(int al, int val){
specs.align = static_cast<alignment>(al);
specs.align_char = val;
}
template<class ParseCtx, class FmtCtx>
constexpr void format_specs_handler<ParseCtx,FmtCtx>::on_sign(int s){
specs.sign = s;
}
template<class ParseCtx, class FmtCtx>
constexpr void format_specs_handler<ParseCtx,FmtCtx>::on_alt_form(void){
specs.alt_form = true;
}
template<class ParseCtx, class FmtCtx>
constexpr void format_specs_handler<ParseCtx,FmtCtx>::on_zero_fill(void){
specs.zero_fill = true;
}
/////////////////////////////dynamic_format_specs_handler//////////////////////////////
template<class ParseCtx>
constexpr dynamic_format_specs_handler<ParseCtx>::dynamic_format_specs_handler(ParseCtx& pc, dynamic_format_specs<typename ParseCtx::char_type>& s):
parse_ctx(pc),
specs(s){}
template<class ParseCtx>
constexpr void dynamic_format_specs_handler<ParseCtx>::on_width(int w){
specs.width = w;
}
template<class ParseCtx>
constexpr void dynamic_format_specs_handler<ParseCtx>::on_dynamic_width(void){
specs.dyn_width.index = parse_ctx.next_arg_id();
}
template<class ParseCtx>
constexpr void dynamic_format_specs_handler<ParseCtx>::on_dynamic_width(int index){
parse_ctx.check_arg_id(index);
specs.dyn_width.index = index;
}
template<class ParseCtx>
template<class It>
constexpr void dynamic_format_specs_handler<ParseCtx>::on_dynamic_width(It first, It last){
parse_ctx.check_arg_id(0); //just to make sure we aren't switching indexing modes
specs.dyn_width.name = {first, std::size_t(last - first)};
specs.width_type = dyn_type::string;
}
template<class ParseCtx>
constexpr void dynamic_format_specs_handler<ParseCtx>::on_precision(int p){
specs.precision = p;
}
template<class ParseCtx>
constexpr void dynamic_format_specs_handler<ParseCtx>::on_dynamic_precision(void){
specs.dyn_precision.index = parse_ctx.next_arg_id();
}
template<class ParseCtx>
constexpr void dynamic_format_specs_handler<ParseCtx>::on_dynamic_precision(int index){
parse_ctx.check_arg_id(index);
specs.dyn_precision.index = index;
}
template<class ParseCtx>
template<class It>
constexpr void dynamic_format_specs_handler<ParseCtx>::on_dynamic_precision(It first, It last){
parse_ctx.check_arg_id(0); //just to make sure we aren't switching indexing modes
specs.dyn_precision.name = {first, std::size_t(last - first)};
specs.precision_type = dyn_type::string;
}
template<class ParseCtx>
constexpr void dynamic_format_specs_handler<ParseCtx>::on_locale(void){
specs.locale = true;
}
template<class ParseCtx>
constexpr void dynamic_format_specs_handler<ParseCtx>::on_type_option(int o){
specs.type = o;
}
template<class ParseCtx>
constexpr void dynamic_format_specs_handler<ParseCtx>::on_align(int al, int val){
specs.align = static_cast<alignment>(al);
specs.align_char = val;
}
template<class ParseCtx>
constexpr void dynamic_format_specs_handler<ParseCtx>::on_sign(int s){
specs.sign = s;
}
template<class ParseCtx>
constexpr void dynamic_format_specs_handler<ParseCtx>::on_alt_form(void){
specs.alt_form = true;
}
template<class ParseCtx>
constexpr void dynamic_format_specs_handler<ParseCtx>::on_zero_fill(void){
specs.zero_fill = true;
}
/////////////////////////////cx_format_specs_handler//////////////////////////////
template<class ParseCtx, class... Args>
constexpr void cx_format_specs_handler<ParseCtx,Args...>::on_dynamic_width(void){
const auto index = this->parse_ctx.next_arg_id();
if(!is_dynamic_integer<char_type,Args...>(index)){
REXY_THROW_FORMAT_ERROR("Invalid type given as dynamic width");
}
}
template<class ParseCtx, class... Args>
constexpr void cx_format_specs_handler<ParseCtx,Args...>::on_dynamic_width(int index){
this->parse_ctx.check_arg_id(index);
if(!is_dynamic_integer<char_type,Args...>(index)){
REXY_THROW_FORMAT_ERROR("Invalid type given as dynamic width");
}
}
template<class ParseCtx, class... Args>
template<class It>
constexpr void cx_format_specs_handler<ParseCtx,Args...>::on_dynamic_width(It first, It last){
on_dynamic_width(find_static_named_arg_id<It,Args...>(first, last));
}
template<class ParseCtx, class... Args>
constexpr void cx_format_specs_handler<ParseCtx,Args...>::on_dynamic_precision(void){
const auto index = this->parse_ctx.next_arg_id();
if(!is_dynamic_integer<char_type,Args...>(index)){
REXY_THROW_FORMAT_ERROR("Invalid type given as dynamic precision");
}
}
template<class ParseCtx, class... Args>
constexpr void cx_format_specs_handler<ParseCtx,Args...>::on_dynamic_precision(int index){
this->parse_ctx.check_arg_id(index);
if(!is_dynamic_integer<char_type,Args...>(index)){
REXY_THROW_FORMAT_ERROR("Invalid type given as dynamic precision");
}
}
template<class ParseCtx, class... Args>
template<class It>
constexpr void cx_format_specs_handler<ParseCtx,Args...>::on_dynamic_precision(It first, It last){
on_dynamic_precision(find_static_named_arg_id<It,Args...>(first, last));
}
/////////////////////////////format_specs_checker//////////////////////////////
template<class Handler>
constexpr format_specs_checker<Handler>::format_specs_checker(const Handler& h, storage_type type):
Handler(h), arg_type(type){}
template<class Handler>
constexpr void format_specs_checker<Handler>::on_precision(int p){
check_precision();
Handler::on_precision(p);
}
template<class Handler>
constexpr void format_specs_checker<Handler>::on_dynamic_precision(void){
check_precision();
Handler::on_dynamic_precision();
}
template<class Handler>
constexpr void format_specs_checker<Handler>::on_dynamic_precision(int index){
check_precision();
Handler::on_dynamic_precision(index);
}
template<class Handler>
template<class It>
constexpr void format_specs_checker<Handler>::on_dynamic_precision(It first, It last){
check_precision();
Handler::on_dynamic_precision(first, last);
}
template<class Handler>
constexpr void format_specs_checker<Handler>::on_locale(void){
check_arithmetic_type();
Handler::on_locale();
}
template<class Handler>
constexpr void format_specs_checker<Handler>::on_sign(int s){
requires_arithmetic_presentation = true;
check_arithmetic_type();
Handler::on_sign(s);
}
template<class Handler>
constexpr void format_specs_checker<Handler>::on_alt_form(void){
requires_arithmetic_presentation = true;
check_arithmetic_type();
Handler::on_alt_form();
}
template<class Handler>
constexpr void format_specs_checker<Handler>::on_zero_fill(void){
requires_arithmetic_presentation = true;
check_arithmetic_type();
Handler::on_zero_fill();
}
template<class Handler>
constexpr void format_specs_checker<Handler>::on_type_option(int type){
presentation& chosen = this->specs.present;
switch(type){
case 0:
break;
case 'b':
case 'B':
case 'd':
case 'o':
case 'x':
case 'X':
chosen = presentation::int_t;
break;
case 'c':
chosen = presentation::char_t;
break;
case 's':
chosen = presentation::string_t;
break;
case 'a':
case 'A':
case 'e':
case 'E':
case 'f':
case 'F':
case 'g':
case 'G':
chosen = presentation::float_t;
break;
case 'p':
chosen = presentation::ptr_t;
break;
default:
REXY_THROW_FORMAT_ERROR("Invalid type specifier");
};
switch(arg_type){
case storage_type::none_t:
REXY_THROW_FORMAT_ERROR("Invalid argument");
break;
case storage_type::bool_t:
if(chosen == presentation::default_t){
chosen = presentation::string_t;
}
if(chosen != presentation::string_t && chosen != presentation::int_t && chosen != presentation::char_t){
REXY_THROW_FORMAT_ERROR("Invalid type specifier for bool");
}
break;
case storage_type::char_t:
if(chosen == presentation::default_t){
chosen = presentation::char_t;
}
if(chosen != presentation::char_t && chosen != presentation::int_t){
REXY_THROW_FORMAT_ERROR("Invalid type specifier for character");
}
break;
case storage_type::int_t:
case storage_type::uint_t:
case storage_type::long_long_t:
case storage_type::ulong_long_t:
if(chosen == presentation::default_t){
chosen = presentation::int_t;
}
if(chosen != presentation::int_t && chosen != presentation::char_t){
REXY_THROW_FORMAT_ERROR("Invalid type specifier for integral");
}
break;
case storage_type::float_t:
case storage_type::double_t:
case storage_type::long_double_t:
if(chosen == presentation::default_t){
chosen = presentation::float_t;
}
if(chosen != presentation::float_t){
REXY_THROW_FORMAT_ERROR("Invalid type specifier for floating point");
}
break;
case storage_type::char_ptr_t:
case storage_type::string_t:
if(chosen == presentation::default_t){
chosen = presentation::string_t;
}
if(chosen != presentation::string_t){
REXY_THROW_FORMAT_ERROR("Invalid type specifier for string");
}
break;
case storage_type::ptr_t:
if(chosen == presentation::default_t){
chosen = presentation::ptr_t;
}
if(chosen != presentation::ptr_t){
REXY_THROW_FORMAT_ERROR("Invalid type specifier for pointer");
}
break;
case storage_type::custom_t:
break;
};
if(requires_arithmetic_presentation && chosen != presentation::int_t && chosen != presentation::float_t){
REXY_THROW_FORMAT_ERROR("Invalid type specifier with modifier requiring integer presentation");
}
return Handler::on_type_option(type);
}
template<class Handler>
constexpr void format_specs_checker<Handler>::check_precision(void){
if(!(is_floating_storage_type(arg_type) || is_string_storage_type(arg_type))){
REXY_THROW_FORMAT_ERROR("Precision not valid for argument type");
}
}
template<class Handler>
constexpr void format_specs_checker<Handler>::check_arithmetic_type(void){
if(!is_integral_storage_type(arg_type)){
REXY_THROW_FORMAT_ERROR("Formatting argument requires an arithmetic type");
}
}
}
#endif

View File

@ -1,38 +0,0 @@
/**
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_DETAIL_FORMAT_STANDARD_TYPES_HPP
#define REXY_DETAIL_FORMAT_STANDARD_TYPES_HPP
namespace rexy::fmt{
template<class T, class Char>
class formatter;
template<class Context>
class basic_format_arg;
template<class Context>
class basic_format_args;
template<class OutIt, class Char>
class basic_format_context;
template<class Char>
class basic_format_parse_context;
struct format_error;
}
#endif

View File

@ -1,71 +0,0 @@
/**
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_DETAIL_FORMAT_STORAGE_HPP
#define REXY_DETAIL_FORMAT_STORAGE_HPP
#include <type_traits> //declval
namespace rexy::fmt::detail{
//tag for use in basic_format_arg(s)
enum class storage_type{
none_t = 0,
bool_t,
char_t,
int_t,
uint_t,
long_long_t,
ulong_long_t,
float_t,
double_t,
long_double_t,
char_ptr_t,
string_t,
ptr_t,
custom_t
};
constexpr bool is_integral_storage_type(storage_type t){
return t >= storage_type::bool_t && t <= storage_type::long_double_t;
}
constexpr bool is_integer_storage_type(storage_type t){
return t >= storage_type::bool_t && t <= storage_type::ulong_long_t;
}
constexpr bool is_floating_storage_type(storage_type t){
return t >= storage_type::float_t && t <= storage_type::long_double_t;
}
constexpr bool is_string_storage_type(storage_type t){
return t == storage_type::string_t || t == storage_type::char_ptr_t;
}
//Mappings from actual type to the type used in basic_format_arg(s)
template<class T, class Char>
consteval storage_type map_to_storage_enum(void);
template<class T, class Char>
static constexpr storage_type map_to_storage_enum_v = map_to_storage_enum<T,Char>();
template<class Context>
struct map_to_stored_type_helper;
template<class T, class Context>
using stored_type_t = decltype(std::declval<map_to_stored_type_helper<Context>>()(std::declval<T>()));
}
#endif

View File

@ -1,114 +0,0 @@
/**
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_DETAIL_FORMAT_STORAGE_TPP
#define REXY_DETAIL_FORMAT_STORAGE_TPP
#include "storage.hpp"
#include "named_args.hpp"
#include <type_traits> //is_pointer, remove_cvref, remove_pointer, is_same, is_arithmetic
#include <concepts> //unsigned_integral, signed_integral, floating_point
#include <cstddef> //nullptr_t
namespace rexy::fmt::detail{
///////////////////////storage type mapping////////////////////////
template<class T, class Char>
consteval storage_type map_to_storage_enum(void){
using type = std::remove_cvref_t<T>;
if constexpr(std::is_pointer_v<type>){
if constexpr(std::is_same_v<std::remove_cv_t<std::remove_pointer_t<type>>,Char>){
return storage_type::char_ptr_t;
}else{
return storage_type::ptr_t;
}
}else if constexpr(std::is_same_v<type,basic_string_view<Char>>){
return storage_type::string_t;
}else if constexpr(std::is_same_v<type,Char>){
return storage_type::char_t;
}else if constexpr(std::is_same_v<type,std::monostate>){
return storage_type::none_t;
}else if constexpr(std::is_same_v<type,bool>){
return storage_type::bool_t;
}else if constexpr(std::is_same_v<type,int>){
return storage_type::int_t;
}else if constexpr(std::is_same_v<type,unsigned int>){
return storage_type::uint_t;
}else if constexpr(std::is_same_v<type,long long>){
return storage_type::long_long_t;
}else if constexpr(std::is_same_v<type,unsigned long long>){
return storage_type::ulong_long_t;
}else if constexpr(std::is_same_v<type,float>){
return storage_type::float_t;
}else if constexpr(std::is_same_v<type,double>){
return storage_type::double_t;
}else if constexpr(std::is_same_v<type,long double>){
return storage_type::long_double_t;
}else{
return storage_type::custom_t;
}
}
template<class Context>
struct map_to_stored_type_helper{
using char_type = typename Context::char_type;
template<class T>
requires (!std::is_arithmetic_v<T> && !std::is_pointer_v<T> && !NamedArg<T>)
constexpr auto operator()(T) -> typename basic_format_arg<Context>::handle;
constexpr auto operator()(std::unsigned_integral auto i){
if constexpr(sizeof(i) <= sizeof(unsigned int)){
using uint = unsigned int;
return uint{};
}else{
using ull = unsigned long long;
return ull{};
}
}
constexpr auto operator()(std::signed_integral auto i){
if constexpr(sizeof(i) <= sizeof(int)){
return int{};
}else{
using ll = long long;
return ll{};
}
}
template<std::floating_point T>
constexpr auto operator()(T) -> std::remove_cvref_t<T>;
constexpr auto operator()(char_type) -> char_type;
constexpr auto operator()(bool) -> bool;
constexpr auto operator()(float) -> float;
constexpr auto operator()(double) -> double;
constexpr auto operator()(long double) -> long double;
constexpr auto operator()(const char_type*) -> const char_type*;
constexpr auto operator()(basic_string_view<char_type>) -> basic_string_view<char_type>;
constexpr auto operator()(basic_string<char_type>) -> basic_string_view<char_type>;
template<class T>
requires (!std::is_same_v<std::remove_cvref_t<T>,char_type>)
constexpr auto operator()(T*) -> const void*;
constexpr auto operator()(std::nullptr_t) -> const void*;
template<NamedArg T>
constexpr auto operator()(T t) -> decltype((*this)(t.value));
};
}
#endif

View File

@ -1,150 +0,0 @@
/**
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_DETAIL_FORMAT_TRAITS_HPP
#define REXY_DETAIL_FORMAT_TRAITS_HPP
#include <type_traits> //is_convertible, is_floating_point, integral_constant, is_same, remove_cvref
#include <concepts> //integral, floating_point
#include <cstddef> //size_t
#include "standard_types.hpp"
#include "storage.hpp"
#include "named_args.hpp"
#include "internal_types.hpp"
namespace rexy::fmt::detail{
template<class T>
struct valid_dynamic_index{
static constexpr bool value = std::is_convertible_v<T,long long> && !std::is_floating_point_v<T>;
};
template<class T>
static constexpr bool valid_dynamic_index_v = valid_dynamic_index<T>::value;
template<class T>
struct is_char_or_bool : public std::false_type{};
template<>
struct is_char_or_bool<bool> : public std::true_type{};
template<>
struct is_char_or_bool<char> : public std::true_type{};
template<>
struct is_char_or_bool<wchar_t> : public std::true_type{};
template<class T>
concept Integral = std::integral<T> && !is_char_or_bool<T>::value;
template<class T>
concept Bool = std::is_same_v<std::remove_cvref_t<T>,bool>;
template<class T>
concept Floating = std::floating_point<T>;
template<class T>
concept Arithmetic = Integral<T> || Floating<T>;
template<class T, class FmtCtx>
concept Handle = std::is_same_v<std::remove_cvref_t<T>,typename basic_format_arg<FmtCtx>::handle>;
template<class T>
concept Formatter_Char = std::is_same_v<T,char> || std::is_same_v<T,wchar_t>;
template<class T, class FmtCtx>
struct is_handle{
static constexpr bool value = Handle<T,FmtCtx>;
};
template<class T, class FmtCtx>
static constexpr bool is_handle_v = is_handle<T,FmtCtx>::value;
template<class T>
struct extract_char_type_from_context;
template<class OutIt, class Char>
struct extract_char_type_from_context<basic_format_context<OutIt,Char>>{
using type = Char;
};
template<class T>
using extract_char_type_from_context_t = typename extract_char_type_from_context<T>::type;
template<std::size_t I, class FmtCtx, class Arg, class... Args>
constexpr bool is_dynamic_integer_impl(int index){
using mapped_t = stored_type_t<Arg,FmtCtx>;
if(index == I){
return valid_dynamic_index_v<mapped_t>;
}
if constexpr(sizeof...(Args) > 0){
return is_dynamic_integer_impl<I+1,FmtCtx,Args...>(index);
}
return false;
}
template<class Char, class... Args>
constexpr bool is_dynamic_integer(int index){
using fmt_ctx_t = fmt_context_t<Char>;
if constexpr(sizeof...(Args) == 0){
return false;
}else{
return is_dynamic_integer_impl<0,fmt_ctx_t,Args...>(index);
}
}
static constexpr unsigned char utf_test[] = "\u00B5";
constexpr bool is_utf8(void){
using uchar = unsigned char;
return (sizeof(utf_test) == 3) && (uchar(utf_test[0]) == 0xC2) && (uchar(utf_test[1]) == 0xB5);
}
static constexpr wchar_t wutf_test[] = L"\U00010437";
constexpr bool is_wutf16(void){
return (sizeof(wutf_test) / sizeof(wutf_test[0]) == 3 &&
wutf_test[0] == 0xD801 && wutf_test[1] == 0xDC37);
}
constexpr bool is_wutf32(void){
return (sizeof(wutf_test) / sizeof(wutf_test[0]) == 2 &&
wutf_test[0] == 0x00010437);
}
template<class T>
struct is_utf8_encoded_string_type : public std::false_type{};
template<>
struct is_utf8_encoded_string_type<char8_t> : public std::true_type{};
template<>
struct is_utf8_encoded_string_type<char> : public std::integral_constant<bool,is_utf8()>{};
template<class T>
static constexpr bool is_utf8_encoded_string_type_v = is_utf8_encoded_string_type<T>::value;
template<class T>
struct is_utf16_encoded_string_type : public std::false_type{};
template<>
struct is_utf16_encoded_string_type<char16_t> : public std::true_type{};
template<>
struct is_utf16_encoded_string_type<wchar_t> : public std::integral_constant<bool,is_wutf16()>{};
template<class T>
static constexpr bool is_utf16_encoded_string_type_v = is_utf16_encoded_string_type<T>::value;
template<class T>
struct is_utf32_encoded_string_type : public std::false_type{};
template<>
struct is_utf32_encoded_string_type<char32_t> : public std::true_type{};
template<>
struct is_utf32_encoded_string_type<wchar_t> : public std::integral_constant<bool,is_wutf32()>{};
template<class T>
static constexpr bool is_utf32_encoded_string_type_v = is_utf32_encoded_string_type<T>::value;
template<class T>
concept UTF8_String = is_utf8_encoded_string_type_v<T>;
template<class T>
concept UTF16_String = is_utf16_encoded_string_type_v<T>;
template<class T>
concept UTF32_String = is_utf32_encoded_string_type_v<T>;
}
#endif

View File

@ -1,91 +0,0 @@
/**
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_DETAIL_FORMAT_UTF_ITERATOR_TPP
#define REXY_DETAIL_FORMAT_UTF_ITERATOR_TPP
namespace rexy::fmt::detail{
template<class Char>
class utf8_iterator
{
private:
const Char* m_start = nullptr;
const Char* m_last = nullptr;
const Char* m_next = nullptr;
char32_t m_value = 0;
public:
constexpr utf8_iterator(const Char* first, const Char* last):
m_start(first), m_last(last)
{
m_next = convert_codepoint(m_start, m_last, m_value);
}
constexpr utf8_iterator& operator++(void){
m_start = m_next;
if(m_start != m_last){
m_next = convert_codepoint(m_start, m_last, m_value);
}
return *this;
}
constexpr utf8_iterator operator++(int){
auto tmp = *this;
++(*this);
return tmp;
}
constexpr char32_t operator*(void)const{
return m_value;
}
constexpr bool valid(void)const{
return m_start != m_last;
}
constexpr std::size_t byte_count(void)const{
return m_next - m_start;
}
private:
static constexpr const Char* convert_codepoint(const Char* first, const Char* last, char32_t& codepoint){
const std::size_t maxlen = last - first;
if((*first & 0x80) == 0){
codepoint = first[0];
return first + 1;
}else if((*first & 0xE0) == 0xC0 && maxlen > 1){
codepoint = ((first[0] & 0x1F) << 6);
codepoint += (first[1] & 0x3F);
return first + 2;
}else if((*first & 0xF0) == 0xE0 && maxlen > 2){
codepoint = ((first[0] & 0x0F) << 12);
codepoint += ((first[1] & 0x3F) << 6);
codepoint += (first[2] & 0x3F);
return first + 3;
}else if((*first & 0xF8) == 0xF0 && maxlen > 3){
codepoint = ((first[0] & 0x07) << 18);
codepoint += ((first[1] & 0x3F) << 12);
codepoint += (first[2] & 0x3F) << 6;
codepoint += (first[3] & 0x3F);
return first + 4;
}
codepoint = 0;
return first;
}
};
}
#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-2022 rexy712 Copyright (C) 2020 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,24 +19,28 @@
#ifndef REXY_DETAIL_HASALLOCATOR_HPP #ifndef REXY_DETAIL_HASALLOCATOR_HPP
#define REXY_DETAIL_HASALLOCATOR_HPP #define REXY_DETAIL_HASALLOCATOR_HPP
#include "../compat/standard.hpp"
#include "../allocator.hpp"
#include <type_traits> //is_empty
namespace rexy::detail{ namespace rexy::detail{
template<REXY_ALLOCATOR_CONCEPT Alloc> template<class Alloc>
struct hasallocator : public Alloc{ struct hasallocator
using Alloc::Alloc; {
using Alloc::operator=; Alloc m_alloc;
constexpr Alloc& allocator(void){
return *this; auto allocate(typename Alloc::size_type bytes)noexcept(noexcept(m_alloc.allocate(0))){
return m_alloc.allocate(bytes);
} }
constexpr const Alloc& allocator(void)const{ void deallocate(typename Alloc::pointer p, typename Alloc::size_type bytes)noexcept(noexcept(m_alloc.deallocate(nullptr,0))){
return *this; m_alloc.deallocate(p, bytes);
}
Alloc& allocator(void){
return m_alloc;
}
const Alloc& allocator(void)const{
return m_alloc;
} }
}; };
} }
#endif #endif

View File

@ -20,8 +20,8 @@
#define REXY_STRING_APPENDER_HPP #define REXY_STRING_APPENDER_HPP
#include "../expression.hpp" #include "../expression.hpp"
#include "../traits.hpp"
#include <utility> //forward #include <utility> //forward
#include <type_traits> //enable_if, declval
namespace rexy::detail{ namespace rexy::detail{
@ -49,7 +49,12 @@ namespace rexy::detail{
template<class Val, std::enable_if_t<!rexy::is_template_derived_type<Val,binary_expression>::value,int> = 0, class = decltype(std::declval<Val>().length())> template<class Val, std::enable_if_t<!rexy::is_template_derived_type<Val,binary_expression>::value,int> = 0, class = decltype(std::declval<Val>().length())>
constexpr void operator()(Val&& v) constexpr void operator()(Val&& v)
{ {
m_targ.append(std::forward<Val>(v).data(), std::forward<Val>(v).length()); m_targ.append(std::forward<Val>(v).get(), std::forward<Val>(v).length());
}
template<class Val, std::enable_if_t<!rexy::is_template_derived_type<Val,binary_expression>::value,void*> = nullptr, class = decltype(std::declval<Val>().size())>
constexpr void operator()(Val&& v)
{
m_targ.append(std::forward<Val>(v).get(), std::forward<Val>(v).size());
} }
}; };

View File

@ -21,15 +21,6 @@
#ifndef REXY_ENUM_CLASS_HPP #ifndef REXY_ENUM_CLASS_HPP
#define REXY_ENUM_CLASS_HPP #define REXY_ENUM_CLASS_HPP
#include "compat/standard.hpp"
#ifndef __cpp_concepts
#error "Cannot use enum_traits without C++20 concept support"
#else //__cpp_concepts
#include <type_traits> #include <type_traits>
template<class T> template<class T>
@ -210,6 +201,4 @@ namespace rexy::enum_traits{
} }
} }
#endif //__cpp_concepts
#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> //is_void, remove_reference, conditional #include <type_traits>
#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_void_v<L>, "Left value of rexy::binary_expression cannot be void!"); 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<R>, "Right 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!");
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_void_v<L>, "Value of rexy::unary_expression cannot be void!"); static_assert(!std::is_same<std::decay_t<L>,void>::value, "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

@ -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-2022 rexy712 Copyright (C) 2020 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,8 +19,6 @@
#ifndef REXY_FILERD_HPP #ifndef REXY_FILERD_HPP
#define REXY_FILERD_HPP #define REXY_FILERD_HPP
#ifndef LIBREXY_HEADER_ONLY
#include <cstdio> //FILE #include <cstdio> //FILE
#include <cstddef> //size_t #include <cstddef> //size_t
#include <type_traits> //is_nothrow_constructible #include <type_traits> //is_nothrow_constructible
@ -30,7 +28,6 @@
#include "utility.hpp" #include "utility.hpp"
#include "rexy.hpp" #include "rexy.hpp"
#include "buffer.hpp" #include "buffer.hpp"
#include "string_view.hpp"
namespace rexy{ namespace rexy{
@ -38,8 +35,7 @@ namespace rexy{
class filerd class filerd
{ {
private: private:
std::FILE* m_fp = nullptr; FILE* m_fp = nullptr;
bool m_finished = false;
public: public:
constexpr filerd(void)noexcept = default; constexpr filerd(void)noexcept = default;
@ -50,39 +46,31 @@ namespace rexy{
~filerd(void)noexcept; ~filerd(void)noexcept;
filerd& operator=(const filerd&) = delete; filerd& operator=(const filerd&) = delete;
constexpr filerd& operator=(filerd&& f)noexcept{ constexpr filerd& operator=(filerd&& f)noexcept{
rexy::swap(m_fp, f.m_fp); swap(m_fp, f.m_fp);
return *this; return *this;
} }
void reset(std::FILE* fp = nullptr)noexcept; void reset(FILE* fp = nullptr)noexcept;
std::FILE* release(void)noexcept; FILE* release(void)noexcept;
std::size_t length(void)noexcept; size_t length(void)noexcept;
std::size_t position(void)const noexcept; size_t position(void)const noexcept;
void rewind(std::size_t pos = 0)noexcept; void rewind(size_t pos = 0)noexcept;
operator std::FILE*(void)noexcept; operator FILE*(void)noexcept;
operator const std::FILE*(void)const noexcept; operator const FILE*(void)const noexcept;
std::FILE* get(void)noexcept; FILE* get(void)noexcept;
const std::FILE* get(void)const noexcept; const FILE* get(void)const noexcept;
operator bool(void)const noexcept; operator bool(void)const noexcept;
bool eof(void)const; size_t read(char* dest, size_t bytes)noexcept;
rexy::string read(size_t bytes)noexcept;
rexy::string readln(size_t max = 0)noexcept;
rexy::buffer<char> read_bin(size_t bytes)noexcept(std::is_nothrow_constructible<rexy::buffer<char>, char*, size_t>::value);
std::size_t read(char* dest, std::size_t bytes)noexcept; size_t write(const char* c, size_t bytes)noexcept;
rexy::string read(std::size_t bytes)noexcept; size_t write(const rexy::string_base<char>& s)noexcept;
rexy::string readln(std::size_t max = 0)noexcept;
rexy::buffer<char> read_bin(std::size_t bytes)noexcept(std::is_nothrow_constructible<rexy::buffer<char>, char*, std::size_t>::value);
std::size_t write(const char* c, std::size_t bytes)noexcept;
std::size_t write(rexy::string_view s)noexcept;
}; };
} }
#else //LIBREXY_HEADER_ONLY
#error "rexy::filerd is not available when built with header only support"
#endif //LIBREXY_HEADER_ONLY
#endif #endif

View File

@ -1,157 +0,0 @@
/**
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_FORMAT_HPP
#define REXY_FORMAT_HPP
#include "string.hpp"
#include "string_view.hpp"
#include "compat/standard.hpp"
REXY_REQUIRES_CPP20;
#ifndef REXY_STANDARD_CPP20
//Maybe I'll make it work someday, but not for now
#error "Cannot use formatting library without C++20 support"
#endif
#include "detail/format/standard_types.hpp"
#include "detail/format/internal_types.hpp"
#include "detail/format/arg_store.hpp"
#include "detail/format/format_string.hpp"
#include "detail/format/format_args.hpp"
#include "detail/format/format_error.hpp"
#include <cstdio> //FILE
#include <cstddef> //size_t
#include <locale> //locale
namespace rexy{
//Alias declarations of standard defined types
using format_context = fmt::detail::fmt_context_t<char>;
using wformat_context = fmt::detail::fmt_context_t<wchar_t>;
using parse_context = fmt::detail::parse_context_t<char>;
using wparse_context = fmt::detail::parse_context_t<wchar_t>;
using format_arg = fmt::basic_format_arg<format_context>;
using wformat_arg = fmt::basic_format_arg<wformat_context>;
using format_args = fmt::basic_format_args<format_context>;
using wformat_args = fmt::basic_format_args<wformat_context>;
template<class Context = format_context, class... Args>
fmt::detail::basic_format_arg_store<Context,Args...> make_format_args(Args&&... args);
template<class Context = wformat_context, class... Args>
fmt::detail::basic_format_arg_store<Context,Args...> make_wformat_args(Args&&... args);
template<class OutIt>
OutIt vformat_to(OutIt out, string_view fmt, format_args args);
template<class OutIt>
OutIt vformat_to(OutIt out, const std::locale& loc, string_view fmt, wformat_args args);
string vformat(string_view fmt, format_args args);
string vformat(const std::locale& loc, string_view fmt, format_args args);
template<class OutIt, class... Args>
OutIt format_to(OutIt out, fmt::detail::format_string<Args...> fmt, Args&&... args);
template<class OutIt, class... Args>
OutIt format_to(OutIt out, const std::locale& loc, fmt::detail::format_string<Args...> fmt, Args&&... args);
template<class OutIt, class... Args>
format_to_n_result<OutIt> format_to_n(OutIt out, std::size_t max, fmt::detail::format_string<Args...> fmt, Args&&... args);
template<class OutIt, class... Args>
format_to_n_result<OutIt> format_to_n(OutIt out, const std::locale& loc, std::size_t max, fmt::detail::format_string<Args...> fmt, Args&&... args);
template<class... Args>
string format(fmt::detail::format_string<Args...> fmt, Args&&... args);
template<class... Args>
string format(const std::locale& loc, fmt::detail::format_string<Args...> fmt, Args&&... args);
template<class... Args>
std::size_t formatted_size(fmt::detail::format_string<Args...> fmt, Args&&... args);
template<class... Args>
std::size_t formatted_size(const std::locale& loc, fmt::detail::format_string<Args...> fmt, Args&&... args);
template<class OutIt>
OutIt vformat_to(OutIt out, wstring_view fmt, wformat_args args);
template<class OutIt>
OutIt vformat_to(OutIt out, const std::locale& loc, wstring_view fmt, wformat_args args);
wstring vformat(wstring_view fmt, format_args args);
wstring vformat(const std::locale& loc, wstring_view fmt, wformat_args args);
template<class OutIt, class... Args>
OutIt format_to(OutIt out, fmt::detail::wformat_string<Args...> fmt, Args&&... args);
template<class OutIt, class... Args>
OutIt format_to(OutIt out, const std::locale& loc, fmt::detail::wformat_string<Args...> fmt, Args&&... args);
template<class OutIt, class... Args>
format_to_n_result<OutIt> format_to_n(OutIt out, std::size_t max, fmt::detail::wformat_string<Args...> fmt, Args&&... args);
template<class OutIt, class... Args>
format_to_n_result<OutIt> format_to_n(OutIt out, const std::locale& loc, std::size_t max, fmt::detail::wformat_string<Args...> fmt, Args&&... args);
template<class... Args>
wstring format(fmt::detail::wformat_string<Args...> fmt, Args&&... args);
template<class... Args>
wstring format(const std::locale& loc, fmt::detail::wformat_string<Args...> fmt, Args&&... args);
template<class... Args>
std::size_t formatted_size(fmt::detail::wformat_string<Args...> fmt, Args&&... args);
template<class... Args>
std::size_t formatted_size(const std::locale& loc, fmt::detail::wformat_string<Args...> fmt, Args&&... args);
template<class... Args>
std::size_t print(fmt::detail::format_string<Args...> fmt, Args&&... args);
template<class... Args>
std::size_t print(FILE* stream, fmt::detail::format_string<Args...> fmt, Args&&... args);
template<class... Args>
std::size_t println(fmt::detail::format_string<Args...> fmt, Args&&... args);
template<class... Args>
std::size_t println(FILE* stream, fmt::detail::format_string<Args...> fmt, Args&&... args);
std::size_t vprint_unicode(string_view fmt, format_args args);
std::size_t vprint_unicode(FILE* stream, string_view fmt, format_args args);
template<class T, class Char>
constexpr auto arg(const Char* name, T&& t);
template<class T, class Char>
constexpr auto arg(rexy::basic_string_view<Char> name, T&& t);
template<rexy::cx::string Name, class T>
constexpr auto arg(T&& t);
inline namespace fmt_literals{
template<rexy::cx::string Name>
constexpr auto operator""_a(void);
}
}
#include "format.tpp"
#endif

View File

@ -1,340 +0,0 @@
/**
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_FORMAT_TPP
#define REXY_FORMAT_TPP
REXY_REQUIRES_CPP20;
#include "detail/format/format_args.tpp"
#include "detail/format/formatter.tpp"
#include "detail/format/storage.tpp"
#include "detail/format/format_string.tpp"
#include "detail/format/arg_store.tpp"
#include "detail/format/output_buffer.tpp"
#include "detail/format/basic_types.tpp"
#include "detail/format/specs_handler.tpp"
#include "detail/format/parse.tpp"
#include "detail/format/context_handler.tpp"
#include "detail/format/named_args.tpp"
#include "detail/format/parse_context.tpp"
#include "detail/format/format_context.tpp"
#include <iterator> //back_insert_iterator
#include <utility> //move, forward
#include <type_traits> //is_same
#include <locale> //locale
#include <cstddef> //size_t
namespace rexy{
namespace fmt::detail{
template<class Char, class OutIt>
OutIt vformat_to(OutIt out, basic_string_view<std::type_identity_t<Char>> fmt, impl_format_args<std::type_identity_t<Char>> args){
using char_type = Char;
using format_handler_t = fmt::detail::format_handler<char_type>;
if constexpr(std::is_same_v<OutIt,fmt::detail::output_iterator_t<char_type>>){
fmt::detail::format_handler<char_type> handler{out, fmt, args};
fmt::detail::parse::perform_parse(handler, fmt);
return std::move(out);
}else{
fmt::detail::basic_format_output_buffer<char_type,OutIt> outbuf{out};
fmt::detail::format_handler<char_type> handler{fmt::detail::output_iterator_t<char_type>{outbuf}, fmt, args};
fmt::detail::parse::perform_parse(handler, fmt);
return std::move(outbuf.out());
}
}
template<class Char, class OutIt>
OutIt vformat_to(OutIt out, const std::locale& loc, basic_string_view<std::type_identity_t<Char>> fmt, impl_format_args<std::type_identity_t<Char>> args){
using char_type = Char;
using format_handler_t = fmt::detail::format_handler<char_type>;
if constexpr(std::is_same_v<OutIt,fmt::detail::output_iterator_t<char_type>>){
fmt::detail::format_handler<char_type> handler{out, fmt, args, loc};
fmt::detail::parse::perform_parse(handler, fmt);
return std::move(out);
}else{
fmt::detail::basic_format_output_buffer<char_type,OutIt> outbuf{out};
fmt::detail::format_handler<char_type> handler{fmt::detail::output_iterator_t<char_type>{outbuf}, fmt, args, loc};
fmt::detail::parse::perform_parse(handler, fmt);
return std::move(outbuf.out());
}
}
template<class Char, class... Args>
basic_format_arg_store<fmt_context_t<Char>,Args...> make_format_args(Args&&... args){
return basic_format_arg_store<fmt_context_t<Char>,Args...>{std::forward<Args>(args)...};
}
}
template<class Context, class... Args>
fmt::detail::basic_format_arg_store<Context,Args...> make_format_args(Args&&... args){
return fmt::detail::basic_format_arg_store<Context,Args...>{std::forward<Args>(args)...};
}
template<class Context, class... Args>
fmt::detail::basic_format_arg_store<Context,Args...> make_wformat_args(Args&&... args){
return fmt::detail::basic_format_arg_store<Context,Args...>{std::forward<Args>(args)...};
}
//////////////////////////////////////char overloads///////////////////////////////////////////////
template<class OutIt>
OutIt vformat_to(OutIt out, string_view fmt, format_args args){
return fmt::detail::vformat_to<char>(std::move(out), fmt, std::move(args));
}
template<class OutIt>
OutIt vformat_to(OutIt out, const std::locale& loc, string_view fmt, format_args args){
return fmt::detail::vformat_to<char>(std::move(out), loc, fmt, std::move(args));
}
string vformat(string_view fmt, format_args args){
using outit_t = std::back_insert_iterator<string>;
string output;
outit_t out{output};
vformat_to(std::move(out), fmt, std::move(args));
return output;
}
string vformat(const std::locale& loc, string_view fmt, format_args args){
using outit_t = std::back_insert_iterator<string>;
string output;
outit_t out{output};
vformat_to(std::move(out), loc, fmt, std::move(args));
return output;
}
template<class OutIt, class... Args>
OutIt format_to(OutIt out, fmt::detail::format_string<Args...> fmt, Args&&... args){
return vformat_to(std::move(out), fmt.str, make_format_args(std::forward<Args>(args)...));
}
template<class OutIt, class... Args>
OutIt format_to(OutIt out, const std::locale& loc, fmt::detail::format_string<Args...> fmt, Args&&... args){
return vformat_to(std::move(out), loc, fmt.str, make_format_args(std::forward<Args>(args)...));
}
template<class OutIt, class... Args>
format_to_n_result<OutIt> format_to_n(OutIt out, std::size_t max, fmt::detail::format_string<Args...> fmt, Args&&... args){
fmt::detail::basic_format_output_n_buffer<char,OutIt> outbuf{std::move(out), max};
vformat_to(fmt::detail::output_iterator_t<char>{outbuf}, fmt.str, make_format_args(std::forward<Args>(args)...));
return {outbuf.out(), outbuf.count()};
}
template<class OutIt, class... Args>
format_to_n_result<OutIt> format_to_n(OutIt out, const std::locale& loc, std::size_t max, fmt::detail::format_string<Args...> fmt, Args&&... args){
fmt::detail::basic_format_output_n_buffer<char,OutIt> outbuf{std::move(out), max};
vformat_to(fmt::detail::output_iterator_t<char>{outbuf}, loc, fmt.str, make_format_args(std::forward<Args>(args)...));
return {outbuf.out(), outbuf.count()};
}
template<class... Args>
string format(fmt::detail::format_string<Args...> fmt, Args&&... args){
using outit_t = std::back_insert_iterator<string>;
string output;
outit_t out{output};
vformat_to(std::move(out), fmt.str, make_format_args(std::forward<Args>(args)...));
return output;
}
template<class... Args>
string format(const std::locale& loc, fmt::detail::format_string<Args...> fmt, Args&&... args){
using outit_t = std::back_insert_iterator<string>;
string output;
outit_t out{output};
vformat_to(std::move(out), loc, fmt.str, make_format_args(std::forward<Args>(args)...));
return output;
}
template<class... Args>
std::size_t formatted_size(fmt::detail::format_string<Args...> fmt, Args&&... args){
fmt::detail::basic_format_size_buffer<char> counter;
vformat_to(fmt::detail::output_iterator_t<char>{counter}, fmt.str, make_format_args(std::forward<Args>(args)...));
return counter.count();
}
template<class... Args>
std::size_t formatted_size(const std::locale& loc, fmt::detail::format_string<Args...> fmt, Args&&... args){
fmt::detail::basic_format_size_buffer<char> counter;
vformat_to(fmt::detail::output_iterator_t<char>{counter}, loc, fmt.str, make_format_args(std::forward<Args>(args)...));
return counter.count();
}
//////////////////////////////////////wchar_t overloads///////////////////////////////////////////////
template<class OutIt>
OutIt vformat_to(OutIt out, wstring_view fmt, wformat_args args){
return fmt::detail::vformat_to<wchar_t>(std::move(out), fmt, std::move(args));
}
template<class OutIt>
OutIt vformat_to(OutIt out, const std::locale& loc, wstring_view fmt, wformat_args args){
return fmt::detail::vformat_to<wchar_t>(std::move(out), loc, fmt, std::move(args));
}
wstring vformat(wstring_view fmt, wformat_args args){
using outit_t = std::back_insert_iterator<wstring>;
wstring output;
outit_t out{output};
vformat_to(std::move(out), fmt, std::move(args));
return output;
}
wstring vformat(const std::locale& loc, wstring_view fmt, wformat_args args){
using outit_t = std::back_insert_iterator<wstring>;
wstring output;
outit_t out{output};
vformat_to(std::move(out), loc, fmt, std::move(args));
return output;
}
template<class OutIt, class... Args>
OutIt format_to(OutIt out, fmt::detail::wformat_string<Args...> fmt, Args&&... args){
return vformat_to(std::move(out), fmt.str, make_wformat_args(std::forward<Args>(args)...));
}
template<class OutIt, class... Args>
OutIt format_to(OutIt out, const std::locale& loc, fmt::detail::wformat_string<Args...> fmt, Args&&... args){
return vformat_to(std::move(out), loc, fmt.str, make_wformat_args(std::forward<Args>(args)...));
}
template<class OutIt, class... Args>
format_to_n_result<OutIt> format_to_n(OutIt out, std::size_t max, fmt::detail::wformat_string<Args...> fmt, Args&&... args){
fmt::detail::basic_format_output_n_buffer<wchar_t,OutIt> outbuf{std::move(out), max};
vformat_to(fmt::detail::output_iterator_t<wchar_t>{outbuf}, fmt.str, make_wformat_args(std::forward<Args>(args)...));
return {outbuf.out(), outbuf.count()};
}
template<class OutIt, class... Args>
format_to_n_result<OutIt> format_to_n(OutIt out, const std::locale& loc, std::size_t max, fmt::detail::wformat_string<Args...> fmt, Args&&... args){
fmt::detail::basic_format_output_n_buffer<wchar_t,OutIt> outbuf{std::move(out), max};
vformat_to(fmt::detail::output_iterator_t<wchar_t>{outbuf}, loc, fmt.str, make_wformat_args(std::forward<Args>(args)...));
return {outbuf.out(), outbuf.count()};
}
template<class... Args>
wstring format(fmt::detail::wformat_string<Args...> fmt, Args&&... args){
using outit_t = std::back_insert_iterator<wstring>;
wstring output;
outit_t out{output};
vformat_to(out, fmt.str, make_wformat_args(std::forward<Args>(args)...));
return output;
}
template<class... Args>
wstring format(const std::locale& loc, fmt::detail::wformat_string<Args...> fmt, Args&&... args){
using outit_t = std::back_insert_iterator<wstring>;
wstring output;
outit_t out{output};
vformat_to(out, fmt.str, make_wformat_args(std::forward<Args>(args)...));
return output;
}
template<class... Args>
std::size_t formatted_size(fmt::detail::wformat_string<Args...> fmt, Args&&... args){
}
template<class... Args>
std::size_t formatted_size(const std::locale& loc, fmt::detail::wformat_string<Args...> fmt, Args&&... args);
//////////////////////////////////////print char overloads///////////////////////////////////////////////
template<class... Args>
std::size_t print(fmt::detail::format_string<Args...> fmt, Args&&... args){
fmt::detail::print_format_buffer<char> out{stdout};
vformat_to(fmt::detail::output_iterator_t<char>{out}, fmt.str, make_format_args(std::forward<Args>(args)...));
return out.count();
}
template<class... Args>
std::size_t print(FILE* stream, fmt::detail::format_string<Args...> fmt, Args&&... args){
fmt::detail::print_format_buffer<char> out{stream};
vformat_to(fmt::detail::output_iterator_t<char>{out}, fmt.str, make_format_args(std::forward<Args>(args)...));
return out.count();
}
template<class... Args>
std::size_t println(fmt::detail::format_string<Args...> fmt, Args&&... args){
fmt::detail::print_format_buffer<char> out{stdout};
vformat_to(fmt::detail::output_iterator_t<char>{out}, fmt.str, make_format_args(std::forward<Args>(args)...));
out.push_back('\n');
return out.count();
}
template<class... Args>
std::size_t println(FILE* stream, fmt::detail::format_string<Args...> fmt, Args&&... args){
fmt::detail::print_format_buffer<char> out{stream};
vformat_to(fmt::detail::output_iterator_t<char>{out}, fmt.str, make_format_args(std::forward<Args>(args)...));
out.push_back('\n');
return out.count();
}
std::size_t vprint_unicode(string_view fmt, format_args args){
fmt::detail::print_format_buffer<char> out{stdout};
vformat_to(fmt::detail::output_iterator_t<char>{out}, fmt, args);
return out.count();
}
std::size_t vprint_unicode(FILE* stream, string_view fmt, format_args args){
fmt::detail::print_format_buffer<char> out{stream};
vformat_to(fmt::detail::output_iterator_t<char>{out}, fmt, args);
return out.count();
}
//creates a runtime argument from a c string
template<class T, class Char>
constexpr auto arg(const Char* name, T&& t){
return fmt::detail::runtime_arg<T,Char>{std::forward<T>(t), name};
}
//creates a runtime argument from a string view
template<class T, class Char>
constexpr auto arg(rexy::basic_string_view<Char> name, T&& t){
return fmt::detail::runtime_arg{std::forward<T>(t), name};
}
//create a compile time argument from a string literal
template<rexy::cx::string Name, class T>
constexpr auto arg(T&& t){
return fmt::detail::static_arg<T,Name>{t};
}
inline namespace fmt_literals{
//create a compile time argument from a string literal
template<rexy::cx::string Name>
constexpr auto operator""_a(void){
return fmt::detail::arg_literal_result<Name>{};
}
}
}
#undef REXY_REAL_STRINGIFY
#undef REXY_STRINGIFY
#undef REXY_THROW_FORMAT_ERROR
#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-2022 rexy712 Copyright (C) 2020 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
@ -20,29 +20,17 @@
#define REXY_HASH_HPP #define REXY_HASH_HPP
#include <climits> //CHAR_BIT #include <climits> //CHAR_BIT
#include <cstddef> //size_t
#include "rexy.hpp" #include "rexy.hpp"
#include "allocator.hpp"
namespace rexy{ namespace rexy{
template<class Char, REXY_ALLOCATOR_CONCEPT Alloc>
class basic_string;
template<class Char>
class basic_string_view;
namespace cx{
template<std::size_t N, class Char>
class string;
}
template<class T> template<class T>
struct hash{ struct hash{
constexpr std::size_t operator()(const T& t, std::size_t salt = 0)const noexcept{ constexpr size_t operator()(const T& t, size_t salt = 0)const noexcept{
constexpr std::size_t bytes = sizeof(std::size_t); constexpr size_t bytes = sizeof(size_t);
std::size_t val = static_cast<std::size_t>(t); size_t val = static_cast<size_t>(t);
std::size_t hash = 5381 + salt; //magic hash number size_t hash = 5381 + salt; //magic hash number
for(std::size_t i = 0;i < bytes;++i){ for(size_t i = 0;i < bytes;++i){
unsigned char c = static_cast<unsigned char>(val >> (i * CHAR_BIT)); unsigned char c = static_cast<unsigned char>(val >> (i * CHAR_BIT));
hash = ((hash << 5) + hash) ^ c; hash = ((hash << 5) + hash) ^ c;
} }
@ -50,32 +38,16 @@ namespace rexy{
} }
}; };
namespace detail{
//jenkins one at a time hash
template<class Str>
struct string_hash{
constexpr std::size_t operator()(const Str& s, std::size_t salt = 0)const noexcept{
std::size_t hash = salt;
for(std::size_t i = 0;i < s.length();++i){
hash += s[i];
hash += (hash << 10);
hash += (hash >> 6);
}
hash += (hash << 3);
hash ^= (hash << 11);
hash += (hash << 15);
return hash;
}
};
}
template<class Char, REXY_ALLOCATOR_CONCEPT Alloc>
struct hash<rexy::basic_string<Char,Alloc>> : public detail::string_hash<rexy::basic_string<Char,Alloc>>{};
template<class Char>
struct hash<rexy::basic_string_view<Char>> : public detail::string_hash<rexy::basic_string_view<Char>>{};
template<std::size_t N, class Char>
struct hash<rexy::cx::string<N,Char>> : public detail::string_hash<rexy::cx::string<N,Char>>{};
} }
#ifdef REXY_STRING_BASE_HPP
#include "string_view_hash.hpp"
#endif
#ifdef REXY_STRING_HPP
#include "basic_string_hash.hpp"
#endif
#ifdef REXY_CX_STRING_HPP
#include "cx_string_hash.hpp"
#endif
#endif #endif

View File

@ -1,400 +0,0 @@
/**
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_LLIST_HPP
#define REXY_LLIST_HPP
#include <cstddef> //size_t, ptrdiff_t
#include "allocator.hpp"
#include "detail/hasallocator.hpp"
#include "traits.hpp"
#include <iterator> //bidirectional_iterator_tag, reverse_iterator
#include <memory> //allocator_traits
#include <initializer_list>
#include <utility> //pair
#include <type_traits>
namespace rexy{
template<class T, class Alloc>
class list;
namespace detail{
struct list_node_base{
list_node_base* next = nullptr;
list_node_base* prev = nullptr;
};
template<class T>
struct list_iterator;
template<class T>
struct const_list_iterator;
template<class T>
struct list_node;
}
template<class T, class Alloc = rexy::allocator<T>>
class list : protected detail::hasallocator<typename std::allocator_traits<Alloc>::template rebind_alloc<detail::list_node<T>>>
{
private:
using node_allocator_type = detail::hasallocator<typename std::allocator_traits<Alloc>::template rebind_alloc<detail::list_node<T>>>;
public:
using value_type = T;
using allocator_type = Alloc;
using size_type = std::size_t;
using difference_type = std::ptrdiff_t;
using reference = value_type&;
using const_reference = const value_type&;
using pointer = value_type*;
using const_pointer = const value_type*;
using iterator = detail::list_iterator<T>;
using const_iterator = detail::const_list_iterator<T>;
using reverse_iterator = std::reverse_iterator<iterator>;
using const_reverse_iterator = std::reverse_iterator<const_iterator>;
public:
using node = detail::list_node<T>;
private:
detail::list_node_base m_sentinel = {&m_sentinel, &m_sentinel};
size_type m_size = 0;
public:
constexpr list(void)noexcept = default;
constexpr explicit list(const allocator_type& alloc)noexcept;
REXY_CPP20_CONSTEXPR
list(size_type count, const_reference value = value_type(), const allocator_type& = allocator_type())noexcept(
std::is_nothrow_copy_constructible_v<value_type> &&
is_nothrow_allocator_v<node_allocator_type>);
REXY_CPP20_CONSTEXPR
explicit list(size_type count, const allocator_type& alloc = allocator_type())noexcept(
std::is_nothrow_default_constructible_v<value_type> &&
is_nothrow_allocator_v<node_allocator_type>);
template<class InputIt>
REXY_CPP20_CONSTEXPR
list(InputIt first, InputIt last, const allocator_type& alloc = allocator_type())noexcept(
std::is_nothrow_constructible_v<value_type, decltype(*first)> &&
is_nothrow_allocator_v<node_allocator_type>);
REXY_CPP20_CONSTEXPR
list(const list& other, const allocator_type& alloc = allocator_type())noexcept(
std::is_nothrow_copy_constructible_v<value_type> &&
is_nothrow_allocator_v<node_allocator_type>);
REXY_CPP20_CONSTEXPR
list(list&& other, const allocator_type& alloc = allocator_type())noexcept;
REXY_CPP20_CONSTEXPR
list(std::initializer_list<value_type> l, const allocator_type& alloc = allocator_type())noexcept(
std::is_nothrow_constructible_v<value_type, decltype(*l.begin())> &&
is_nothrow_allocator_v<node_allocator_type>);
REXY_CPP20_CONSTEXPR
~list(void)noexcept(
std::is_nothrow_destructible_v<value_type> &&
is_nothrow_allocator_v<node_allocator_type>);
REXY_CPP20_CONSTEXPR
list& operator=(const list& other)noexcept(
std::is_nothrow_copy_constructible_v<value_type> &&
is_nothrow_allocator_v<node_allocator_type>);
REXY_CPP20_CONSTEXPR
list& operator=(list&& other)noexcept;
REXY_CPP20_CONSTEXPR
list& operator=(std::initializer_list<value_type> l)noexcept(
std::is_nothrow_constructible_v<value_type, decltype(*l.begin())> &&
is_nothrow_allocator_v<node_allocator_type>);
constexpr bool operator==(const list& other)const noexcept;
#if __cpp_impl_three_way_comparison
constexpr auto operator<=>(const list& other)const noexcept;
#else
constexpr bool operator!=(const list& other)const noexcept;
constexpr bool operator<(const list& other)const noexcept;
constexpr bool operator<=(const list& other)const noexcept;
constexpr bool operator>(const list& other)const noexcept;
constexpr bool operator>=(const list& other)const noexcept;
#endif
REXY_CPP20_CONSTEXPR
void assign(size_type count, const_reference value)noexcept(
std::conditional_t<
std::is_copy_assignable_v<value_type>,
std::is_nothrow_copy_assignable<value_type>,
std::true_type>::value &&
std::is_nothrow_copy_constructible_v<value_type> &&
std::is_nothrow_destructible_v<value_type> &&
is_nothrow_allocator_v<node_allocator_type>);
template<class InputIt>
REXY_CPP20_CONSTEXPR
void assign(InputIt first, InputIt last)noexcept(
std::conditional_t<
std::is_assignable_v<value_type, decltype(*first)>,
std::is_nothrow_assignable<value_type, decltype(*first)>,
std::true_type>::value &&
std::is_nothrow_constructible_v<value_type, decltype(*first)> &&
std::is_nothrow_destructible_v<value_type> &&
is_nothrow_allocator_v<node_allocator_type>);
REXY_CPP20_CONSTEXPR
void assign(std::initializer_list<value_type> l)noexcept(
std::conditional_t<
std::is_assignable_v<value_type, decltype(*l.begin())>,
std::is_nothrow_assignable<value_type, decltype(*l.begin())>,
std::true_type>::value &&
std::is_nothrow_constructible_v<value_type, decltype(*l.begin())> &&
std::is_nothrow_destructible_v<value_type> &&
is_nothrow_allocator_v<node_allocator_type>);
constexpr allocator_type get_allocator(void)const noexcept;
constexpr reference front(void)noexcept;
constexpr const_reference front(void)const noexcept;
constexpr reference back(void)noexcept;
constexpr const_reference back(void)const noexcept;
constexpr iterator begin(void)noexcept;
constexpr const_iterator begin(void)const noexcept;
constexpr const_iterator cbegin(void)const noexcept;
constexpr iterator end(void)noexcept;
constexpr const_iterator end(void)const noexcept;
constexpr const_iterator cend(void)const noexcept;
constexpr iterator next(iterator i)noexcept;
constexpr const_iterator next(const_iterator i)noexcept;
constexpr iterator prev(iterator i)noexcept;
constexpr const_iterator prev(const_iterator i)noexcept;
constexpr reverse_iterator rbegin(void)noexcept;
constexpr const_reverse_iterator rbegin(void)const noexcept;
constexpr const_reverse_iterator crbegin(void)const noexcept;
constexpr reverse_iterator rend(void)noexcept;
constexpr const_reverse_iterator rend(void)const noexcept;
constexpr const_reverse_iterator crend(void)const noexcept;
constexpr bool empty(void)const noexcept;
constexpr size_type size(void)const noexcept;
constexpr size_type max_size(void)const noexcept;
REXY_CPP20_CONSTEXPR
void clear(void)noexcept(
std::is_nothrow_destructible_v<value_type> &&
is_nothrow_allocator_v<node_allocator_type>);
REXY_CPP20_CONSTEXPR
iterator insert(const_iterator pos, const_reference value)noexcept(
std::is_nothrow_copy_constructible_v<value_type> &&
is_nothrow_allocator_v<node_allocator_type>);
REXY_CPP20_CONSTEXPR
iterator insert(const_iterator pos, value_type&& value)noexcept(
std::is_nothrow_move_constructible_v<value_type> &&
is_nothrow_allocator_v<node_allocator_type>);
REXY_CPP20_CONSTEXPR
iterator insert(const_iterator pos, size_type count, const_reference value)noexcept(
std::is_nothrow_copy_constructible_v<value_type> &&
is_nothrow_allocator_v<node_allocator_type>);
template<class InputIt>
REXY_CPP20_CONSTEXPR
iterator insert(const_iterator pos, InputIt first, InputIt last)noexcept(
std::is_nothrow_constructible_v<value_type, decltype(*first)> &&
is_nothrow_allocator_v<node_allocator_type>);
REXY_CPP20_CONSTEXPR
iterator insert(const_iterator pos, std::initializer_list<value_type> l)noexcept(
std::is_nothrow_constructible_v<value_type, decltype(*l.begin())> &&
is_nothrow_allocator_v<node_allocator_type>);
template<class... Args>
REXY_CPP20_CONSTEXPR
iterator emplace(const_iterator pos, Args&&... args)noexcept(
std::is_nothrow_constructible_v<value_type, Args&&...> &&
is_nothrow_allocator_v<node_allocator_type>);
REXY_CPP20_CONSTEXPR
iterator erase(const_iterator pos)noexcept(
std::is_nothrow_destructible_v<value_type> &&
is_nothrow_allocator_v<node_allocator_type>);
REXY_CPP20_CONSTEXPR
iterator erase(const_iterator first, const_iterator last)noexcept(
std::is_nothrow_destructible_v<value_type> &&
is_nothrow_allocator_v<node_allocator_type>);
REXY_CPP20_CONSTEXPR
reference push_back(const_reference value)noexcept(
std::is_nothrow_copy_constructible_v<value_type> &&
is_nothrow_allocator_v<node_allocator_type>);
REXY_CPP20_CONSTEXPR
reference push_back(value_type&& value)noexcept(
std::is_nothrow_move_constructible_v<value_type> &&
is_nothrow_allocator_v<node_allocator_type>);
template<class... Args>
REXY_CPP20_CONSTEXPR
reference emplace_back(Args&&... args)noexcept(
std::is_nothrow_constructible_v<value_type, Args&&...> &&
is_nothrow_allocator_v<node_allocator_type>);
REXY_CPP20_CONSTEXPR
void pop_back(void)noexcept(
std::is_nothrow_destructible_v<value_type> &&
is_nothrow_allocator_v<node_allocator_type>);
REXY_CPP20_CONSTEXPR
reference push_front(const_reference value)noexcept(
std::is_nothrow_copy_constructible_v<value_type> &&
is_nothrow_allocator_v<node_allocator_type>);
REXY_CPP20_CONSTEXPR
reference push_front(value_type&& value)noexcept(
std::is_nothrow_move_constructible_v<value_type> &&
is_nothrow_allocator_v<node_allocator_type>);
template<class... Args>
REXY_CPP20_CONSTEXPR
reference emplace_front(Args&&... args)noexcept(
std::is_nothrow_constructible_v<value_type, Args&&...> &&
is_nothrow_allocator_v<node_allocator_type>);
REXY_CPP20_CONSTEXPR
void pop_front(void)noexcept(
std::is_nothrow_destructible_v<value_type> &&
is_nothrow_allocator_v<node_allocator_type>);
REXY_CPP20_CONSTEXPR
void resize(size_type count)noexcept(
std::is_nothrow_default_constructible_v<value_type> &&
std::is_nothrow_destructible_v<value_type> &&
is_nothrow_allocator_v<node_allocator_type>);
REXY_CPP20_CONSTEXPR
void resize(size_type count, value_type value)noexcept(
std::is_nothrow_copy_constructible_v<value_type> &&
std::is_nothrow_destructible_v<value_type> &&
is_nothrow_allocator_v<node_allocator_type>);
REXY_CPP20_CONSTEXPR
void swap(list& other)noexcept;
REXY_CPP20_CONSTEXPR
void merge(list&& other)noexcept;
template<class Comp>
REXY_CPP20_CONSTEXPR
void merge(list&& other, Comp comp)noexcept;
REXY_CPP20_CONSTEXPR
void splice(const_iterator pos, list&& other)noexcept;
REXY_CPP20_CONSTEXPR
void splice(const_iterator pos, list&& other, const_iterator it)noexcept;
REXY_CPP20_CONSTEXPR
void splice(const_iterator pos, list&& other, const_iterator first, const_iterator last)noexcept;
REXY_CPP20_CONSTEXPR
size_type remove(const_reference value)noexcept(
std::is_nothrow_destructible_v<value_type> &&
is_nothrow_allocator_v<node_allocator_type>);
template<class UnaryPred>
REXY_CPP20_CONSTEXPR
size_type remove_if(UnaryPred p)noexcept(
std::is_nothrow_destructible_v<value_type> &&
is_nothrow_allocator_v<node_allocator_type>);
REXY_CPP20_CONSTEXPR
void reverse(void)noexcept;
REXY_CPP20_CONSTEXPR
size_type unique(void)noexcept(
std::is_nothrow_destructible_v<value_type> &&
is_nothrow_allocator_v<node_allocator_type>);
template<class BinaryPred>
REXY_CPP20_CONSTEXPR
size_type unique(BinaryPred p)noexcept(
std::is_nothrow_destructible_v<value_type> &&
is_nothrow_allocator_v<node_allocator_type>);
REXY_CPP20_CONSTEXPR
void sort(void)noexcept;
template<class Comp>
REXY_CPP20_CONSTEXPR
void sort(Comp comp)noexcept;
private:
template<class InputIt>
REXY_CPP20_CONSTEXPR
void iterator_initialize_(InputIt first, InputIt last)noexcept(
std::is_nothrow_constructible_v<value_type, decltype(*first)> &&
is_nothrow_allocator_v<node_allocator_type>);
REXY_CPP20_CONSTEXPR
void constant_initialize_(size_type count, const_reference value)noexcept(
std::is_nothrow_copy_constructible_v<value_type> &&
is_nothrow_allocator_v<node_allocator_type>);
REXY_CPP20_CONSTEXPR
void default_initialize_(size_type count)noexcept(
std::is_nothrow_default_constructible_v<value_type> &&
is_nothrow_allocator_v<node_allocator_type>);
template<class Comp>
static iterator mergesort(iterator first, size_type firstlen, Comp comp)noexcept;
static std::pair<iterator,size_type> ms_split(iterator it, size_type len)noexcept;
static void insert_node_(detail::list_node_base* prev, detail::list_node_base* n)noexcept;
static void remove_node_(detail::list_node_base* rm)noexcept;
static detail::list_node_base* get_next_then_move_node_(detail::list_node_base* dest, detail::list_node_base* n)noexcept;
};
}
#include "list.tpp"
#endif

View File

@ -1,861 +0,0 @@
/**
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_LLIST_TPP
#define REXY_LLIST_TPP
#include <utility> //move, swap, forward
#include <limits> //numeric_limits
#include <algorithm> //lexicographical_compare_three_way, equal
#include <memory> //construct_at, destroy_at
#include "utility.hpp" //sized_constant_iterator
namespace rexy{
namespace detail{
template<class T>
struct list_node : public list_node_base{
T data;
constexpr list_node(list_node_base* next, list_node_base* prev, const T& d):
list_node_base{next, prev},
data(d){}
constexpr list_node(list_node_base* next, list_node_base* prev, T&& d):
list_node_base{next, prev},
data(std::move(d)){}
template<class... Args>
constexpr list_node(list_node_base* next, list_node_base* prev, Args&&... args):
list_node_base{next, prev},
data(std::forward<Args>(args)...){}
constexpr list_node(const list_node& n):
list_node_base(n),
data(n.data){}
constexpr list_node(list_node&& n):
list_node_base(std::move(n)),
data(std::move(n.data)){}
constexpr list_node* next(void)const{return static_cast<list_node*>(list_node_base::next);}
constexpr list_node* prev(void)const{return static_cast<list_node*>(list_node_base::prev);}
};
template<class T>
struct list_iterator{
using value_type = T;
using reference = value_type&;
using const_reference = const value_type&;
using pointer = value_type*;
using const_pointer = const value_type*;
using node_t = list_node<T>;
list_node_base* current = nullptr;
constexpr bool operator==(const list_iterator& other)const noexcept{return current == other.current;}
constexpr bool operator!=(const list_iterator& other)const noexcept{return current != other.current;}
constexpr bool operator==(const const_list_iterator<T>& other)const noexcept{return current == other.nod();}
constexpr bool operator!=(const const_list_iterator<T>& other)const noexcept{return current != other.nod();}
constexpr reference operator*(void){return static_cast<node_t*>(current)->data;}
constexpr const_reference operator*(void)const{return static_cast<const node_t*>(current)->data;}
constexpr pointer operator->(void){return &(static_cast<node_t*>(current)->data);}
constexpr const_pointer operator->(void)const{return &(static_cast<const node_t*>(current)->data);}
constexpr list_iterator& operator++(void){
current = current->next;
return *this;
}
constexpr list_iterator operator++(int){
list_iterator copy(*this);
++(*this);
return copy;
}
constexpr list_iterator& operator--(void){
current = current->prev;
return *this;
}
constexpr list_iterator operator--(int){
list_iterator copy(*this);
--(*this);
return copy;
}
constexpr operator const_list_iterator<T>(void)const{
return const_list_iterator<T>{current};
}
list_iterator next(void)const{
return list_iterator{current->next};
}
list_iterator prev(void)const{
return list_iterator{current->prev};
}
list_node_base* nod(void){return current;}
list_node_base* nod(void)const{return current;}
};
template<class T>
struct const_list_iterator{
template<class U, class Alloc>
friend class rexy::list;
using value_type = T;
using reference = value_type&;
using const_reference = const value_type&;
using pointer = value_type*;
using const_pointer = const value_type*;
using node_t = list_node<T>;
const list_node_base* current = nullptr;
constexpr bool operator==(const const_list_iterator& other)const noexcept{return current == other.current;}
constexpr bool operator!=(const const_list_iterator& other)const noexcept{return current != other.current;}
constexpr bool operator==(const list_iterator<T>& other)const noexcept{return current == other.nod();}
constexpr bool operator!=(const list_iterator<T>& other)const noexcept{return current != other.nod();}
constexpr const_reference operator*(void){return static_cast<const node_t*>(current)->data;}
constexpr const_reference operator*(void)const{return static_cast<const node_t*>(current)->data;}
constexpr const_pointer operator->(void){return &(static_cast<const node_t*>(current)->data);}
constexpr const_pointer operator->(void)const{return &(static_cast<const node_t*>(current)->data);}
constexpr const_list_iterator& operator++(void){
current = current->next;
return *this;
}
constexpr const_list_iterator operator++(int){
const_list_iterator copy(*this);
++(*this);
return copy;
}
constexpr const_list_iterator& operator--(void){
current = current->prev;
return *this;
}
constexpr const_list_iterator operator--(int){
const_list_iterator copy(*this);
--(*this);
return copy;
}
const_list_iterator next(void)const{
return const_list_iterator{current->next};
}
const_list_iterator prev(void)const{
return const_list_iterator{current->prev};
}
const list_node_base* nod(void)const{return current;}
protected:
list_iterator<T> unconst(void){
return list_iterator<T>{const_cast<list_node_base*>(current)};
}
};
}
namespace detail{
}
template<class T, class Alloc>
constexpr list<T,Alloc>::list(const allocator_type& alloc)noexcept:
node_allocator_type(alloc){}
template<class T, class Alloc>
REXY_CPP20_CONSTEXPR
list<T,Alloc>::list(size_type count, const_reference value, const allocator_type& alloc)noexcept(
std::is_nothrow_copy_constructible_v<value_type> &&
is_nothrow_allocator_v<node_allocator_type>):
node_allocator_type(alloc)
{
constant_initialize_(count, value);
}
template<class T, class Alloc>
REXY_CPP20_CONSTEXPR
list<T,Alloc>::list(size_type count, const allocator_type& alloc)noexcept(
std::is_nothrow_default_constructible_v<value_type> &&
is_nothrow_allocator_v<node_allocator_type>):
node_allocator_type(alloc)
{
default_initialize_(count);
}
template<class T, class Alloc>
template<class InputIt>
REXY_CPP20_CONSTEXPR
list<T,Alloc>::list(InputIt first, InputIt last, const allocator_type& alloc)noexcept(
std::is_nothrow_constructible_v<value_type, decltype(*first)> &&
is_nothrow_allocator_v<node_allocator_type>):
node_allocator_type(alloc)
{
iterator_initialize_(first, last);
}
template<class T, class Alloc>
REXY_CPP20_CONSTEXPR
list<T,Alloc>::list(const list& other, const allocator_type& alloc)noexcept(
std::is_nothrow_copy_constructible_v<value_type> &&
is_nothrow_allocator_v<node_allocator_type>):
node_allocator_type(alloc)
{
iterator_initialize_(other.begin(), other.end());
}
template<class T, class Alloc>
REXY_CPP20_CONSTEXPR
list<T,Alloc>::list(list&& other, const allocator_type& alloc)noexcept:
node_allocator_type(alloc)
{
swap(other);
}
template<class T, class Alloc>
REXY_CPP20_CONSTEXPR
list<T,Alloc>::list(std::initializer_list<value_type> l, const allocator_type& alloc)noexcept(
std::is_nothrow_constructible_v<value_type, decltype(*l.begin())> &&
is_nothrow_allocator_v<node_allocator_type>):
node_allocator_type(alloc)
{
iterator_initialize_(l.begin(), l.end());
}
template<class T, class Alloc>
REXY_CPP20_CONSTEXPR
list<T,Alloc>::~list(void)noexcept(
std::is_nothrow_destructible_v<value_type> &&
is_nothrow_allocator_v<node_allocator_type>)
{
clear();
}
template<class T, class Alloc>
REXY_CPP20_CONSTEXPR
auto list<T,Alloc>::operator=(const list& other)noexcept(
std::is_nothrow_copy_constructible_v<value_type> &&
is_nothrow_allocator_v<node_allocator_type>) -> list&
{
return (*this = list(other));
}
template<class T, class Alloc>
REXY_CPP20_CONSTEXPR
auto list<T,Alloc>::operator=(list&& other)noexcept -> list&{
swap(other);
return *this;
}
template<class T, class Alloc>
REXY_CPP20_CONSTEXPR
auto list<T,Alloc>::operator=(std::initializer_list<value_type> l)noexcept(
std::is_nothrow_constructible_v<value_type, decltype(*l.begin())> &&
is_nothrow_allocator_v<node_allocator_type>) -> list&
{
return (*this = list(l));
}
template<class T, class Alloc>
constexpr bool list<T,Alloc>::operator==(const list& other)const noexcept{
if(m_size != other.m_size)
return false;
return std::equal(cbegin(), cend(), other.cbegin());
}
#if __cpp_impl_three_way_comparison
template<class T, class Alloc>
constexpr auto list<T,Alloc>::operator<=>(const list& other)const noexcept{
return std::lexicographical_compare_three_way(cbegin(), cend(), other.cbegin(), other.cend());
}
#else
template<class T, class Alloc>
constexpr bool list<T,Alloc>::operator!=(const list& other)const noexcept{
return !(*this == other);
}
template<class T, class Alloc>
constexpr bool list<T,Alloc>::operator<(const list& other)const noexcept{
return std::lexicographical_compare(cbegin(), cend(), other.cbegin(), other.cend());
}
template<class T, class Alloc>
constexpr bool list<T,Alloc>::operator<=(const list& other)const noexcept{
return !(other < *this);
}
template<class T, class Alloc>
constexpr bool list<T,Alloc>::operator>(const list& other)const noexcept{
return other < *this;
}
template<class T, class Alloc>
constexpr bool list<T,Alloc>::operator>=(const list& other)const noexcept{
return !(*this < other);
}
#endif
template<class T, class Alloc>
REXY_CPP20_CONSTEXPR
void list<T,Alloc>::assign(size_type count, const_reference value)noexcept(
std::conditional_t<
std::is_copy_assignable_v<value_type>,
std::is_nothrow_copy_assignable<value_type>,
std::true_type>::value &&
std::is_nothrow_copy_constructible_v<value_type> &&
std::is_nothrow_destructible_v<value_type> &&
is_nothrow_allocator_v<node_allocator_type>)
{
assign(sized_constant_iterator{value, count}, sized_constant_iterator<value_type>{{}, 0});
}
template<class T, class Alloc>
template<class InputIt>
REXY_CPP20_CONSTEXPR
void list<T,Alloc>::assign(InputIt first, InputIt last)noexcept(
std::conditional_t<
std::is_assignable_v<value_type, decltype(*first)>,
std::is_nothrow_assignable<value_type, decltype(*first)>,
std::true_type>::value &&
std::is_nothrow_constructible_v<value_type, decltype(*first)> &&
std::is_nothrow_destructible_v<value_type> &&
is_nothrow_allocator_v<node_allocator_type>)
{
auto current = begin();
size_type i = 0;
if constexpr(std::is_assignable_v<value_type, decltype(*first)>){
for(;i < m_size && first != last;++i){
*current++ = *first++;
}
if(first == last){
erase(current, end());
}
m_size = i;
}else{
clear();
current = end();
}
while(first != last){
current = ++(emplace(current, *first++));
}
}
template<class T, class Alloc>
REXY_CPP20_CONSTEXPR
void list<T,Alloc>::assign(std::initializer_list<value_type> l)noexcept(
std::conditional_t<
std::is_assignable_v<value_type, decltype(*l.begin())>,
std::is_nothrow_assignable<value_type, decltype(*l.begin())>,
std::true_type>::value &&
std::is_nothrow_constructible_v<value_type, decltype(*l.begin())> &&
std::is_nothrow_destructible_v<value_type> &&
is_nothrow_allocator_v<node_allocator_type>)
{
assign(l.begin(), l.end());
}
template<class T, class Alloc>
constexpr auto list<T,Alloc>::get_allocator(void)const noexcept -> allocator_type{return {*this};}
//Direct accessors
template<class T, class Alloc>
constexpr auto list<T,Alloc>::front(void)noexcept -> reference{return *begin();}
template<class T, class Alloc>
constexpr auto list<T,Alloc>::front(void)const noexcept -> const_reference{return *begin();}
template<class T, class Alloc>
constexpr auto list<T,Alloc>::back(void)noexcept -> reference{return *(--end());}
template<class T, class Alloc>
constexpr auto list<T,Alloc>::back(void)const noexcept-> const_reference{return *(--end());}
//Iterators
template<class T, class Alloc>
constexpr auto list<T,Alloc>::begin(void)noexcept -> iterator{return iterator{m_sentinel.next};}
template<class T, class Alloc>
constexpr auto list<T,Alloc>::begin(void)const noexcept -> const_iterator{return const_iterator{m_sentinel.next};}
template<class T, class Alloc>
constexpr auto list<T,Alloc>::cbegin(void)const noexcept -> const_iterator{return const_iterator{m_sentinel.next};}
template<class T, class Alloc>
constexpr auto list<T,Alloc>::end(void)noexcept -> iterator{return iterator{&m_sentinel};}
template<class T, class Alloc>
constexpr auto list<T,Alloc>::end(void)const noexcept -> const_iterator{return const_iterator{&m_sentinel};}
template<class T, class Alloc>
constexpr auto list<T,Alloc>::cend(void)const noexcept -> const_iterator{return const_iterator{&m_sentinel};}
template<class T, class Alloc>
constexpr auto list<T,Alloc>::next(iterator i)noexcept -> iterator{return i.next();}
template<class T, class Alloc>
constexpr auto list<T,Alloc>::next(const_iterator i)noexcept -> const_iterator{return i.next();}
template<class T, class Alloc>
constexpr auto list<T,Alloc>::prev(iterator i)noexcept -> iterator{return i.prev();}
template<class T, class Alloc>
constexpr auto list<T,Alloc>::prev(const_iterator i)noexcept -> const_iterator{return i.prev();}
template<class T, class Alloc>
constexpr auto list<T,Alloc>::rbegin(void)noexcept -> reverse_iterator{return reverse_iterator{end()};}
template<class T, class Alloc>
constexpr auto list<T,Alloc>::rbegin(void)const noexcept -> const_reverse_iterator{return const_reverse_iterator{end()};}
template<class T, class Alloc>
constexpr auto list<T,Alloc>::crbegin(void)const noexcept -> const_reverse_iterator{return const_reverse_iterator{end()};}
template<class T, class Alloc>
constexpr auto list<T,Alloc>::rend(void)noexcept -> reverse_iterator{return reverse_iterator{begin()};}
template<class T, class Alloc>
constexpr auto list<T,Alloc>::rend(void)const noexcept -> const_reverse_iterator{return const_reverse_iterator{begin()};}
template<class T, class Alloc>
constexpr auto list<T,Alloc>::crend(void)const noexcept -> const_reverse_iterator{return const_reverse_iterator{begin()};}
//Queries
template<class T, class Alloc>
constexpr bool list<T,Alloc>::empty(void)const noexcept{return m_sentinel.next == nullptr;}
template<class T, class Alloc>
constexpr auto list<T,Alloc>::size(void)const noexcept -> size_type{return m_size;}
template<class T, class Alloc>
constexpr auto list<T,Alloc>::max_size(void)const noexcept -> size_type{return std::numeric_limits<difference_type>::max();}
template<class T, class Alloc>
REXY_CPP20_CONSTEXPR
void list<T,Alloc>::clear(void)noexcept(
std::is_nothrow_destructible_v<value_type> &&
is_nothrow_allocator_v<node_allocator_type>)
{
erase(begin(), end());
}
template<class T, class Alloc>
REXY_CPP20_CONSTEXPR
auto list<T,Alloc>::insert(const_iterator pos, const_reference value)noexcept(
std::is_nothrow_copy_constructible_v<value_type> &&
is_nothrow_allocator_v<node_allocator_type>) -> iterator
{
return insert(pos, value_type{value});
}
template<class T, class Alloc>
REXY_CPP20_CONSTEXPR
auto list<T,Alloc>::insert(const_iterator pos, value_type&& value)noexcept(
std::is_nothrow_move_constructible_v<value_type> &&
is_nothrow_allocator_v<node_allocator_type>) -> iterator
{
auto* prev = (--pos).unconst().nod();
auto* next = prev->next;
prev->next = this->allocate(1);
next->prev = prev->next;
std::construct_at(static_cast<node*>(prev->next), next, prev, std::move(value));
++m_size;
return iterator{prev->next};
}
template<class T, class Alloc>
REXY_CPP20_CONSTEXPR
auto list<T,Alloc>::insert(const_iterator pos, size_type count, const_reference value)noexcept(
std::is_nothrow_copy_constructible_v<value_type> &&
is_nothrow_allocator_v<node_allocator_type>) -> iterator
{
auto start = pos.unconst();
for(auto i = count;i > 0;--i){
pos = ++(insert(pos, value_type{value}));
}
return start;
}
template<class T, class Alloc>
template<class InputIt>
REXY_CPP20_CONSTEXPR
auto list<T,Alloc>::insert(const_iterator pos, InputIt first, InputIt last)noexcept(
std::is_nothrow_constructible_v<value_type, decltype(*first)> &&
is_nothrow_allocator_v<node_allocator_type>) -> iterator
{
auto start = pos.unconst();
while(first != last){
pos = ++(insert(pos, *first++));
}
return start;
}
template<class T, class Alloc>
REXY_CPP20_CONSTEXPR
auto list<T,Alloc>::insert(const_iterator pos, std::initializer_list<value_type> l)noexcept(
std::is_nothrow_constructible_v<value_type, decltype(*l.begin())> &&
is_nothrow_allocator_v<node_allocator_type>) -> iterator
{
return insert(pos, l.begin(), l.end());
}
template<class T, class Alloc>
template<class... Args>
REXY_CPP20_CONSTEXPR
auto list<T,Alloc>::emplace(const_iterator pos, Args&&... args)noexcept(
std::is_nothrow_constructible_v<value_type, Args&&...> &&
is_nothrow_allocator_v<node_allocator_type>) -> iterator
{
auto* prev = (--pos).unconst().nod();
auto* next = prev->next;
prev->next = this->allocate(1);
next->prev = prev->next;
std::construct_at(static_cast<node*>(prev->next), next, prev, std::forward<Args>(args)...);
++m_size;
return iterator{prev->next};
}
template<class T, class Alloc>
REXY_CPP20_CONSTEXPR
auto list<T,Alloc>::erase(const_iterator pos)noexcept(
std::is_nothrow_destructible_v<value_type> &&
is_nothrow_allocator_v<node_allocator_type>) -> iterator
{
auto* n = pos.unconst().nod();
auto* next = n->next;
remove_node_(n);
std::destroy_at(static_cast<node*>(n));
this->deallocate(static_cast<node*>(n), 1);
--m_size;
return iterator{next};
}
template<class T, class Alloc>
REXY_CPP20_CONSTEXPR
auto list<T,Alloc>::erase(const_iterator first, const_iterator last)noexcept(
std::is_nothrow_destructible_v<value_type> &&
is_nothrow_allocator_v<node_allocator_type>) -> iterator
{
while(first != last){
first = erase(first);
}
return last.unconst();
}
template<class T, class Alloc>
REXY_CPP20_CONSTEXPR
auto list<T,Alloc>::push_back(const_reference value)noexcept(
std::is_nothrow_copy_constructible_v<value_type> &&
is_nothrow_allocator_v<node_allocator_type>) -> reference
{
return *insert(cend(), value);
}
template<class T, class Alloc>
REXY_CPP20_CONSTEXPR
auto list<T,Alloc>::push_back(value_type&& value)noexcept(
std::is_nothrow_move_constructible_v<value_type> &&
is_nothrow_allocator_v<node_allocator_type>) -> reference
{
return *insert(cend(), std::move(value));
}
template<class T, class Alloc>
template<class... Args>
REXY_CPP20_CONSTEXPR
auto list<T,Alloc>::emplace_back(Args&&... args)noexcept(
std::is_nothrow_constructible_v<value_type, Args&&...> &&
is_nothrow_allocator_v<node_allocator_type>) -> reference
{
return *emplace(cend(), std::forward<Args>(args)...);
}
template<class T, class Alloc>
REXY_CPP20_CONSTEXPR
void list<T,Alloc>::pop_back(void)noexcept(
std::is_nothrow_destructible_v<value_type> &&
is_nothrow_allocator_v<node_allocator_type>)
{
erase(--cend());
}
template<class T, class Alloc>
REXY_CPP20_CONSTEXPR
auto list<T,Alloc>::push_front(const_reference value)noexcept(
std::is_nothrow_copy_constructible_v<value_type> &&
is_nothrow_allocator_v<node_allocator_type>) -> reference
{
return insert(cbegin(), value);
}
template<class T, class Alloc>
REXY_CPP20_CONSTEXPR
auto list<T,Alloc>::push_front(value_type&& value)noexcept(
std::is_nothrow_move_constructible_v<value_type> &&
is_nothrow_allocator_v<node_allocator_type>) -> reference
{
return insert(cbegin(), std::move(value));
}
template<class T, class Alloc>
template<class... Args>
REXY_CPP20_CONSTEXPR
auto list<T,Alloc>::emplace_front(Args&&... args)noexcept(
std::is_nothrow_constructible_v<value_type, Args&&...> &&
is_nothrow_allocator_v<node_allocator_type>) -> reference
{
return emplace(cbegin(), std::forward<Args>(args)...);
}
template<class T, class Alloc>
REXY_CPP20_CONSTEXPR
void list<T,Alloc>::pop_front(void)noexcept(
std::is_nothrow_destructible_v<value_type> &&
is_nothrow_allocator_v<node_allocator_type>)
{
erase(cbegin());
}
template<class T, class Alloc>
REXY_CPP20_CONSTEXPR
void list<T,Alloc>::resize(size_type count)noexcept(
std::is_nothrow_default_constructible_v<value_type> &&
std::is_nothrow_destructible_v<value_type> &&
is_nothrow_allocator_v<node_allocator_type>)
{
resize(count, value_type{});
}
template<class T, class Alloc>
REXY_CPP20_CONSTEXPR
void list<T,Alloc>::resize(size_type count, value_type value)noexcept(
std::is_nothrow_copy_constructible_v<value_type> &&
std::is_nothrow_destructible_v<value_type> &&
is_nothrow_allocator_v<node_allocator_type>)
{
while(count > m_size){
insert(cend(), value);
}
while(m_size > count){
erase(--cend());
}
}
template<class T, class Alloc>
REXY_CPP20_CONSTEXPR
void list<T,Alloc>::swap(list& other)noexcept{
std::swap(m_sentinel, other.m_sentinel);
std::swap(m_size, other.m_size);
}
template<class T, class Alloc>
REXY_CPP20_CONSTEXPR
void list<T,Alloc>::merge(list&& other)noexcept{
merge(std::move(other), [](const_reference a, const_reference b){return a < b;});
}
template<class T, class Alloc>
template<class Comp>
REXY_CPP20_CONSTEXPR
void list<T,Alloc>::merge(list&& other, Comp comp)noexcept{
if(&other == this){
return;
}
auto it = begin();
auto oit = other.begin();
while(it != end() && oit != other.end()){
if(comp(*oit, *it)){
oit = iterator{get_next_then_move_node_(it.prev().nod(), oit.nod())};
++m_size;
}else{
++it;
}
}
splice(it, std::move(other));
}
template<class T, class Alloc>
REXY_CPP20_CONSTEXPR
void list<T,Alloc>::splice(const_iterator pos, list&& other)noexcept{
splice(pos, std::move(other), other.begin(), other.end());
}
template<class T, class Alloc>
REXY_CPP20_CONSTEXPR
void list<T,Alloc>::splice(const_iterator pos, list&& other, const_iterator it)noexcept{
auto oit = it.unconst();
auto prev = (--pos).unconst();
remove_node_(oit.nod());
insert_node_(prev.nod(), oit.nod());
++m_size;
--other.m_size;
}
template<class T, class Alloc>
REXY_CPP20_CONSTEXPR
void list<T,Alloc>::splice(const_iterator pos, list&& other, const_iterator first, const_iterator last)noexcept{
for(auto oit = first;oit != last;){
auto onext = oit.next();
splice(pos, std::move(other), oit);
oit = onext;
}
}
template<class T, class Alloc>
REXY_CPP20_CONSTEXPR
auto list<T,Alloc>::remove(const_reference value)noexcept(
std::is_nothrow_destructible_v<value_type> &&
is_nothrow_allocator_v<node_allocator_type>) -> size_type
{
return remove_if(
[&value](const_reference r) -> bool{
return r == value;
}
);
}
template<class T, class Alloc>
template<class UnaryPred>
REXY_CPP20_CONSTEXPR
auto list<T,Alloc>::remove_if(UnaryPred pred)noexcept(
std::is_nothrow_destructible_v<value_type> &&
is_nothrow_allocator_v<node_allocator_type>) -> size_type
{
size_type count = 0;
for(auto it = begin();it != end();){
if(pred(*it)){
it = erase(it);
++count;
}else{
++it;
}
}
return count;
}
template<class T, class Alloc>
REXY_CPP20_CONSTEXPR
void list<T,Alloc>::reverse(void)noexcept{
auto* it = begin().nod();
std::swap(m_sentinel.next, m_sentinel.prev);
for(;it != &m_sentinel;it = it->prev){
std::swap(it->next, it->prev);
}
}
template<class T, class Alloc>
REXY_CPP20_CONSTEXPR
auto list<T,Alloc>::unique(void)noexcept(
std::is_nothrow_destructible_v<value_type> &&
is_nothrow_allocator_v<node_allocator_type>) -> size_type
{
return unique(
[](const_reference first, const_reference second) -> bool{
return first == second;
}
);
}
template<class T, class Alloc>
template<class BinaryPred>
REXY_CPP20_CONSTEXPR
auto list<T,Alloc>::unique(BinaryPred pred)noexcept(
std::is_nothrow_destructible_v<value_type> &&
is_nothrow_allocator_v<node_allocator_type>) -> size_type
{
size_type count = 0;
for(auto it = begin();it != end();++it){
auto next = it.next();
while(next != end() && pred(*it, *next)){
next = erase(next);
++count;
}
}
return count;
}
template<class T, class Alloc>
REXY_CPP20_CONSTEXPR
void list<T,Alloc>::sort(void)noexcept{
mergesort(begin(), m_size, [](const_reference first, const_reference second)->bool{
return first < second;
});
}
template<class T, class Alloc>
template<class Comp>
REXY_CPP20_CONSTEXPR
void list<T,Alloc>::sort(Comp comp)noexcept{
mergesort(begin(), m_size, comp);
}
template<class T, class Alloc>
template<class InputIt>
REXY_CPP20_CONSTEXPR
void list<T,Alloc>::iterator_initialize_(InputIt first, InputIt last)noexcept(
std::is_nothrow_constructible_v<value_type, decltype(*first)> &&
is_nothrow_allocator_v<node_allocator_type>)
{
for(;first != last;++first){
emplace_back(*first);
}
}
template<class T, class Alloc>
REXY_CPP20_CONSTEXPR
void list<T,Alloc>::constant_initialize_(size_type count, const_reference value)noexcept(
std::is_nothrow_copy_constructible_v<value_type> &&
is_nothrow_allocator_v<node_allocator_type>)
{
for(;count > 0;--count){
emplace_back(value);
}
}
template<class T, class Alloc>
REXY_CPP20_CONSTEXPR
void list<T,Alloc>::default_initialize_(size_type count)noexcept(
std::is_nothrow_default_constructible_v<value_type> &&
is_nothrow_allocator_v<node_allocator_type>)
{
for(;count > 0;--count){
emplace_back();
}
}
template<class T, class Alloc>
template<class Comp>
auto list<T,Alloc>::mergesort(iterator first, size_type firstlen, Comp comp)noexcept -> iterator{
if(firstlen == 1)
return first;
auto [second, secondlen] = ms_split(first, firstlen);
firstlen -= secondlen;
first = mergesort(first, firstlen, comp);
second = mergesort(second, secondlen, comp);
iterator result = first;
//do this outside the loop to save return value
if(comp(*second, *first)){
result = second;
second = iterator{get_next_then_move_node_(first.prev().nod(), second.nod())};
--secondlen;
}
while(firstlen > 0 && secondlen > 0){
if(comp(*second, *first)){
second = iterator{get_next_then_move_node_(first.prev().nod(), second.nod())};
--secondlen;
}else{
++first;
--firstlen;
}
}
//finish off the second list if it isn't already done
while(secondlen > 0){
second = iterator{get_next_then_move_node_(first.prev().nod(), second.nod())};
--secondlen;
}
return result;
}
template<class T, class Alloc>
auto list<T,Alloc>::ms_split(iterator it, size_type len)noexcept -> std::pair<iterator,size_type>{
size_type second_half_len = len / 2;
size_type dist = len - second_half_len;
for(auto i = dist;i > 0;--i){
++it;
}
return {it, second_half_len};
}
template<class T, class Alloc>
void list<T,Alloc>::insert_node_(detail::list_node_base* prev, detail::list_node_base* n)noexcept{
n->next = prev->next;
n->prev = prev;
prev->next->prev = n;
prev->next = n;
}
template<class T, class Alloc>
void list<T,Alloc>::remove_node_(detail::list_node_base* rm)noexcept{
rm->prev->next = rm->next;
rm->next->prev = rm->prev;
}
template<class T, class Alloc>
detail::list_node_base* list<T,Alloc>::get_next_then_move_node_(detail::list_node_base* dest, detail::list_node_base* n)noexcept{
auto* next = n->next;
remove_node_(n);
insert_node_(dest, n);
return next;
}
}
#endif

View File

@ -20,7 +20,7 @@
#define REXY_MPMC_QUEUE_HPP #define REXY_MPMC_QUEUE_HPP
#include <vector> //vector (duh) #include <vector> //vector (duh)
#include <cstddef> //size_t #include <cstdlib> //size_t
#include <atomic> //atomic (duh) #include <atomic> //atomic (duh)
#include "rexy.hpp" #include "rexy.hpp"
@ -43,7 +43,7 @@ namespace rexy{
{ {
public: public:
using value_type = T; using value_type = T;
using size_type = std::size_t; using size_type = size_t;
using pointer = value_type*; using pointer = value_type*;
using const_pointer = const value_type*; using const_pointer = const value_type*;
using reference = value_type&; using reference = value_type&;
@ -55,13 +55,13 @@ namespace rexy{
//libc++ bug //libc++ bug
// https://bugs.llvm.org/show_bug.cgi?id=41423 // https://bugs.llvm.org/show_bug.cgi?id=41423
#if defined(_LIBCPP_VERSION) && _LIBCPP_VERSION >= 12000 #if defined(_LIBCPP_VERSION) && _LIBCPP_VERSION >= 12000
static constexpr std::size_t cacheline_size = std::hardware_destructive_interference_size; static constexpr size_t cacheline_size = std::hardware_destructive_interference_size;
#else #else
static constexpr std::size_t cacheline_size = 64; static constexpr size_t cacheline_size = 64;
#endif #endif
#else #else
//Best guess //Best guess
static constexpr std::size_t cacheline_size = 64; static constexpr size_t cacheline_size = 64;
#endif #endif
class slot class slot

View File

@ -21,20 +21,20 @@
#include <utility> //forward, move #include <utility> //forward, move
#include <atomic> //memory_order, atomic #include <atomic> //memory_order, atomic
#include "utility.hpp" //memcpy #include <cstring> //memcpy
namespace rexy{ namespace rexy{
template<class T> template<class T>
mpmc_queue<T>::slot::slot(const slot& s): mpmc_queue<T>::slot::slot(const slot& s):
m_turn(s.m_turn.load(std::memory_order_acquire)) m_turn(s.m_turn.load(std::memory_order_acquire))
{ {
rexy::memcpy(m_data, s.m_data, sizeof(s.m_data)); memcpy(m_data, s.m_data, sizeof(s.m_data));
} }
template<class T> template<class T>
mpmc_queue<T>::slot::slot(slot&& s): mpmc_queue<T>::slot::slot(slot&& s):
m_turn(s.m_turn.load(std::memory_order_acquire)) m_turn(s.m_turn.load(std::memory_order_acquire))
{ {
rexy::memcpy(m_data, s.m_data, sizeof(s.m_data)); memcpy(m_data, s.m_data, sizeof(s.m_data));
} }
template<class T> template<class T>
mpmc_queue<T>::slot::~slot(){ mpmc_queue<T>::slot::~slot(){

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) 2021-2022 rexy712 Copyright (C) 2021 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,20 +16,9 @@
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_CONFIG_REXY_HPP #ifndef REXY_REXY_HPP
#define REXY_CONFIG_REXY_HPP #define REXY_REXY_HPP
#define LIBREXY_BUILD_HEADER_ONLY @librexy_HEADER_ONLY_BUILD@
#if LIBREXY_BUILD_HEADER_ONLY
#define LIBREXY_HEADER_ONLY 1
#endif
#define LIBREXY_VERSION @librexy_VERSION_STRING@ #define LIBREXY_VERSION @librexy_VERSION_STRING@
#define LIBREXY_VERSION_MAJOR @librexy_VERSION_MAJOR@
#define LIBREXY_VERSION_MINOR @librexy_VERSION_MINOR@
#define LIBREXY_VERSION_REVISION @librexy_VERSION_REVISION@
#define LIBREXY_ENABLE_SSO @librexy_ENABLE_SSO@
#endif #endif

View File

@ -22,8 +22,6 @@
#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.
@ -44,19 +42,7 @@ namespace rexy{
static constexpr auto size = sizeof(value_type); static constexpr auto size = sizeof(value_type);
private: private:
#if __cplusplus >= 202002L && __has_cpp_attribute(no_unique_address) && __cpp_constexpr >= 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:
@ -67,7 +53,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);
REXY_CPP20_CONSTEXPR ~storage_for(void)noexcept(std::is_nothrow_destructible<value_type>::value); 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);
@ -79,9 +65,6 @@ 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,141 +21,6 @@
#include <utility> //forward, move #include <utility> //forward, move
#if __cplusplus >= 202002L && __has_cpp_attribute(no_unique_address) && __cpp_constexpr >= 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>
REXY_CPP20_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>
@ -185,7 +50,7 @@ namespace rexy{
m_dirty = 1; m_dirty = 1;
} }
template<class T> template<class T>
REXY_CPP20_CONSTEXPR storage_for<T>::~storage_for(void)noexcept(std::is_nothrow_destructible<value_type>::value){ constexpr storage_for<T>::~storage_for(void)noexcept(std::is_nothrow_destructible<value_type>::value){
if(m_dirty){ if(m_dirty){
destroy(); destroy();
} }
@ -223,22 +88,13 @@ 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 reinterpret_cast<const_reference>(m_storage); return 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);
@ -264,6 +120,4 @@ namespace rexy{
} }
#endif //__cplusplus
#endif #endif

View File

@ -26,25 +26,25 @@
namespace rexy{ namespace rexy{
//new allocated string //new allocated string
using string = basic_string<char>; using string = basic_string<char,allocator<char>>;
using wstring = basic_string<wchar_t>;
using u16string = basic_string<char16_t>;
using u32string = basic_string<char32_t>;
#ifdef __cpp_char8_t
using u8string = basic_string<char8_t>;
#endif
#ifndef LIBREXY_HEADER_ONLY
extern template class basic_string<char,allocator<char>>; extern template class basic_string<char,allocator<char>>;
extern template class basic_string<wchar_t,allocator<wchar_t>>; extern template class basic_string<wchar_t,allocator<wchar_t>>;
extern template class basic_string<char16_t,allocator<char16_t>>; extern template class basic_string<char16_t,allocator<char16_t>>;
extern template class basic_string<char32_t,allocator<char32_t>>; extern template class basic_string<char32_t,allocator<char32_t>>;
#ifdef __cpp_char8_t
extern template class basic_string<char8_t,allocator<char8_t>>;
#endif
#endif //LIBREXY_HEADER_ONLY
using string_view = basic_string_view<char>;
extern template class basic_string_view<char>;
extern template class basic_string_view<wchar_t>;
extern template class basic_string_view<char16_t>;
extern template class basic_string_view<char32_t>;
} }
#ifdef REXY_CX_HASH_HPP
#include "cx/basic_string_hash.hpp"
#endif
#endif #endif

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

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) 2022 rexy712 Copyright (C) 2020 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,19 +16,30 @@
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_COMPAT_STRING_BASE_HPP #ifndef REXY_STRING_HASH_HPP
#define REXY_COMPAT_STRING_BASE_HPP #define REXY_STRING_HASH_HPP
#include <cstddef> //size_t #include "rexy.hpp"
namespace rexy{ namespace rexy{
static constexpr std::size_t npos = std::size_t(-1);
//jenkns one at a time hash
template<class Str>
struct string_hash{
constexpr size_t operator()(const Str& s, size_t salt = 0)const noexcept{
size_t hash = salt;
for(size_t i = 0;i < s.length();++i){
hash += s[i];
hash += (hash << 10);
hash += (hash >> 6);
}
hash += (hash << 3);
hash ^= (hash << 11);
hash += (hash << 15);
return hash;
}
};
} }
#ifdef __cpp_concepts
#include "cpp20/string_base.hpp"
#else
#include "cpp17/string_base.hpp"
#endif
#endif #endif

View File

@ -19,11 +19,10 @@
#ifndef REXY_STRING_VIEW_HPP #ifndef REXY_STRING_VIEW_HPP
#define REXY_STRING_VIEW_HPP #define REXY_STRING_VIEW_HPP
#include <cstddef> //std::size_t, ptrdiff_t #include <cstddef> //size_t, ptrdiff_t
#include <iterator> //reverse_iterator #include <iterator> //reverse_iterator
#include "compat/standard.hpp" #include "compat/constexpr.hpp"
#include "rexy.hpp"
namespace rexy{ namespace rexy{
@ -35,150 +34,92 @@ namespace rexy{
{ {
public: public:
using value_type = Char; using value_type = Char;
using size_type = std::size_t; using size_type = size_t;
using difference_type = ptrdiff_t; using difference_type = ptrdiff_t;
using pointer = value_type*; using pointer = value_type*;
using const_pointer = const value_type*; using const_pointer = const value_type*;
using reference = value_type&; using reference = value_type&;
using const_reference = const value_type&; using const_reference = const value_type&;
using iterator = const_pointer; using iterator = pointer;
using const_iterator = const_pointer; using const_iterator = const_pointer;
using reverse_iterator = std::reverse_iterator<iterator>; using reverse_iterator = std::reverse_iterator<iterator>;
using const_reverse_iterator = std::reverse_iterator<const_iterator>; using const_reverse_iterator = std::reverse_iterator<const_iterator>;
static constexpr size_type npos = size_type(-1);
private: private:
const_pointer m_data = nullptr; const_pointer m_data = nullptr;
size_type m_length = 0; size_type m_length = 0;
public: public:
constexpr basic_string_view(void)noexcept = default; constexpr basic_string_view(void)noexcept;
constexpr basic_string_view(const_pointer str, size_type len)noexcept; constexpr basic_string_view(const_pointer str, size_type len)noexcept;
constexpr basic_string_view(const_pointer c)noexcept; constexpr basic_string_view(const_pointer c)noexcept;
constexpr basic_string_view(const basic_string_view& s)noexcept = default; constexpr basic_string_view(const basic_string_view& s)noexcept;
constexpr basic_string_view(const string_base<Char>& s)noexcept; constexpr basic_string_view(const string_base<Char>& s)noexcept;
constexpr basic_string_view(basic_string_view&& s)noexcept = default; constexpr basic_string_view(basic_string_view&& s)noexcept;
template<class InIter> template<class InIter>
constexpr basic_string_view(InIter start, InIter fin)noexcept; constexpr basic_string_view(InIter start, InIter fin)noexcept;
REXY_CPP20_CONSTEXPR ~basic_string_view(void)noexcept = default; REXY_CPP20_CONSTEXPR ~basic_string_view(void)noexcept = default;
constexpr basic_string_view& operator=(const_pointer c)noexcept; constexpr basic_string_view& operator=(const_pointer c)noexcept;
constexpr basic_string_view& operator=(const basic_string_view& s)noexcept = default; constexpr basic_string_view& operator=(const basic_string_view& s)noexcept;
constexpr basic_string_view& operator=(basic_string_view&&)noexcept = default; constexpr basic_string_view& operator=(basic_string_view&&)noexcept;
//Length of string not including null terminator //Length of string not including null terminator
constexpr size_type length(void)const noexcept{return m_length;} constexpr size_type length(void)const noexcept{return m_length;}
constexpr size_type size(void)const noexcept{return m_length;}
//direct access to managed pointer //direct access to managed pointer
constexpr const_pointer c_str(void)const noexcept{return m_data;} constexpr const_pointer c_str(void)const noexcept{return m_data;}
constexpr const_pointer data(void)const noexcept{return m_data;} constexpr const_pointer get(void)const noexcept{return m_data;}
constexpr operator const_pointer(void)const noexcept{return m_data;}
//true if m_data is not empty //true if m_data is not empty
constexpr bool valid(void)const noexcept{return m_length > 0;} constexpr bool valid(void)const noexcept{return m_length > 0;}
constexpr bool empty(void)const noexcept{return m_length == 0;}
constexpr const_reference operator[](size_type i)const noexcept{return m_data[i];} constexpr const_reference operator[](size_type i)const noexcept{return m_data[i];}
constexpr const_reference at(size_type i)const noexcept{return m_data[i];}
constexpr const_reference front(void)const noexcept{return m_data[0];}
constexpr const_reference back(void)const noexcept{return m_data[m_length-1];}
constexpr const_iterator it_at(size_type i)const noexcept{return m_data + i;}
constexpr const_iterator search(basic_string_view s)const noexcept; constexpr const_iterator search(const basic_string_view& s)const;
constexpr const_iterator search(const_pointer c)const noexcept; constexpr const_iterator search(const_pointer c)const;
template<class Searcher> template<class Searcher>
constexpr const_iterator search(basic_string_view s, const Searcher& searcher)const noexcept( constexpr const_iterator search(const basic_string_view& s, const Searcher& searcher)const;
std::is_nothrow_invocable_v<Searcher, const_iterator, const_iterator, const_iterator, const_iterator>);
template<class Searcher> template<class Searcher>
constexpr const_iterator search(const_pointer c, const Searcher& searcher)const noexcept( constexpr const_iterator search(const_pointer c, const Searcher& searcher)const;
std::is_nothrow_invocable_v<Searcher, const_iterator, const_iterator, const_pointer, const_pointer>); constexpr bool compare(const basic_string_view& s)const{return *this == s;}
constexpr bool compare(const_pointer c)const{return *this == c;}
constexpr bool starts_with(basic_string_view sv)const noexcept; constexpr const_iterator begin(void)const{return m_data;}
constexpr bool starts_with(value_type v)const noexcept; constexpr const_iterator end(void)const{return m_data+m_length;}
constexpr bool starts_with(const_pointer str)const noexcept; constexpr const_iterator cbegin(void)const{return begin();}
constexpr const_iterator cend(void)const{return end();}
constexpr bool ends_with(basic_string_view sv)const noexcept;
constexpr bool ends_with(value_type v)const noexcept;
constexpr bool ends_with(const_pointer str)const noexcept;
constexpr bool contains(basic_string_view sv)const noexcept;
constexpr bool contains(value_type sv)const noexcept;
constexpr bool contains(const_pointer str)const noexcept;
constexpr bool compare(const basic_string_view& s)const noexcept{return *this == s;}
constexpr bool compare(const_pointer c)const noexcept{return *this == c;}
constexpr const_iterator begin(void)const noexcept{return m_data;}
constexpr const_iterator end(void)const noexcept{return m_data+m_length;}
constexpr const_iterator cbegin(void)const noexcept{return begin();}
constexpr const_iterator cend(void)const noexcept{return end();}
constexpr const_reverse_iterator rbegin(void)const noexcept{return const_reverse_iterator(m_data+m_length);}
constexpr const_reverse_iterator rend(void)const noexcept{return const_reverse_iterator(m_data);}
constexpr const_reverse_iterator crbegin(void)const noexcept{return rbegin();}
constexpr const_reverse_iterator crend(void)const noexcept{return rend();}
constexpr void remove_prefix(size_type i)noexcept;
constexpr void remove_suffix(size_type i)noexcept;
constexpr basic_string_view substr(size_type pos, size_type count = npos)const noexcept;
constexpr size_type find_first_of(basic_string_view str, size_type pos = 0)const noexcept;
constexpr size_type find_first_of(value_type v, size_type start = 0)const noexcept;
constexpr size_type find_first_of(const_pointer c, size_type pos = 0)const noexcept;
constexpr size_type find_first_of(const_pointer c, size_type pos, size_type size)const noexcept;
constexpr size_type find_first_not_of(basic_string_view str, size_type pos = 0)const noexcept;
constexpr size_type find_first_not_of(value_type v, size_type start = 0)const noexcept;
constexpr size_type find_first_not_of(const_pointer c, size_type pos = 0)const noexcept;
constexpr size_type find_first_not_of(const_pointer c, size_type pos, size_type size)const noexcept;
constexpr size_type find_last_of(basic_string_view str, size_type pos = 0)const noexcept;
constexpr size_type find_last_of(value_type v, size_type start = 0)const noexcept;
constexpr size_type find_last_of(const_pointer c, size_type pos = 0)const noexcept;
constexpr size_type find_last_of(const_pointer c, size_type pos, size_type size)const noexcept;
constexpr size_type find_last_not_of(basic_string_view str, size_type pos = 0)const noexcept;
constexpr size_type find_last_not_of(value_type v, size_type start = 0)const noexcept;
constexpr size_type find_last_not_of(const_pointer c, size_type pos = 0)const noexcept;
constexpr size_type find_last_not_of(const_pointer c, size_type pos, size_type size)const noexcept;
constexpr const_reverse_iterator rbegin(void)const{return const_reverse_iterator(m_data+m_length);}
constexpr const_reverse_iterator rend(void)const{return const_reverse_iterator(m_data-1);}
constexpr const_reverse_iterator crbegin(void)const{return rbegin();}
constexpr const_reverse_iterator crend(void)const{return rend();}
}; };
template<class T> template<class T>
basic_string_view(const T*) -> basic_string_view<T>; basic_string_view(const T*) -> basic_string_view<T>;
template<class T> template<class T>
basic_string_view(const T*, std::size_t) -> basic_string_view<T>; basic_string_view(const T*, size_t) -> basic_string_view<T>;
using string_view = basic_string_view<char>; template<class T>
using wstring_view = basic_string_view<wchar_t>; using static_string [[deprecated]] = basic_string_view<T>;
#ifndef LIBREXY_HEADER_ONLY
extern template class basic_string_view<char>;
extern template class basic_string_view<wchar_t>;
extern template class basic_string_view<char16_t>;
extern template class basic_string_view<char32_t>;
#endif
inline namespace str_literals{
constexpr inline rexy::basic_string_view<char> operator"" _sv(const char* str, std::size_t len)noexcept{
return rexy::basic_string_view(str, len);
}
constexpr inline rexy::basic_string_view<wchar_t> operator"" _sv(const wchar_t* str, std::size_t len)noexcept{
return rexy::basic_string_view(str, len);
}
constexpr inline rexy::basic_string_view<char16_t> operator"" _sv(const char16_t* str, std::size_t len)noexcept{
return rexy::basic_string_view(str, len);
}
constexpr inline rexy::basic_string_view<char32_t> operator"" _sv(const char32_t* str, std::size_t len)noexcept{
return rexy::basic_string_view(str, len);
}
}
} }
namespace{ namespace{
constexpr inline rexy::basic_string_view<char> operator"" _sv(const char* str, size_t len)noexcept{
return rexy::basic_string_view(str, len);
}
constexpr inline rexy::basic_string_view<wchar_t> operator"" _sv(const wchar_t* str, size_t len)noexcept{
return rexy::basic_string_view(str, len);
}
constexpr inline rexy::basic_string_view<char16_t> operator"" _sv(const char16_t* str, size_t len)noexcept{
return rexy::basic_string_view(str, len);
}
constexpr inline rexy::basic_string_view<char32_t> operator"" _sv(const char32_t* str, size_t len)noexcept{
return rexy::basic_string_view(str, len);
}
template<class Char> template<class Char>
std::ostream& operator<<(std::ostream& os, const rexy::basic_string_view<Char>& str){ std::ostream& operator<<(std::ostream& os, const rexy::basic_string_view<Char>& str){
return os << str.c_str(); return os << str.c_str();
@ -187,5 +128,4 @@ namespace{
#include "string_view.tpp" #include "string_view.tpp"
#endif #endif

View File

@ -22,219 +22,50 @@
#include "compat/to_address.hpp" #include "compat/to_address.hpp"
#include "utility.hpp" #include "utility.hpp"
#include "string_base.hpp" #include "string_base.hpp"
#include "algorithm.hpp" //two_way_search
#include "compat/string_base.hpp"
namespace rexy{ namespace rexy{
template<class Char>
constexpr basic_string_view<Char>::basic_string_view(void)noexcept:
basic_string_view(nullptr, 0){}
template<class Char> template<class Char>
constexpr basic_string_view<Char>::basic_string_view(const_pointer str, size_type len)noexcept: constexpr basic_string_view<Char>::basic_string_view(const_pointer str, size_type len)noexcept:
m_data(str), m_length(len){} m_data(str), m_length(len){}
template<class Char> template<class Char>
constexpr basic_string_view<Char>::basic_string_view(const basic_string_view& s)noexcept:
m_data(s.m_data), m_length(s.m_length){}
template<class Char>
constexpr basic_string_view<Char>::basic_string_view(const string_base<Char>& s)noexcept: constexpr basic_string_view<Char>::basic_string_view(const string_base<Char>& s)noexcept:
m_data(s.c_str()), m_length(s.length()){} m_data(s.c_str()), m_length(s.length()){}
template<class Char> template<class Char>
constexpr basic_string_view<Char>::basic_string_view(basic_string_view&& s)noexcept:
m_data(s.m_data), m_length(s.m_length){}
template<class Char>
template<class InIter> template<class InIter>
constexpr basic_string_view<Char>::basic_string_view(InIter start, InIter fin)noexcept: constexpr basic_string_view<Char>::basic_string_view(InIter start, InIter fin)noexcept:
basic_string_view(compat::to_address(start), fin - start){} basic_string_view(compat::to_address(start), fin - start){}
template<class Char>
constexpr basic_string_view<Char>& basic_string_view<Char>::operator=(const basic_string_view& s)noexcept{
m_data = s.m_data;
m_length = s.m_length;
return *this;
}
template<class Char> template<class Char>
constexpr basic_string_view<Char>::basic_string_view(const_pointer c)noexcept: constexpr basic_string_view<Char>::basic_string_view(const_pointer c)noexcept:
basic_string_view(c, rexy::strlen(c)){} basic_string_view(c, strlen(c)){}
template<class Char> template<class Char>
constexpr basic_string_view<Char>& basic_string_view<Char>::operator=(const_pointer c)noexcept{ constexpr basic_string_view<Char>& basic_string_view<Char>::operator=(const_pointer c)noexcept{
m_data = c; m_data = c;
m_length = rexy::strlen(c); m_length = strlen(c);
return *this; return *this;
} }
template<class Char> template<class Char>
constexpr auto basic_string_view<Char>::search(basic_string_view s)const noexcept -> const_iterator{ constexpr basic_string_view<Char>& basic_string_view<Char>::operator=(basic_string_view&& s)noexcept{
if(s.length() > m_length){ m_data = s.m_data;
return cend(); m_length = s.m_length;
} return *this;
return two_way_search(cbegin(), cend(), s.cbegin(), s.cend());
}
template<class Char>
constexpr auto basic_string_view<Char>::search(const_pointer c)const noexcept -> const_iterator{
basic_string_view tmp(c);
return search(tmp);
}
template<class Char>
template<class Searcher>
constexpr auto basic_string_view<Char>::search(basic_string_view s, const Searcher& searcher)const noexcept(
std::is_nothrow_invocable_v<Searcher, const_iterator, const_iterator, const_iterator, const_iterator>) -> const_iterator
{
if(s.length() > m_length){
return cend();
}
return searcher(cbegin(), cend(), s.cbegin(), s.cend());
}
template<class Char>
template<class Searcher>
constexpr auto basic_string_view<Char>::search(const_pointer c, const Searcher& searcher)const noexcept(
std::is_nothrow_invocable_v<Searcher, const_iterator, const_iterator, const_pointer, const_pointer>) -> const_iterator
{
basic_string_view tmp(c);
return search(tmp, searcher);
}
template<class Char>
constexpr bool basic_string_view<Char>::starts_with(basic_string_view sv)const noexcept{
if(sv.length() > length()){
return false;
}
auto it = two_way_search(begin(), begin() + sv.length(), sv.cbegin(), sv.cend());
if(it == begin()){
return true;
}
return false;
}
template<class Char>
constexpr bool basic_string_view<Char>::starts_with(value_type v)const noexcept{
return front() == v;
}
template<class Char>
constexpr bool basic_string_view<Char>::starts_with(const_pointer s)const noexcept{
return starts_with(basic_string_view(s));
}
template<class Char>
constexpr bool basic_string_view<Char>::ends_with(basic_string_view sv)const noexcept{
if(sv.length() > length()){
return false;
}
const auto start = end() - sv.length();
auto it = two_way_search(start, end(), sv.cbegin(), sv.cend());
if(it == start){
return true;
}
return false;
}
template<class Char>
constexpr bool basic_string_view<Char>::ends_with(value_type v)const noexcept{
return back() == v;
}
template<class Char>
constexpr bool basic_string_view<Char>::ends_with(const_pointer s)const noexcept{
return ends_with(basic_string_view(s));
}
template<class Char>
constexpr bool basic_string_view<Char>::contains(basic_string_view sv)const noexcept{
const auto it = two_way_search(cbegin(), cend(), sv.cbegin(), sv.cend());
if(it != cend()){
return true;
}
return false;
}
template<class Char>
constexpr bool basic_string_view<Char>::contains(value_type v)const noexcept{
for(size_type i = 0;i < length();++i){
if(m_data[i] == v){
return true;
}
}
return false;
}
template<class Char>
constexpr bool basic_string_view<Char>::contains(const_pointer str)const noexcept{
return contains(basic_string_view(str));
}
template<class Char>
constexpr basic_string_view<Char> basic_string_view<Char>::substr(size_type pos, size_type count)const noexcept{
const size_type real_count = rexy::min(count, length() - pos);
return basic_string_view{m_data + pos, real_count};
}
template<class Char>
constexpr void basic_string_view<Char>::remove_prefix(size_type i) noexcept{
if(i > m_length){
m_data = end();
m_length = 0;
}else{
m_data = begin() + i;
m_length -= i;
}
}
template<class Char>
constexpr void basic_string_view<Char>::remove_suffix(size_type i) noexcept{
if(i > m_length){
m_length = 0;
}else{
m_length -= i;
}
}
template<class Char>
constexpr auto basic_string_view<Char>::find_first_of(basic_string_view str, size_type pos)const noexcept -> size_type{
return rexy::find_first_of(*this, str.c_str(), pos, str.length());
}
template<class Char>
constexpr auto basic_string_view<Char>::find_first_of(value_type v, size_type start)const noexcept -> size_type{
return rexy::find_first_of(*this, &v, start, 1);
}
template<class Char>
constexpr auto basic_string_view<Char>::find_first_of(const_pointer c, size_type start)const noexcept -> size_type{
return rexy::find_first_of(*this, c, start, rexy::strlen(c));
}
template<class Char>
constexpr auto basic_string_view<Char>::find_first_of(const_pointer c, size_type start, size_type size)const noexcept -> size_type{
return rexy::find_first_of(*this, c, start, size);
}
template<class Char>
constexpr auto basic_string_view<Char>::find_first_not_of(basic_string_view str, size_type pos)const noexcept -> size_type{
return rexy::find_first_not_of(*this, str.c_str(), pos, str.length());
}
template<class Char>
constexpr auto basic_string_view<Char>::find_first_not_of(value_type v, size_type start)const noexcept -> size_type{
return rexy::find_first_not_of(*this, &v, start, 1);
}
template<class Char>
constexpr auto basic_string_view<Char>::find_first_not_of(const_pointer c, size_type start)const noexcept -> size_type{
return rexy::find_first_not_of(*this, c, start, rexy::strlen(c));
}
template<class Char>
constexpr auto basic_string_view<Char>::find_first_not_of(const_pointer c, size_type start, size_type size)const noexcept -> size_type{
return rexy::find_first_not_of(*this, c, start, size);
}
template<class Char>
constexpr auto basic_string_view<Char>::find_last_of(basic_string_view str, size_type pos)const noexcept -> size_type{
return rexy::find_last_of(*this, str.c_str(), pos, str.length());
}
template<class Char>
constexpr auto basic_string_view<Char>::find_last_of(value_type v, size_type start)const noexcept -> size_type{
return rexy::find_last_of(*this, &v, start, 1);
}
template<class Char>
constexpr auto basic_string_view<Char>::find_last_of(const_pointer c, size_type start)const noexcept -> size_type{
return rexy::find_last_of(*this, c, start, rexy::strlen(c));
}
template<class Char>
constexpr auto basic_string_view<Char>::find_last_of(const_pointer c, size_type start, size_type size)const noexcept -> size_type{
return rexy::find_last_of(*this, c, start, size);
}
template<class Char>
constexpr auto basic_string_view<Char>::find_last_not_of(basic_string_view str, size_type pos)const noexcept -> size_type{
return rexy::find_last_not_of(*this, str.c_str(), pos, str.length());
}
template<class Char>
constexpr auto basic_string_view<Char>::find_last_not_of(value_type v, size_type start)const noexcept -> size_type{
return rexy::find_last_not_of(*this, &v, start, 1);
}
template<class Char>
constexpr auto basic_string_view<Char>::find_last_not_of(const_pointer c, size_type start)const noexcept -> size_type{
return rexy::find_last_not_of(*this, c, start, rexy::strlen(c));
}
template<class Char>
constexpr auto basic_string_view<Char>::find_last_not_of(const_pointer c, size_type start, size_type size)const noexcept -> size_type{
return rexy::find_last_not_of(*this, c, start, size);
} }
} }

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) 2022 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
@ -16,17 +16,17 @@
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_COMPAT_CPP17_TRAITS_HPP #ifndef REXY_STRING_VIEW_HASH_HPP
#define REXY_COMPAT_CPP17_TRAITS_HPP #define REXY_STRING_VIEW_HASH_HPP
#include <type_traits> //remove_cvref #include "string_hash.hpp"
#include "string_base.hpp"
#include "rexy.hpp"
namespace rexy{ namespace rexy{
template<class T> template<class Char>
struct remove_cvref : public std::remove_cvref<T>{}; struct hash<rexy::basic_string_view<Char>> : public string_hash<rexy::basic_string_view<Char>>{};
template<class T>
using remove_cvref_t = typename remove_cvref<T>::type;
} }

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-2022 rexy712 Copyright (C) 2020 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
@ -29,7 +29,6 @@
#include <future> //future, packaged_task #include <future> //future, packaged_task
#include <functional> //function, bind #include <functional> //function, bind
#include <utility> //move, forward #include <utility> //move, forward
#include <type_traits> //invoke_result_t
#include "rexy.hpp" #include "rexy.hpp"
@ -63,14 +62,14 @@ namespace rexy{
void invalidate(void); void invalidate(void);
template<class Func, class... Args> template<class Func, class... Args>
auto add_job(Func&& f, Args&&... args) -> std::future<std::invoke_result_t<Func,Args...>>; auto add_job(Func&& f, Args&&... args) -> std::future<decltype(std::forward<Func>(f)(std::forward<Args>(args)...))>;
private: private:
void worker_loop(void); void worker_loop(void);
}; };
template<class Func, class... Args> template<class Func, class... Args>
auto threadpool::add_job(Func&& f, Args&&... args) -> std::future<std::invoke_result_t<Func,Args...>>{ auto threadpool::add_job(Func&& f, Args&&... args) -> std::future<decltype(std::forward<Func>(f)(std::forward<Args>(args)...))>{
using return_t = decltype(std::forward<Func>(f)(std::forward<Args>(args)...)); using return_t = decltype(std::forward<Func>(f)(std::forward<Args>(args)...));
using task_t = std::packaged_task<return_t(void)>; using task_t = std::packaged_task<return_t(void)>;

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-2022 rexy712 Copyright (C) 2020 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
@ -20,9 +20,7 @@
#define REXY_TRAITS_HPP #define REXY_TRAITS_HPP
#include <type_traits> //is_same, decay, integral_constant, declval #include <type_traits> //is_same, decay, integral_constant, declval
#include <iterator> //iterator_traits
#include "compat/traits.hpp"
#include "rexy.hpp" #include "rexy.hpp"
namespace rexy{ namespace rexy{
@ -32,7 +30,7 @@ namespace rexy{
static std::true_type check(U*); static std::true_type check(U*);
static std::false_type check(...); static std::false_type check(...);
static constexpr bool value = decltype(check(std::declval<std::decay_t<T>*>()))::value; static constexpr bool value = std::is_same<std::true_type,decltype(check(std::declval<std::decay_t<T>*>()))>::value;
}; };
template<class T, template<class...> class U> template<class T, template<class...> class U>
@ -56,86 +54,6 @@ namespace rexy{
}; };
template<class T, class = void>
struct is_dereferencable : public std::false_type{};
template<class T>
struct is_dereferencable<T,std::void_t<decltype(*(std::declval<T>()))>> : public std::true_type{};
template<class T>
static constexpr bool is_dereferencable_v = is_dereferencable<T>::value;
template<class T, class = void>
struct is_pointer_dereferencable : public std::false_type{};
template<class T>
struct is_pointer_dereferencable<T,std::void_t<decltype(std::declval<T>().operator->())>> : public std::true_type{};
template<class T>
struct is_pointer_dereferencable<T*,std::void_t<T*>> : public std::true_type{};
template<class T>
static constexpr bool is_pointer_dereferencable_v = is_pointer_dereferencable<T>::value;
template<class T, class = void>
struct is_prefix_incrementable : public std::false_type{};
template<class T>
struct is_prefix_incrementable<T,std::void_t<decltype(++(std::declval<std::add_lvalue_reference_t<remove_cvref_t<T>>>()))>> : public std::true_type{};
template<class T>
static constexpr bool is_prefix_incrementable_v = is_prefix_incrementable<T>::value;
template<class T, class = void>
struct is_postfix_incrementable : public std::false_type{};
template<class T>
struct is_postfix_incrementable<T,std::void_t<decltype((std::declval<std::add_lvalue_reference_t<remove_cvref_t<T>>>())++)>> : public std::true_type{};
template<class T>
static constexpr bool is_postfix_incrementable_v = is_postfix_incrementable<T>::value;
template<class T, class = void>
struct is_equality_comparable : public std::false_type{};
template<class T>
struct is_equality_comparable<T,std::void_t<decltype(std::declval<T>() == std::declval<T>())>> : public std::true_type{};
template<class T>
static constexpr bool is_equality_comparable_v = is_equality_comparable<T>::value;
template<class T, class = void>
struct is_inequality_comparable : public std::false_type{};
template<class T>
struct is_inequality_comparable<T,std::void_t<decltype(std::declval<T>() != std::declval<T>())>> : public std::true_type{};
template<class T>
static constexpr bool is_inequality_comparable_v = is_inequality_comparable<T>::value;
template<class T>
struct is_legacy_iterator{
static constexpr bool value = is_prefix_incrementable_v<T> && is_dereferencable_v<T>;
};
template<class T>
static constexpr bool is_legacy_iterator_v = is_legacy_iterator<T>::value;
template<class T, bool = is_legacy_iterator_v<T> &&
is_inequality_comparable_v<T> &&
is_pointer_dereferencable_v<T> &&
is_postfix_incrementable_v<T>>
struct is_legacy_input_iterator : public std::false_type{};
template<class T>
struct is_legacy_input_iterator<T,true>{
static constexpr bool value = std::is_convertible_v<decltype(std::declval<T>() == std::declval<T>()),bool> &&
std::is_same_v<decltype(*(std::declval<T>())),typename std::iterator_traits<T>::reference> &&
std::is_same_v<decltype(++std::declval<std::add_lvalue_reference_t<remove_cvref_t<T>>>()),std::add_lvalue_reference_t<T>> &&
std::is_convertible_v<decltype(*std::declval<std::add_lvalue_reference_t<remove_cvref_t<T>>>()++), typename std::iterator_traits<T>::value_type>;
};
template<class T>
static constexpr bool is_legacy_input_iterator_v = is_legacy_input_iterator<T>::value;
template<class T, class Value, bool = is_legacy_iterator_v<T> &&
is_postfix_incrementable_v<T>>
struct is_legacy_output_iterator : public std::false_type{};
template<class T, class Value>
struct is_legacy_output_iterator<T,Value,true>{
static constexpr bool value =
std::is_same_v<decltype(++std::declval<std::add_lvalue_reference_t<remove_cvref_t<T>>>()),std::add_lvalue_reference_t<T>> &&
std::is_convertible_v<decltype(std::declval<std::add_lvalue_reference_t<remove_cvref_t<T>>>()++), std::add_lvalue_reference_t<std::add_const_t<T>>>;
};
template<class T, class Value>
static constexpr bool is_legacy_output_iterator_v = is_legacy_output_iterator<T,Value>::value;
} }
#endif #endif

View File

@ -20,18 +20,10 @@
#define REXY_UTILITY_HPP #define REXY_UTILITY_HPP
#include <utility> //forward, move #include <utility> //forward, move
#include <cstddef> //size_t #include <cstdlib> //size_t
#include <type_traits> //too many to enumerate #include <type_traits>
#include <string> //char_traits
#include "compat/if_consteval.hpp"
#include "rexy.hpp" #include "rexy.hpp"
#include "storage_for.hpp"
#ifdef REXY_if_consteval
#include <cstring> //strlen, strcmp, memcpy
#include <cwchar> //wcslen
#endif
namespace rexy{ namespace rexy{
@ -54,7 +46,7 @@ namespace rexy{
} }
return start2; return start2;
} }
template<class T, std::size_t N> template<class T, size_t N>
constexpr void swap(T (&l)[N], T (&r)[N]) constexpr void swap(T (&l)[N], T (&r)[N])
noexcept(noexcept(swap(*l, *r))) noexcept(noexcept(swap(*l, *r)))
{ {
@ -92,70 +84,11 @@ namespace rexy{
return cmp(l, r) ? l : r; return cmp(l, r) ? l : r;
} }
template<class T> template<class T>
constexpr T abs(const T& val){ constexpr size_t strlen(const T* c)noexcept{
return val > 0 ? val : -val; size_t i = 0;
for(;c[i];++i);
return i;
} }
template<class T>
constexpr std::size_t strlen(const T* c)noexcept{
return std::char_traits<T>::length(c);
}
#ifdef REXY_if_consteval
template<class T>
constexpr int strcmp(const T* l, const T* r)noexcept{
REXY_if_not_consteval{
if constexpr(std::is_same_v<std::remove_cvref_t<T>,char>){
return std::strcmp(l, r);
}else if constexpr(std::is_same_v<std::remove_cvref_t<T>,wchar_t>){
return std::wcscmp(l, r);
}
}
for(;*l == *r && *l;++l, ++r);
return *l - *r;
}
template<class T>
constexpr int strncmp(const T* l, const T* r, std::size_t max)noexcept{
REXY_if_not_consteval{
if constexpr(std::is_same_v<std::remove_cvref_t<T>,char>){
return std::strncmp(l, r, max);
}else if constexpr (std::is_same_v<std::remove_cvref_t<T>,wchar_t>){
return std::wcsncmp(l, r, max);
}
}
for(std::size_t i = 1;*l == *r && *l && i < max;++i, ++l, ++r);
return *l - *r;
}
template<class T, class Compare>
constexpr int strncmp(const T* l, const T* r, std::size_t max, Compare cmp)noexcept{
REXY_if_not_consteval{
if constexpr(std::is_same_v<std::remove_cvref_t<T>,char>){
return std::strncmp(l, r, max);
}else if constexpr (std::is_same_v<std::remove_cvref_t<T>,wchar_t>){
return std::wcsncmp(l, r, max);
}
}
for(std::size_t i = 1;cmp(l, r) && *l && i < max;++i, ++l, ++r);
return *l - *r;
}
template<class T, class Compare>
constexpr int strcmp(const T* l, const T* r, Compare cmp)noexcept{
for(;cmp(*l, *r) && *l;++l, ++r);
return *l - *r;
}
constexpr void memcpy(void* l, const void* r, std::size_t n){
REXY_if_not_consteval{
std::memcpy(l, r, n);
}else{
char* ld = static_cast<char*>(l);
const char* rd = static_cast<const char*>(r);
for(std::size_t i = 0;i < n;++i){
ld[i] = rd[i];
}
}
}
}
#else // REXY_if_consteval
template<class T> template<class T>
constexpr int strcmp(const T* l, const T* r)noexcept{ constexpr int strcmp(const T* l, const T* r)noexcept{
for(;*l == *r && *l;++l, ++r); for(;*l == *r && *l;++l, ++r);
@ -166,66 +99,14 @@ namespace rexy{
for(;cmp(*l, *r) && *l;++l, ++r); for(;cmp(*l, *r) && *l;++l, ++r);
return *l - *r; return *l - *r;
} }
template<class T> constexpr void memcpy(void* l, const void* r, size_t n){
constexpr int strncmp(const T* l, const T* r, std::size_t max)noexcept{
for(std::size_t i = 0;*l == *r && *l && i < max;++i, ++l, ++r);
return *l - *r;
}
constexpr void memcpy(void* l, const void* r, std::size_t n){
char* ld = static_cast<char*>(l); char* ld = static_cast<char*>(l);
const char* rd = static_cast<const char*>(r); const char* rd = static_cast<const char*>(r);
for(std::size_t i = 0;i < n;++i){ for(size_t i = 0;i < n;++i){
ld[i] = rd[i]; ld[i] = rd[i];
} }
} }
} }
#endif // REXY_if_consteval
template<class T>
struct constant_iterator
{
rexy::storage_for<T> val;
constexpr constant_iterator& operator++(void)noexcept{return *this;}
constexpr constant_iterator operator++(int)noexcept{return *this;}
constexpr T operator*(void)const noexcept{return val;}
constexpr bool operator==(const constant_iterator& other)const{return val == other.val;}
constexpr bool operator!=(const constant_iterator& other)const{return val != other.val;}
};
template<class T>
constant_iterator(T&&) -> constant_iterator<std::remove_cvref_t<T>>;
template<class T>
struct sized_constant_iterator
{
rexy::storage_for<T> val;
std::size_t count;
constexpr sized_constant_iterator& operator++(void)noexcept{
--count;
return *this;
}
constexpr sized_constant_iterator operator++(int)noexcept{
sized_constant_iterator other(*this);
--count;
return other;
}
constexpr const T& operator*(void)const noexcept{return val;}
constexpr bool operator==(const sized_constant_iterator& other)const{
return count == other.count;
}
constexpr bool operator!=(const sized_constant_iterator& other)const{
return !(*this == other);
}
};
template<class T>
sized_constant_iterator(T&&, std::size_t) -> sized_constant_iterator<std::remove_cvref_t<T>>;
} }

View File

@ -1,35 +1,29 @@
//Never actually used in the project. This just ensures that all syntax is correct during builds. //Never actually used in the project. This just ensures that all syntax is correct during builds.
#include "rexy/rexy.hpp"
#include "rexy/algorithm.hpp" #include "rexy/algorithm.hpp"
#include "rexy/allocator.hpp" #include "rexy/allocator.hpp"
#include "rexy/buffer.hpp" #include "rexy/buffer.hpp"
#include "rexy/buffer.tpp" #include "rexy/buffer.tpp"
#include "rexy/expression.hpp" #include "rexy/expression.hpp"
#include "rexy/filerd.hpp"
#include "rexy/hash.hpp" #include "rexy/hash.hpp"
#include "rexy/mpmc_queue.hpp" #include "rexy/mpmc_queue.hpp"
#include "rexy/steal.hpp" #include "rexy/steal.hpp"
#include "rexy/string_base.hpp" #include "rexy/string_base.hpp"
#include "rexy/string_base.tpp" #include "rexy/string_base.tpp"
#include "rexy/string_hash.hpp"
#include "rexy/string.hpp" #include "rexy/string.hpp"
#include "rexy/traits.hpp" #include "rexy/traits.hpp"
#include "rexy/utility.hpp" #include "rexy/utility.hpp"
#include "rexy/meta.hpp" #include "rexy/meta.hpp"
#include "rexy/enum_traits.hpp" #include "rexy/enum_traits.hpp"
#include "rexy/deferred.hpp" #include "rexy/deferred.hpp"
#include "rexy/demangle.hpp"
#include "rexy/debug_print.hpp" #include "rexy/debug_print.hpp"
#include "rexy/storage_for.hpp" #include "rexy/storage_for.hpp"
#include "rexy/visitor.hpp" #include "rexy/visitor.hpp"
#include "rexy/string_view.hpp" #include "rexy/string_view.hpp"
#include "rexy/string_view.tpp" #include "rexy/string_view.tpp"
#include "rexy/list.hpp"
#include "rexy/list.tpp"
#ifndef LIBREXY_HEADER_ONLY
#include "rexy/filerd.hpp"
#include "rexy/threadpool.hpp"
#include "rexy/demangle.hpp"
#endif
#include "rexy/detail/string_appender.hpp" #include "rexy/detail/string_appender.hpp"

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-2022 rexy712 Copyright (C) 2020 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,12 +16,6 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#ifdef _MSC_VER
//Disable warning from msvc for not using fopen_s
//which is not standard in c++ as of c++23, though it is in c since c11.
#define _CRT_SECURE_NO_WARNINGS
#endif
#include "rexy/filerd.hpp" #include "rexy/filerd.hpp"
#include <cstdio> //fopen, fclose #include <cstdio> //fopen, fclose
@ -31,99 +25,88 @@
namespace rexy{ namespace rexy{
filerd::filerd(const char* f, const char* mode)noexcept: filerd::filerd(const char* f, const char* mode)noexcept:
m_fp(std::fopen(f, mode)){} m_fp(fopen(f, mode)){}
filerd::~filerd(void)noexcept{ filerd::~filerd(void)noexcept{
if(m_fp) if(m_fp)
std::fclose(m_fp); fclose(m_fp);
} }
void filerd::reset(std::FILE* fp)noexcept{ void filerd::reset(FILE* fp)noexcept{
if(m_fp) if(m_fp)
std::fclose(m_fp); fclose(m_fp);
m_fp = fp; m_fp = fp;
} }
std::FILE* filerd::release(void)noexcept{ FILE* filerd::release(void)noexcept{
return std::exchange(m_fp, nullptr); return std::exchange(m_fp, nullptr);
} }
std::size_t filerd::length(void)noexcept{ size_t filerd::length(void)noexcept{
if(!m_fp) if(!m_fp)
return 0; return 0;
std::size_t tmp, ret; size_t tmp, ret;
tmp = std::ftell(m_fp); tmp = ftell(m_fp);
std::fseek(m_fp, 0, SEEK_END); fseek(m_fp, 0, SEEK_END);
ret = std::ftell(m_fp); ret = ftell(m_fp);
std::fseek(m_fp, long(tmp), SEEK_SET); fseek(m_fp, tmp, SEEK_SET);
return ret; return ret;
} }
std::size_t filerd::position(void)const noexcept{ size_t filerd::position(void)const noexcept{
return std::ftell(m_fp); return ftell(m_fp);
} }
void filerd::rewind(std::size_t pos)noexcept{ void filerd::rewind(size_t pos)noexcept{
std::fseek(m_fp, long(pos), SEEK_SET); fseek(m_fp, pos, SEEK_SET);
} }
filerd::operator std::FILE*(void)noexcept{ filerd::operator FILE*(void)noexcept{
return m_fp; return m_fp;
} }
filerd::operator const std::FILE*(void)const noexcept{ filerd::operator const FILE*(void)const noexcept{
return m_fp; return m_fp;
} }
std::FILE* filerd::get(void)noexcept{ FILE* filerd::get(void)noexcept{
return m_fp; return m_fp;
} }
const std::FILE* filerd::get(void)const noexcept{ const FILE* filerd::get(void)const noexcept{
return m_fp; return m_fp;
} }
filerd::operator bool(void)const noexcept{ filerd::operator bool(void)const noexcept{
return m_fp; return m_fp;
} }
bool filerd::eof(void)const{ size_t filerd::read(char* dest, size_t bytes)noexcept{
return m_finished; return fread(dest, 1, bytes, m_fp);
} }
rexy::string filerd::read(size_t bytes)noexcept{
std::size_t filerd::read(char* dest, std::size_t bytes)noexcept{
const auto rdcnt = std::fread(dest, 1, bytes, m_fp);
if(rdcnt < bytes){
m_finished = true;
}
return rdcnt;
}
rexy::string filerd::read(std::size_t bytes)noexcept{
rexy::string ret; rexy::string ret;
char* tmp = ret.allocator().allocate(bytes); char* tmp = ret.allocator().allocate(bytes);
std::size_t written = read(tmp, bytes); size_t written = read(tmp, bytes);
ret.reset(tmp, written); ret.reset(tmp, written);
return ret; return ret;
} }
rexy::string filerd::readln(std::size_t max)noexcept{ rexy::string filerd::readln(size_t max)noexcept{
rexy::string ret; rexy::string ret;
int c; int c;
std::size_t count = 0; size_t count = 0;
for(c = std::fgetc(m_fp);c != EOF && c != '\n';c = std::fgetc(m_fp)){ for(c = fgetc(m_fp);c != EOF && c != '\n';c = fgetc(m_fp)){
char ch = c; char ch = c;
ret.append(&ch, 1); ret.append(&ch, 1);
if(++count == max) if(++count == max)
break; break;
} }
if(c == EOF){
m_finished = true;
}
return ret; return ret;
} }
rexy::buffer<char> filerd::read_bin(std::size_t bytes) rexy::buffer<char> filerd::read_bin(size_t bytes)
noexcept(std::is_nothrow_constructible<rexy::buffer<char>, char*, std::size_t>::value) noexcept(std::is_nothrow_constructible<rexy::buffer<char>, char*, size_t>::value)
{ {
rexy::buffer<char> ret{bytes}; rexy::buffer<char> ret{bytes};
const auto written = read(ret.data(), bytes); size_t written = read(ret.data(), bytes);
ret.set_size(written); ret.set_size(written);
return ret; return ret;
} }
std::size_t filerd::write(const char* c, std::size_t bytes)noexcept{ size_t filerd::write(const char* c, size_t bytes)noexcept{
return std::fwrite(c, 1, bytes, m_fp); return fwrite(c, 1, bytes, m_fp);
} }
std::size_t filerd::write(rexy::string_view c)noexcept{ size_t filerd::write(const rexy::string_base<char>& c)noexcept{
return write(c.data(), c.length()); return write(c.get(), c.length());
} }
} }

View File

@ -25,8 +25,5 @@ namespace rexy{
template class basic_string<wchar_t,allocator<wchar_t>>; template class basic_string<wchar_t,allocator<wchar_t>>;
template class basic_string<char16_t,allocator<char16_t>>; template class basic_string<char16_t,allocator<char16_t>>;
template class basic_string<char32_t,allocator<char32_t>>; template class basic_string<char32_t,allocator<char32_t>>;
#ifdef __cpp_char8_t
template class basic_string<char8_t,allocator<char8_t>>;
#endif
} }

View File

@ -52,7 +52,7 @@ namespace rexy{
m_valid(true) m_valid(true)
{ {
m_ctor_lock.unlock(); m_ctor_lock.unlock();
for(std::size_t i = 0;i < other.m_workers.size();++i){ for(size_t i = 0;i < other.m_workers.size();++i){
m_workers.emplace_back(&threadpool::worker_loop, this); m_workers.emplace_back(&threadpool::worker_loop, this);
} }
m_qcv.notify_all(); m_qcv.notify_all();

View File

@ -2,14 +2,7 @@ cmake_minimum_required(VERSION 3.0.2)
project(rexylib_tests) project(rexylib_tests)
set(INCLUDE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/../include) set(INCLUDE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/../include)
include_directories("${INCLUDE_PATH}") include_directories("${INCLUDE_PATH}")
add_compile_options(-Wall -Wextra -pedantic -std=c++17)
set(CMAKE_CXX_STANDARD 20)
if(MSVC)
add_compile_options("/Zc:__cplusplus")
else()
add_compile_options("-Wall" "-Wextra" "-pedantic")
endif()
link_libraries(rexy) link_libraries(rexy)
if(ENABLE_PROFILING) if(ENABLE_PROFILING)
@ -18,9 +11,6 @@ if(ENABLE_PROFILING)
endif() endif()
add_executable(basic_string "basic_string.cpp") add_executable(basic_string "basic_string.cpp")
add_executable(format "format.cpp")
set_target_properties(basic_string PROPERTIES OUTPUT_NAME basic_string) set_target_properties(basic_string PROPERTIES OUTPUT_NAME basic_string)
set_target_properties(format PROPERTIES OUTPUT_NAME format)
add_test(NAME basic_string-test COMMAND basic_string) add_test(NAME basic_string-test COMMAND basic_string)
add_test(NAME format-test COMMAND format)

View File

@ -1,22 +1,16 @@
#define LIBREXY_ENABLE_DEBUG_LEVEL 2
#include "rexy/string.hpp" #include "rexy/string.hpp"
#include "rexy/allocator.hpp" #include "rexy/allocator.hpp"
#include "rexy/utility.hpp"
#include "rexy/debug_print.hpp"
#include <cstdlib> #include <cstdlib>
#include <cstdio> #include <cstdio>
#include <cstring>
#include <utility> #include <utility>
#include <new>
[[noreturn]] void error(const char* str){ [[noreturn]] void error(const char* str){
fprintf(stderr, "%s", str); fprintf(stderr, "%s", str);
exit(1); exit(1);
} }
using namespace rexy::str_literals;
using test_str = rexy::basic_string<char,rexy::allocator<char>>; using test_str = rexy::basic_string<char,rexy::allocator<char>>;
void check_empty_construction(){ void check_empty_construction(){
@ -24,16 +18,18 @@ void check_empty_construction(){
if(str1.length() != 0) if(str1.length() != 0)
error("length() should return 0 on default init\n"); error("length() should return 0 on default init\n");
if(test_str::uses_sso()){ if(test_str::uses_sso()){
if(str1.data()[0] != 0) if(str1.get()[0] != 0)
error("data() should return an empty, zero length string\n"); error("get() should return an empty, zero length string\n");
}else{ }else{
if(str1.data() != nullptr) if(str1.get() != nullptr)
error("data() should return a null string\n"); error("get() should return a null string\n");
} }
if(str1.valid()) if(str1.valid())
error("valid() should return false on empty string\n"); error("valid() should return false on empty string\n");
if(str1.data() != str1.c_str()) if(str1.get() != str1.c_str())
error("c_str() should be a synonymn of data()\n"); error("c_str() should be a synonymn of get()\n");
if(char* c = str1;c != str1.get())
error("conversion to pointer type should be synonymous with get()\n");
test_str str2(str1); test_str str2(str1);
if(str2.length() != str1.length()) if(str2.length() != str1.length())
@ -41,11 +37,11 @@ void check_empty_construction(){
if(str2.capacity() != str1.capacity()) if(str2.capacity() != str1.capacity())
error("copy construction on empty string should give equivalent capacity()\n"); error("copy construction on empty string should give equivalent capacity()\n");
if(test_str::uses_sso()){ if(test_str::uses_sso()){
if(str2.data()[0] != str1.data()[0]) if(str2.get()[0] != str1.get()[0])
error("copy construction on empty string should give equivalent data()\n"); error("copy construction on empty string should give equivalent get()\n");
}else{ }else{
if(str2.data() != str1.data()) if(str2.get() != str1.get())
error("copy construction on empty string should give equivalent data()\n"); error("copy construction on empty string should give equivalent get()\n");
} }
test_str str3(std::move(str2)); test_str str3(std::move(str2));
@ -54,11 +50,11 @@ void check_empty_construction(){
if(str3.capacity() != str1.capacity()) if(str3.capacity() != str1.capacity())
error("move construction on empty string should give equivalent capacity()\n"); error("move construction on empty string should give equivalent capacity()\n");
if(test_str::uses_sso()){ if(test_str::uses_sso()){
if(str3.data()[0] != str1.data()[0]) if(str3.get()[0] != str1.get()[0])
error("move construction on empty string should give equivalent data()\n"); error("move construction on empty string should give equivalent get()\n");
}else{ }else{
if(str3.data() != str1.data()) if(str3.get() != str1.get())
error("move construction on empty string should give equivalent data()\n"); error("move construction on empty string should give equivalent get()\n");
} }
} }
@ -72,51 +68,51 @@ void check_short_construction(){
error("short constructed string 'a' should be length() == 1\n"); error("short constructed string 'a' should be length() == 1\n");
if(str1.capacity() != cap) if(str1.capacity() != cap)
error("short constructed string 'a' should be capacity() == short_string_size()\n"); error("short constructed string 'a' should be capacity() == short_string_size()\n");
if(strcmp(str1.data(), "a")) if(strcmp(str1.get(), "a"))
error("short constructed string 'a' should be !strcmp(data(), \"a\")\n"); error("short constructed string 'a' should be !strcmp(get(), \"a\")\n");
test_str str2(str1); test_str str2(str1);
if(str2.length() != str1.length()) if(str2.length() != str1.length())
error("short copy constructed string should have equal length()\n"); error("short copy constructed string should have equal length()\n");
if(str2.capacity() != str1.capacity()) if(str2.capacity() != str1.capacity())
error("short copy constructed string should have equal capacity()\n"); error("short copy constructed string should have equal capacity()\n");
if(strcmp(str2.data(), str1.data())) if(strcmp(str2.get(), str1.get()))
error("short copy constructed string should have equivalent data()\n"); error("short copy constructed string should have equivalent get()\n");
test_str str3(std::move(str2)); test_str str3(std::move(str2));
if(str3.length() != str1.length()) if(str3.length() != str1.length())
error("short move constructed string should have equal length()\n"); error("short move constructed string should have equal length()\n");
if(str3.capacity() != str1.capacity()) if(str3.capacity() != str1.capacity())
error("short move constructed string should have equal capacity()\n"); error("short move constructed string should have equal capacity()\n");
if(strcmp(str3.data(), str1.data())) if(strcmp(str3.get(), str1.get()))
error("short move constructed string should have equivalent data()\n"); error("short move constructed string should have equivalent get()\n");
} }
void check_long_construction(){ void check_long_construction(){
const char* data = "this is a really long string that should ensure that it makes a dynamic allocation even if it has a big buffer."; const char* data = "this is a really long string that should ensure that it makes a dynamic allocation even if it has a big buffer.";
std::size_t len = rexy::strlen(data); size_t len = strlen(data);
test_str str1(data); test_str str1(data);
if(str1.length() != len) if(str1.length() != len)
error("long constructed string should be length() == strlen(data)\n"); error("long constructed string should be length() == strlen(data)\n");
if(str1.capacity() < len) if(str1.capacity() < len)
error("long constructed string should be capacity() >= strlen(data)\n"); error("long constructed string should be capacity() >= strlen(data)\n");
if(strcmp(str1.data(), data)) if(strcmp(str1.get(), data))
error("long constructed string should be !strcmp(data(), data)\n"); error("long constructed string should be !strcmp(get(), data)\n");
test_str str2(str1); test_str str2(str1);
if(str2.length() != str1.length()) if(str2.length() != str1.length())
error("long copy constructed string should have equal length()\n"); error("long copy constructed string should have equal length()\n");
if(str2.capacity() != str1.capacity()) if(str2.capacity() != str1.capacity())
error("long copy constructed string should have equal capacity()\n"); error("long copy constructed string should have equal capacity()\n");
if(strcmp(str2.data(), str1.data())) if(strcmp(str2.get(), str1.get()))
error("long copy constructed string should have equivalent data()\n"); error("long copy constructed string should have equivalent get()\n");
test_str str3(std::move(str2)); test_str str3(std::move(str2));
if(str3.length() != str1.length()) if(str3.length() != str1.length())
error("long move constructed string should have equal length()\n"); error("long move constructed string should have equal length()\n");
if(str3.capacity() != str1.capacity()) if(str3.capacity() != str1.capacity())
error("long move constructed string should have equal capacity()\n"); error("long move constructed string should have equal capacity()\n");
if(strcmp(str3.data(), str1.data())) if(strcmp(str3.get(), str1.get()))
error("long move constructed string should have equivalent data()\n"); error("long move constructed string should have equivalent get()\n");
} }
void check_short_assignment(){ void check_short_assignment(){
if(!test_str::uses_sso()) if(!test_str::uses_sso())
@ -131,8 +127,8 @@ void check_short_assignment(){
error("short assigned string 'a' should be length() == 1\n"); error("short assigned string 'a' should be length() == 1\n");
if(str1.capacity() != cap) if(str1.capacity() != cap)
error("short assigned string 'a' should be capacity() == short_string_size()\n"); error("short assigned string 'a' should be capacity() == short_string_size()\n");
if(strcmp(str1.data(), "a")) if(strcmp(str1.get(), "a"))
error("short assigned string 'a' should be !strcmp(data(), \"a\")\n"); error("short assigned string 'a' should be !strcmp(get(), \"a\")\n");
test_str str2("ba"); test_str str2("ba");
str2 = str1; str2 = str1;
@ -140,8 +136,8 @@ void check_short_assignment(){
error("short copy assigned string should have equal length()\n"); error("short copy assigned string should have equal length()\n");
if(str2.capacity() != str1.capacity()) if(str2.capacity() != str1.capacity())
error("short copy assigned string should have equal capacity()\n"); error("short copy assigned string should have equal capacity()\n");
if(strcmp(str2.data(), str1.data())) if(strcmp(str2.get(), str1.get()))
error("short copy assigned string should have equivalent data()\n"); error("short copy assigned string should have equivalent get()\n");
test_str str3("cb"); test_str str3("cb");
str3 = std::move(str2); str3 = std::move(str2);
@ -149,8 +145,8 @@ void check_short_assignment(){
error("short move assigned string should have equal length()\n"); error("short move assigned string should have equal length()\n");
if(str3.capacity() != str1.capacity()) if(str3.capacity() != str1.capacity())
error("short move assigned string should have equal capacity()\n"); error("short move assigned string should have equal capacity()\n");
if(strcmp(str3.data(), str1.data())) if(strcmp(str3.get(), str1.get()))
error("short move assigned string should have equivalent data()\n"); error("short move assigned string should have equivalent get()\n");
test_str str4(longstartdata); test_str str4(longstartdata);
str4 = str1; str4 = str1;
@ -158,8 +154,8 @@ void check_short_assignment(){
error("long->short copy assigned string should have equal length()\n"); error("long->short copy assigned string should have equal length()\n");
if(str4.capacity() < str1.capacity()) if(str4.capacity() < str1.capacity())
error("long->short copy assigned string should have equal or greater capacity()\n"); error("long->short copy assigned string should have equal or greater capacity()\n");
if(strcmp(str4.data(), str1.data())) if(strcmp(str4.get(), str1.get()))
error("long->short copy assigned string should have equivalent data()\n"); error("long->short copy assigned string should have equivalent get()\n");
test_str str5(longstartdata); test_str str5(longstartdata);
str5 = std::move(str4); str5 = std::move(str4);
@ -167,22 +163,22 @@ void check_short_assignment(){
error("long->short move assigned string should have equal length()\n"); error("long->short move assigned string should have equal length()\n");
if(str5.capacity() < str1.capacity()) if(str5.capacity() < str1.capacity())
error("long->short move assigned string should have equal or greater capacity()\n"); error("long->short move assigned string should have equal or greater capacity()\n");
if(strcmp(str5.data(), str1.data())) if(strcmp(str5.get(), str1.get()))
error("long->short move assigned string should have equivalent data()\n"); error("long->short move assigned string should have equivalent get()\n");
} }
void check_long_assignment(){ void check_long_assignment(){
const char* startdata1 = "this is another really long string that should ensure that it makes some sort of dyn alloc for big buf"; const char* startdata1 = "this is another really long string that should ensure that it makes some sort of dyn alloc for big buf";
const char* startdata2 = "zy"; const char* startdata2 = "zy";
const char* data = "this is a really long string that should ensure that it makes a dynamic allocation even if it has a big buffer."; const char* data = "this is a really long string that should ensure that it makes a dynamic allocation even if it has a big buffer.";
std::size_t len = rexy::strlen(data); size_t len = strlen(data);
test_str str1(startdata1); test_str str1(startdata1);
str1 = data; str1 = data;
if(str1.length() != len) if(str1.length() != len)
error("long assigned string should be length() == strlen(data)\n"); error("long assigned string should be length() == strlen(data)\n");
if(str1.capacity() < len) if(str1.capacity() < len)
error("long assigned string should be capacity() >= strlen(data)\n"); error("long assigned string should be capacity() >= strlen(data)\n");
if(strcmp(str1.data(), data)) if(strcmp(str1.get(), data))
error("long assigned string should be !strcmp(data(), data)\n"); error("long assigned string should be !strcmp(get(), data)\n");
test_str str2(startdata1); test_str str2(startdata1);
str2 = str1; str2 = str1;
@ -190,8 +186,8 @@ void check_long_assignment(){
error("long copy assigned string should have equal length()\n"); error("long copy assigned string should have equal length()\n");
if(str2.capacity() != str1.capacity()) if(str2.capacity() != str1.capacity())
error("long copy assigned string should have equal capacity()\n"); error("long copy assigned string should have equal capacity()\n");
if(strcmp(str2.data(), str1.data())) if(strcmp(str2.get(), str1.get()))
error("long copy assigned string should have equivalent data()\n"); error("long copy assigned string should have equivalent get()\n");
test_str str3(startdata1); test_str str3(startdata1);
str3 = std::move(str2); str3 = std::move(str2);
@ -199,8 +195,8 @@ void check_long_assignment(){
error("long move assigned string should have equal length()\n"); error("long move assigned string should have equal length()\n");
if(str3.capacity() != str1.capacity()) if(str3.capacity() != str1.capacity())
error("long move assigned string should have equal capacity()\n"); error("long move assigned string should have equal capacity()\n");
if(strcmp(str3.data(), str1.data())) if(strcmp(str3.get(), str1.get()))
error("long move assigned string should have equivalent data()\n"); error("long move assigned string should have equivalent get()\n");
test_str str4(startdata2); test_str str4(startdata2);
str4 = str1; str4 = str1;
@ -208,8 +204,8 @@ void check_long_assignment(){
error("short->long copy assigned string should have equal length()\n"); error("short->long copy assigned string should have equal length()\n");
if(str4.capacity() != str1.capacity()) if(str4.capacity() != str1.capacity())
error("short->long copy assigned string should have equal capacity()\n"); error("short->long copy assigned string should have equal capacity()\n");
if(strcmp(str4.data(), str1.data())) if(strcmp(str4.get(), str1.get()))
error("short->long copy assigned string should have equivalent data()\n"); error("short->long copy assigned string should have equivalent get()\n");
test_str str5(startdata2); test_str str5(startdata2);
str5 = std::move(str4); str5 = std::move(str4);
@ -217,8 +213,8 @@ void check_long_assignment(){
error("short->long move assigned string should have equal length()\n"); error("short->long move assigned string should have equal length()\n");
if(str5.capacity() != str1.capacity()) if(str5.capacity() != str1.capacity())
error("short->long move assigned string should have equal capacity()\n"); error("short->long move assigned string should have equal capacity()\n");
if(strcmp(str5.data(), str1.data())) if(strcmp(str5.get(), str1.get()))
error("short->long move assigned string should have equivalent data()\n"); error("short->long move assigned string should have equivalent get()\n");
} }
void check_short_append(){ void check_short_append(){
test_str str1; test_str str1;
@ -227,10 +223,10 @@ void check_short_append(){
str1.append("a"); str1.append("a");
str1.append("b"); str1.append("b");
str1.append(str2); str1.append(str2);
if(strcmp(str1.data(), "abbc")) if(strcmp(str1.get(), "abbc"))
error("short append should have resulted in abbc\n"); error("short append should have resulted in abbc\n");
str1.append(str3); str1.append(str3);
if(strcmp(str1.c_str(), "abbcreally long string that should trigger a short to long conversion in the string")) if(strcmp(str1, "abbcreally long string that should trigger a short to long conversion in the string"))
error("short->long append should have resulted in abbcreally long string that should trigger a short to long conversion in the string\n"); error("short->long append should have resulted in abbcreally long string that should trigger a short to long conversion in the string\n");
} }
void check_long_append(){ void check_long_append(){
@ -238,111 +234,21 @@ void check_long_append(){
const char* appendeddata = "this is another really long string that should ensure that it makes some sort of dyn alloc for big bufstuff"; const char* appendeddata = "this is another really long string that should ensure that it makes some sort of dyn alloc for big bufstuff";
test_str str1(startdata1); test_str str1(startdata1);
str1.append("stuff"); str1.append("stuff");
if(strcmp(str1.c_str(), appendeddata)) if(strcmp(str1.get(), appendeddata))
error("long append should have resulted in this is another really long string that should ensure that it makes some sort of dyn alloc for big bufstuff\n"); error("long append should have resulted in this is another really long string that should ensure that it makes some sort of dyn alloc for big bufstuff\n");
} }
void check_substring(){ void check_substring(){
rexy::string test = "this is a test string"; rexy::string test = "this is a test string";
rexy::string test2 = test.substring(5, 7); rexy::string test2 = test.substring(5, 7);
if(strcmp(test2.c_str(), "is") || test2.length() != 2) if(strcmp(test2.get(), "is") || test2.length() != 2)
error("substring operation should have resulted in 'is'\n"); error("substring operation should have resulted in 'is'\n");
} }
void check_string_search(){ void check_string_search(){
rexy::string test1 = "this is a test string"; rexy::string test1 = "this is a test string";
rexy::string_view test2 = "string"; rexy::string test2 = "string";
auto res = test1.search(test2); auto res = test1.search(test2);
if(test1.begin() + 15 != res){ if(test1 + 15 != res){
error("string search operation 1 did not result in a correct result\n"); error("string search operation did not result in a correct result");
}
test1 = "this string has multiple strings of the word string in it";
res = test1.search(test2);
if(test1.begin() + 5 != res){
error("string search operation 2 did not result in a correct result\n");
}
res = test1.rsearch(test2);
if(test1.begin() + 45 != res){
error("string search operation 3 did not result in a correct result\n");
}
}
void check_string_insert(){
rexy::string test = "this is a string";
auto it = test.search("string");
if(it == test.end()){
error("string search failed\n");
}
test.insert(it - test.begin(), "test ", 5);
if(test != "this is a test string" || test.length() != 21){
error("string insert operation failed\n");
}
test.insert(0, "wow ");
if(test != "wow this is a test string" || test.length() != 25){
error("string insert operation 2 failed\n");
}
test.insert(test.length(), " oof");
if(test != "wow this is a test string oof" || test.length() != 29){
error("string insert operation 3 failed\n");
}
}
void check_string_erase(){
rexy::string test = "this is a test string";
test.erase(0, 5);
if(test != "is a test string" || test.length() != 16){
error("string erase operation 1 did not result in a correct result\n");
}
test.erase(5, 5);
if(test != "is a string" || test.length() != 11){
error("string erase operation 2 did not result in a correct result\n");
}
test.erase(9, 2);
if(test != "is a stri" || test.length() != 9){
error("string erase operation 3 did not result in a correct result\n");
}
test.erase(8, 2);
if(test != "is a str" || test.length() != 8){
error("string erase operation 4 did not result in a correct result\n");
}
test.pop_back();
if(test != "is a st" || test.length() != 7){
error("string erase operation 5 did not result in a correct result\n");
}
}
void check_string_replace(){
rexy::string test = "test string";
test.replace(0, 4, "yolo");
if(test != "yolo string"){
error("string replace operation 1 did not result in a correct result\n");
}
test = "this is a long string version"_sv;
auto it = test.search("long"_sv);
if(it == test.end()){
error("string search failed in replace test\n");
}
test.replace(it, it+4, "thic");
if(test != "this is a thic string version"){
error("string replace operation 2 did not result in a correct result\n");
}
test.replace(test.end() - 7, test.end(), "vrisiod");
if(test != "this is a thic string vrisiod"){
error("string replace operation 3 did not result in a correct result\n");
}
test.replace(test.begin(), test.end(), "change");
if(test != "changes a thic string vrisiod"){
error("string replace operation 4 did not result in a correct result\n");
}
test = "short"_sv;
test.replace(test.begin(), test.end(), "a longer string");
if(test != "a lon"){
error("string replace operation 5 did not result in a correct result\n");
} }
} }
@ -356,8 +262,4 @@ int main(){
check_long_append(); check_long_append();
check_substring(); check_substring();
check_string_search(); check_string_search();
check_string_insert();
check_string_erase();
check_string_replace();
rexy::debug::print_succ("String test success\n");
} }

View File

@ -1,240 +0,0 @@
#define LIBREXY_ENABLE_DEBUG_LEVEL 2
#include "rexy/format.hpp"
#include "rexy/string.hpp"
#include "rexy/debug_print.hpp"
#include "rexy/cx/string.hpp"
#include <cstdio>
#include <limits>
using namespace rexy::str_literals;
[[noreturn]]
void test_fail(rexy::string_view failed, rexy::string_view expected){
rexy::debug::print_error("expected \"%s\", got \"%s\"\n", expected.c_str(), failed.c_str());
exit(-1);
}
[[noreturn]]
void test_fail(void){
exit(-1);
}
#define PERFORM_TEST(desired, formt, ...) \
do{ \
rexy::string str = rexy::format( (formt), __VA_ARGS__); \
if(str != (desired) ){ \
test_fail( (str), (desired) ); \
} \
}while(0)
struct X
{
int i;
constexpr operator int(void)const{return i;}
};
template<class Char>
class rexy::formatter<X,Char> : public rexy::formatter<int,Char>
{
public:
template<class FormatContext>
auto format(X&, FormatContext& ctx) -> decltype(ctx.out()){
*(ctx.out()++) = 'X';
return std::move(ctx.out());
//return rexy::formatter<int,Char>::format(t.i, ctx);
}
};
void do_brace_escapes(void){
PERFORM_TEST("this is } a test string"_sv, "this is }} a test string", nullptr);
PERFORM_TEST("this is a test {string"_sv, "this is a test {{string", nullptr);
PERFORM_TEST("this }is a test {string"_sv, "this }}is a test {{string", nullptr);
PERFORM_TEST("this {is a tes}t string"_sv, "this {{is a tes}}t string", nullptr);
}
void do_empty_format(void){
PERFORM_TEST("0"_sv, "{}", 0);
PERFORM_TEST("0"_sv, "{}", 0ul);
PERFORM_TEST("0"_sv, "{}", short{0});
PERFORM_TEST("0"_sv, "{}", 0.0f);
PERFORM_TEST("0x0"_sv, "{}", nullptr);
PERFORM_TEST("true"_sv, "{}", true);
PERFORM_TEST("cstring"_sv, "{}", "cstring");
PERFORM_TEST("string_view"_sv, "{}", "string_view"_sv);
PERFORM_TEST("string"_sv, "{}", rexy::string{"string"});
PERFORM_TEST("c"_sv, "{}", 'c');
}
void do_index_specifiers(void){
PERFORM_TEST("2"_sv, "{2}", 0, 1, 2);
PERFORM_TEST("0"_sv, "{0}", 0, 1, 2);
PERFORM_TEST("1"_sv, "{1}", "string", 1, 2.8f);
}
void do_named_index_specifiers(void){
using namespace rexy::fmt_literals;
PERFORM_TEST("2"_sv, "{two}", "zero"_a=0, "one"_a=1, "two"_a=2);
PERFORM_TEST("0"_sv, "{zero}", rexy::arg<"zero">(0), rexy::arg<"one">(1), rexy::arg<"two">(2));
PERFORM_TEST("1"_sv, "{named}", "string", "named"_a = 1, 2.8f);
}
void do_dynamic_index_specifiers(void){
PERFORM_TEST("| 5|"_sv, "|{:{}}|", 5, 10);
PERFORM_TEST("| 5.80|"_sv, "|{:{}.{}f}|", 5.8f, 10, 2);
PERFORM_TEST("| 5|"_sv, "|{1:{0}}|", 10, 5);
PERFORM_TEST("| 5.80|"_sv, "|{0:{4}.{1}f}|", 5.8f, 2, 7, 8, 10);
}
void do_dynamic_named_index_specifiers(void){
using namespace rexy::fmt_literals;
PERFORM_TEST("| 5|"_sv, "|{0:{ten}}|", 5, "ten"_a=10);
PERFORM_TEST("| 5.80|"_sv, "|{0:{first}.{second}f}|", 5.8f, rexy::arg<"first">(10), "second"_a=2);
PERFORM_TEST("| 5|"_sv, "|{1:{arg}}|", "arg"_a=10, 5);
PERFORM_TEST("| 5.80|"_sv, "|{value:{width}.{precision}f}|", "value"_a=5.8f, "precision"_a=2, 7, 8, "width"_a=10);
}
void do_width(void){
//integer right align
PERFORM_TEST("| 6|", "|{:6}|", 6);
//float right align
PERFORM_TEST("| 6.4|", "|{:6}|", 6.4f);
//anything els left align
PERFORM_TEST("|str |", "|{:6}|", "str");
PERFORM_TEST("|true |", "|{:6}|", true);
}
void do_precision(void){
//how many decimal digits for floating
PERFORM_TEST("6.00000", "{:.5f}", 6.0f);
//max width for string
PERFORM_TEST("strin", "{:.5}", "string");
}
void do_fill_and_align(void){
//basic left, right, center checks
PERFORM_TEST("*****6"_sv, "{:*>6}", 6);
PERFORM_TEST("6*****"_sv, "{:*<6}", 6);
PERFORM_TEST("**6***"_sv, "{:*^6}", 6);
//Check types with added info after initial conversion
PERFORM_TEST("*8.20*"_sv, "{:*^6.2f}", 8.2f);
PERFORM_TEST("-2****"_sv, "{:*<6}", -2);
PERFORM_TEST("***str"_sv, "{:*>6}", "str");
}
void do_sign(void){
//Positive numbers
PERFORM_TEST("6"_sv, "{}", 6);
PERFORM_TEST("6"_sv, "{:-}", 6);
PERFORM_TEST("+6"_sv, "{:+}", 6);
PERFORM_TEST(" 6"_sv, "{: }", 6);
//Negative numbers
PERFORM_TEST("-6"_sv, "{}", -6);
PERFORM_TEST("-6"_sv, "{:-}", -6);
PERFORM_TEST("-6"_sv, "{:+}", -6);
PERFORM_TEST("-6"_sv, "{: }", -6);
//Works for inf and nan and negative zero
PERFORM_TEST("-0.0"_sv, "{:.1f}", -0.0f);
PERFORM_TEST("-inf"_sv, "{:.1f}", -std::numeric_limits<float>::infinity());
PERFORM_TEST("-nan"_sv, "{:.1f}", -std::numeric_limits<float>::quiet_NaN());
}
void do_alt_form(void){
//Put base prefix after the sign for integers
PERFORM_TEST("-0x6"_sv, "{:#x}", -6);
//Always output decimal point for floating point
PERFORM_TEST("6."_sv, "{:#}", 6.0f);
//Using 'g' or 'G' type, don't trim trailing zeros
PERFORM_TEST("6.000000"_sv, "{:#g}", 6.0f);
}
void do_zero_pad(void){
PERFORM_TEST("000006"_sv, "{:06}", 6);
PERFORM_TEST("0x0006"_sv, "{:06x}", 6);
//Overriden by fill align option
PERFORM_TEST("*****6"_sv, "{:*>06}", 6);
//should not fill for inf or nan
PERFORM_TEST("inf", "{:06}", std::numeric_limits<float>::infinity());
PERFORM_TEST("nan", "{:06}", std::numeric_limits<float>::quiet_NaN());
}
void do_type_specifiers(void){
//integers
PERFORM_TEST("0b1000", "{:b}", 8);
PERFORM_TEST("0B1000", "{:B}", 8);
PERFORM_TEST("a", "{:c}", static_cast<int>('a'));
PERFORM_TEST("8", "{:d}", 8);
PERFORM_TEST("0", "{:o}", 0);
PERFORM_TEST("010", "{:o}", 8);
PERFORM_TEST("0xc", "{:x}", 12);
PERFORM_TEST("0XC", "{:X}", 12);
//strings
PERFORM_TEST("str", "{:s}", "str"_sv);
//char
PERFORM_TEST("a", "{:c}", 'a');
PERFORM_TEST("0b101", "{:b}", static_cast<char>(5));
PERFORM_TEST("0B101", "{:B}", static_cast<char>(5));
PERFORM_TEST("5", "{:d}", static_cast<char>(5));
PERFORM_TEST("0", "{:o}", static_cast<char>(0));
PERFORM_TEST("05", "{:o}", static_cast<char>(5));
PERFORM_TEST("0xa", "{:x}", static_cast<char>(10));
PERFORM_TEST("0XA", "{:X}", static_cast<char>(10));
//bool
PERFORM_TEST("true", "{:s}", true);
PERFORM_TEST("false", "{:s}", false);
PERFORM_TEST("0b1", "{:b}", true);
PERFORM_TEST("0B1", "{:B}", true);
PERFORM_TEST("\u0001", "{:c}", true);
PERFORM_TEST("1", "{:d}", true);
PERFORM_TEST("0", "{:o}", false);
PERFORM_TEST("01", "{:o}", true);
PERFORM_TEST("0x1", "{:x}", true);
PERFORM_TEST("0X1", "{:X}", true);
//float
PERFORM_TEST("1.ap+2", "{:a}", 6.5f);
PERFORM_TEST("1.8dp+2", "{:.2a}", 6.2f);
PERFORM_TEST("1.AP+2", "{:A}", 6.5f);
PERFORM_TEST("1.8DP+2", "{:.2A}", 6.2f);
PERFORM_TEST("6.200000e+00", "{:e}", 6.2f);
PERFORM_TEST("6.200000E+00", "{:E}", 6.2f);
PERFORM_TEST("6.200000", "{:f}", 6.2f);
PERFORM_TEST("6.200000", "{:F}", 6.2f);
PERFORM_TEST("6.2", "{:g}", 6.2f);
PERFORM_TEST("6.2", "{:G}", 6.2f);
//pointer
PERFORM_TEST("0x0", "{:p}", nullptr);
}
//TODO more test coverage
int main(){
do_brace_escapes();
do_empty_format();
do_width();
do_precision();
do_index_specifiers();
do_named_index_specifiers();
do_dynamic_index_specifiers();
do_dynamic_named_index_specifiers();
do_fill_and_align();
do_sign();
do_alt_form();
do_zero_pad();
do_type_specifiers();
rexy::debug::print_succ("Format test success\n");
}