Compare commits

..

67 Commits

Author SHA1 Message Date
9ad75d9f82 Fix list::assign 2022-07-22 10:02:32 -07:00
eafe6f5a21 Add missing exception specs to basic_string and basic_string_view. Also implement some missing std functionality to them 2022-07-20 19:13:21 -07:00
2fe6f0b4e1 Add noexcept specification to list 2022-07-20 17:29:08 -07:00
dff3303733 Add string comparisons 2022-07-20 13:31:11 -07:00
a21c7312f5 Change node names to list_node 2022-07-19 17:43:21 -07:00
11d64d12a0 I couldn't figure out how to make construct_at work with an aggregate. I checked the libstdc++ sources and they call placement new directly, so I'm giving up on that. Changed node over to manual constructors and it seems to work much better. Also made it so list constructors don't call 'assign' because that required stored types to implement assignment operators during construction. 2022-07-19 17:39:29 -07:00
b6a8c83e0b Add list class 2022-07-19 16:44:34 -07:00
c4d83b0310 Update detail::value_iterator to become constant_iterator 2022-07-19 16:44:25 -07:00
858f7533e6 Make hasallocator take advantage of empty base optimization for simpler code 2022-07-19 16:43:19 -07:00
43dc4b6654 Add some functionality to make filerd more usable 2022-07-19 16:42:47 -07:00
e636ba47fe Fix insert_impl... again 2022-07-16 19:55:33 -07:00
13ce1550e2 Fix string_view not checking for oversized needles during in search function 2022-07-16 18:31:13 -07:00
0ac55f3699 Improve build times by not including functional 2022-07-15 16:37:42 -07:00
1d06516181 Use correct memcpy 2022-07-15 16:37:06 -07:00
26b7723f19 Make buffer actually constexpr-able 2022-07-15 16:36:54 -07:00
c054600b05 Add success printout to tests to make it easier for robert to tell if they succeeded or not 2022-06-30 12:49:48 -07:00
eb10cf1375 Make dll builds on msvc output to same directory as tests so they should be able to run without manually copying 2022-06-30 12:42:43 -07:00
503efeb6b5 Update filerd to use std namespace types/functions. 2022-06-30 12:22:09 -07:00
6372262304 Fix msvc warnings 2022-06-30 12:14:46 -07:00
7ee0beb343 Remove some dead code and fix format output when to_chars gives a value other than 'nan' or 'inf' 2022-06-30 12:14:23 -07:00
dfa4202e55 Add some formatting tests 2022-06-30 11:37:58 -07:00
5eb7821cfc Fix some formatting output issues 2022-06-30 11:37:46 -07:00
d4ab8fb401 List initialize default integer types because I think it looks better 2022-06-30 10:43:35 -07:00
3c9b318218 Fix warning 2022-06-30 10:43:10 -07:00
4e764d2205 General header #include cleanup 2022-06-29 17:59:49 -07:00
50f6f81a05 Simplify print logic and make utf-8, utf-16, and utf-32 encodings detectible 2022-06-29 17:48:53 -07:00
2f64eb40f0 Fix MSVC build failure 2022-06-29 17:46:57 -07:00
1165165657 Fix utility not using the correct functions for runtime 2022-06-29 17:34:26 -07:00
31327248e0 Change buffer to use allocator traits 2022-06-29 17:32:02 -07:00
d9cba881ac Remove excess include 2022-06-26 14:56:41 -07:00
272b5c1238 Use invoke_result_t in threadpool 2022-06-26 14:56:24 -07:00
2ecbff1cdd Clean up rexy::hash stuff 2022-06-26 14:20:44 -07:00
fb19bdec67 Macro to check for c++20 support instead of long form writing 2022-06-26 11:55:56 -07:00
7f47a4cbf8 Make allocator and string_base compat easier to read and remove unneccessary concepts subdirectory 2022-06-26 11:55:35 -07:00
5a6d7023ac Separate versioning compat in traits 2022-06-25 09:06:13 -07:00
329a79c3ea Fix c++17 build failure 2022-06-24 23:02:27 -07:00
5ce29908df Change size_t to std::size_t 2022-06-23 15:54:09 -07:00
61987d937b Add rsearch to basic_string to find last match in buffer 2022-06-23 15:39:35 -07:00
cdd51a6e3a Remove deprecated string functions. Add replace functions to basic_string. 2022-06-23 15:03:40 -07:00
2a69be29a6 Some minor cleanup to macros and future standard stuff 2022-06-22 18:18:38 -07:00
37e02ca871 Fix C++17 build 2022-06-22 16:43:34 -07:00
3d17d3ec8c Add rexy::format and rexy::print functions 2022-06-22 13:42:53 -07:00
1bc234cbca Change API macro names to be more consistent. 2022-06-22 13:42:07 -07:00
24ef556ab7 Improve basic_string type to include more functionality of std::string. Also add option to disable SSO if so desired 2022-06-22 13:30:00 -07:00
1799c1640b Add insertion functions to basic_string. Add size function to basic_string. 2022-06-21 19:31:07 -07:00
00355a64c6 Update cx::string to be more level with string_view 2022-06-19 18:46:02 -07:00
e5d8c0f567 Move string_view literal operators to str_literals namespace 2022-06-19 11:39:54 -07:00
d5a0f1927d Add iterators to cx/string and fix including null terminator in length when created from an array 2022-06-19 11:27:57 -07:00
588835d80a Fix strcmp not using 'if constexpr' and add strncmp 2022-06-19 11:27:26 -07:00
da5eb13d94 Fix not using constexpr-able version of strlen in strings 2022-06-18 10:38:08 -07:00
6937f1a885 Fix GCC >= 10 -Wstringop-overflow warning when build with -O3 2022-06-18 10:33:23 -07:00
a2ba8b9eb6 Separate debug defines from debug_print to allow using them in other stuff 2022-06-17 13:43:30 -07:00
56d680e036 Add substring function to string_view 2022-06-17 13:41:10 -07:00
a7474d5939 Update cx::string to have better c++20 support 2022-06-17 13:40:48 -07:00
6f07577bf0 Fix abs placement in utility 2022-06-11 08:49:04 -07:00
35b64db13c Merge remote-tracking branch 'refs/remotes/origin/master' 2022-06-11 08:47:50 -07:00
feca03e9c9 Change utility functions to use more efficient runtime versions when invoked at runtime 2022-06-11 08:47:47 -07:00
1acce4c588 Add is_nothrow_allocator type trait 2022-06-08 19:21:49 -07:00
e96899ceba Fixed abs just not doing anything 2022-06-08 16:06:24 -07:00
8ab328fcfe Make string_view trivially copy-able and trivially move-able. Add wstring alias for basic_string<wchar_t>. Add constexpr version of 'abs' function. 2022-06-08 12:09:52 -07:00
fd22069562 Add some iterator manipulation to string_view. Add a push_back function to strings so that they can be used in standard library stuff more 2022-05-28 16:06:08 -07:00
058ebe026e Fix string hashing header includes 2022-05-25 12:30:35 -07:00
99b0ac2ed3 Fix storage_for and revert one changed macro to correct constexpr behavior 2022-05-24 17:35:14 -07:00
2578895b40 More general cleanup of conditional C++20 support. Add Allocator concept and apply it to strings. 2022-05-24 17:24:55 -07:00
9a55d8594b General code cleanup and allow for more C++20 2022-05-24 14:37:41 -07:00
8b6b421c52 Merge branch 'master' of ssh://rexy712.chickenkiller.com:1995/rexy712/rexylib 2022-05-23 18:26:26 -07:00
5e49ed5f9a Enable header only build 2022-05-23 18:25:44 -07:00
91 changed files with 10121 additions and 1343 deletions

