Compare commits
67 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 9ad75d9f82 | |||
| eafe6f5a21 | |||
| 2fe6f0b4e1 | |||
| dff3303733 | |||
| a21c7312f5 | |||
| 11d64d12a0 | |||
| b6a8c83e0b | |||
| c4d83b0310 | |||
| 858f7533e6 | |||
| 43dc4b6654 | |||
| e636ba47fe | |||
| 13ce1550e2 | |||
| 0ac55f3699 | |||
| 1d06516181 | |||
| 26b7723f19 | |||
| c054600b05 | |||
| eb10cf1375 | |||
| 503efeb6b5 | |||
| 6372262304 | |||
| 7ee0beb343 | |||
| dfa4202e55 | |||
| 5eb7821cfc | |||
| d4ab8fb401 | |||
| 3c9b318218 | |||
| 4e764d2205 | |||
| 50f6f81a05 | |||
| 2f64eb40f0 | |||
| 1165165657 | |||
| 31327248e0 | |||
| d9cba881ac | |||
| 272b5c1238 | |||
| 2ecbff1cdd | |||
| fb19bdec67 | |||
| 7f47a4cbf8 | |||
| 5a6d7023ac | |||
| 329a79c3ea | |||
| 5ce29908df | |||
| 61987d937b | |||
| cdd51a6e3a | |||
| 2a69be29a6 | |||
| 37e02ca871 | |||
| 3d17d3ec8c | |||
| 1bc234cbca | |||
| 24ef556ab7 | |||
| 1799c1640b | |||
| 00355a64c6 | |||
| e5d8c0f567 | |||
| d5a0f1927d | |||
| 588835d80a | |||
| da5eb13d94 | |||
| 6937f1a885 | |||
| a2ba8b9eb6 | |||
| 56d680e036 | |||
| a7474d5939 | |||
| 6f07577bf0 | |||
| 35b64db13c | |||
| feca03e9c9 | |||
| 1acce4c588 | |||
| e96899ceba | |||
| 8ab328fcfe | |||
| fd22069562 | |||
| 058ebe026e | |||
| 99b0ac2ed3 | |||
| 2578895b40 | |||
| 9a55d8594b | |||
| 8b6b421c52 | |||
| 5e49ed5f9a |
@ -1,6 +1,9 @@
|
|||||||
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)
|
||||||
@ -9,38 +12,82 @@ 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()
|
||||||
|
|
||||||
option(ENABLE_SHARED "Build shared library" ON)
|
cmake_dependent_option(ENABLE_SHARED "Build shared library" ON "NOT BUILD_HEADER_ONLY" OFF)
|
||||||
|
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)
|
||||||
|
|
||||||
set(SOURCE_LIST "src/filerd.cpp" "src/string.cpp" "src/string_view.cpp" "src/threadpool.cpp" "src/demangle.cpp")
|
if(MSVC)
|
||||||
add_library(ensure OBJECT "src/ensure.cpp")
|
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/out CACHE STRING "")
|
||||||
target_compile_options(ensure PRIVATE -Wall -Wextra -pedantic -std=c++20)
|
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/out CACHE STRING "")
|
||||||
if(ENABLE_SHARED)
|
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/out CACHE STRING "")
|
||||||
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()
|
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)
|
set(CMAKE_CXX_STANDARD 20)
|
||||||
target_link_options(rexy PRIVATE -fsanitize=address -fno-omit-frame-pointer -fno-optimize-sibling-calls)
|
|
||||||
|
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()
|
||||||
|
set(LIBREXY_BUILT_LIBRARY_HEADERS "include/rexy/filerd.hpp" "include/rexy/threadpool.hpp" "include/rexy/demangle.hpp")
|
||||||
|
set(librexy_HEADER_ONLY_BUILD 0)
|
||||||
|
set(SOURCE_LIST "src/filerd.cpp" "src/string.cpp" "src/string_view.cpp" "src/threadpool.cpp" "src/demangle.cpp")
|
||||||
|
|
||||||
|
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()
|
||||||
|
|
||||||
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}
|
||||||
@ -50,9 +97,11 @@ 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"
|
||||||
|
|||||||
@ -19,11 +19,7 @@
|
|||||||
#ifndef REXY_ALGORITHM_HPP
|
#ifndef REXY_ALGORITHM_HPP
|
||||||
#define REXY_ALGORITHM_HPP
|
#define REXY_ALGORITHM_HPP
|
||||||
|
|
||||||
#include "utility.hpp" //swap
|
|
||||||
#include "rexy.hpp"
|
#include "rexy.hpp"
|
||||||
#include <cstdint> //SIZE_MAX
|
|
||||||
#include <cstdlib> //size_t
|
|
||||||
#include <type_traits>
|
|
||||||
|
|
||||||
#include "detail/algorithm.hpp"
|
#include "detail/algorithm.hpp"
|
||||||
|
|
||||||
@ -33,84 +29,20 @@ namespace rexy{
|
|||||||
//right is one past the end of the list
|
//right is one past the end of the list
|
||||||
template<class Iter, class Compare>
|
template<class Iter, class Compare>
|
||||||
constexpr void quicksort(Iter left, Iter right, const Compare& cmp)
|
constexpr void quicksort(Iter left, Iter right, const Compare& cmp)
|
||||||
noexcept(noexcept(detail::qs_partition(left, right, cmp)))
|
noexcept(noexcept(detail::qs_partition(left, right, cmp)));
|
||||||
{
|
|
||||||
while(left < right){
|
|
||||||
auto real_right = right-1;
|
|
||||||
auto pivot = detail::qs_partition(left, real_right, cmp);
|
|
||||||
quicksort(left, pivot, cmp);
|
|
||||||
left = ++pivot;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//Requires Iterators to be LegacyRandomAccessIterators
|
//Requires Iterators to be LegacyRandomAccessIterators
|
||||||
template<class HIter, class NIter>
|
template<class HIter, class NIter>
|
||||||
constexpr HIter two_way_search(const HIter& hstart, const HIter& hend, const NIter& nstart, const NIter& nend){
|
constexpr HIter two_way_search(HIter hstart, HIter hend, NIter nstart, NIter nend);
|
||||||
size_t j = 0;
|
|
||||||
size_t i = 0;
|
|
||||||
size_t nlen = nend - nstart;
|
|
||||||
size_t hlen = hend - hstart;
|
|
||||||
auto [suffix, period] = detail::critical_factorization(nstart, nend);
|
|
||||||
|
|
||||||
if(detail::iter_compare(nstart, nstart + period, suffix)){
|
|
||||||
size_t memory = SIZE_MAX;
|
|
||||||
while(j <= hlen - nlen){
|
|
||||||
i = max(suffix, memory) + 1;
|
|
||||||
//right side
|
|
||||||
while(i < nlen && nstart[i] == hstart[i + j]){
|
|
||||||
++i;
|
|
||||||
}
|
|
||||||
if(i >= nlen){
|
|
||||||
i = suffix;
|
|
||||||
//left side
|
|
||||||
while(i > memory && nstart[i] == hstart[i + j]){
|
|
||||||
--i;
|
|
||||||
}
|
|
||||||
if(i <= memory){
|
|
||||||
return hstart + j;
|
|
||||||
}
|
|
||||||
j += period;
|
|
||||||
memory = nlen - period - 1;
|
|
||||||
}else{
|
|
||||||
j += (i - suffix);
|
|
||||||
memory = SIZE_MAX;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}else{
|
|
||||||
period = max(suffix + 1, nlen - suffix - 1) + 1;
|
|
||||||
j = 0;
|
|
||||||
while(j <= hlen - nlen){
|
|
||||||
i = suffix + 1;
|
|
||||||
//right side
|
|
||||||
while(i < nlen && nstart[i] == hstart[i + j]){
|
|
||||||
++i;
|
|
||||||
}
|
|
||||||
if(i >= nlen){
|
|
||||||
i = suffix;
|
|
||||||
//left side
|
|
||||||
while(i != SIZE_MAX && nstart[i] == hstart[i + j]){
|
|
||||||
--i;
|
|
||||||
}
|
|
||||||
if(i == SIZE_MAX){
|
|
||||||
return hstart + j;
|
|
||||||
}
|
|
||||||
j += period;
|
|
||||||
}else{
|
|
||||||
j += (i - suffix);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return hend;
|
|
||||||
}
|
|
||||||
|
|
||||||
//searcher for use with generic search wrappers
|
//searcher for use with generic search wrappers
|
||||||
struct two_way_searcher{
|
struct two_way_searcher{
|
||||||
template<class HIter, class NIter>
|
template<class HIter, class NIter>
|
||||||
constexpr HIter operator()(const HIter& hstart, const HIter& hend, const NIter& nstart, const NIter& nend)const{
|
constexpr HIter operator()(HIter hstart, HIter hend, NIter nstart, NIter nend)const;
|
||||||
return two_way_search(hstart, hend, nstart, nend);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#include "algorithm.tpp"
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
108
include/rexy/algorithm.tpp
Normal file
108
include/rexy/algorithm.tpp
Normal file
@ -0,0 +1,108 @@
|
|||||||
|
/**
|
||||||
|
This file is a part of rexy's general purpose library
|
||||||
|
Copyright (C) 2020-2022 rexy712
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef REXY_ALGORITHM_TPP
|
||||||
|
#define REXY_ALGORITHM_TPP
|
||||||
|
|
||||||
|
#include <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
|
||||||
@ -1,6 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
This file is a part of rexy's general purpose library
|
This file is a part of rexy's general purpose library
|
||||||
Copyright (C) 2020 rexy712
|
Copyright (C) 2020-2022 rexy712
|
||||||
|
|
||||||
This program is free software: you can redistribute it and/or modify
|
This program is free software: you can redistribute it and/or modify
|
||||||
it under the terms of the GNU General Public License as published by
|
it under the terms of the GNU General Public License as published by
|
||||||
@ -19,78 +19,12 @@
|
|||||||
#ifndef REXY_DEFAULT_ALLOCATOR_HPP
|
#ifndef REXY_DEFAULT_ALLOCATOR_HPP
|
||||||
#define REXY_DEFAULT_ALLOCATOR_HPP
|
#define REXY_DEFAULT_ALLOCATOR_HPP
|
||||||
|
|
||||||
#include <cstdlib> //size_t
|
#include "compat/allocator.hpp"
|
||||||
#include <cstddef> //ptrdiff_t
|
|
||||||
#include <type_traits> //true_type, false_type
|
|
||||||
#include <new>
|
|
||||||
#include <limits> //numeric_limits
|
|
||||||
|
|
||||||
#include "rexy.hpp"
|
#include <type_traits> //declval
|
||||||
|
|
||||||
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;
|
||||||
@ -99,6 +33,14 @@ 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
|
||||||
|
|||||||
@ -21,10 +21,12 @@
|
|||||||
|
|
||||||
#include "allocator.hpp"
|
#include "allocator.hpp"
|
||||||
#include "detail/hasallocator.hpp"
|
#include "detail/hasallocator.hpp"
|
||||||
#include <cstdlib> //size_t, ptrdiff_t
|
#include <cstddef> //size_t, ptrdiff_t
|
||||||
#include <iterator> //reverse_iterator
|
#include <iterator> //reverse_iterator
|
||||||
|
|
||||||
#include "compat/constexpr.hpp"
|
#include "storage_for.hpp"
|
||||||
|
|
||||||
|
#include "compat/standard.hpp"
|
||||||
|
|
||||||
namespace rexy{
|
namespace rexy{
|
||||||
|
|
||||||
@ -33,7 +35,7 @@ namespace rexy{
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
using value_type = T;
|
using value_type = T;
|
||||||
using size_type = size_t;
|
using size_type = std::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*;
|
||||||
@ -52,17 +54,16 @@ namespace rexy{
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
constexpr buffer(void);
|
constexpr buffer(void);
|
||||||
REXY_CPP20_CONSTEXPR buffer(const_pointer data, size_type length)noexcept(noexcept(this->allocate(0)));
|
REXY_CPP20_CONSTEXPR buffer(const_pointer data, size_type length)noexcept(is_nothrow_allocator_v<Allocator>);
|
||||||
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(noexcept(this->allocate(0)));
|
REXY_CPP20_CONSTEXPR buffer(size_type cap)noexcept(is_nothrow_allocator_v<Allocator>);
|
||||||
REXY_CPP20_CONSTEXPR buffer(const buffer& b)noexcept(noexcept(this->allocate(0)));
|
REXY_CPP20_CONSTEXPR buffer(const buffer& b)noexcept(is_nothrow_allocator_v<Allocator>);
|
||||||
constexpr buffer(buffer&& b)noexcept;
|
constexpr buffer(buffer&& b)noexcept;
|
||||||
REXY_CPP20_CONSTEXPR ~buffer(void)noexcept(noexcept(this->deallocate(nullptr, 0)));
|
REXY_CPP20_CONSTEXPR ~buffer(void)noexcept(is_nothrow_allocator_v<Allocator>);
|
||||||
|
|
||||||
REXY_CPP20_CONSTEXPR buffer& operator=(const buffer& b)
|
REXY_CPP20_CONSTEXPR buffer& operator=(const buffer& b)
|
||||||
noexcept(noexcept(this->allocate(0)) &&
|
noexcept(is_nothrow_allocator_v<Allocator>);
|
||||||
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);
|
||||||
@ -93,6 +94,8 @@ 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);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -21,51 +21,52 @@
|
|||||||
|
|
||||||
#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(noexcept(this->allocate(0))):
|
REXY_CPP20_CONSTEXPR buffer<T,Allocator>::buffer(const_pointer data, size_type length)noexcept(is_nothrow_allocator_v<Allocator>):
|
||||||
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){
|
||||||
new (m_data + i) T(data[i]);
|
std::construct_at(m_data + i, 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 != end;++it){
|
for(auto it = start;it != last;++it){
|
||||||
++count;
|
++count;
|
||||||
}
|
}
|
||||||
m_data = this->allocate(sizeof(value_type) * count);
|
m_data = this->allocate(sizeof(value_type) * count);
|
||||||
m_cap = count;
|
m_cap = count;
|
||||||
|
|
||||||
count = 0;
|
count = 0;
|
||||||
for(auto it = start;it != end;++it){
|
for(auto it = start;it != last;++it){
|
||||||
new (m_data + count) T(*it);
|
std::construct_at(m_data + count, *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(noexcept(this->allocate(0))):
|
REXY_CPP20_CONSTEXPR buffer<T,Allocator>::buffer(size_type cap)noexcept(is_nothrow_allocator_v<Allocator>):
|
||||||
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(noexcept(this->allocate(0))):
|
REXY_CPP20_CONSTEXPR buffer<T,Allocator>::buffer(const buffer& b)noexcept(is_nothrow_allocator_v<Allocator>):
|
||||||
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){
|
||||||
new (m_data + i) T(b.m_data[i]);
|
std::construct_at(m_data + i, b.m_data[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
template<class T, class Allocator>
|
template<class T, class Allocator>
|
||||||
@ -74,16 +75,15 @@ 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(noexcept(this->deallocate(nullptr, 0))){
|
REXY_CPP20_CONSTEXPR buffer<T,Allocator>::~buffer(void)noexcept(is_nothrow_allocator_v<Allocator>){
|
||||||
for(size_type i = 0;i < m_size;++i){
|
for(size_type i = 0;i < m_size;++i){
|
||||||
m_data[i].~T();
|
std::destroy_at(m_data + i);
|
||||||
}
|
}
|
||||||
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(noexcept(this->allocate(0)) &&
|
noexcept(is_nothrow_allocator_v<Allocator>)
|
||||||
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){
|
||||||
new (tmp.m_data + i) T(std::move(m_data[i]));
|
std::construct_at(tmp.m_data + i, std::move(m_data[i]));
|
||||||
}
|
}
|
||||||
std::swap(tmp.m_data, m_data);
|
std::swap(tmp.m_data, m_data);
|
||||||
}
|
}
|
||||||
@ -203,12 +203,16 @@ 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){
|
||||||
resize(std::max(m_cap * 2, len + m_size));
|
buffer b(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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
This file is a part of rexy's general purpose library
|
This file is a part of rexy's general purpose library
|
||||||
Copyright (C) 2020 rexy712
|
Copyright (C) 2022 rexy712
|
||||||
|
|
||||||
This program is free software: you can redistribute it and/or modify
|
This program is free software: you can redistribute it and/or modify
|
||||||
it under the terms of the GNU General Public License as published by
|
it under the terms of the GNU General Public License as published by
|
||||||
@ -16,17 +16,8 @@
|
|||||||
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_CX_CX_STRING_HASH_HPP
|
#if __cplusplus >= 202002L
|
||||||
#define REXY_CX_CX_STRING_HASH_HPP
|
#include "cpp20/allocator.hpp"
|
||||||
|
#else
|
||||||
#include "../string_hash.hpp"
|
#include "cpp17/allocator.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
|
||||||
100
include/rexy/compat/cpp17/allocator.hpp
Normal file
100
include/rexy/compat/cpp17/allocator.hpp
Normal file
@ -0,0 +1,100 @@
|
|||||||
|
/**
|
||||||
|
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
|
||||||
44
include/rexy/compat/cpp17/cx/string.hpp
Normal file
44
include/rexy/compat/cpp17/cx/string.hpp
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
/**
|
||||||
|
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
|
||||||
@ -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 __cplusplus >= 202002L && !defined(__clang__)
|
#if defined(__cpp_consteval) && !defined(__clang__)
|
||||||
#define CONSTEVAL consteval
|
#define CONSTEVAL consteval
|
||||||
#else
|
#else
|
||||||
#define CONSTEVAL constexpr
|
#define CONSTEVAL constexpr
|
||||||
|
|||||||
336
include/rexy/compat/cpp17/string_base.hpp
Normal file
336
include/rexy/compat/cpp17/string_base.hpp
Normal file
@ -0,0 +1,336 @@
|
|||||||
|
/**
|
||||||
|
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
|
||||||
73
include/rexy/compat/cpp17/traits.hpp
Normal file
73
include/rexy/compat/cpp17/traits.hpp
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
/**
|
||||||
|
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
|
||||||
139
include/rexy/compat/cpp20/allocator.hpp
Normal file
139
include/rexy/compat/cpp20/allocator.hpp
Normal file
@ -0,0 +1,139 @@
|
|||||||
|
/**
|
||||||
|
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
|
||||||
46
include/rexy/compat/cpp20/cx/string.hpp
Normal file
46
include/rexy/compat/cpp20/cx/string.hpp
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
/**
|
||||||
|
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
|
||||||
257
include/rexy/compat/cpp20/string_base.hpp
Normal file
257
include/rexy/compat/cpp20/string_base.hpp
Normal file
@ -0,0 +1,257 @@
|
|||||||
|
/**
|
||||||
|
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
|
||||||
@ -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) 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_STRING_VIEW_HASH_HPP
|
#ifndef REXY_COMPAT_CPP17_TRAITS_HPP
|
||||||
#define REXY_STRING_VIEW_HASH_HPP
|
#define REXY_COMPAT_CPP17_TRAITS_HPP
|
||||||
|
|
||||||
#include "string_hash.hpp"
|
#include <type_traits> //remove_cvref
|
||||||
#include "string_base.hpp"
|
|
||||||
#include "rexy.hpp"
|
|
||||||
|
|
||||||
namespace rexy{
|
namespace rexy{
|
||||||
|
|
||||||
template<class Char>
|
template<class T>
|
||||||
struct hash<rexy::basic_string_view<Char>> : public string_hash<rexy::basic_string_view<Char>>{};
|
struct remove_cvref : public std::remove_cvref<T>{};
|
||||||
|
template<class T>
|
||||||
|
using remove_cvref_t = typename remove_cvref<T>::type;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
30
include/rexy/compat/if_consteval.hpp
Normal file
30
include/rexy/compat/if_consteval.hpp
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
/**
|
||||||
|
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
|
||||||
45
include/rexy/compat/standard.hpp
Normal file
45
include/rexy/compat/standard.hpp
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
/**
|
||||||
|
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
|
||||||
34
include/rexy/compat/string_base.hpp
Normal file
34
include/rexy/compat/string_base.hpp
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
/**
|
||||||
|
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_STRING_BASE_HPP
|
||||||
|
#define REXY_COMPAT_STRING_BASE_HPP
|
||||||
|
|
||||||
|
#include <cstddef> //size_t
|
||||||
|
|
||||||
|
namespace rexy{
|
||||||
|
static constexpr std::size_t npos = std::size_t(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef __cpp_concepts
|
||||||
|
#include "cpp20/string_base.hpp"
|
||||||
|
#else
|
||||||
|
#include "cpp17/string_base.hpp"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
@ -1,6 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
This file is a part of rexy's general purpose library
|
This file is a part of rexy's general purpose library
|
||||||
Copyright (C) 2020 rexy712
|
Copyright (C) 2022 rexy712
|
||||||
|
|
||||||
This program is free software: you can redistribute it and/or modify
|
This program is free software: you can redistribute it and/or modify
|
||||||
it under the terms of the GNU General Public License as published by
|
it under the terms of the GNU General Public License as published by
|
||||||
@ -16,19 +16,14 @@
|
|||||||
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_BASIC_STRING_HASH_HPP
|
#ifndef REXY_COMPAT_TRAITS_HPP
|
||||||
#define REXY_BASIC_STRING_HASH_HPP
|
#define REXY_COMPAT_TRAITS_HPP
|
||||||
|
|
||||||
#include "string_hash.hpp"
|
#ifdef REXY_STANDARD_CPP20
|
||||||
#include "string.hpp"
|
#include "cpp20/traits.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
|
||||||
@ -27,12 +27,12 @@
|
|||||||
|
|
||||||
namespace rexy::cx{
|
namespace rexy::cx{
|
||||||
|
|
||||||
template<class T, size_t N>
|
template<class T, std::size_t N>
|
||||||
class array
|
class array
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
using value_type = T;
|
using value_type = T;
|
||||||
using size_type = size_t;
|
using size_type = std::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<size_t N>
|
template<std::size_t N>
|
||||||
class array<bool,N> : public detail::bool_specialize_base
|
class array<bool,N> : public detail::bool_specialize_base
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
|||||||
@ -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 = size_t;
|
using size_type = std::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;
|
||||||
~boolean(void)noexcept = default;
|
REXY_CPP20_CONSTEXPR ~boolean(void)noexcept = default;
|
||||||
|
|
||||||
constexpr boolean& operator=(const boolean& b)noexcept{
|
constexpr boolean& operator=(const boolean& b)noexcept{
|
||||||
return *this = static_cast<bool>(b);
|
return *this = static_cast<bool>(b);
|
||||||
@ -86,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;
|
||||||
~booleans(void)noexcept = default;
|
REXY_CPP20_CONSTEXPR ~booleans(void)noexcept = default;
|
||||||
|
|
||||||
constexpr booleans& operator=(const booleans&)noexcept = default;
|
constexpr booleans& operator=(const booleans&)noexcept = default;
|
||||||
constexpr booleans& operator=(booleans&&)noexcept = default;
|
constexpr booleans& operator=(booleans&&)noexcept = default;
|
||||||
|
|
||||||
constexpr boolean operator[](size_t i){
|
constexpr boolean operator[](std::size_t i){
|
||||||
return boolean{m_value, i};
|
return boolean{m_value, i};
|
||||||
}
|
}
|
||||||
constexpr boolean operator[](size_t i)const{
|
constexpr boolean operator[](std::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;
|
||||||
~bool_iter(void)noexcept = default;
|
REXY_CPP20_CONSTEXPR ~bool_iter(void)noexcept = default;
|
||||||
|
|
||||||
constexpr bool_iter& operator=(const bool_iter&)noexcept = default;
|
constexpr bool_iter& operator=(const bool_iter&)noexcept = default;
|
||||||
constexpr bool_iter& operator=(bool_iter&&)noexcept = default;
|
constexpr bool_iter& operator=(bool_iter&&)noexcept = default;
|
||||||
@ -209,7 +209,7 @@ namespace rexy::cx::detail{
|
|||||||
m_offset(b.m_offset){}
|
m_offset(b.m_offset){}
|
||||||
constexpr const_bool_iter(const const_bool_iter&)noexcept = default;
|
constexpr const_bool_iter(const const_bool_iter&)noexcept = default;
|
||||||
constexpr const_bool_iter(const_bool_iter&&)noexcept = default;
|
constexpr const_bool_iter(const_bool_iter&&)noexcept = default;
|
||||||
~const_bool_iter(void)noexcept = default;
|
REXY_CPP20_CONSTEXPR ~const_bool_iter(void)noexcept = default;
|
||||||
|
|
||||||
constexpr const_bool_iter& operator=(const const_bool_iter&)noexcept = default;
|
constexpr const_bool_iter& operator=(const const_bool_iter&)noexcept = default;
|
||||||
constexpr const_bool_iter& operator=(const_bool_iter&&)noexcept = default;
|
constexpr const_bool_iter& operator=(const_bool_iter&&)noexcept = default;
|
||||||
|
|||||||
@ -25,7 +25,7 @@
|
|||||||
#include "../hash.hpp"
|
#include "../hash.hpp"
|
||||||
|
|
||||||
#include <climits> //CHAR_BIT
|
#include <climits> //CHAR_BIT
|
||||||
#include <cstddef> //size_t, ptrdiff_t
|
#include <cstddef> //std::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, size_t N, class Hash = hash<Key>>
|
template<class Key, class Value, std::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 = size_t;
|
using size_type = std::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,size_t>::value);
|
std::is_nothrow_invocable<Hash,Key,std::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,8 +89,4 @@ namespace rexy::cx{
|
|||||||
|
|
||||||
#include "hashmap.tpp"
|
#include "hashmap.tpp"
|
||||||
|
|
||||||
#ifdef REXY_STRING_BASE_HPP
|
|
||||||
#include "../string_hash.hpp"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@ -23,12 +23,12 @@
|
|||||||
|
|
||||||
namespace rexy::cx{
|
namespace rexy::cx{
|
||||||
|
|
||||||
template<class Key, class Value, size_t N, class Hash>
|
template<class Key, class Value, std::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,size_t>::value)
|
std::is_nothrow_invocable<Hash,Key,std::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, size_t N, class Hash>
|
template<class Key, class Value, std::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, size_t N, class Hash>
|
template<class Key, class Value, std::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, size_t N, class Hash>
|
template<class Key, class Value, std::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, size_t N, class Hash = hash<Key>>
|
template<class Key, class Value, std::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);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
This file is a part of rexy's general purpose library
|
This file is a part of rexy's general purpose library
|
||||||
Copyright (C) 2020 rexy712
|
Copyright (C) 2020-2022 rexy712
|
||||||
|
|
||||||
This program is free software: you can redistribute it and/or modify
|
This program is free software: you can redistribute it and/or modify
|
||||||
it under the terms of the GNU General Public License as published by
|
it under the terms of the GNU General Public License as published by
|
||||||
@ -19,57 +19,69 @@
|
|||||||
#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<size_t N, class Char>
|
template<std::size_t N, class Char>
|
||||||
class string;
|
class string;
|
||||||
}
|
}
|
||||||
|
|
||||||
#include "../string_base.hpp"
|
#include "../string_base.hpp" //string_cat_expr
|
||||||
#include "../utility.hpp"
|
#include "../string_view.hpp"
|
||||||
#include <type_traits>
|
#include "../utility.hpp" //strlen, strcmp
|
||||||
#include <cstddef> //ptrdiff_t, size_t
|
#include "../detail/string_appender.hpp"
|
||||||
|
#include "../traits.hpp" //remove_cvref
|
||||||
|
#include <type_traits> //nothrow_invocable, integral_constant, declval
|
||||||
|
#include <iterator> //reverse_iterator
|
||||||
|
|
||||||
#include "../compat/constexpr.hpp"
|
#include "../compat/standard.hpp"
|
||||||
|
|
||||||
//This is different from rexy::static_string in that this doesn't hold a pointer to a constant string array.
|
//This is different from rexy::string_view in that this doesn't hold a pointer to a constant string array.
|
||||||
//This holds a mutable array of data which can be modified during compile time. static_string is
|
//This holds a mutable array of data which can be modified during compile time. string_view is
|
||||||
//designed to be a thin wrapper around a raw char*, this is designed to allow compile time string concatenation
|
//designed to be a thin wrapper around a raw char*, this is designed to allow compile time string concatenation
|
||||||
|
|
||||||
namespace rexy::cx{
|
namespace rexy::cx{
|
||||||
|
|
||||||
template<size_t N, class Char = char>
|
template<std::size_t N, class Char = char>
|
||||||
class string
|
class string
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
using value_type = Char;
|
using value_type = Char;
|
||||||
using size_type = size_t;
|
using size_type = std::size_t;
|
||||||
using difference_type = ptrdiff_t;
|
using difference_type = std::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 char(&data)[M])noexcept:
|
constexpr string(const value_type(&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(strlen(data))
|
m_length(rexy::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];
|
||||||
@ -92,7 +104,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>>, decltype(expr)>::value)
|
noexcept(std::is_nothrow_invocable<rexy::detail::string_appender<cx::string<N,Char>>, 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);
|
||||||
@ -103,7 +115,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 = strlen(c);
|
m_length = rexy::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];
|
||||||
}
|
}
|
||||||
@ -112,45 +124,64 @@ 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{
|
constexpr size_type length(void)const noexcept{return m_length;}
|
||||||
return m_length;
|
constexpr size_type size(void)const noexcept{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 size_type capacity(void)const noexcept{
|
constexpr const_iterator search(const_pointer c)const{
|
||||||
return max_size;
|
return search(basic_string_view<value_type>{c});
|
||||||
}
|
}
|
||||||
constexpr pointer c_str(void)noexcept{
|
template<class Searcher>
|
||||||
return m_data;
|
constexpr const_iterator search(const basic_string_view<value_type>& s, Searcher&& searcher)const{
|
||||||
|
return searcher(cbegin(), cend(), s.cbegin(), s.cend());
|
||||||
}
|
}
|
||||||
constexpr const_pointer c_str(void)const noexcept{
|
template<class Searcher>
|
||||||
return m_data;
|
constexpr const_iterator search(const_pointer c, Searcher&& searcher)const{
|
||||||
}
|
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 bool valid(void)const noexcept{
|
constexpr iterator begin(void)noexcept{return m_data;}
|
||||||
return m_length > 0;
|
constexpr const_iterator begin(void)const noexcept{return m_data;}
|
||||||
}
|
constexpr const_iterator cbegin(void)const noexcept{return m_data;}
|
||||||
constexpr reference operator[](size_type i)noexcept{
|
constexpr iterator end(void)noexcept{return m_data+m_length;}
|
||||||
return m_data[i];
|
constexpr const_iterator end(void)const noexcept{return m_data+m_length;}
|
||||||
}
|
constexpr const_iterator cend(void)const noexcept{return m_data+m_length;}
|
||||||
constexpr const_reference operator[](size_type i)const noexcept{
|
|
||||||
return m_data[i];
|
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();}
|
||||||
|
|
||||||
|
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;
|
||||||
@ -161,48 +192,48 @@ namespace rexy::cx{
|
|||||||
m_data[m_length++] = data[i];
|
m_data[m_length++] = data[i];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
constexpr void append(const_pointer data)noexcept{
|
constexpr void append(const_pointer data)noexcept{append(data, rexy::strlen(data));}
|
||||||
append(data, strlen(data));
|
constexpr void append(const string& s)noexcept{append(s.get(), s.length());}
|
||||||
|
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 void append(const string& s)noexcept{
|
constexpr size_type find_first_of(const_pointer c, size_type pos = 0)const{
|
||||||
append(s.get(), s.length());
|
return rexy::find_first_of(*this, c, pos, rexy::strlen(c));
|
||||||
}
|
}
|
||||||
constexpr void append(const rexy::basic_string_view<value_type>& s)noexcept{
|
constexpr size_type find_first_of(const_pointer c, size_type start, size_type size)const{
|
||||||
append(s.get(), s.length());
|
return rexy::find_first_of(*this, c, start, size);
|
||||||
}
|
}
|
||||||
|
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 REXY_CX_HASH_HPP
|
#ifdef __cpp_concepts
|
||||||
#include "cx_string_hash.hpp"
|
#include "../compat/cpp20/cx/string.hpp"
|
||||||
#endif
|
#else //__cpp_concepts
|
||||||
|
#include "../compat/cpp17/cx/string.hpp"
|
||||||
|
#endif //__cpp_concepts
|
||||||
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@ -24,17 +24,18 @@
|
|||||||
|
|
||||||
#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, size_t N>
|
template<class T, std::size_t N>
|
||||||
class vector
|
class vector
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
using value_type = T;
|
using value_type = T;
|
||||||
using size_type = size_t;
|
using size_type = std::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*;
|
||||||
@ -63,7 +64,7 @@ namespace rexy::cx{
|
|||||||
m_size = min(count, max_elements);
|
m_size = min(count, max_elements);
|
||||||
}
|
}
|
||||||
|
|
||||||
~vector(void)noexcept = default;
|
REXY_CPP20_CONSTEXPR ~vector(void)noexcept = default;
|
||||||
|
|
||||||
constexpr vector& operator=(const vector&)noexcept(std::is_nothrow_copy_assignable<T>::value) = default;
|
constexpr vector& operator=(const vector&)noexcept(std::is_nothrow_copy_assignable<T>::value) = default;
|
||||||
constexpr vector& operator=(vector&&)noexcept(std::is_nothrow_move_assignable<T>::value) = default;
|
constexpr vector& operator=(vector&&)noexcept(std::is_nothrow_move_assignable<T>::value) = default;
|
||||||
@ -243,7 +244,7 @@ namespace rexy::cx{
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
template<size_t N>
|
template<std::size_t N>
|
||||||
class vector<bool,N> : public detail::bool_specialize_base
|
class vector<bool,N> : public detail::bool_specialize_base
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@ -283,7 +284,7 @@ namespace rexy::cx{
|
|||||||
m_size = min(count, max_elements);
|
m_size = min(count, max_elements);
|
||||||
}
|
}
|
||||||
|
|
||||||
~vector(void)noexcept = default;
|
REXY_CPP20_CONSTEXPR ~vector(void)noexcept = default;
|
||||||
|
|
||||||
constexpr vector& operator=(const vector&)noexcept = default;
|
constexpr vector& operator=(const vector&)noexcept = default;
|
||||||
constexpr vector& operator=(vector&&)noexcept = default;
|
constexpr vector& operator=(vector&&)noexcept = default;
|
||||||
|
|||||||
@ -19,24 +19,18 @@
|
|||||||
#ifndef REXY_DEBUG_PRINT_HPP
|
#ifndef REXY_DEBUG_PRINT_HPP
|
||||||
#define REXY_DEBUG_PRINT_HPP
|
#define REXY_DEBUG_PRINT_HPP
|
||||||
|
|
||||||
#ifndef REXY_ENABLE_DEBUG_LEVEL
|
#include "detail/debug_config.hpp"
|
||||||
#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 REXY_ENABLE_DEBUG_LEVEL > 0
|
#if LIBREXY_ENABLE_DEBUG_LEVEL > 0
|
||||||
#define REXY_ENABLE_DEBUG_OUTPUT
|
#define LIBREXY_ENABLE_DEBUG_OUTPUT
|
||||||
#endif
|
#endif
|
||||||
#if REXY_ENABLE_DEBUG_LEVEL > 2
|
#if LIBREXY_ENABLE_DEBUG_LEVEL > 2
|
||||||
#define REXY_ENABLE_DEBUG_VERBOSE_OUTPUT
|
#define LIBREXY_ENABLE_DEBUG_VERBOSE_OUTPUT
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
#ifdef REXY_ENABLE_DEBUG_OUTPUT
|
#ifdef LIBREXY_ENABLE_DEBUG_OUTPUT
|
||||||
#include <cstdio> //fprintf, vfprintf
|
#include <cstdio> //fprintf, vfprintf
|
||||||
#include <utility> //forward
|
#include <utility> //forward
|
||||||
|
|
||||||
@ -64,7 +58,7 @@
|
|||||||
};
|
};
|
||||||
template<class... Args>
|
template<class... Args>
|
||||||
print(Args&&...) -> print<Args&&...>;
|
print(Args&&...) -> print<Args&&...>;
|
||||||
#ifdef REXY_ENABLE_COLOR_DEBUG
|
#ifdef LIBREXY_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()){
|
||||||
@ -138,7 +132,7 @@
|
|||||||
public:
|
public:
|
||||||
using print<Args...>::print;
|
using print<Args...>::print;
|
||||||
};
|
};
|
||||||
#endif //REXY_ENABLE_COLOR_DEBUG
|
#endif //LIBREXY_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>
|
||||||
@ -148,7 +142,7 @@
|
|||||||
template<class... Args>
|
template<class... Args>
|
||||||
print_succ(Args&&...) -> print_succ<Args&&...>;
|
print_succ(Args&&...) -> print_succ<Args&&...>;
|
||||||
|
|
||||||
#ifdef REXY_ENABLE_DEBUG_VERBOSE_OUTPUT
|
#ifdef LIBREXY_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...>{
|
||||||
@ -196,7 +190,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 //REXY_ENABLE_DEBUG_VERBOSE_OUTPUT
|
#endif //LIBREXY_ENABLE_DEBUG_VERBOSE_OUTPUT
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
namespace rexy::debug{
|
namespace rexy::debug{
|
||||||
@ -213,6 +207,6 @@
|
|||||||
static constexpr inline void print_error(...){}
|
static constexpr inline void print_error(...){}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif //REXY_ENABLE_DEBUG_OUTPUT
|
#endif //LIBREXY_ENABLE_DEBUG_OUTPUT
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@ -21,6 +21,9 @@
|
|||||||
|
|
||||||
#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>
|
||||||
@ -36,17 +39,17 @@ namespace rexy{
|
|||||||
template<class... FArgs>
|
template<class... FArgs>
|
||||||
constexpr deferred(FArgs&&... args);
|
constexpr deferred(FArgs&&... args);
|
||||||
|
|
||||||
deferred(const deferred&) = default;
|
REXY_CPP20_CONSTEXPR deferred(const deferred&) = default;
|
||||||
deferred(deferred&&) = default;
|
REXY_CPP20_CONSTEXPR deferred(deferred&&) = default;
|
||||||
~deferred(void) = default;
|
REXY_CPP20_CONSTEXPR ~deferred(void) = default;
|
||||||
deferred& operator=(const deferred&) = default;
|
REXY_CPP20_CONSTEXPR deferred& operator=(const deferred&) = default;
|
||||||
deferred& operator=(deferred&&) = default;
|
REXY_CPP20_CONSTEXPR 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<size_t... Is>
|
template<std::size_t... Is>
|
||||||
constexpr value_type get_value_(std::index_sequence<Is...>);
|
constexpr value_type get_value_(std::index_sequence<Is...>);
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -70,7 +73,7 @@ namespace rexy{
|
|||||||
constexpr operator ret_t(void);
|
constexpr operator ret_t(void);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
template<size_t... Is>
|
template<std::size_t... Is>
|
||||||
constexpr decltype(auto) get_value_(std::index_sequence<Is...>);
|
constexpr decltype(auto) get_value_(std::index_sequence<Is...>);
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -87,7 +90,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<size_t... Is>
|
template<std::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))...};
|
||||||
}
|
}
|
||||||
@ -112,7 +115,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<size_t... Is>
|
template<std::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))...);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -22,10 +22,9 @@
|
|||||||
#include "../utility.hpp" //swap
|
#include "../utility.hpp" //swap
|
||||||
|
|
||||||
#include <type_traits> //nothrow_invocable
|
#include <type_traits> //nothrow_invocable
|
||||||
#include <functional> //less, greater
|
#include <utility> //pair, forward
|
||||||
#include <utility> //pair
|
|
||||||
#include <iterator> //iterator_traits
|
#include <iterator> //iterator_traits
|
||||||
#include <cstdlib> //size_t
|
#include <cstddef> //size_t
|
||||||
|
|
||||||
namespace rexy::detail{
|
namespace rexy::detail{
|
||||||
|
|
||||||
@ -51,12 +50,12 @@ namespace rexy::detail{
|
|||||||
return left;
|
return left;
|
||||||
}
|
}
|
||||||
template<class Iter, class Op>
|
template<class Iter, class Op>
|
||||||
constexpr std::pair<size_t,size_t> max_suffix(const Iter& needle, size_t nlen, const Op& op = Op()){
|
constexpr std::pair<std::size_t,size_t> max_suffix(const Iter& needle, std::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;
|
||||||
size_t max_suffix = -1;
|
std::size_t max_suffix = -1;
|
||||||
size_t j = 0;
|
std::size_t j = 0;
|
||||||
size_t k = 1;
|
std::size_t k = 1;
|
||||||
size_t period = 1;
|
std::size_t period = 1;
|
||||||
value_type a;
|
value_type a;
|
||||||
value_type b;
|
value_type b;
|
||||||
|
|
||||||
@ -81,18 +80,23 @@ namespace rexy::detail{
|
|||||||
}
|
}
|
||||||
return {max_suffix, period};
|
return {max_suffix, period};
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class Iter>
|
template<class Iter>
|
||||||
constexpr std::pair<size_t,size_t> critical_factorization(const Iter& nstart, const Iter& nend){
|
constexpr std::pair<std::size_t,size_t> critical_factorization(const Iter& nstart, const Iter& nend){
|
||||||
auto msuffix = max_suffix(nstart, nend - nstart, std::less{});
|
auto msuffix = max_suffix(nstart, nend - nstart, []<class T, class U>(T&& left, U&& right) constexpr{
|
||||||
auto msuffix_rev = max_suffix(nstart, nend - nstart, std::greater{});
|
return std::forward<T>(left) < std::forward<U>(right);
|
||||||
|
});
|
||||||
|
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, size_t length){
|
constexpr bool iter_compare(const Iter& left, const Iter& right, std::size_t length){
|
||||||
for(size_t i = 0;i < length;++i){
|
for(std::size_t i = 0;i < length;++i){
|
||||||
if(left[i] != right[i])
|
if(left[i] != right[i])
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
This file is a part of rexy's general purpose library
|
This file is a part of rexy's general purpose library
|
||||||
Copyright (C) 2020 rexy712
|
Copyright (C) 2022 rexy712
|
||||||
|
|
||||||
This program is free software: you can redistribute it and/or modify
|
This program is free software: you can redistribute it and/or modify
|
||||||
it under the terms of the GNU General Public License as published by
|
it under the terms of the GNU General Public License as published by
|
||||||
@ -16,15 +16,11 @@
|
|||||||
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
|
|
||||||
|
|
||||||
#if __cplusplus > 201703L
|
|
||||||
#define REXY_CPP20_CONSTEXPR constexpr
|
|
||||||
#define REXY_CPP20_CONSTEVAL consteval
|
|
||||||
#else
|
|
||||||
#define REXY_CPP20_CONSTEXPR
|
|
||||||
#define REXY_CPP20_CONSTEVAL constexpr
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
#ifndef LIBREXY_ENABLE_DEBUG_LEVEL
|
||||||
|
#define LIBREXY_ENABLE_DEBUG_LEVEL 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef LIBREXY_ENABLE_COLOR_DEBUG
|
||||||
|
#define LIBREXY_ENABLE_COLOR_DEBUG 1
|
||||||
#endif
|
#endif
|
||||||
105
include/rexy/detail/format/arg_store.hpp
Normal file
105
include/rexy/detail/format/arg_store.hpp
Normal file
@ -0,0 +1,105 @@
|
|||||||
|
/**
|
||||||
|
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
|
||||||
89
include/rexy/detail/format/arg_store.tpp
Normal file
89
include/rexy/detail/format/arg_store.tpp
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
/**
|
||||||
|
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
|
||||||
100
include/rexy/detail/format/basic_types.hpp
Normal file
100
include/rexy/detail/format/basic_types.hpp
Normal file
@ -0,0 +1,100 @@
|
|||||||
|
/**
|
||||||
|
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
|
||||||
55
include/rexy/detail/format/basic_types.tpp
Normal file
55
include/rexy/detail/format/basic_types.tpp
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
/**
|
||||||
|
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
|
||||||
97
include/rexy/detail/format/context_handler.hpp
Normal file
97
include/rexy/detail/format/context_handler.hpp
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
/**
|
||||||
|
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
|
||||||
162
include/rexy/detail/format/context_handler.tpp
Normal file
162
include/rexy/detail/format/context_handler.tpp
Normal file
@ -0,0 +1,162 @@
|
|||||||
|
/**
|
||||||
|
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
|
||||||
130
include/rexy/detail/format/format_args.hpp
Normal file
130
include/rexy/detail/format/format_args.hpp
Normal file
@ -0,0 +1,130 @@
|
|||||||
|
/**
|
||||||
|
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
|
||||||
236
include/rexy/detail/format/format_args.tpp
Normal file
236
include/rexy/detail/format/format_args.tpp
Normal file
@ -0,0 +1,236 @@
|
|||||||
|
/**
|
||||||
|
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
|
||||||
77
include/rexy/detail/format/format_context.hpp
Normal file
77
include/rexy/detail/format/format_context.hpp
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
/**
|
||||||
|
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
|
||||||
70
include/rexy/detail/format/format_context.tpp
Normal file
70
include/rexy/detail/format/format_context.tpp
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
/**
|
||||||
|
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
|
||||||
47
include/rexy/detail/format/format_error.hpp
Normal file
47
include/rexy/detail/format/format_error.hpp
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
/**
|
||||||
|
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
|
||||||
@ -1,6 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
This file is a part of rexy's general purpose library
|
This file is a part of rexy's general purpose library
|
||||||
Copyright (C) 2020 rexy712
|
Copyright (C) 2022 rexy712
|
||||||
|
|
||||||
This program is free software: you can redistribute it and/or modify
|
This program is free software: you can redistribute it and/or modify
|
||||||
it under the terms of the GNU General Public License as published by
|
it under the terms of the GNU General Public License as published by
|
||||||
@ -16,28 +16,23 @@
|
|||||||
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_STRING_HASH_HPP
|
#ifndef REXY_DETAIL_FORMAT_FORMAT_STRING_HPP
|
||||||
#define REXY_STRING_HASH_HPP
|
#define REXY_DETAIL_FORMAT_FORMAT_STRING_HPP
|
||||||
|
|
||||||
#include "rexy.hpp"
|
#include "../../string_view.hpp"
|
||||||
|
|
||||||
namespace rexy{
|
namespace rexy::fmt::detail{
|
||||||
|
|
||||||
//jenkns one at a time hash
|
//Class accepted as an argument to 'format' calls. Will call a constant-evaluated parse of the format string
|
||||||
template<class Str>
|
//to check for correctness.
|
||||||
struct string_hash{
|
template<class Char, class... Args>
|
||||||
constexpr size_t operator()(const Str& s, size_t salt = 0)const noexcept{
|
class basic_format_string
|
||||||
size_t hash = salt;
|
{
|
||||||
for(size_t i = 0;i < s.length();++i){
|
public:
|
||||||
hash += s[i];
|
basic_string_view<Char> str;
|
||||||
hash += (hash << 10);
|
|
||||||
hash += (hash >> 6);
|
consteval basic_format_string(const Char* f);
|
||||||
}
|
consteval basic_format_string(basic_string_view<Char> f);
|
||||||
hash += (hash << 3);
|
|
||||||
hash ^= (hash << 11);
|
|
||||||
hash += (hash << 15);
|
|
||||||
return hash;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
43
include/rexy/detail/format/format_string.tpp
Normal file
43
include/rexy/detail/format/format_string.tpp
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
/**
|
||||||
|
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
|
||||||
178
include/rexy/detail/format/formatter.hpp
Normal file
178
include/rexy/detail/format/formatter.hpp
Normal file
@ -0,0 +1,178 @@
|
|||||||
|
/**
|
||||||
|
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
|
||||||
665
include/rexy/detail/format/formatter.tpp
Normal file
665
include/rexy/detail/format/formatter.tpp
Normal file
@ -0,0 +1,665 @@
|
|||||||
|
/**
|
||||||
|
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
|
||||||
83
include/rexy/detail/format/internal_types.hpp
Normal file
83
include/rexy/detail/format/internal_types.hpp
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
/**
|
||||||
|
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
|
||||||
117
include/rexy/detail/format/named_args.hpp
Normal file
117
include/rexy/detail/format/named_args.hpp
Normal file
@ -0,0 +1,117 @@
|
|||||||
|
/**
|
||||||
|
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
|
||||||
57
include/rexy/detail/format/named_args.tpp
Normal file
57
include/rexy/detail/format/named_args.tpp
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
/**
|
||||||
|
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
|
||||||
123
include/rexy/detail/format/output_buffer.hpp
Normal file
123
include/rexy/detail/format/output_buffer.hpp
Normal file
@ -0,0 +1,123 @@
|
|||||||
|
/**
|
||||||
|
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
|
||||||
153
include/rexy/detail/format/output_buffer.tpp
Normal file
153
include/rexy/detail/format/output_buffer.tpp
Normal file
@ -0,0 +1,153 @@
|
|||||||
|
/**
|
||||||
|
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
|
||||||
116
include/rexy/detail/format/parse.hpp
Normal file
116
include/rexy/detail/format/parse.hpp
Normal file
@ -0,0 +1,116 @@
|
|||||||
|
/**
|
||||||
|
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
|
||||||
354
include/rexy/detail/format/parse.tpp
Normal file
354
include/rexy/detail/format/parse.tpp
Normal file
@ -0,0 +1,354 @@
|
|||||||
|
/**
|
||||||
|
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
|
||||||
61
include/rexy/detail/format/parse_context.hpp
Normal file
61
include/rexy/detail/format/parse_context.hpp
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
/**
|
||||||
|
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
|
||||||
80
include/rexy/detail/format/parse_context.tpp
Normal file
80
include/rexy/detail/format/parse_context.tpp
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
/**
|
||||||
|
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
|
||||||
129
include/rexy/detail/format/specs_handler.hpp
Normal file
129
include/rexy/detail/format/specs_handler.hpp
Normal file
@ -0,0 +1,129 @@
|
|||||||
|
/**
|
||||||
|
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
|
||||||
389
include/rexy/detail/format/specs_handler.tpp
Normal file
389
include/rexy/detail/format/specs_handler.tpp
Normal file
@ -0,0 +1,389 @@
|
|||||||
|
/**
|
||||||
|
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
|
||||||
38
include/rexy/detail/format/standard_types.hpp
Normal file
38
include/rexy/detail/format/standard_types.hpp
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
/**
|
||||||
|
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
|
||||||
71
include/rexy/detail/format/storage.hpp
Normal file
71
include/rexy/detail/format/storage.hpp
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
/**
|
||||||
|
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
|
||||||
114
include/rexy/detail/format/storage.tpp
Normal file
114
include/rexy/detail/format/storage.tpp
Normal file
@ -0,0 +1,114 @@
|
|||||||
|
/**
|
||||||
|
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
|
||||||
150
include/rexy/detail/format/traits.hpp
Normal file
150
include/rexy/detail/format/traits.hpp
Normal file
@ -0,0 +1,150 @@
|
|||||||
|
/**
|
||||||
|
This file is a part of rexy's general purpose library
|
||||||
|
Copyright (C) 2022 rexy712
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef REXY_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
|
||||||
91
include/rexy/detail/format/utf_iterator.hpp
Normal file
91
include/rexy/detail/format/utf_iterator.hpp
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
/**
|
||||||
|
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
|
||||||
@ -1,6 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
This file is a part of rexy's general purpose library
|
This file is a part of rexy's general purpose library
|
||||||
Copyright (C) 2020 rexy712
|
Copyright (C) 2020-2022 rexy712
|
||||||
|
|
||||||
This program is free software: you can redistribute it and/or modify
|
This program is free software: you can redistribute it and/or modify
|
||||||
it under the terms of the GNU General Public License as published by
|
it under the terms of the GNU General Public License as published by
|
||||||
@ -19,28 +19,24 @@
|
|||||||
#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<class Alloc>
|
template<REXY_ALLOCATOR_CONCEPT Alloc>
|
||||||
struct hasallocator
|
struct hasallocator : public Alloc{
|
||||||
{
|
using Alloc::Alloc;
|
||||||
Alloc m_alloc;
|
using Alloc::operator=;
|
||||||
|
constexpr Alloc& allocator(void){
|
||||||
auto allocate(typename Alloc::size_type bytes)noexcept(noexcept(m_alloc.allocate(0))){
|
return *this;
|
||||||
return m_alloc.allocate(bytes);
|
|
||||||
}
|
}
|
||||||
void deallocate(typename Alloc::pointer p, typename Alloc::size_type bytes)noexcept(noexcept(m_alloc.deallocate(nullptr,0))){
|
constexpr const Alloc& allocator(void)const{
|
||||||
m_alloc.deallocate(p, bytes);
|
return *this;
|
||||||
}
|
|
||||||
|
|
||||||
Alloc& allocator(void){
|
|
||||||
return m_alloc;
|
|
||||||
}
|
|
||||||
const Alloc& allocator(void)const{
|
|
||||||
return m_alloc;
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@ -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,12 +49,7 @@ 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).get(), std::forward<Val>(v).length());
|
m_targ.append(std::forward<Val>(v).data(), 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());
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -21,6 +21,15 @@
|
|||||||
#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>
|
||||||
@ -201,4 +210,6 @@ namespace rexy::enum_traits{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endif //__cpp_concepts
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@ -19,7 +19,7 @@
|
|||||||
#ifndef REXY_EXPRESSION_HPP
|
#ifndef REXY_EXPRESSION_HPP
|
||||||
#define REXY_EXPRESSION_HPP
|
#define REXY_EXPRESSION_HPP
|
||||||
|
|
||||||
#include <type_traits>
|
#include <type_traits> //is_void, remove_reference, conditional
|
||||||
#include <utility> //forward
|
#include <utility> //forward
|
||||||
|
|
||||||
#include "rexy.hpp"
|
#include "rexy.hpp"
|
||||||
@ -29,8 +29,8 @@ namespace rexy{
|
|||||||
template<class L, class R>
|
template<class L, class R>
|
||||||
class binary_expression
|
class binary_expression
|
||||||
{
|
{
|
||||||
static_assert(!std::is_same<std::decay_t<L>,void>::value, "Left value of rexy::binary_expression cannot be void!");
|
static_assert(!std::is_void_v<L>, "Left value of rexy::binary_expression cannot be void!");
|
||||||
static_assert(!std::is_same<std::decay_t<R>,void>::value, "Right value of rexy::binary_expression cannot be void!");
|
static_assert(!std::is_void_v<R>, "Right value of rexy::binary_expression cannot be void!");
|
||||||
|
|
||||||
public:
|
public:
|
||||||
using left_type = std::conditional_t<std::is_rvalue_reference<L>::value,
|
using left_type = std::conditional_t<std::is_rvalue_reference<L>::value,
|
||||||
@ -78,7 +78,7 @@ namespace rexy{
|
|||||||
template<class L>
|
template<class L>
|
||||||
class unary_expression
|
class unary_expression
|
||||||
{
|
{
|
||||||
static_assert(!std::is_same<std::decay_t<L>,void>::value, "Value of rexy::unary_expression cannot be void!");
|
static_assert(!std::is_void_v<L>, "Value of rexy::unary_expression cannot be void!");
|
||||||
|
|
||||||
public:
|
public:
|
||||||
using left_type = std::conditional_t<std::is_rvalue_reference<L>::value,
|
using left_type = std::conditional_t<std::is_rvalue_reference<L>::value,
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
This file is a part of rexy's general purpose library
|
This file is a part of rexy's general purpose library
|
||||||
Copyright (C) 2020 rexy712
|
Copyright (C) 2020-2022 rexy712
|
||||||
|
|
||||||
This program is free software: you can redistribute it and/or modify
|
This program is free software: you can redistribute it and/or modify
|
||||||
it under the terms of the GNU General Public License as published by
|
it under the terms of the GNU General Public License as published by
|
||||||
@ -19,6 +19,8 @@
|
|||||||
#ifndef REXY_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
|
||||||
@ -28,6 +30,7 @@
|
|||||||
#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{
|
||||||
|
|
||||||
@ -35,7 +38,8 @@ namespace rexy{
|
|||||||
class filerd
|
class filerd
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
FILE* m_fp = nullptr;
|
std::FILE* m_fp = nullptr;
|
||||||
|
bool m_finished = false;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
constexpr filerd(void)noexcept = default;
|
constexpr filerd(void)noexcept = default;
|
||||||
@ -46,31 +50,39 @@ 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{
|
||||||
swap(m_fp, f.m_fp);
|
rexy::swap(m_fp, f.m_fp);
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
void reset(FILE* fp = nullptr)noexcept;
|
void reset(std::FILE* fp = nullptr)noexcept;
|
||||||
FILE* release(void)noexcept;
|
std::FILE* release(void)noexcept;
|
||||||
size_t length(void)noexcept;
|
std::size_t length(void)noexcept;
|
||||||
size_t position(void)const noexcept;
|
std::size_t position(void)const noexcept;
|
||||||
void rewind(size_t pos = 0)noexcept;
|
void rewind(std::size_t pos = 0)noexcept;
|
||||||
|
|
||||||
operator FILE*(void)noexcept;
|
operator std::FILE*(void)noexcept;
|
||||||
operator const FILE*(void)const noexcept;
|
operator const std::FILE*(void)const noexcept;
|
||||||
FILE* get(void)noexcept;
|
std::FILE* get(void)noexcept;
|
||||||
const FILE* get(void)const noexcept;
|
const std::FILE* get(void)const noexcept;
|
||||||
operator bool(void)const noexcept;
|
operator bool(void)const noexcept;
|
||||||
|
|
||||||
size_t read(char* dest, size_t bytes)noexcept;
|
bool eof(void)const;
|
||||||
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);
|
|
||||||
|
|
||||||
size_t write(const char* c, size_t bytes)noexcept;
|
std::size_t read(char* dest, std::size_t bytes)noexcept;
|
||||||
size_t write(const rexy::string_base<char>& s)noexcept;
|
rexy::string read(std::size_t bytes)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
|
||||||
|
|||||||
157
include/rexy/format.hpp
Normal file
157
include/rexy/format.hpp
Normal file
@ -0,0 +1,157 @@
|
|||||||
|
/**
|
||||||
|
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
|
||||||
340
include/rexy/format.tpp
Normal file
340
include/rexy/format.tpp
Normal file
@ -0,0 +1,340 @@
|
|||||||
|
/**
|
||||||
|
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
|
||||||
|
|
||||||
@ -1,6 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
This file is a part of rexy's general purpose library
|
This file is a part of rexy's general purpose library
|
||||||
Copyright (C) 2020 rexy712
|
Copyright (C) 2020-2022 rexy712
|
||||||
|
|
||||||
This program is free software: you can redistribute it and/or modify
|
This program is free software: you can redistribute it and/or modify
|
||||||
it under the terms of the GNU General Public License as published by
|
it under the terms of the GNU General Public License as published by
|
||||||
@ -20,17 +20,29 @@
|
|||||||
#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 size_t operator()(const T& t, size_t salt = 0)const noexcept{
|
constexpr std::size_t operator()(const T& t, std::size_t salt = 0)const noexcept{
|
||||||
constexpr size_t bytes = sizeof(size_t);
|
constexpr std::size_t bytes = sizeof(std::size_t);
|
||||||
size_t val = static_cast<size_t>(t);
|
std::size_t val = static_cast<std::size_t>(t);
|
||||||
size_t hash = 5381 + salt; //magic hash number
|
std::size_t hash = 5381 + salt; //magic hash number
|
||||||
for(size_t i = 0;i < bytes;++i){
|
for(std::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;
|
||||||
}
|
}
|
||||||
@ -38,16 +50,32 @@ 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
|
||||||
|
|||||||
400
include/rexy/list.hpp
Normal file
400
include/rexy/list.hpp
Normal file
@ -0,0 +1,400 @@
|
|||||||
|
/**
|
||||||
|
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
|
||||||
861
include/rexy/list.tpp
Normal file
861
include/rexy/list.tpp
Normal file
@ -0,0 +1,861 @@
|
|||||||
|
/**
|
||||||
|
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
|
||||||
@ -20,7 +20,7 @@
|
|||||||
#define REXY_MPMC_QUEUE_HPP
|
#define REXY_MPMC_QUEUE_HPP
|
||||||
|
|
||||||
#include <vector> //vector (duh)
|
#include <vector> //vector (duh)
|
||||||
#include <cstdlib> //size_t
|
#include <cstddef> //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 = size_t;
|
using size_type = std::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 size_t cacheline_size = std::hardware_destructive_interference_size;
|
static constexpr std::size_t cacheline_size = std::hardware_destructive_interference_size;
|
||||||
#else
|
#else
|
||||||
static constexpr size_t cacheline_size = 64;
|
static constexpr std::size_t cacheline_size = 64;
|
||||||
#endif
|
#endif
|
||||||
#else
|
#else
|
||||||
//Best guess
|
//Best guess
|
||||||
static constexpr size_t cacheline_size = 64;
|
static constexpr std::size_t cacheline_size = 64;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
class slot
|
class slot
|
||||||
|
|||||||
@ -21,20 +21,20 @@
|
|||||||
|
|
||||||
#include <utility> //forward, move
|
#include <utility> //forward, move
|
||||||
#include <atomic> //memory_order, atomic
|
#include <atomic> //memory_order, atomic
|
||||||
#include <cstring> //memcpy
|
#include "utility.hpp" //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))
|
||||||
{
|
{
|
||||||
memcpy(m_data, s.m_data, sizeof(s.m_data));
|
rexy::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))
|
||||||
{
|
{
|
||||||
memcpy(m_data, s.m_data, sizeof(s.m_data));
|
rexy::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(){
|
||||||
|
|||||||
@ -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 rexy712
|
Copyright (C) 2021-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,9 +16,20 @@
|
|||||||
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_REXY_HPP
|
#ifndef REXY_CONFIG_REXY_HPP
|
||||||
#define REXY_REXY_HPP
|
#define REXY_CONFIG_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
|
||||||
|
|||||||
@ -22,6 +22,8 @@
|
|||||||
#include <utility> //forward, move
|
#include <utility> //forward, move
|
||||||
#include <type_traits> //is_nothrow_constructible, etc
|
#include <type_traits> //is_nothrow_constructible, etc
|
||||||
|
|
||||||
|
#include "compat/standard.hpp"
|
||||||
|
|
||||||
namespace rexy{
|
namespace rexy{
|
||||||
|
|
||||||
//A wrapper around raw storage.
|
//A wrapper around raw storage.
|
||||||
@ -42,7 +44,19 @@ namespace rexy{
|
|||||||
static constexpr auto size = sizeof(value_type);
|
static constexpr auto size = sizeof(value_type);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
#if __cplusplus >= 202002L && __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:
|
||||||
@ -53,7 +67,7 @@ namespace rexy{
|
|||||||
constexpr storage_for(rvalue_reference t)noexcept(std::is_nothrow_move_constructible<value_type>::value);
|
constexpr storage_for(rvalue_reference t)noexcept(std::is_nothrow_move_constructible<value_type>::value);
|
||||||
constexpr storage_for(const storage_for& s)noexcept(std::is_nothrow_copy_constructible<value_type>::value);
|
constexpr storage_for(const storage_for& s)noexcept(std::is_nothrow_copy_constructible<value_type>::value);
|
||||||
constexpr storage_for(storage_for&& s)noexcept(std::is_nothrow_move_constructible<value_type>::value);
|
constexpr storage_for(storage_for&& s)noexcept(std::is_nothrow_move_constructible<value_type>::value);
|
||||||
constexpr ~storage_for(void)noexcept(std::is_nothrow_destructible<value_type>::value);
|
REXY_CPP20_CONSTEXPR ~storage_for(void)noexcept(std::is_nothrow_destructible<value_type>::value);
|
||||||
|
|
||||||
constexpr storage_for& operator=(const storage_for& s)noexcept(std::is_nothrow_copy_assignable<value_type>::value);
|
constexpr storage_for& operator=(const storage_for& s)noexcept(std::is_nothrow_copy_assignable<value_type>::value);
|
||||||
constexpr storage_for& operator=(storage_for&& s)noexcept(std::is_nothrow_move_assignable<value_type>::value);
|
constexpr storage_for& operator=(storage_for&& s)noexcept(std::is_nothrow_move_assignable<value_type>::value);
|
||||||
@ -65,6 +79,9 @@ namespace rexy{
|
|||||||
constexpr operator const_reference(void)const noexcept;
|
constexpr operator const_reference(void)const noexcept;
|
||||||
constexpr operator rvalue_reference(void)&& noexcept;
|
constexpr operator rvalue_reference(void)&& noexcept;
|
||||||
|
|
||||||
|
constexpr reference get(void)noexcept;
|
||||||
|
constexpr const_reference get(void)const noexcept;
|
||||||
|
|
||||||
constexpr pointer operator->(void)noexcept;
|
constexpr pointer operator->(void)noexcept;
|
||||||
constexpr const_pointer operator->(void)const noexcept;
|
constexpr const_pointer operator->(void)const noexcept;
|
||||||
constexpr reference operator*(void)noexcept;
|
constexpr reference operator*(void)noexcept;
|
||||||
|
|||||||
@ -21,6 +21,141 @@
|
|||||||
|
|
||||||
#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>
|
||||||
@ -50,7 +185,7 @@ namespace rexy{
|
|||||||
m_dirty = 1;
|
m_dirty = 1;
|
||||||
}
|
}
|
||||||
template<class T>
|
template<class T>
|
||||||
constexpr storage_for<T>::~storage_for(void)noexcept(std::is_nothrow_destructible<value_type>::value){
|
REXY_CPP20_CONSTEXPR storage_for<T>::~storage_for(void)noexcept(std::is_nothrow_destructible<value_type>::value){
|
||||||
if(m_dirty){
|
if(m_dirty){
|
||||||
destroy();
|
destroy();
|
||||||
}
|
}
|
||||||
@ -88,13 +223,22 @@ namespace rexy{
|
|||||||
}
|
}
|
||||||
template<class T>
|
template<class T>
|
||||||
constexpr storage_for<T>::operator const_reference(void)const noexcept{
|
constexpr storage_for<T>::operator const_reference(void)const noexcept{
|
||||||
return m_storage;
|
return reinterpret_cast<const_reference>(m_storage);
|
||||||
}
|
}
|
||||||
template<class T>
|
template<class T>
|
||||||
constexpr storage_for<T>::operator rvalue_reference(void)&& noexcept{
|
constexpr storage_for<T>::operator rvalue_reference(void)&& noexcept{
|
||||||
return reinterpret_cast<rvalue_reference>(std::move(m_storage));
|
return reinterpret_cast<rvalue_reference>(std::move(m_storage));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<class T>
|
||||||
|
constexpr auto storage_for<T>::get(void)noexcept -> reference{
|
||||||
|
return reinterpret_cast<reference>(m_storage);
|
||||||
|
}
|
||||||
|
template<class T>
|
||||||
|
constexpr auto storage_for<T>::get(void)const noexcept -> const_reference{
|
||||||
|
return reinterpret_cast<const_reference>(m_storage);
|
||||||
|
}
|
||||||
|
|
||||||
template<class T>
|
template<class T>
|
||||||
constexpr auto storage_for<T>::operator->(void)noexcept -> pointer{
|
constexpr auto storage_for<T>::operator->(void)noexcept -> pointer{
|
||||||
return reinterpret_cast<T*>(&m_storage);
|
return reinterpret_cast<T*>(&m_storage);
|
||||||
@ -120,4 +264,6 @@ namespace rexy{
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endif //__cplusplus
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@ -26,25 +26,25 @@
|
|||||||
namespace rexy{
|
namespace rexy{
|
||||||
|
|
||||||
//new allocated string
|
//new allocated string
|
||||||
using string = basic_string<char,allocator<char>>;
|
using string = basic_string<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
@ -19,10 +19,11 @@
|
|||||||
#ifndef REXY_STRING_VIEW_HPP
|
#ifndef REXY_STRING_VIEW_HPP
|
||||||
#define REXY_STRING_VIEW_HPP
|
#define REXY_STRING_VIEW_HPP
|
||||||
|
|
||||||
#include <cstddef> //size_t, ptrdiff_t
|
#include <cstddef> //std::size_t, ptrdiff_t
|
||||||
#include <iterator> //reverse_iterator
|
#include <iterator> //reverse_iterator
|
||||||
|
|
||||||
#include "compat/constexpr.hpp"
|
#include "compat/standard.hpp"
|
||||||
|
#include "rexy.hpp"
|
||||||
|
|
||||||
namespace rexy{
|
namespace rexy{
|
||||||
|
|
||||||
@ -34,92 +35,150 @@ namespace rexy{
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
using value_type = Char;
|
using value_type = Char;
|
||||||
using size_type = size_t;
|
using size_type = std::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 = pointer;
|
using iterator = const_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;
|
constexpr basic_string_view(void)noexcept = default;
|
||||||
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;
|
constexpr basic_string_view(const basic_string_view& s)noexcept = default;
|
||||||
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;
|
constexpr basic_string_view(basic_string_view&& s)noexcept = default;
|
||||||
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;
|
constexpr basic_string_view& operator=(const basic_string_view& s)noexcept = default;
|
||||||
constexpr basic_string_view& operator=(basic_string_view&&)noexcept;
|
constexpr basic_string_view& operator=(basic_string_view&&)noexcept = default;
|
||||||
|
|
||||||
|
|
||||||
//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 get(void)const noexcept{return m_data;}
|
constexpr const_pointer data(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(const basic_string_view& s)const;
|
constexpr const_iterator search(basic_string_view s)const noexcept;
|
||||||
constexpr const_iterator search(const_pointer c)const;
|
constexpr const_iterator search(const_pointer c)const noexcept;
|
||||||
template<class Searcher>
|
template<class Searcher>
|
||||||
constexpr const_iterator search(const basic_string_view& s, const Searcher& searcher)const;
|
constexpr const_iterator search(basic_string_view s, const Searcher& searcher)const noexcept(
|
||||||
|
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;
|
constexpr const_iterator search(const_pointer c, const Searcher& searcher)const noexcept(
|
||||||
constexpr bool compare(const basic_string_view& s)const{return *this == s;}
|
std::is_nothrow_invocable_v<Searcher, const_iterator, const_iterator, const_pointer, const_pointer>);
|
||||||
constexpr bool compare(const_pointer c)const{return *this == c;}
|
|
||||||
|
|
||||||
constexpr const_iterator begin(void)const{return m_data;}
|
constexpr bool starts_with(basic_string_view sv)const noexcept;
|
||||||
constexpr const_iterator end(void)const{return m_data+m_length;}
|
constexpr bool starts_with(value_type v)const noexcept;
|
||||||
constexpr const_iterator cbegin(void)const{return begin();}
|
constexpr bool starts_with(const_pointer str)const noexcept;
|
||||||
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*, size_t) -> basic_string_view<T>;
|
basic_string_view(const T*, std::size_t) -> basic_string_view<T>;
|
||||||
|
|
||||||
template<class T>
|
using string_view = basic_string_view<char>;
|
||||||
using static_string [[deprecated]] = basic_string_view<T>;
|
using wstring_view = basic_string_view<wchar_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();
|
||||||
@ -128,4 +187,5 @@ namespace{
|
|||||||
|
|
||||||
#include "string_view.tpp"
|
#include "string_view.tpp"
|
||||||
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@ -22,50 +22,219 @@
|
|||||||
#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, strlen(c)){}
|
basic_string_view(c, rexy::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 = strlen(c);
|
m_length = rexy::strlen(c);
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class Char>
|
template<class Char>
|
||||||
constexpr basic_string_view<Char>& basic_string_view<Char>::operator=(basic_string_view&& s)noexcept{
|
constexpr auto basic_string_view<Char>::search(basic_string_view s)const noexcept -> const_iterator{
|
||||||
m_data = s.m_data;
|
if(s.length() > m_length){
|
||||||
m_length = s.m_length;
|
return cend();
|
||||||
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
This file is a part of rexy's general purpose library
|
This file is a part of rexy's general purpose library
|
||||||
Copyright (C) 2020 rexy712
|
Copyright (C) 2020-2022 rexy712
|
||||||
|
|
||||||
This program is free software: you can redistribute it and/or modify
|
This program is free software: you can redistribute it and/or modify
|
||||||
it under the terms of the GNU General Public License as published by
|
it under the terms of the GNU General Public License as published by
|
||||||
@ -29,6 +29,7 @@
|
|||||||
#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"
|
||||||
|
|
||||||
@ -62,14 +63,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<decltype(std::forward<Func>(f)(std::forward<Args>(args)...))>;
|
auto add_job(Func&& f, Args&&... args) -> std::future<std::invoke_result_t<Func,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<decltype(std::forward<Func>(f)(std::forward<Args>(args)...))>{
|
auto threadpool::add_job(Func&& f, Args&&... args) -> std::future<std::invoke_result_t<Func,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)>;
|
||||||
|
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
This file is a part of rexy's general purpose library
|
This file is a part of rexy's general purpose library
|
||||||
Copyright (C) 2020 rexy712
|
Copyright (C) 2020-2022 rexy712
|
||||||
|
|
||||||
This program is free software: you can redistribute it and/or modify
|
This program is free software: you can redistribute it and/or modify
|
||||||
it under the terms of the GNU General Public License as published by
|
it under the terms of the GNU General Public License as published by
|
||||||
@ -20,7 +20,9 @@
|
|||||||
#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{
|
||||||
@ -30,7 +32,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 = std::is_same<std::true_type,decltype(check(std::declval<std::decay_t<T>*>()))>::value;
|
static constexpr bool value = decltype(check(std::declval<std::decay_t<T>*>()))::value;
|
||||||
};
|
};
|
||||||
|
|
||||||
template<class T, template<class...> class U>
|
template<class T, template<class...> class U>
|
||||||
@ -54,6 +56,86 @@ 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
|
||||||
|
|||||||
@ -20,10 +20,18 @@
|
|||||||
#define REXY_UTILITY_HPP
|
#define REXY_UTILITY_HPP
|
||||||
|
|
||||||
#include <utility> //forward, move
|
#include <utility> //forward, move
|
||||||
#include <cstdlib> //size_t
|
#include <cstddef> //size_t
|
||||||
#include <type_traits>
|
#include <type_traits> //too many to enumerate
|
||||||
|
#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{
|
||||||
|
|
||||||
@ -46,7 +54,7 @@ namespace rexy{
|
|||||||
}
|
}
|
||||||
return start2;
|
return start2;
|
||||||
}
|
}
|
||||||
template<class T, size_t N>
|
template<class T, std::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)))
|
||||||
{
|
{
|
||||||
@ -84,11 +92,70 @@ namespace rexy{
|
|||||||
return cmp(l, r) ? l : r;
|
return cmp(l, r) ? l : r;
|
||||||
}
|
}
|
||||||
template<class T>
|
template<class T>
|
||||||
constexpr size_t strlen(const T* c)noexcept{
|
constexpr T abs(const T& val){
|
||||||
size_t i = 0;
|
return val > 0 ? val : -val;
|
||||||
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);
|
||||||
@ -99,14 +166,66 @@ namespace rexy{
|
|||||||
for(;cmp(*l, *r) && *l;++l, ++r);
|
for(;cmp(*l, *r) && *l;++l, ++r);
|
||||||
return *l - *r;
|
return *l - *r;
|
||||||
}
|
}
|
||||||
constexpr void memcpy(void* l, const void* r, size_t n){
|
template<class T>
|
||||||
|
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(size_t i = 0;i < n;++i){
|
for(std::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>>;
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,29 +1,35 @@
|
|||||||
//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"
|
||||||
|
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
This file is a part of rexy's general purpose library
|
This file is a part of rexy's general purpose library
|
||||||
Copyright (C) 2020 rexy712
|
Copyright (C) 2020-2022 rexy712
|
||||||
|
|
||||||
This program is free software: you can redistribute it and/or modify
|
This program is free software: you can redistribute it and/or modify
|
||||||
it under the terms of the GNU General Public License as published by
|
it under the terms of the GNU General Public License as published by
|
||||||
@ -16,6 +16,12 @@
|
|||||||
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
|
||||||
@ -25,88 +31,99 @@
|
|||||||
namespace rexy{
|
namespace rexy{
|
||||||
|
|
||||||
filerd::filerd(const char* f, const char* mode)noexcept:
|
filerd::filerd(const char* f, const char* mode)noexcept:
|
||||||
m_fp(fopen(f, mode)){}
|
m_fp(std::fopen(f, mode)){}
|
||||||
filerd::~filerd(void)noexcept{
|
filerd::~filerd(void)noexcept{
|
||||||
if(m_fp)
|
if(m_fp)
|
||||||
fclose(m_fp);
|
std::fclose(m_fp);
|
||||||
}
|
}
|
||||||
|
|
||||||
void filerd::reset(FILE* fp)noexcept{
|
void filerd::reset(std::FILE* fp)noexcept{
|
||||||
if(m_fp)
|
if(m_fp)
|
||||||
fclose(m_fp);
|
std::fclose(m_fp);
|
||||||
m_fp = fp;
|
m_fp = fp;
|
||||||
}
|
}
|
||||||
FILE* filerd::release(void)noexcept{
|
std::FILE* filerd::release(void)noexcept{
|
||||||
return std::exchange(m_fp, nullptr);
|
return std::exchange(m_fp, nullptr);
|
||||||
}
|
}
|
||||||
size_t filerd::length(void)noexcept{
|
std::size_t filerd::length(void)noexcept{
|
||||||
if(!m_fp)
|
if(!m_fp)
|
||||||
return 0;
|
return 0;
|
||||||
size_t tmp, ret;
|
std::size_t tmp, ret;
|
||||||
tmp = ftell(m_fp);
|
tmp = std::ftell(m_fp);
|
||||||
fseek(m_fp, 0, SEEK_END);
|
std::fseek(m_fp, 0, SEEK_END);
|
||||||
ret = ftell(m_fp);
|
ret = std::ftell(m_fp);
|
||||||
fseek(m_fp, tmp, SEEK_SET);
|
std::fseek(m_fp, long(tmp), SEEK_SET);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
size_t filerd::position(void)const noexcept{
|
std::size_t filerd::position(void)const noexcept{
|
||||||
return ftell(m_fp);
|
return std::ftell(m_fp);
|
||||||
}
|
}
|
||||||
void filerd::rewind(size_t pos)noexcept{
|
void filerd::rewind(std::size_t pos)noexcept{
|
||||||
fseek(m_fp, pos, SEEK_SET);
|
std::fseek(m_fp, long(pos), SEEK_SET);
|
||||||
}
|
}
|
||||||
|
|
||||||
filerd::operator FILE*(void)noexcept{
|
filerd::operator std::FILE*(void)noexcept{
|
||||||
return m_fp;
|
return m_fp;
|
||||||
}
|
}
|
||||||
filerd::operator const FILE*(void)const noexcept{
|
filerd::operator const std::FILE*(void)const noexcept{
|
||||||
return m_fp;
|
return m_fp;
|
||||||
}
|
}
|
||||||
FILE* filerd::get(void)noexcept{
|
std::FILE* filerd::get(void)noexcept{
|
||||||
return m_fp;
|
return m_fp;
|
||||||
}
|
}
|
||||||
const FILE* filerd::get(void)const noexcept{
|
const std::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;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t filerd::read(char* dest, size_t bytes)noexcept{
|
bool filerd::eof(void)const{
|
||||||
return fread(dest, 1, bytes, m_fp);
|
return m_finished;
|
||||||
}
|
}
|
||||||
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);
|
||||||
size_t written = read(tmp, bytes);
|
std::size_t written = read(tmp, bytes);
|
||||||
ret.reset(tmp, written);
|
ret.reset(tmp, written);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
rexy::string filerd::readln(size_t max)noexcept{
|
rexy::string filerd::readln(std::size_t max)noexcept{
|
||||||
rexy::string ret;
|
rexy::string ret;
|
||||||
int c;
|
int c;
|
||||||
size_t count = 0;
|
std::size_t count = 0;
|
||||||
for(c = fgetc(m_fp);c != EOF && c != '\n';c = fgetc(m_fp)){
|
for(c = std::fgetc(m_fp);c != EOF && c != '\n';c = std::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(size_t bytes)
|
rexy::buffer<char> filerd::read_bin(std::size_t bytes)
|
||||||
noexcept(std::is_nothrow_constructible<rexy::buffer<char>, char*, size_t>::value)
|
noexcept(std::is_nothrow_constructible<rexy::buffer<char>, char*, std::size_t>::value)
|
||||||
{
|
{
|
||||||
rexy::buffer<char> ret{bytes};
|
rexy::buffer<char> ret{bytes};
|
||||||
size_t written = read(ret.data(), bytes);
|
const auto written = read(ret.data(), bytes);
|
||||||
ret.set_size(written);
|
ret.set_size(written);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
size_t filerd::write(const char* c, size_t bytes)noexcept{
|
std::size_t filerd::write(const char* c, std::size_t bytes)noexcept{
|
||||||
return fwrite(c, 1, bytes, m_fp);
|
return std::fwrite(c, 1, bytes, m_fp);
|
||||||
}
|
}
|
||||||
size_t filerd::write(const rexy::string_base<char>& c)noexcept{
|
std::size_t filerd::write(rexy::string_view c)noexcept{
|
||||||
return write(c.get(), c.length());
|
return write(c.data(), c.length());
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -25,5 +25,8 @@ 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
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -52,7 +52,7 @@ namespace rexy{
|
|||||||
m_valid(true)
|
m_valid(true)
|
||||||
{
|
{
|
||||||
m_ctor_lock.unlock();
|
m_ctor_lock.unlock();
|
||||||
for(size_t i = 0;i < other.m_workers.size();++i){
|
for(std::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();
|
||||||
|
|||||||
@ -2,7 +2,14 @@ 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)
|
||||||
@ -11,6 +18,9 @@ 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)
|
||||||
|
|||||||
@ -1,16 +1,22 @@
|
|||||||
|
#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(){
|
||||||
@ -18,18 +24,16 @@ 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.get()[0] != 0)
|
if(str1.data()[0] != 0)
|
||||||
error("get() should return an empty, zero length string\n");
|
error("data() should return an empty, zero length string\n");
|
||||||
}else{
|
}else{
|
||||||
if(str1.get() != nullptr)
|
if(str1.data() != nullptr)
|
||||||
error("get() should return a null string\n");
|
error("data() 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.get() != str1.c_str())
|
if(str1.data() != str1.c_str())
|
||||||
error("c_str() should be a synonymn of get()\n");
|
error("c_str() should be a synonymn of data()\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())
|
||||||
@ -37,11 +41,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.get()[0] != str1.get()[0])
|
if(str2.data()[0] != str1.data()[0])
|
||||||
error("copy construction on empty string should give equivalent get()\n");
|
error("copy construction on empty string should give equivalent data()\n");
|
||||||
}else{
|
}else{
|
||||||
if(str2.get() != str1.get())
|
if(str2.data() != str1.data())
|
||||||
error("copy construction on empty string should give equivalent get()\n");
|
error("copy construction on empty string should give equivalent data()\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
test_str str3(std::move(str2));
|
test_str str3(std::move(str2));
|
||||||
@ -50,11 +54,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.get()[0] != str1.get()[0])
|
if(str3.data()[0] != str1.data()[0])
|
||||||
error("move construction on empty string should give equivalent get()\n");
|
error("move construction on empty string should give equivalent data()\n");
|
||||||
}else{
|
}else{
|
||||||
if(str3.get() != str1.get())
|
if(str3.data() != str1.data())
|
||||||
error("move construction on empty string should give equivalent get()\n");
|
error("move construction on empty string should give equivalent data()\n");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -68,51 +72,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.get(), "a"))
|
if(strcmp(str1.data(), "a"))
|
||||||
error("short constructed string 'a' should be !strcmp(get(), \"a\")\n");
|
error("short constructed string 'a' should be !strcmp(data(), \"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.get(), str1.get()))
|
if(strcmp(str2.data(), str1.data()))
|
||||||
error("short copy constructed string should have equivalent get()\n");
|
error("short copy constructed string should have equivalent data()\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.get(), str1.get()))
|
if(strcmp(str3.data(), str1.data()))
|
||||||
error("short move constructed string should have equivalent get()\n");
|
error("short move constructed string should have equivalent data()\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.";
|
||||||
size_t len = strlen(data);
|
std::size_t len = rexy::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.get(), data))
|
if(strcmp(str1.data(), data))
|
||||||
error("long constructed string should be !strcmp(get(), data)\n");
|
error("long constructed string should be !strcmp(data(), 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.get(), str1.get()))
|
if(strcmp(str2.data(), str1.data()))
|
||||||
error("long copy constructed string should have equivalent get()\n");
|
error("long copy constructed string should have equivalent data()\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.get(), str1.get()))
|
if(strcmp(str3.data(), str1.data()))
|
||||||
error("long move constructed string should have equivalent get()\n");
|
error("long move constructed string should have equivalent data()\n");
|
||||||
}
|
}
|
||||||
void check_short_assignment(){
|
void check_short_assignment(){
|
||||||
if(!test_str::uses_sso())
|
if(!test_str::uses_sso())
|
||||||
@ -127,8 +131,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.get(), "a"))
|
if(strcmp(str1.data(), "a"))
|
||||||
error("short assigned string 'a' should be !strcmp(get(), \"a\")\n");
|
error("short assigned string 'a' should be !strcmp(data(), \"a\")\n");
|
||||||
|
|
||||||
test_str str2("ba");
|
test_str str2("ba");
|
||||||
str2 = str1;
|
str2 = str1;
|
||||||
@ -136,8 +140,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.get(), str1.get()))
|
if(strcmp(str2.data(), str1.data()))
|
||||||
error("short copy assigned string should have equivalent get()\n");
|
error("short copy assigned string should have equivalent data()\n");
|
||||||
|
|
||||||
test_str str3("cb");
|
test_str str3("cb");
|
||||||
str3 = std::move(str2);
|
str3 = std::move(str2);
|
||||||
@ -145,8 +149,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.get(), str1.get()))
|
if(strcmp(str3.data(), str1.data()))
|
||||||
error("short move assigned string should have equivalent get()\n");
|
error("short move assigned string should have equivalent data()\n");
|
||||||
|
|
||||||
test_str str4(longstartdata);
|
test_str str4(longstartdata);
|
||||||
str4 = str1;
|
str4 = str1;
|
||||||
@ -154,8 +158,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.get(), str1.get()))
|
if(strcmp(str4.data(), str1.data()))
|
||||||
error("long->short copy assigned string should have equivalent get()\n");
|
error("long->short copy assigned string should have equivalent data()\n");
|
||||||
|
|
||||||
test_str str5(longstartdata);
|
test_str str5(longstartdata);
|
||||||
str5 = std::move(str4);
|
str5 = std::move(str4);
|
||||||
@ -163,22 +167,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.get(), str1.get()))
|
if(strcmp(str5.data(), str1.data()))
|
||||||
error("long->short move assigned string should have equivalent get()\n");
|
error("long->short move assigned string should have equivalent data()\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.";
|
||||||
size_t len = strlen(data);
|
std::size_t len = rexy::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.get(), data))
|
if(strcmp(str1.data(), data))
|
||||||
error("long assigned string should be !strcmp(get(), data)\n");
|
error("long assigned string should be !strcmp(data(), data)\n");
|
||||||
|
|
||||||
test_str str2(startdata1);
|
test_str str2(startdata1);
|
||||||
str2 = str1;
|
str2 = str1;
|
||||||
@ -186,8 +190,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.get(), str1.get()))
|
if(strcmp(str2.data(), str1.data()))
|
||||||
error("long copy assigned string should have equivalent get()\n");
|
error("long copy assigned string should have equivalent data()\n");
|
||||||
|
|
||||||
test_str str3(startdata1);
|
test_str str3(startdata1);
|
||||||
str3 = std::move(str2);
|
str3 = std::move(str2);
|
||||||
@ -195,8 +199,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.get(), str1.get()))
|
if(strcmp(str3.data(), str1.data()))
|
||||||
error("long move assigned string should have equivalent get()\n");
|
error("long move assigned string should have equivalent data()\n");
|
||||||
|
|
||||||
test_str str4(startdata2);
|
test_str str4(startdata2);
|
||||||
str4 = str1;
|
str4 = str1;
|
||||||
@ -204,8 +208,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.get(), str1.get()))
|
if(strcmp(str4.data(), str1.data()))
|
||||||
error("short->long copy assigned string should have equivalent get()\n");
|
error("short->long copy assigned string should have equivalent data()\n");
|
||||||
|
|
||||||
test_str str5(startdata2);
|
test_str str5(startdata2);
|
||||||
str5 = std::move(str4);
|
str5 = std::move(str4);
|
||||||
@ -213,8 +217,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.get(), str1.get()))
|
if(strcmp(str5.data(), str1.data()))
|
||||||
error("short->long move assigned string should have equivalent get()\n");
|
error("short->long move assigned string should have equivalent data()\n");
|
||||||
}
|
}
|
||||||
void check_short_append(){
|
void check_short_append(){
|
||||||
test_str str1;
|
test_str str1;
|
||||||
@ -223,10 +227,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.get(), "abbc"))
|
if(strcmp(str1.data(), "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, "abbcreally long string that should trigger a short to long conversion in the string"))
|
if(strcmp(str1.c_str(), "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(){
|
||||||
@ -234,21 +238,111 @@ 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.get(), appendeddata))
|
if(strcmp(str1.c_str(), 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.get(), "is") || test2.length() != 2)
|
if(strcmp(test2.c_str(), "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 test2 = "string";
|
rexy::string_view test2 = "string";
|
||||||
auto res = test1.search(test2);
|
auto res = test1.search(test2);
|
||||||
if(test1 + 15 != res){
|
if(test1.begin() + 15 != res){
|
||||||
error("string search operation did not result in a correct result");
|
error("string search operation 1 did not result in a correct result\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
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");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -262,4 +356,8 @@ 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");
|
||||||
}
|
}
|
||||||
|
|||||||
240
tests/format.cpp
Normal file
240
tests/format.cpp
Normal file
@ -0,0 +1,240 @@
|
|||||||
|
#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");
|
||||||
|
}
|
||||||
|
|
||||||
Loading…
x
Reference in New Issue
Block a user