View File

@ -1,6 +1,9 @@
project(librexy)
cmake_minimum_required(VERSION 3.0.2)
include(GNUInstallDirs)
include(CMakeDependentOption)
set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS 1)
set(librexy_VERSION_STRING "000020000L")
set(librexy_VERSION_MAJOR 0)
@ -9,38 +12,82 @@ set(librexy_VERSION_REVISION 0)
set(INCLUDE_PATH ${CMAKE_SOURCE_DIR}/include)
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(BUILD_TESTS "Enable testing" OFF)
option(BUILD_HEADER_ONLY "Enable header only build" OFF)
mark_as_advanced(ENABLE_PROFILING)
set(SOURCE_LIST "src/filerd.cpp" "src/string.cpp" "src/string_view.cpp" "src/threadpool.cpp" "src/demangle.cpp")
add_library(ensure OBJECT "src/ensure.cpp")
target_compile_options(ensure PRIVATE -Wall -Wextra -pedantic -std=c++20)
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")
if(MSVC)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/out CACHE STRING "")
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/out CACHE STRING "")
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/out CACHE STRING "")
endif()
set_target_properties(rexy PROPERTIES VERSION "${librexy_VERSION_MAJOR}.${librexy_VERSION_MINOR}.${librexy_VERSION_REVISION}")
if(ENABLE_PROFILING)
target_compile_options(rexy PRIVATE -fsanitize=address -fno-omit-frame-pointer -fno-optimize-sibling-calls)
target_link_options(rexy PRIVATE -fsanitize=address -fno-omit-frame-pointer -fno-optimize-sibling-calls)
set(CMAKE_CXX_STANDARD 20)
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()
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()
add_subdirectory(tests)
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
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
@ -50,9 +97,11 @@ install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/pc/librexy.pc"
DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig"
)
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/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/concepts" DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/rexy" FILES_MATCHING PATTERN "*.hpp" PATTERN "*.tpp")
configure_file(
"${CMAKE_CURRENT_SOURCE_DIR}/pc/librexy.pc.cmake.in"

View File

@ -19,11 +19,7 @@
#ifndef REXY_ALGORITHM_HPP
#define REXY_ALGORITHM_HPP
#include "utility.hpp" //swap
#include "rexy.hpp"
#include <cstdint> //SIZE_MAX
#include <cstdlib> //size_t
#include <type_traits>
#include "detail/algorithm.hpp"
@ -33,84 +29,20 @@ namespace rexy{
//right is one past the end of the list
template<class Iter, class Compare>
constexpr void quicksort(Iter left, Iter right, const Compare& cmp)
noexcept(noexcept(detail::qs_partition(left, right, cmp)))
{
while(left < right){
auto real_right = right-1;
auto pivot = detail::qs_partition(left, real_right, cmp);
quicksort(left, pivot, cmp);
left = ++pivot;
}
}
noexcept(noexcept(detail::qs_partition(left, right, cmp)));
//Requires Iterators to be LegacyRandomAccessIterators
template<class HIter, class NIter>
constexpr HIter two_way_search(const HIter& hstart, const HIter& hend, const NIter& nstart, const NIter& nend){
size_t j = 0;
size_t i = 0;
size_t nlen = nend - nstart;
size_t hlen = hend - hstart;
auto [suffix, period] = detail::critical_factorization(nstart, nend);
if(detail::iter_compare(nstart, nstart + period, suffix)){
size_t memory = SIZE_MAX;
while(j <= hlen - nlen){
i = max(suffix, memory) + 1;
//right side
while(i < nlen && nstart[i] == hstart[i + j]){
++i;
}
if(i >= nlen){
i = suffix;
//left side
while(i > memory && nstart[i] == hstart[i + j]){
--i;
}
if(i <= memory){
return hstart + j;
}
j += period;
memory = nlen - period - 1;
}else{
j += (i - suffix);
memory = SIZE_MAX;
}
}
}else{
period = max(suffix + 1, nlen - suffix - 1) + 1;
j = 0;
while(j <= hlen - nlen){
i = suffix + 1;
//right side
while(i < nlen && nstart[i] == hstart[i + j]){
++i;
}
if(i >= nlen){
i = suffix;
//left side
while(i != SIZE_MAX && nstart[i] == hstart[i + j]){
--i;
}
if(i == SIZE_MAX){
return hstart + j;
}
j += period;
}else{
j += (i - suffix);
}
}
}
return hend;
}
constexpr HIter two_way_search(HIter hstart, HIter hend, NIter nstart, NIter nend);
//searcher for use with generic search wrappers
struct two_way_searcher{
template<class HIter, class NIter>
constexpr HIter operator()(const HIter& hstart, const HIter& hend, const NIter& nstart, const NIter& nend)const{
return two_way_search(hstart, hend, nstart, nend);
}
constexpr HIter operator()(HIter hstart, HIter hend, NIter nstart, NIter nend)const;
};
}
#include "algorithm.tpp"
#endif

108
include/rexy/algorithm.tpp Normal file
View 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

View File

@ -1,6 +1,6 @@
/**
This file is a part of rexy's general purpose library
Copyright (C) 2020 rexy712
Copyright (C) 2020-2022 rexy712
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -19,78 +19,12 @@
#ifndef REXY_DEFAULT_ALLOCATOR_HPP
#define REXY_DEFAULT_ALLOCATOR_HPP
#include <cstdlib> //size_t
#include <cstddef> //ptrdiff_t
#include <type_traits> //true_type, false_type
#include <new>
#include <limits> //numeric_limits
#include "compat/allocator.hpp"
#include "rexy.hpp"
#include <type_traits> //declval
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>
constexpr bool operator==(const allocator<T>&, const allocator<U>&){
return true;
@ -99,6 +33,14 @@ namespace rexy{
constexpr bool operator!=(const allocator<T>&, const allocator<U>&){
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

View File

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

View File

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

View File

@ -1,6 +1,6 @@
/**
This file is a part of rexy's general purpose library
Copyright (C) 2020 rexy712
Copyright (C) 2022 rexy712
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -16,17 +16,8 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef REXY_CX_CX_STRING_HASH_HPP
#define REXY_CX_CX_STRING_HASH_HPP
#include "../string_hash.hpp"
#include "string.hpp"
namespace rexy::cx{
template<size_t N>
struct hash<rexy::cx::string<N>> : public string_hash<rexy::cx::string<N>>{};
}
#if __cplusplus >= 202002L
#include "cpp20/allocator.hpp"
#else
#include "cpp17/allocator.hpp"
#endif

View 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

View 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

View File

@ -22,7 +22,7 @@
#include <cstdint> //uint_least32_t
//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
#else
#define CONSTEVAL constexpr

View 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

View 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

View 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

View 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

View 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

View File

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

View 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

View 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

View 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

View File

@ -1,6 +1,6 @@
/**
This file is a part of rexy's general purpose library
Copyright (C) 2020 rexy712
Copyright (C) 2022 rexy712
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -16,19 +16,14 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef REXY_BASIC_STRING_HASH_HPP
#define REXY_BASIC_STRING_HASH_HPP
#ifndef REXY_COMPAT_TRAITS_HPP
#define REXY_COMPAT_TRAITS_HPP
#include "string_hash.hpp"
#include "string.hpp"
#ifdef REXY_STANDARD_CPP20
#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

View File

@ -27,12 +27,12 @@
namespace rexy::cx{
template<class T, size_t N>
template<class T, std::size_t N>
class array
{
public:
using value_type = T;
using size_type = size_t;
using size_type = std::size_t;
using difference_type = ptrdiff_t;
using reference = 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
{
public:

View File

@ -23,13 +23,13 @@
#include <utility> //move, forward, pair
#include <climits> //CHAR_BIT
#include "../../utility.hpp" //swap
#include "../../compat/standard.hpp"
namespace rexy::cx::detail{
class bool_specialize_base
{
protected:
using size_type = size_t;
using size_type = std::size_t;
using difference_type = ptrdiff_t;
static constexpr size_type bits_per_byte = CHAR_BIT;
@ -59,7 +59,7 @@ namespace rexy::cx::detail{
m_offset(offset){}
constexpr boolean(const boolean&)noexcept = default;
constexpr boolean(boolean&&)noexcept = default;
~boolean(void)noexcept = default;
REXY_CPP20_CONSTEXPR ~boolean(void)noexcept = default;
constexpr boolean& operator=(const boolean& b)noexcept{
return *this = static_cast<bool>(b);
@ -86,15 +86,15 @@ namespace rexy::cx::detail{
constexpr booleans(void)noexcept = default;
constexpr booleans(const booleans&)noexcept = default;
constexpr booleans(booleans&&)noexcept = default;
~booleans(void)noexcept = default;
REXY_CPP20_CONSTEXPR ~booleans(void)noexcept = default;
constexpr booleans& operator=(const booleans&)noexcept = default;
constexpr booleans& operator=(booleans&&)noexcept = default;
constexpr boolean operator[](size_t i){
constexpr boolean operator[](std::size_t 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};
}
constexpr uchar& data(void){
@ -121,7 +121,7 @@ namespace rexy::cx::detail{
m_offset(offset){}
constexpr bool_iter(const bool_iter&)noexcept = default;
constexpr bool_iter(bool_iter&&)noexcept = default;
~bool_iter(void)noexcept = default;
REXY_CPP20_CONSTEXPR ~bool_iter(void)noexcept = default;
constexpr bool_iter& operator=(const bool_iter&)noexcept = default;
constexpr bool_iter& operator=(bool_iter&&)noexcept = default;
@ -209,7 +209,7 @@ namespace rexy::cx::detail{
m_offset(b.m_offset){}
constexpr const_bool_iter(const const_bool_iter&)noexcept = default;
constexpr const_bool_iter(const_bool_iter&&)noexcept = default;
~const_bool_iter(void)noexcept = default;
REXY_CPP20_CONSTEXPR ~const_bool_iter(void)noexcept = default;
constexpr const_bool_iter& operator=(const const_bool_iter&)noexcept = default;
constexpr const_bool_iter& operator=(const_bool_iter&&)noexcept = default;

View File

@ -25,7 +25,7 @@
#include "../hash.hpp"
#include <climits> //CHAR_BIT
#include <cstddef> //size_t, ptrdiff_t
#include <cstddef> //std::size_t, ptrdiff_t
#include <type_traits> //decay
#include <initializer_list>
@ -42,14 +42,14 @@ namespace rexy::cx{
template<class Key, class 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
{
public:
using key_type = Key;
using mapped_type = Value;
using value_type = element<Key,Value>;
using size_type = size_t;
using size_type = std::size_t;
using difference_type = ptrdiff_t;
using hasher = Hash;
using reference = mapped_type&;
@ -72,7 +72,7 @@ namespace rexy::cx{
noexcept(std::is_nothrow_default_constructible<value_type>::value &&
std::is_nothrow_copy_constructible<value_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 :)
template<class U, class UHash = hash<std::decay_t<U>>>
@ -89,8 +89,4 @@ namespace rexy::cx{
#include "hashmap.tpp"
#ifdef REXY_STRING_BASE_HPP
#include "../string_hash.hpp"
#endif
#endif

View File

@ -23,12 +23,12 @@
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])
noexcept(std::is_nothrow_default_constructible<value_type>::value &&
std::is_nothrow_copy_constructible<value_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<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 :)
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>
constexpr auto hashmap<Key,Value,N,Hash>::operator[](U&& key)noexcept -> reference{
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[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>
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];
@ -118,7 +118,7 @@ namespace rexy::cx{
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>
constexpr bool hashmap<Key,Value,N,Hash>::contains(U&& key)const noexcept{
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);
}
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]){
return hashmap<Key,Value,N,Hash>(list);
}

View File

@ -1,6 +1,6 @@
/**
This file is a part of rexy's general purpose library
Copyright (C) 2020 rexy712
Copyright (C) 2020-2022 rexy712
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -19,57 +19,69 @@
#ifndef REXY_CX_STRING_HPP
#define REXY_CX_STRING_HPP
#include <cstddef> //ptrdiff_t, size_t
namespace rexy::cx{
template<size_t N, class Char>
template<std::size_t N, class Char>
class string;
}
#include "../string_base.hpp"
#include "../utility.hpp"
#include <type_traits>
#include <cstddef> //ptrdiff_t, size_t
#include "../string_base.hpp" //string_cat_expr
#include "../string_view.hpp"
#include "../utility.hpp" //strlen, strcmp
#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 holds a mutable array of data which can be modified during compile time. static_string is
//This is different from rexy::string_view in that this doesn't hold a pointer to a constant string array.
//This holds a mutable array of data which can be modified during compile time. string_view is
//designed to be a thin wrapper around a raw char*, this is designed to allow compile time string concatenation
namespace rexy::cx{
template<size_t N, class Char = char>
template<std::size_t N, class Char = char>
class string
{
public:
using value_type = Char;
using size_type = size_t;
using difference_type = ptrdiff_t;
using size_type = std::size_t;
using difference_type = std::ptrdiff_t;
using pointer = value_type*;
using const_pointer = const value_type*;
using reference = value_type&;
using const_reference = const value_type&;
using iterator = 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:
static constexpr size_t max_size = N;
private:
value_type m_data[N] = {};
size_type m_length = 0;
public:
constexpr string(void) = default;
template<size_type M>
constexpr string(const char(&data)[M])noexcept:
constexpr string(const value_type(&data)[M])noexcept:
m_length(M)
{
static_assert(M <= N);
for(size_type i = 0;i < M;++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:
m_length(strlen(data))
m_length(rexy::strlen(data))
{
for(size_type i = 0;i < m_length;++i){
m_data[i] = data[i];
@ -92,7 +104,7 @@ namespace rexy::cx{
}
template<class Left, class Right>
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);
append(expr);
@ -103,7 +115,7 @@ namespace rexy::cx{
REXY_CPP20_CONSTEXPR ~string(void)noexcept = default;
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){
m_data[i] = c[i];
}
@ -112,45 +124,64 @@ namespace rexy::cx{
constexpr string& operator=(const string&)noexcept = default;
constexpr string& operator=(string&&)noexcept = default;
constexpr bool operator==(const string& s)noexcept{return !rexy::strcmp(m_data, s.m_data);}
constexpr bool operator!=(const string& s)noexcept{return rexy::strcmp(m_data, s.m_data);}
constexpr size_type length(void)const noexcept{
return m_length;
constexpr size_type length(void)const noexcept{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{
return max_size;
constexpr const_iterator search(const_pointer c)const{
return search(basic_string_view<value_type>{c});
}
constexpr pointer c_str(void)noexcept{
return m_data;
template<class Searcher>
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{
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;
template<class Searcher>
constexpr const_iterator search(const_pointer c, Searcher&& searcher)const{
return search(basic_string_view<value_type>{c}, searcher);
}
constexpr bool valid(void)const noexcept{
return m_length > 0;
}
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 iterator begin(void)noexcept{return m_data;}
constexpr const_iterator begin(void)const noexcept{return m_data;}
constexpr const_iterator cbegin(void)const noexcept{return m_data;}
constexpr iterator end(void)noexcept{return m_data+m_length;}
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_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{
if(i >= capacity())
if(i >= capacity()){
return false;
}
m_length = i;
m_data[m_length] = 0;
return true;
@ -161,48 +192,48 @@ namespace rexy::cx{
m_data[m_length++] = data[i];
}
}
constexpr void append(const_pointer data)noexcept{
append(data, strlen(data));
constexpr void append(const_pointer data)noexcept{append(data, rexy::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{
append(s.get(), s.length());
constexpr size_type find_first_of(const_pointer c, size_type pos = 0)const{
return rexy::find_first_of(*this, c, pos, rexy::strlen(c));
}
constexpr void append(const rexy::basic_string_view<value_type>& s)noexcept{
append(s.get(), s.length());
constexpr size_type find_first_of(const_pointer c, size_type start, size_type size)const{
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
#include "cx_string_hash.hpp"
#endif
#ifdef __cpp_concepts
#include "../compat/cpp20/cx/string.hpp"
#else //__cpp_concepts
#include "../compat/cpp17/cx/string.hpp"
#endif //__cpp_concepts
#endif

View File

@ -24,17 +24,18 @@
#include "detail/bool_specialize_base.hpp"
#include "../utility.hpp" //swap
#include "../compat/standard.hpp"
#include <type_traits>
namespace rexy::cx{
template<class T, size_t N>
template<class T, std::size_t N>
class vector
{
public:
using value_type = T;
using size_type = size_t;
using size_type = std::size_t;
using difference_type = ptrdiff_t;
using iterator = T*;
using const_iterator = const T*;
@ -63,7 +64,7 @@ namespace rexy::cx{
m_size = min(count, max_elements);
}
~vector(void)noexcept = default;
REXY_CPP20_CONSTEXPR ~vector(void)noexcept = default;
constexpr vector& operator=(const vector&)noexcept(std::is_nothrow_copy_assignable<T>::value) = default;
constexpr vector& operator=(vector&&)noexcept(std::is_nothrow_move_assignable<T>::value) = default;
@ -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
{
public:
@ -283,7 +284,7 @@ namespace rexy::cx{
m_size = min(count, max_elements);
}
~vector(void)noexcept = default;
REXY_CPP20_CONSTEXPR ~vector(void)noexcept = default;
constexpr vector& operator=(const vector&)noexcept = default;
constexpr vector& operator=(vector&&)noexcept = default;

View File

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

View File

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

View File

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

View File

@ -1,6 +1,6 @@
/**
This file is a part of rexy's general purpose library
Copyright (C) 2020 rexy712
Copyright (C) 2022 rexy712
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -16,15 +16,11 @@
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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View File

@ -1,6 +1,6 @@
/**
This file is a part of rexy's general purpose library
Copyright (C) 2020 rexy712
Copyright (C) 2022 rexy712
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -16,28 +16,23 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef REXY_STRING_HASH_HPP
#define REXY_STRING_HASH_HPP
#ifndef REXY_DETAIL_FORMAT_FORMAT_STRING_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
template<class Str>
struct string_hash{
constexpr size_t operator()(const Str& s, size_t salt = 0)const noexcept{
size_t hash = salt;
for(size_t i = 0;i < s.length();++i){
hash += s[i];
hash += (hash << 10);
hash += (hash >> 6);
}
hash += (hash << 3);
hash ^= (hash << 11);
hash += (hash << 15);
return hash;
}
//Class accepted as an argument to 'format' calls. Will call a constant-evaluated parse of the format string
//to check for correctness.
template<class Char, class... Args>
class basic_format_string
{
public:
basic_string_view<Char> str;
consteval basic_format_string(const Char* f);
consteval basic_format_string(basic_string_view<Char> f);
};
}

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View File

@ -1,6 +1,6 @@
/**
This file is a part of rexy's general purpose library
Copyright (C) 2020 rexy712
Copyright (C) 2020-2022 rexy712
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -19,28 +19,24 @@
#ifndef REXY_DETAIL_HASALLOCATOR_HPP
#define REXY_DETAIL_HASALLOCATOR_HPP
#include "../compat/standard.hpp"
#include "../allocator.hpp"
#include <type_traits> //is_empty
namespace rexy::detail{
template<class Alloc>
struct hasallocator
{
Alloc m_alloc;
auto allocate(typename Alloc::size_type bytes)noexcept(noexcept(m_alloc.allocate(0))){
return m_alloc.allocate(bytes);
template<REXY_ALLOCATOR_CONCEPT Alloc>
struct hasallocator : public Alloc{
using Alloc::Alloc;
using Alloc::operator=;
constexpr Alloc& allocator(void){
return *this;
}
void deallocate(typename Alloc::pointer p, typename Alloc::size_type bytes)noexcept(noexcept(m_alloc.deallocate(nullptr,0))){
m_alloc.deallocate(p, bytes);
}
Alloc& allocator(void){
return m_alloc;
}
const Alloc& allocator(void)const{
return m_alloc;
constexpr const Alloc& allocator(void)const{
return *this;
}
};
}
#endif

View File

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

View File

@ -21,6 +21,15 @@
#ifndef 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>
template<class T>
@ -201,4 +210,6 @@ namespace rexy::enum_traits{
}
}
#endif //__cpp_concepts
#endif

View File

@ -19,7 +19,7 @@
#ifndef REXY_EXPRESSION_HPP
#define REXY_EXPRESSION_HPP
#include <type_traits>
#include <type_traits> //is_void, remove_reference, conditional
#include <utility> //forward
#include "rexy.hpp"
@ -29,8 +29,8 @@ namespace rexy{
template<class L, class R>
class binary_expression
{
static_assert(!std::is_same<std::decay_t<L>,void>::value, "Left value of rexy::binary_expression cannot be void!");
static_assert(!std::is_same<std::decay_t<R>,void>::value, "Right value of rexy::binary_expression cannot be void!");
static_assert(!std::is_void_v<L>, "Left value of rexy::binary_expression cannot be void!");
static_assert(!std::is_void_v<R>, "Right value of rexy::binary_expression cannot be void!");
public:
using left_type = std::conditional_t<std::is_rvalue_reference<L>::value,
@ -78,7 +78,7 @@ namespace rexy{
template<class L>
class unary_expression
{
static_assert(!std::is_same<std::decay_t<L>,void>::value, "Value of rexy::unary_expression cannot be void!");
static_assert(!std::is_void_v<L>, "Value of rexy::unary_expression cannot be void!");
public:
using left_type = std::conditional_t<std::is_rvalue_reference<L>::value,

View File

@ -1,6 +1,6 @@
/**
This file is a part of rexy's general purpose library
Copyright (C) 2020 rexy712
Copyright (C) 2020-2022 rexy712
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -19,6 +19,8 @@
#ifndef REXY_FILERD_HPP
#define REXY_FILERD_HPP
#ifndef LIBREXY_HEADER_ONLY
#include <cstdio> //FILE
#include <cstddef> //size_t
#include <type_traits> //is_nothrow_constructible
@ -28,6 +30,7 @@
#include "utility.hpp"
#include "rexy.hpp"
#include "buffer.hpp"
#include "string_view.hpp"
namespace rexy{
@ -35,7 +38,8 @@ namespace rexy{
class filerd
{
private:
FILE* m_fp = nullptr;
std::FILE* m_fp = nullptr;
bool m_finished = false;
public:
constexpr filerd(void)noexcept = default;
@ -46,31 +50,39 @@ namespace rexy{
~filerd(void)noexcept;
filerd& operator=(const filerd&) = delete;
constexpr filerd& operator=(filerd&& f)noexcept{
swap(m_fp, f.m_fp);
rexy::swap(m_fp, f.m_fp);
return *this;
}
void reset(FILE* fp = nullptr)noexcept;
FILE* release(void)noexcept;
size_t length(void)noexcept;
size_t position(void)const noexcept;
void rewind(size_t pos = 0)noexcept;
void reset(std::FILE* fp = nullptr)noexcept;
std::FILE* release(void)noexcept;
std::size_t length(void)noexcept;
std::size_t position(void)const noexcept;
void rewind(std::size_t pos = 0)noexcept;
operator FILE*(void)noexcept;
operator const FILE*(void)const noexcept;
FILE* get(void)noexcept;
const FILE* get(void)const noexcept;
operator std::FILE*(void)noexcept;
operator const std::FILE*(void)const noexcept;
std::FILE* get(void)noexcept;
const std::FILE* get(void)const noexcept;
operator bool(void)const noexcept;
size_t read(char* dest, size_t bytes)noexcept;
rexy::string read(size_t bytes)noexcept;
rexy::string readln(size_t max = 0)noexcept;
rexy::buffer<char> read_bin(size_t bytes)noexcept(std::is_nothrow_constructible<rexy::buffer<char>, char*, size_t>::value);
bool eof(void)const;
size_t write(const char* c, size_t bytes)noexcept;
size_t write(const rexy::string_base<char>& s)noexcept;
std::size_t read(char* dest, std::size_t bytes)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

157
include/rexy/format.hpp Normal file
View 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
View 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

View File

@ -1,6 +1,6 @@
/**
This file is a part of rexy's general purpose library
Copyright (C) 2020 rexy712
Copyright (C) 2020-2022 rexy712
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -20,17 +20,29 @@
#define REXY_HASH_HPP
#include <climits> //CHAR_BIT
#include <cstddef> //size_t
#include "rexy.hpp"
#include "allocator.hpp"
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>
struct hash{
constexpr size_t operator()(const T& t, size_t salt = 0)const noexcept{
constexpr size_t bytes = sizeof(size_t);
size_t val = static_cast<size_t>(t);
size_t hash = 5381 + salt; //magic hash number
for(size_t i = 0;i < bytes;++i){
constexpr std::size_t operator()(const T& t, std::size_t salt = 0)const noexcept{
constexpr std::size_t bytes = sizeof(std::size_t);
std::size_t val = static_cast<std::size_t>(t);
std::size_t hash = 5381 + salt; //magic hash number
for(std::size_t i = 0;i < bytes;++i){
unsigned char c = static_cast<unsigned char>(val >> (i * CHAR_BIT));
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

400
include/rexy/list.hpp Normal file
View 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
View 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

View File

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

View File

@ -21,20 +21,20 @@
#include <utility> //forward, move
#include <atomic> //memory_order, atomic
#include <cstring> //memcpy
#include "utility.hpp" //memcpy
namespace rexy{
template<class T>
mpmc_queue<T>::slot::slot(const slot& s):
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>
mpmc_queue<T>::slot::slot(slot&& s):
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>
mpmc_queue<T>::slot::~slot(){

View File

@ -1,6 +1,6 @@
/**
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
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/>.
*/
#ifndef REXY_REXY_HPP
#define REXY_REXY_HPP
#ifndef REXY_CONFIG_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_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

View File

@ -22,6 +22,8 @@
#include <utility> //forward, move
#include <type_traits> //is_nothrow_constructible, etc
#include "compat/standard.hpp"
namespace rexy{
//A wrapper around raw storage.
@ -42,7 +44,19 @@ namespace rexy{
static constexpr auto size = sizeof(value_type);
private:
#if __cplusplus >= 202002L && __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];
#endif //__cplusplus
unsigned char m_dirty:1 = 0;
public:
@ -53,7 +67,7 @@ namespace rexy{
constexpr storage_for(rvalue_reference t)noexcept(std::is_nothrow_move_constructible<value_type>::value);
constexpr storage_for(const storage_for& s)noexcept(std::is_nothrow_copy_constructible<value_type>::value);
constexpr storage_for(storage_for&& s)noexcept(std::is_nothrow_move_constructible<value_type>::value);
constexpr ~storage_for(void)noexcept(std::is_nothrow_destructible<value_type>::value);
REXY_CPP20_CONSTEXPR ~storage_for(void)noexcept(std::is_nothrow_destructible<value_type>::value);
constexpr storage_for& operator=(const storage_for& s)noexcept(std::is_nothrow_copy_assignable<value_type>::value);
constexpr storage_for& operator=(storage_for&& s)noexcept(std::is_nothrow_move_assignable<value_type>::value);
@ -65,6 +79,9 @@ namespace rexy{
constexpr operator const_reference(void)const noexcept;
constexpr operator rvalue_reference(void)&& noexcept;
constexpr reference get(void)noexcept;
constexpr const_reference get(void)const noexcept;
constexpr pointer operator->(void)noexcept;
constexpr const_pointer operator->(void)const noexcept;
constexpr reference operator*(void)noexcept;

View File

@ -21,6 +21,141 @@
#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{
template<class T>
@ -50,7 +185,7 @@ namespace rexy{
m_dirty = 1;
}
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){
destroy();
}
@ -88,13 +223,22 @@ namespace rexy{
}
template<class T>
constexpr storage_for<T>::operator const_reference(void)const noexcept{
return m_storage;
return reinterpret_cast<const_reference>(m_storage);
}
template<class T>
constexpr storage_for<T>::operator rvalue_reference(void)&& noexcept{
return reinterpret_cast<rvalue_reference>(std::move(m_storage));
}
template<class T>
constexpr auto storage_for<T>::get(void)noexcept -> reference{
return reinterpret_cast<reference>(m_storage);
}
template<class T>
constexpr auto storage_for<T>::get(void)const noexcept -> const_reference{
return reinterpret_cast<const_reference>(m_storage);
}
template<class T>
constexpr auto storage_for<T>::operator->(void)noexcept -> pointer{
return reinterpret_cast<T*>(&m_storage);
@ -120,4 +264,6 @@ namespace rexy{
}
#endif //__cplusplus
#endif

View File

@ -26,25 +26,25 @@
namespace rexy{
//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<wchar_t,allocator<wchar_t>>;
extern template class basic_string<char16_t,allocator<char16_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
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>;
#endif //LIBREXY_HEADER_ONLY
}
#ifdef REXY_CX_HASH_HPP
#include "cx/basic_string_hash.hpp"
#endif
#endif

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -19,10 +19,11 @@
#ifndef 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 "compat/constexpr.hpp"
#include "compat/standard.hpp"
#include "rexy.hpp"
namespace rexy{
@ -34,92 +35,150 @@ namespace rexy{
{
public:
using value_type = Char;
using size_type = size_t;
using size_type = std::size_t;
using difference_type = ptrdiff_t;
using pointer = value_type*;
using const_pointer = const value_type*;
using reference = value_type&;
using const_reference = const value_type&;
using iterator = pointer;
using iterator = const_pointer;
using const_iterator = const_pointer;
using reverse_iterator = std::reverse_iterator<iterator>;
using const_reverse_iterator = std::reverse_iterator<const_iterator>;
static constexpr size_type npos = size_type(-1);
private:
const_pointer m_data = nullptr;
size_type m_length = 0;
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 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(basic_string_view&& s)noexcept;
constexpr basic_string_view(basic_string_view&& s)noexcept = default;
template<class InIter>
constexpr basic_string_view(InIter start, InIter fin)noexcept;
REXY_CPP20_CONSTEXPR ~basic_string_view(void)noexcept = default;
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=(basic_string_view&&)noexcept;
constexpr basic_string_view& operator=(const basic_string_view& s)noexcept = default;
constexpr basic_string_view& operator=(basic_string_view&&)noexcept = default;
//Length of string not including null terminator
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
constexpr const_pointer c_str(void)const noexcept{return m_data;}
constexpr const_pointer get(void)const noexcept{return m_data;}
constexpr operator const_pointer(void)const noexcept{return m_data;}
constexpr const_pointer data(void)const noexcept{return m_data;}
//true if m_data is not empty
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 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(const_pointer c)const;
constexpr const_iterator search(basic_string_view s)const noexcept;
constexpr const_iterator search(const_pointer c)const noexcept;
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>
constexpr const_iterator search(const_pointer c, const Searcher& searcher)const;
constexpr bool compare(const basic_string_view& s)const{return *this == s;}
constexpr bool compare(const_pointer c)const{return *this == c;}
constexpr const_iterator search(const_pointer c, const Searcher& searcher)const noexcept(
std::is_nothrow_invocable_v<Searcher, const_iterator, const_iterator, const_pointer, const_pointer>);
constexpr const_iterator begin(void)const{return m_data;}
constexpr const_iterator end(void)const{return m_data+m_length;}
constexpr const_iterator cbegin(void)const{return begin();}
constexpr const_iterator cend(void)const{return end();}
constexpr bool starts_with(basic_string_view sv)const noexcept;
constexpr bool starts_with(value_type v)const noexcept;
constexpr bool starts_with(const_pointer str)const noexcept;
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>
basic_string_view(const T*) -> basic_string_view<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 static_string [[deprecated]] = basic_string_view<T>;
using string_view = basic_string_view<char>;
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{
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>
std::ostream& operator<<(std::ostream& os, const rexy::basic_string_view<Char>& str){
return os << str.c_str();
@ -128,4 +187,5 @@ namespace{
#include "string_view.tpp"
#endif

View File

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

View File

@ -1,6 +1,6 @@
/**
This file is a part of rexy's general purpose library
Copyright (C) 2020 rexy712
Copyright (C) 2020-2022 rexy712
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -29,6 +29,7 @@
#include <future> //future, packaged_task
#include <functional> //function, bind
#include <utility> //move, forward
#include <type_traits> //invoke_result_t
#include "rexy.hpp"
@ -62,14 +63,14 @@ namespace rexy{
void invalidate(void);
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:
void worker_loop(void);
};
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 task_t = std::packaged_task<return_t(void)>;

View File

@ -1,6 +1,6 @@
/**
This file is a part of rexy's general purpose library
Copyright (C) 2020 rexy712
Copyright (C) 2020-2022 rexy712
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -20,7 +20,9 @@
#define REXY_TRAITS_HPP
#include <type_traits> //is_same, decay, integral_constant, declval
#include <iterator> //iterator_traits
#include "compat/traits.hpp"
#include "rexy.hpp"
namespace rexy{
@ -30,7 +32,7 @@ namespace rexy{
static std::true_type check(U*);
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>
@ -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

View File

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

View File

@ -1,29 +1,35 @@
//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/allocator.hpp"
#include "rexy/buffer.hpp"
#include "rexy/buffer.tpp"
#include "rexy/expression.hpp"
#include "rexy/filerd.hpp"
#include "rexy/hash.hpp"
#include "rexy/mpmc_queue.hpp"
#include "rexy/steal.hpp"
#include "rexy/string_base.hpp"
#include "rexy/string_base.tpp"
#include "rexy/string_hash.hpp"
#include "rexy/string.hpp"
#include "rexy/traits.hpp"
#include "rexy/utility.hpp"
#include "rexy/meta.hpp"
#include "rexy/enum_traits.hpp"
#include "rexy/deferred.hpp"
#include "rexy/demangle.hpp"
#include "rexy/debug_print.hpp"
#include "rexy/storage_for.hpp"
#include "rexy/visitor.hpp"
#include "rexy/string_view.hpp"
#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"

View File

@ -1,6 +1,6 @@
/**
This file is a part of rexy's general purpose library
Copyright (C) 2020 rexy712
Copyright (C) 2020-2022 rexy712
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -16,6 +16,12 @@
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 <cstdio> //fopen, fclose
@ -25,88 +31,99 @@
namespace rexy{
filerd::filerd(const char* f, const char* mode)noexcept:
m_fp(fopen(f, mode)){}
m_fp(std::fopen(f, mode)){}
filerd::~filerd(void)noexcept{
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)
fclose(m_fp);
std::fclose(m_fp);
m_fp = fp;
}
FILE* filerd::release(void)noexcept{
std::FILE* filerd::release(void)noexcept{
return std::exchange(m_fp, nullptr);
}
size_t filerd::length(void)noexcept{
std::size_t filerd::length(void)noexcept{
if(!m_fp)
return 0;
size_t tmp, ret;
tmp = ftell(m_fp);
fseek(m_fp, 0, SEEK_END);
ret = ftell(m_fp);
fseek(m_fp, tmp, SEEK_SET);
std::size_t tmp, ret;
tmp = std::ftell(m_fp);
std::fseek(m_fp, 0, SEEK_END);
ret = std::ftell(m_fp);
std::fseek(m_fp, long(tmp), SEEK_SET);
return ret;
}
size_t filerd::position(void)const noexcept{
return ftell(m_fp);
std::size_t filerd::position(void)const noexcept{
return std::ftell(m_fp);
}
void filerd::rewind(size_t pos)noexcept{
fseek(m_fp, pos, SEEK_SET);
void filerd::rewind(std::size_t pos)noexcept{
std::fseek(m_fp, long(pos), SEEK_SET);
}
filerd::operator FILE*(void)noexcept{
filerd::operator std::FILE*(void)noexcept{
return m_fp;
}
filerd::operator const FILE*(void)const noexcept{
filerd::operator const std::FILE*(void)const noexcept{
return m_fp;
}
FILE* filerd::get(void)noexcept{
std::FILE* filerd::get(void)noexcept{
return m_fp;
}
const FILE* filerd::get(void)const noexcept{
const std::FILE* filerd::get(void)const noexcept{
return m_fp;
}
filerd::operator bool(void)const noexcept{
return m_fp;
}
size_t filerd::read(char* dest, size_t bytes)noexcept{
return fread(dest, 1, bytes, m_fp);
bool filerd::eof(void)const{
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;
char* tmp = ret.allocator().allocate(bytes);
size_t written = read(tmp, bytes);
std::size_t written = read(tmp, bytes);
ret.reset(tmp, written);
return ret;
}
rexy::string filerd::readln(size_t max)noexcept{
rexy::string filerd::readln(std::size_t max)noexcept{
rexy::string ret;
int c;
size_t count = 0;
for(c = fgetc(m_fp);c != EOF && c != '\n';c = fgetc(m_fp)){
std::size_t count = 0;
for(c = std::fgetc(m_fp);c != EOF && c != '\n';c = std::fgetc(m_fp)){
char ch = c;
ret.append(&ch, 1);
if(++count == max)
break;
}
if(c == EOF){
m_finished = true;
}
return ret;
}
rexy::buffer<char> filerd::read_bin(size_t bytes)
noexcept(std::is_nothrow_constructible<rexy::buffer<char>, char*, size_t>::value)
rexy::buffer<char> filerd::read_bin(std::size_t bytes)
noexcept(std::is_nothrow_constructible<rexy::buffer<char>, char*, std::size_t>::value)
{
rexy::buffer<char> ret{bytes};
size_t written = read(ret.data(), bytes);
const auto written = read(ret.data(), bytes);
ret.set_size(written);
return ret;
}
size_t filerd::write(const char* c, size_t bytes)noexcept{
return fwrite(c, 1, bytes, m_fp);
std::size_t filerd::write(const char* c, std::size_t bytes)noexcept{
return std::fwrite(c, 1, bytes, m_fp);
}
size_t filerd::write(const rexy::string_base<char>& c)noexcept{
return write(c.get(), c.length());
std::size_t filerd::write(rexy::string_view c)noexcept{
return write(c.data(), c.length());
}
}

View File

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

View File

@ -52,7 +52,7 @@ namespace rexy{
m_valid(true)
{
m_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_qcv.notify_all();

View File

@ -2,7 +2,14 @@ cmake_minimum_required(VERSION 3.0.2)
project(rexylib_tests)
set(INCLUDE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/../include)
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)
if(ENABLE_PROFILING)
@ -11,6 +18,9 @@ if(ENABLE_PROFILING)
endif()
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(format PROPERTIES OUTPUT_NAME format)
add_test(NAME basic_string-test COMMAND basic_string)
add_test(NAME format-test COMMAND format)

View File

@ -1,16 +1,22 @@
#define LIBREXY_ENABLE_DEBUG_LEVEL 2
#include "rexy/string.hpp"
#include "rexy/allocator.hpp"
#include "rexy/utility.hpp"
#include "rexy/debug_print.hpp"
#include <cstdlib>
#include <cstdio>
#include <cstring>
#include <utility>
#include <new>
[[noreturn]] void error(const char* str){
fprintf(stderr, "%s", str);
exit(1);
}
using namespace rexy::str_literals;
using test_str = rexy::basic_string<char,rexy::allocator<char>>;
void check_empty_construction(){
@ -18,18 +24,16 @@ void check_empty_construction(){
if(str1.length() != 0)
error("length() should return 0 on default init\n");
if(test_str::uses_sso()){
if(str1.get()[0] != 0)
error("get() should return an empty, zero length string\n");
if(str1.data()[0] != 0)
error("data() should return an empty, zero length string\n");
}else{
if(str1.get() != nullptr)
error("get() should return a null string\n");
if(str1.data() != nullptr)
error("data() should return a null string\n");
}
if(str1.valid())
error("valid() should return false on empty string\n");
if(str1.get() != str1.c_str())
error("c_str() should be a synonymn of get()\n");
if(char* c = str1;c != str1.get())
error("conversion to pointer type should be synonymous with get()\n");
if(str1.data() != str1.c_str())
error("c_str() should be a synonymn of data()\n");
test_str str2(str1);
if(str2.length() != str1.length())
@ -37,11 +41,11 @@ void check_empty_construction(){
if(str2.capacity() != str1.capacity())
error("copy construction on empty string should give equivalent capacity()\n");
if(test_str::uses_sso()){
if(str2.get()[0] != str1.get()[0])
error("copy construction on empty string should give equivalent get()\n");
if(str2.data()[0] != str1.data()[0])
error("copy construction on empty string should give equivalent data()\n");
}else{
if(str2.get() != str1.get())
error("copy construction on empty string should give equivalent get()\n");
if(str2.data() != str1.data())
error("copy construction on empty string should give equivalent data()\n");
}
test_str str3(std::move(str2));
@ -50,11 +54,11 @@ void check_empty_construction(){
if(str3.capacity() != str1.capacity())
error("move construction on empty string should give equivalent capacity()\n");
if(test_str::uses_sso()){
if(str3.get()[0] != str1.get()[0])
error("move construction on empty string should give equivalent get()\n");
if(str3.data()[0] != str1.data()[0])
error("move construction on empty string should give equivalent data()\n");
}else{
if(str3.get() != str1.get())
error("move construction on empty string should give equivalent get()\n");
if(str3.data() != str1.data())
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");
if(str1.capacity() != cap)
error("short constructed string 'a' should be capacity() == short_string_size()\n");
if(strcmp(str1.get(), "a"))
error("short constructed string 'a' should be !strcmp(get(), \"a\")\n");
if(strcmp(str1.data(), "a"))
error("short constructed string 'a' should be !strcmp(data(), \"a\")\n");
test_str str2(str1);
if(str2.length() != str1.length())
error("short copy constructed string should have equal length()\n");
if(str2.capacity() != str1.capacity())
error("short copy constructed string should have equal capacity()\n");
if(strcmp(str2.get(), str1.get()))
error("short copy constructed string should have equivalent get()\n");
if(strcmp(str2.data(), str1.data()))
error("short copy constructed string should have equivalent data()\n");
test_str str3(std::move(str2));
if(str3.length() != str1.length())
error("short move constructed string should have equal length()\n");
if(str3.capacity() != str1.capacity())
error("short move constructed string should have equal capacity()\n");
if(strcmp(str3.get(), str1.get()))
error("short move constructed string should have equivalent get()\n");
if(strcmp(str3.data(), str1.data()))
error("short move constructed string should have equivalent data()\n");
}
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.";
size_t len = strlen(data);
std::size_t len = rexy::strlen(data);
test_str str1(data);
if(str1.length() != len)
error("long constructed string should be length() == strlen(data)\n");
if(str1.capacity() < len)
error("long constructed string should be capacity() >= strlen(data)\n");
if(strcmp(str1.get(), data))
error("long constructed string should be !strcmp(get(), data)\n");
if(strcmp(str1.data(), data))
error("long constructed string should be !strcmp(data(), data)\n");
test_str str2(str1);
if(str2.length() != str1.length())
error("long copy constructed string should have equal length()\n");
if(str2.capacity() != str1.capacity())
error("long copy constructed string should have equal capacity()\n");
if(strcmp(str2.get(), str1.get()))
error("long copy constructed string should have equivalent get()\n");
if(strcmp(str2.data(), str1.data()))
error("long copy constructed string should have equivalent data()\n");
test_str str3(std::move(str2));
if(str3.length() != str1.length())
error("long move constructed string should have equal length()\n");
if(str3.capacity() != str1.capacity())
error("long move constructed string should have equal capacity()\n");
if(strcmp(str3.get(), str1.get()))
error("long move constructed string should have equivalent get()\n");
if(strcmp(str3.data(), str1.data()))
error("long move constructed string should have equivalent data()\n");
}
void check_short_assignment(){
if(!test_str::uses_sso())
@ -127,8 +131,8 @@ void check_short_assignment(){
error("short assigned string 'a' should be length() == 1\n");
if(str1.capacity() != cap)
error("short assigned string 'a' should be capacity() == short_string_size()\n");
if(strcmp(str1.get(), "a"))
error("short assigned string 'a' should be !strcmp(get(), \"a\")\n");
if(strcmp(str1.data(), "a"))
error("short assigned string 'a' should be !strcmp(data(), \"a\")\n");
test_str str2("ba");
str2 = str1;
@ -136,8 +140,8 @@ void check_short_assignment(){
error("short copy assigned string should have equal length()\n");
if(str2.capacity() != str1.capacity())
error("short copy assigned string should have equal capacity()\n");
if(strcmp(str2.get(), str1.get()))
error("short copy assigned string should have equivalent get()\n");
if(strcmp(str2.data(), str1.data()))
error("short copy assigned string should have equivalent data()\n");
test_str str3("cb");
str3 = std::move(str2);
@ -145,8 +149,8 @@ void check_short_assignment(){
error("short move assigned string should have equal length()\n");
if(str3.capacity() != str1.capacity())
error("short move assigned string should have equal capacity()\n");
if(strcmp(str3.get(), str1.get()))
error("short move assigned string should have equivalent get()\n");
if(strcmp(str3.data(), str1.data()))
error("short move assigned string should have equivalent data()\n");
test_str str4(longstartdata);
str4 = str1;
@ -154,8 +158,8 @@ void check_short_assignment(){
error("long->short copy assigned string should have equal length()\n");
if(str4.capacity() < str1.capacity())
error("long->short copy assigned string should have equal or greater capacity()\n");
if(strcmp(str4.get(), str1.get()))
error("long->short copy assigned string should have equivalent get()\n");
if(strcmp(str4.data(), str1.data()))
error("long->short copy assigned string should have equivalent data()\n");
test_str str5(longstartdata);
str5 = std::move(str4);
@ -163,22 +167,22 @@ void check_short_assignment(){
error("long->short move assigned string should have equal length()\n");
if(str5.capacity() < str1.capacity())
error("long->short move assigned string should have equal or greater capacity()\n");
if(strcmp(str5.get(), str1.get()))
error("long->short move assigned string should have equivalent get()\n");
if(strcmp(str5.data(), str1.data()))
error("long->short move assigned string should have equivalent data()\n");
}
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* 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.";
size_t len = strlen(data);
std::size_t len = rexy::strlen(data);
test_str str1(startdata1);
str1 = data;
if(str1.length() != len)
error("long assigned string should be length() == strlen(data)\n");
if(str1.capacity() < len)
error("long assigned string should be capacity() >= strlen(data)\n");
if(strcmp(str1.get(), data))
error("long assigned string should be !strcmp(get(), data)\n");
if(strcmp(str1.data(), data))
error("long assigned string should be !strcmp(data(), data)\n");
test_str str2(startdata1);
str2 = str1;
@ -186,8 +190,8 @@ void check_long_assignment(){
error("long copy assigned string should have equal length()\n");
if(str2.capacity() != str1.capacity())
error("long copy assigned string should have equal capacity()\n");
if(strcmp(str2.get(), str1.get()))
error("long copy assigned string should have equivalent get()\n");
if(strcmp(str2.data(), str1.data()))
error("long copy assigned string should have equivalent data()\n");
test_str str3(startdata1);
str3 = std::move(str2);
@ -195,8 +199,8 @@ void check_long_assignment(){
error("long move assigned string should have equal length()\n");
if(str3.capacity() != str1.capacity())
error("long move assigned string should have equal capacity()\n");
if(strcmp(str3.get(), str1.get()))
error("long move assigned string should have equivalent get()\n");
if(strcmp(str3.data(), str1.data()))
error("long move assigned string should have equivalent data()\n");
test_str str4(startdata2);
str4 = str1;
@ -204,8 +208,8 @@ void check_long_assignment(){
error("short->long copy assigned string should have equal length()\n");
if(str4.capacity() != str1.capacity())
error("short->long copy assigned string should have equal capacity()\n");
if(strcmp(str4.get(), str1.get()))
error("short->long copy assigned string should have equivalent get()\n");
if(strcmp(str4.data(), str1.data()))
error("short->long copy assigned string should have equivalent data()\n");
test_str str5(startdata2);
str5 = std::move(str4);
@ -213,8 +217,8 @@ void check_long_assignment(){
error("short->long move assigned string should have equal length()\n");
if(str5.capacity() != str1.capacity())
error("short->long move assigned string should have equal capacity()\n");
if(strcmp(str5.get(), str1.get()))
error("short->long move assigned string should have equivalent get()\n");
if(strcmp(str5.data(), str1.data()))
error("short->long move assigned string should have equivalent data()\n");
}
void check_short_append(){
test_str str1;
@ -223,10 +227,10 @@ void check_short_append(){
str1.append("a");
str1.append("b");
str1.append(str2);
if(strcmp(str1.get(), "abbc"))
if(strcmp(str1.data(), "abbc"))
error("short append should have resulted in abbc\n");
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");
}
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";
test_str str1(startdata1);
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");
}
void check_substring(){
rexy::string test = "this is a test string";
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");
}
void check_string_search(){
rexy::string test1 = "this is a test string";
rexy::string test2 = "string";
rexy::string_view test2 = "string";
auto res = test1.search(test2);
if(test1 + 15 != res){
error("string search operation did not result in a correct result");
if(test1.begin() + 15 != res){
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_substring();
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
View 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");
}