From 8297b0b45f0bc467fb22e79322bb54c864bccf05 Mon Sep 17 00:00:00 2001 From: jeanlemotan Date: Tue, 2 Jul 2024 18:06:33 +0200 Subject: [PATCH] First --- CMakeLists.txt | 75 + include/tl/abs_path.h | 879 ++ include/tl/adaptors.h | 3 + include/tl/algorithm.h | 9 + include/tl/algorithm/adjacent_find.h | 30 + include/tl/algorithm/all_any_none_of.h | 30 + include/tl/algorithm/append.h | 59 + include/tl/algorithm/bits.h | 102 + .../tl/algorithm/chained_sorting_predicate.h | 20 + include/tl/algorithm/container.h | 36 + include/tl/algorithm/contains.h | 39 + include/tl/algorithm/copy.h | 14 + include/tl/algorithm/count.h | 30 + include/tl/algorithm/find.h | 155 + include/tl/algorithm/find_end.h | 30 + include/tl/algorithm/find_first_of.h | 28 + include/tl/algorithm/for_each.h | 24 + include/tl/algorithm/mismatch.h | 27 + include/tl/algorithm/move.h | 14 + include/tl/algorithm/remove.h | 41 + include/tl/algorithm/search.h | 40 + include/tl/algorithm/sort.h | 42 + include/tl/algorithm/string.h | 622 ++ include/tl/algorithm/unique.h | 25 + include/tl/any.h | 3 + include/tl/api.h | 24 + include/tl/array.h | 3 + include/tl/assert.h | 6 + include/tl/async_queue.h | 88 + include/tl/atomic.h | 9 + include/tl/bitset.h | 9 + include/tl/bitvector.h | 9 + include/tl/blocking_queue.h | 471 + include/tl/call_traits.h | 3 + include/tl/chrono.h | 49 + include/tl/compact_signal.h | 542 ++ include/tl/compact_signal_mt.h | 72 + include/tl/compact_signal_st.h | 70 + include/tl/compressed_pair.h | 3 + include/tl/crash.h | 15 + include/tl/debug.h | 64 + include/tl/deque.h | 9 + include/tl/detail/internal_assert.h | 278 + include/tl/detail/internal_crash.h | 66 + include/tl/detail/magic_enum.h | 1139 +++ include/tl/detail/outcome.hpp | 7903 +++++++++++++++++ include/tl/detail/path_base.h | 272 + include/tl/detail/path_system_utils.h | 383 + include/tl/detail/prologue.h | 6 + include/tl/detail/string_db.h | 128 + include/tl/enum.h | 134 + include/tl/finally.h | 9 + include/tl/fixed_allocator.h | 3 + include/tl/fixed_function.h | 9 + include/tl/fixed_hash_map.h | 3 + include/tl/fixed_hash_set.h | 3 + include/tl/fixed_list.h | 3 + include/tl/fixed_map.h | 3 + include/tl/fixed_ring_buffer.h | 3 + include/tl/fixed_set.h | 3 + include/tl/fixed_slist.h | 3 + include/tl/fixed_string.h | 9 + include/tl/fixed_substring.h | 3 + include/tl/fixed_tuple_vector.h | 3 + include/tl/fixed_vector.h | 9 + include/tl/flag_set.h | 340 + include/tl/flag_set2.h | 356 + include/tl/format.h | 61 + include/tl/functional.h | 9 + include/tl/hash_and_combine.h | 60 + include/tl/hash_map.h | 9 + include/tl/hash_set.h | 3 + include/tl/heap.h | 3 + include/tl/identifier.h | 54 + include/tl/initializer_list.h | 3 + include/tl/intrusive_hash_map.h | 3 + include/tl/intrusive_hash_set.h | 3 + include/tl/intrusive_list.h | 3 + include/tl/intrusive_ptr.h | 3 + include/tl/intrusive_sdlist.h | 3 + include/tl/intrusive_slist.h | 3 + include/tl/iterator.h | 3 + include/tl/linked_array.h | 3 + include/tl/linked_ptr.h | 3 + include/tl/list.h | 9 + include/tl/list_map.h | 3 + include/tl/lru_cache.h | 3 + include/tl/map.h | 9 + include/tl/memory.h | 53 + include/tl/memory_buffer.h | 223 + include/tl/meta.h | 3 + include/tl/murmur_hash.h | 43 + include/tl/narrow_cast.h | 31 + include/tl/numeric.h | 9 + include/tl/numeric_limits.h | 9 + include/tl/optional.h | 23 + include/tl/path_system.h | 278 + include/tl/plain_assert.h | 5 + include/tl/plain_crash.h | 12 + include/tl/platform.h | 120 + include/tl/priority_queue.h | 3 + include/tl/ptr.h | 1241 +++ include/tl/queue.h | 3 + include/tl/random.h | 3 + include/tl/ratio.h | 3 + include/tl/rel_path.h | 760 ++ include/tl/result.h | 82 + include/tl/ring_buffer.h | 3 + include/tl/safe_ptr.h | 3 + include/tl/scoped_array.h | 3 + include/tl/scoped_ptr.h | 3 + include/tl/segmented_vector.h | 3 + include/tl/set.h | 9 + include/tl/shared_array.h | 9 + include/tl/shared_ptr.h | 9 + include/tl/signal_mt.h | 3 + include/tl/signal_st.h | 3 + include/tl/slist.h | 3 + include/tl/sort.h | 10 + include/tl/sort_extra.h | 3 + include/tl/span.h | 9 + include/tl/stack.h | 3 + include/tl/string.h | 1190 +++ include/tl/string_hash_map.h | 3 + include/tl/string_map.h | 3 + include/tl/string_view.h | 9 + include/tl/toolchain.h | 96 + include/tl/tuple.h | 3 + include/tl/tuple_vector.h | 3 + include/tl/type_traits.h | 123 + include/tl/unique_ptr.h | 9 + include/tl/unordered_map.h | 9 + include/tl/unordered_set.h | 9 + include/tl/utility.h | 9 + include/tl/variant.h | 16 + include/tl/vector.h | 13 + include/tl/vector_map.h | 9 + include/tl/vector_multimap.h | 3 + include/tl/vector_multiset.h | 3 + include/tl/vector_set.h | 9 + include/tl/weak_ptr.h | 9 + natvis/EASTL.natvis | 565 ++ natvis/outcome.natvis | 38 + natvis/tl.natjmc | 4 + natvis/tl.natstepfilter | 5 + natvis/tl.natvis | 92 + src/assert.cpp | 161 + src/crash.cpp | 59 + src/memory_buffer.cpp | 307 + src/murmur_hash.cpp | 615 ++ src/ptr.cpp | 28 + src/string.cpp | 437 + src/string_db.cpp | 304 + test/main.cpp | 34 + test/stdafx.cpp | 1 + test/stdafx.h | 1 + test/test_ptr.cpp | 2559 ++++++ 157 files changed, 24865 insertions(+) create mode 100644 CMakeLists.txt create mode 100644 include/tl/abs_path.h create mode 100644 include/tl/adaptors.h create mode 100644 include/tl/algorithm.h create mode 100644 include/tl/algorithm/adjacent_find.h create mode 100644 include/tl/algorithm/all_any_none_of.h create mode 100644 include/tl/algorithm/append.h create mode 100644 include/tl/algorithm/bits.h create mode 100644 include/tl/algorithm/chained_sorting_predicate.h create mode 100644 include/tl/algorithm/container.h create mode 100644 include/tl/algorithm/contains.h create mode 100644 include/tl/algorithm/copy.h create mode 100644 include/tl/algorithm/count.h create mode 100644 include/tl/algorithm/find.h create mode 100644 include/tl/algorithm/find_end.h create mode 100644 include/tl/algorithm/find_first_of.h create mode 100644 include/tl/algorithm/for_each.h create mode 100644 include/tl/algorithm/mismatch.h create mode 100644 include/tl/algorithm/move.h create mode 100644 include/tl/algorithm/remove.h create mode 100644 include/tl/algorithm/search.h create mode 100644 include/tl/algorithm/sort.h create mode 100644 include/tl/algorithm/string.h create mode 100644 include/tl/algorithm/unique.h create mode 100644 include/tl/any.h create mode 100644 include/tl/api.h create mode 100644 include/tl/array.h create mode 100644 include/tl/assert.h create mode 100644 include/tl/async_queue.h create mode 100644 include/tl/atomic.h create mode 100644 include/tl/bitset.h create mode 100644 include/tl/bitvector.h create mode 100644 include/tl/blocking_queue.h create mode 100644 include/tl/call_traits.h create mode 100644 include/tl/chrono.h create mode 100644 include/tl/compact_signal.h create mode 100644 include/tl/compact_signal_mt.h create mode 100644 include/tl/compact_signal_st.h create mode 100644 include/tl/compressed_pair.h create mode 100644 include/tl/crash.h create mode 100644 include/tl/debug.h create mode 100644 include/tl/deque.h create mode 100644 include/tl/detail/internal_assert.h create mode 100644 include/tl/detail/internal_crash.h create mode 100644 include/tl/detail/magic_enum.h create mode 100644 include/tl/detail/outcome.hpp create mode 100644 include/tl/detail/path_base.h create mode 100644 include/tl/detail/path_system_utils.h create mode 100644 include/tl/detail/prologue.h create mode 100644 include/tl/detail/string_db.h create mode 100644 include/tl/enum.h create mode 100644 include/tl/finally.h create mode 100644 include/tl/fixed_allocator.h create mode 100644 include/tl/fixed_function.h create mode 100644 include/tl/fixed_hash_map.h create mode 100644 include/tl/fixed_hash_set.h create mode 100644 include/tl/fixed_list.h create mode 100644 include/tl/fixed_map.h create mode 100644 include/tl/fixed_ring_buffer.h create mode 100644 include/tl/fixed_set.h create mode 100644 include/tl/fixed_slist.h create mode 100644 include/tl/fixed_string.h create mode 100644 include/tl/fixed_substring.h create mode 100644 include/tl/fixed_tuple_vector.h create mode 100644 include/tl/fixed_vector.h create mode 100644 include/tl/flag_set.h create mode 100644 include/tl/flag_set2.h create mode 100644 include/tl/format.h create mode 100644 include/tl/functional.h create mode 100644 include/tl/hash_and_combine.h create mode 100644 include/tl/hash_map.h create mode 100644 include/tl/hash_set.h create mode 100644 include/tl/heap.h create mode 100644 include/tl/identifier.h create mode 100644 include/tl/initializer_list.h create mode 100644 include/tl/intrusive_hash_map.h create mode 100644 include/tl/intrusive_hash_set.h create mode 100644 include/tl/intrusive_list.h create mode 100644 include/tl/intrusive_ptr.h create mode 100644 include/tl/intrusive_sdlist.h create mode 100644 include/tl/intrusive_slist.h create mode 100644 include/tl/iterator.h create mode 100644 include/tl/linked_array.h create mode 100644 include/tl/linked_ptr.h create mode 100644 include/tl/list.h create mode 100644 include/tl/list_map.h create mode 100644 include/tl/lru_cache.h create mode 100644 include/tl/map.h create mode 100644 include/tl/memory.h create mode 100644 include/tl/memory_buffer.h create mode 100644 include/tl/meta.h create mode 100644 include/tl/murmur_hash.h create mode 100644 include/tl/narrow_cast.h create mode 100644 include/tl/numeric.h create mode 100644 include/tl/numeric_limits.h create mode 100644 include/tl/optional.h create mode 100644 include/tl/path_system.h create mode 100644 include/tl/plain_assert.h create mode 100644 include/tl/plain_crash.h create mode 100644 include/tl/platform.h create mode 100644 include/tl/priority_queue.h create mode 100644 include/tl/ptr.h create mode 100644 include/tl/queue.h create mode 100644 include/tl/random.h create mode 100644 include/tl/ratio.h create mode 100644 include/tl/rel_path.h create mode 100644 include/tl/result.h create mode 100644 include/tl/ring_buffer.h create mode 100644 include/tl/safe_ptr.h create mode 100644 include/tl/scoped_array.h create mode 100644 include/tl/scoped_ptr.h create mode 100644 include/tl/segmented_vector.h create mode 100644 include/tl/set.h create mode 100644 include/tl/shared_array.h create mode 100644 include/tl/shared_ptr.h create mode 100644 include/tl/signal_mt.h create mode 100644 include/tl/signal_st.h create mode 100644 include/tl/slist.h create mode 100644 include/tl/sort.h create mode 100644 include/tl/sort_extra.h create mode 100644 include/tl/span.h create mode 100644 include/tl/stack.h create mode 100644 include/tl/string.h create mode 100644 include/tl/string_hash_map.h create mode 100644 include/tl/string_map.h create mode 100644 include/tl/string_view.h create mode 100644 include/tl/toolchain.h create mode 100644 include/tl/tuple.h create mode 100644 include/tl/tuple_vector.h create mode 100644 include/tl/type_traits.h create mode 100644 include/tl/unique_ptr.h create mode 100644 include/tl/unordered_map.h create mode 100644 include/tl/unordered_set.h create mode 100644 include/tl/utility.h create mode 100644 include/tl/variant.h create mode 100644 include/tl/vector.h create mode 100644 include/tl/vector_map.h create mode 100644 include/tl/vector_multimap.h create mode 100644 include/tl/vector_multiset.h create mode 100644 include/tl/vector_set.h create mode 100644 include/tl/weak_ptr.h create mode 100644 natvis/EASTL.natvis create mode 100644 natvis/outcome.natvis create mode 100644 natvis/tl.natjmc create mode 100644 natvis/tl.natstepfilter create mode 100644 natvis/tl.natvis create mode 100644 src/assert.cpp create mode 100644 src/crash.cpp create mode 100644 src/memory_buffer.cpp create mode 100644 src/murmur_hash.cpp create mode 100644 src/ptr.cpp create mode 100644 src/string.cpp create mode 100644 src/string_db.cpp create mode 100644 test/main.cpp create mode 100644 test/stdafx.cpp create mode 100644 test/stdafx.h create mode 100644 test/test_ptr.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..aa58c00 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,75 @@ +cmake_minimum_required(VERSION 3.16.5) + +project(TL) + +include(${PROJECT_SOURCE_DIR}/../build-utils/cmake/compiler_settings.cmake) + +if (MSVC) + add_definitions(/bigobj) + set(LIB_FOLDER "vs2022") +elseif (ANDROID) + set(LIB_FOLDER "Android") +elseif (APPLE) + set(LIB_FOLDER "macos") +endif() + +if(NOT TARGET EASTL) + set(EASTL_DIR "${PROJECT_SOURCE_DIR}/../EASTL") + set(EASTL_LIB_DIR "${EASTL_DIR}/release/lib/${LIB_FOLDER}") + add_library(EASTL STATIC IMPORTED) + list(APPEND EASTL_INCLUDE_DIRS "${EASTL_DIR}/include" + "${EASTL_DIR}/test/packages/EAAssert/include" + "${EASTL_DIR}/test/packages/EABase/include/Common" + "${EASTL_DIR}/test/packages/EAMain/include" + "${EASTL_DIR}/test/packages/EAStdC/include" + "${EASTL_DIR}/test/packages/EATest/include" + "${EASTL_DIR}/test/packages/EAThread/include") + list(APPEND LIB_INCLUDE_DIRS "dir2") + set_target_properties(EASTL PROPERTIES + IMPORTED_LOCATION "${EASTL_LIB_DIR}/Release/${LIB_PREFIX}EASTL${LIB_SUFFIX}" + IMPORTED_LOCATION_DEBUG "${EASTL_LIB_DIR}/Debug/${LIB_PREFIX}EASTL${LIB_SUFFIX}" + INTERFACE_INCLUDE_DIRECTORIES "${EASTL_INCLUDE_DIRS}") +endif() + +file(GLOB_RECURSE SRC "src/*.cpp") +file(GLOB_RECURSE HEADERS "include/*.h" "include/*.inl" "include/*.hpp" "include/*.tpp") +file(GLOB_RECURSE NATVIS "natvis/*.natjmc" "natvis/*.natstepfilter" "natvis/*.natvis") + +foreach(_source IN ITEMS ${HEADERS}) +get_filename_component(_source_path "${_source}" PATH) +file(RELATIVE_PATH _source_path_rel "${PROJECT_SOURCE_DIR}" "${_source_path}") +string(REPLACE "/" "\\" _group_path "${_source_path_rel}") +source_group("${_group_path}" FILES "${_source}") +endforeach() + +foreach(_source IN ITEMS ${SRC}) +get_filename_component(_source_path "${_source}" PATH) +file(RELATIVE_PATH _source_path_rel "${PROJECT_SOURCE_DIR}" "${_source_path}") +string(REPLACE "/" "\\" _group_path "${_source_path_rel}") +source_group("${_group_path}" FILES "${_source}") +endforeach() + +add_library(TL STATIC ${SRC} ${HEADERS} ${NATVIS}) +target_include_directories(TL PUBLIC "include") +target_link_libraries(TL EASTL) + +#if(BUILD_SHARED_LIBS) +# target_compile_definitions(TL PUBLIC TL_BUILD_SHARED_LIB) +#endif() + +if (0) + #////////////////////////////////// + + set(Boost_INCLUDE_DIR "${PROJECT_SOURCE_DIR}/../boost") + set(Boost_LIBRARY_DIR "${PROJECT_SOURCE_DIR}/../boost/stage/lib") + set(Boost_USE_STATIC_LIBS ON) + set(Boost_USE_MULTITHREADED ON) + set(Boost_USE_STATIC_RUNTIME OFF) + find_package(Boost 1.69 COMPONENTS unit_test_framework) + + file(GLOB_RECURSE TEST_SRC "test/*.cpp" "test/*.h") + add_executable(TL_Test ${TEST_SRC}) + target_link_libraries(TL_Test TL) + target_link_libraries(TL_Test ${Boost_LIBRARIES}) + target_include_directories(TL_Test PUBLIC ${Boost_INCLUDE_DIRS}) +endif() \ No newline at end of file diff --git a/include/tl/abs_path.h b/include/tl/abs_path.h new file mode 100644 index 0000000..4d0d7d9 --- /dev/null +++ b/include/tl/abs_path.h @@ -0,0 +1,879 @@ +#pragma once + +#include "tl/detail/prologue.h" +#include +#include "tl/path_system.h" +#include "tl/rel_path.h" +#include "tl/detail/path_base.h" +#include "tl/plain_crash.h" +#include "tl/format.h" + +namespace tl +{ + +template class abs_path; +template struct path_key_less; + +////////////////////////////////////////////////////////////////////////// + +template +class abs_path : public detail::path_system::path_base +{ + template friend class rel_path; + +public: + using rel_path_type = rel_path; + + abs_path() noexcept; + explicit abs_path(const char* path) noexcept; + abs_path(const char* path, size_t size) noexcept; + + template requires (!std::is_same_v && !std::is_same_v) + explicit abs_path(const Str& path) noexcept; + + abs_path(const abs_path& other) noexcept; + abs_path(abs_path&& other) noexcept; + + // operators + const string& operator[](size_t idx) const noexcept; + string& operator[](size_t idx) noexcept; + + abs_path& operator=(const abs_path& other) noexcept; + abs_path& operator=(abs_path&& other) noexcept; + + abs_path& operator=(const char* path) noexcept; + template requires (!std::is_same_v && !std::is_same_v) + abs_path& operator=(const Str& path) noexcept; + + abs_path operator+(const char* path) const noexcept; + abs_path operator+(const rel_path& path) const noexcept; + abs_path operator+(rel_path&& path) const noexcept; + template requires (!std::is_same_v && !std::is_same_v) + abs_path operator+(const Str& path) const noexcept; + + abs_path& operator+=(const char* path) noexcept; + abs_path& operator+=(const rel_path& path) noexcept; + abs_path& operator+=(rel_path&& path) noexcept; + template requires (!std::is_same_v && !std::is_same_v) + abs_path& operator+=(const Str& path) noexcept; + + bool operator==(const abs_path& other) const noexcept; + bool operator!=(const abs_path& other) const noexcept; + + auto operator<=>(const abs_path& other) const noexcept; + + void swap(abs_path& other) noexcept; + + void clear() noexcept; + string str() const noexcept; + eastl::string eastl_str() const noexcept; + std::string std_str() const noexcept; + + rel_path subpath(size_t idx, int count = 0) const noexcept; + abs_path parent() const noexcept; + abs_path parent(size_t levels) const noexcept; + rel_path path_to(const abs_path& to) const noexcept; + void path_to(rel_path& dst, const abs_path& to) const noexcept; + bool is_prefix_of(const abs_path& path) const noexcept; + + template + bool is() const noexcept; + bool is_valid() const noexcept; + + bool push_back(string element) noexcept override; + const string& front() const noexcept; + const string& back() const noexcept; + string& front() noexcept; + string& back() noexcept; + + void take_elements(tl::vector&& elements) noexcept override; + + + //Parses a string. + // 1st attempt is done to parse it as an absolute path. If it succeeds, it returns the path. + // 2nd attempt is done to parse it as a relative path and if it succeeds, it returns it as fallbackRoot + relative path. This happens ONLY if the fallbackRoot is valid + template + static abs_path from_string(const Str& string, const abs_path& fallbackRoot) noexcept; + + //Parses a string as an absolute path. If it fails it returns an invalid path. + template + static abs_path from_string(const Str& string) noexcept; + + template + static bool is_valid_string(const Str& string) noexcept; + template + static bool has_valid_tag(const Str& string) noexcept; + + bool parse(const char* path, size_t size) noexcept; + +private: + bool collapse_path() noexcept; + + int m_pathSystemId = -1; +}; + +////////////////////////////////////////////////////////////////////////// + +} + +////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////// + +namespace tl +{ +////////////////////////////////////////////////////////////////////////// + +template +abs_path::abs_path() noexcept +{ + m_pathSystemId = -1; + this->m_hash = 0; +} + +////////////////////////////////////////////////////////////////////////// + +template +abs_path::abs_path(const char* path, size_t size) noexcept +{ + if (!path) + TL_PLAIN_CRASH("null path in abs_path constructor"); + + if (parse(path, size) == false) + TL_PLAIN_FAIL("Failed to parse absolute path"); +} + +////////////////////////////////////////////////////////////////////////// + +template +abs_path::abs_path(const char* path) noexcept +{ + if (!path) + TL_PLAIN_CRASH("null path in abs_path constructor"); + + if (parse(path, strlen(path)) == false) + TL_PLAIN_FAIL("Failed to parse absolute path"); +} + +////////////////////////////////////////////////////////////////////////// + +template +template requires (!std::is_same_v && !std::is_same_v) +abs_path::abs_path(const Str& path) noexcept +{ + if (parse(path.data(), path.size()) == false) + TL_PLAIN_FAIL("Failed to parse absolute path"); +} + +////////////////////////////////////////////////////////////////////////// + +template +abs_path::abs_path(const abs_path& other) noexcept + : m_pathSystemId(other.m_pathSystemId) +{ + this->m_elements = other.m_elements; + this->m_hash = other.m_hash; +} + +////////////////////////////////////////////////////////////////////////// + +template +abs_path::abs_path(abs_path&& other) noexcept + : m_pathSystemId(other.m_pathSystemId) +{ + this->m_elements = std::move(other.m_elements); + this->m_hash = other.m_hash; + other.m_hash = 0; +} + +////////////////////////////////////////////////////////////////////////// + +template +const string& abs_path::operator[](size_t idx) const noexcept +{ + return this->m_elements[idx]; +} + +//////////////////////////////////////////////////////////////////////////////////////////////////////////// + +template +string& abs_path::operator[](size_t idx) noexcept +{ + this->m_hash = 0; + return this->m_elements[idx]; +} + +//////////////////////////////////////////////////////////////////////////////////////////////////////////// + +template +abs_path& abs_path::operator=(const abs_path& other) noexcept +{ + this->m_elements = other.m_elements; + this->m_hash = other.m_hash; + m_pathSystemId = other.m_pathSystemId; + return *this; +} + +////////////////////////////////////////////////////////////////////////// + +template +abs_path& abs_path::operator=(abs_path&& other) noexcept +{ + std::swap(this->m_elements, other.m_elements); + std::swap(this->m_hash, other.m_hash); + std::swap(m_pathSystemId, other.m_pathSystemId); + return *this; +} + +////////////////////////////////////////////////////////////////////////// + +template +template requires (!std::is_same_v && !std::is_same_v) +abs_path& abs_path::operator=(const Str& path) noexcept +{ + if (parse(path.data(), path.size()) == false) + TL_PLAIN_FAIL("Failed to parse absolute path"); + + return *this; +} + +////////////////////////////////////////////////////////////////////////// + +template +abs_path& abs_path::operator=(const char* path) noexcept +{ + if (!path) + TL_PLAIN_CRASH("null path in abs_path assignment"); + + if (parse(path, strlen(path)) == false) + TL_PLAIN_FAIL("Failed to parse absolute path"); + + return *this; +} + +//////////////////////////////////////////////////////////////////////////////////////////////////////////// + +template +abs_path abs_path::operator+(const char* path) const noexcept +{ + if (path && path[0] != '\0') + { + abs_path p; + p.reserve(this->size() + 1); + p = *this; + + p += path; + return p; + } + return *this; +} + +////////////////////////////////////////////////////////////////////////// + +template +template requires (!std::is_same_v && !std::is_same_v) +abs_path abs_path::operator+(const Str& path) const noexcept +{ + if (!path.empty()) + { + abs_path p; + p.reserve(this->size() + 1); + p = *this; + + p += path; + return p; + } + return *this; +} + +////////////////////////////////////////////////////////////////////////// + +template +abs_path abs_path::operator+(const rel_path& path) const noexcept +{ + if (!path.empty()) + { + abs_path p; + p.reserve(this->size() + 1); + p = *this; + + p += path; + return p; + } + return *this; +} + +////////////////////////////////////////////////////////////////////////// + +template +abs_path abs_path::operator+(rel_path&& path) const noexcept +{ + if (!path.empty()) + { + abs_path p; + p.reserve(this->size() + 1); + p = *this; + + p += std::move(path); + return p; + } + return *this; +} + +//////////////////////////////////////////////////////////////////////////////////////////////////////////// + +template +abs_path& abs_path::operator+=(const char* path) noexcept +{ + if (m_pathSystemId < 0 && this->empty()) + this->operator=(path); + else if (path && path[0] != '\0') + *this += rel_path(path); + + return *this; +} + +////////////////////////////////////////////////////////////////////////// + +template +template requires (!std::is_same_v && !std::is_same_v) +abs_path& abs_path::operator+=(const Str& path) noexcept +{ + if (m_pathSystemId < 0 && this->empty()) + this->operator=(path); + else if (!path.empty()) + *this += rel_path(path); + + return *this; +} + +////////////////////////////////////////////////////////////////////////// + +template +abs_path& abs_path::operator+=(const rel_path& path) noexcept +{ + if (!path.empty()) + { + //use the move version + *this += rel_path(path); + this->collapse_path(); + } + return *this; +} + +////////////////////////////////////////////////////////////////////////// + +template +abs_path& abs_path::operator+=(rel_path&& path) noexcept +{ + if (!path.empty()) + { + this->m_elements.reserve(this->m_elements.size() + path.size()); + for (auto& e : path) + { + if (!push_back(std::move(e))) + return *this; + } + this->m_hash = 0; + this->collapse_path(); + } + return *this; +} + +//////////////////////////////////////////////////////////////////////////////////////////////////////////// + +template +bool abs_path::operator==(const abs_path& other) const noexcept +{ + return (!this->m_hash || !other.m_hash || this->m_hash == other.m_hash) && + this->m_pathSystemId == other.m_pathSystemId && + this->m_elements == other.m_elements; +} + +////////////////////////////////////////////////////////////////////////// + +template +bool abs_path::operator!=(const abs_path& other) const noexcept +{ + return !(*this == other); +} + +////////////////////////////////////////////////////////////////////////// + +template +auto abs_path::operator<=>(const abs_path& other) const noexcept +{ +// return std::compare_three_way{}(this->m_elements, other.m_elements); + const size_t sz1 = this->m_elements.size(); + const size_t sz2 = other.m_elements.size(); + for (size_t i = 0; i < sz1 && i < sz2; ++i) + { + auto r = this->m_elements[i] <=> other.m_elements[i]; + if (r != decltype(r)::equal) + return r; + } + + return sz1 <=> sz2; +} + +////////////////////////////////////////////////////////////////////////// + +template +bool abs_path::parse(const char* path, size_t size) noexcept +{ + this->clear(); + if (size == 0 || !path || path[0] == 0) + return true; + + m_pathSystemId = PathSystems::deduce_system_type(path, size); + if (m_pathSystemId < 0) + return false; + + PathSystems::parse_absolute(this->m_elements, m_pathSystemId, path, size); + for (const string& element : this->m_elements) + { + if (!this->validate_element(element)) + { + this->clear(); + return false; + } + } + + if (collapse_path() == false) + { + this->clear(); + return false; + } + + return true; +} + +////////////////////////////////////////////////////////////////////////// + +template +bool abs_path::collapse_path() noexcept +{ + if (this->empty()) + return is_valid(); + + this->m_hash = 0; + for (size_t i = 0; i < this->m_elements.size();) + { + if (i >= 1 && + this->m_elements[i] == path_system::back_t::value() && + this->m_elements[i - 1] != path_system::back_t::value()) + { + this->m_elements.erase(this->m_elements.begin() + i - 1, this->m_elements.begin() + i + 1); //remove the .. and the parent + i -= 1; + } + else if (this->m_elements[i] == path_system::current_t::value()) + this->m_elements.erase(this->m_elements.begin() + i); //remove the . + else + i++; + } + + if (!PathSystems::validate_abs_path(m_pathSystemId, this->m_elements)) + return false; + + // if the first element is ".." the collapse failed + if (!this->m_elements.empty() && this->m_elements[0] == path_system::back_t::value()) + return false; + + return true; +} + +////////////////////////////////////////////////////////////////////////// + +template +void abs_path::swap(abs_path& other) noexcept +{ + std::swap(this->m_elements, other.m_elements); + std::swap(this->m_hash, other.m_hash); + std::swap(this->m_pathSystemId, other.m_pathSystemId); +} + +////////////////////////////////////////////////////////////////////////// + +template +void abs_path::clear() noexcept +{ + this->m_elements.resize(0); + this->m_hash = 0; + m_pathSystemId = -1; +} + +////////////////////////////////////////////////////////////////////////// + +template +string abs_path::str() const noexcept +{ + if (!is_valid() && this->empty()) + return {}; + + return PathSystems::template format_absolute(m_pathSystemId, this->m_elements); +} + +////////////////////////////////////////////////////////////////////////// + +template +eastl::string abs_path::eastl_str() const noexcept +{ + if (!is_valid() && this->empty()) + return {}; + + return PathSystems::template format_absolute(m_pathSystemId, this->m_elements); +} + +////////////////////////////////////////////////////////////////////////// + +template +std::string abs_path::std_str() const noexcept +{ + if (!is_valid() && this->empty()) + return {}; + + return PathSystems::template format_absolute(m_pathSystemId, this->m_elements); +} + +////////////////////////////////////////////////////////////////////////// + +template +rel_path abs_path::subpath(size_t idx, int count /* = 0 */) const noexcept +{ + rel_path dst; + if (count == 0) + count = (int)this->size() - (int)idx; + + if (count < 0) + count = (int)this->size() - (int)idx + count; + + if (count > 0 && idx < this->size()) + { + dst.reserve(count); + for (int i = 0; i < count; i++) + dst.push_element(this->m_elements[idx + i]); + } + return dst; +} + +////////////////////////////////////////////////////////////////////////// + +template +abs_path abs_path::parent() const noexcept +{ + if (this->empty()) + { + TL_PLAIN_FAIL("Invalid path"); + return abs_path(); + } + + abs_path p = *this; + + if (p.back() == path_system::back_t::value()) + { + //do not do a pop_back here!! + //The parent of '/..' is '/../..'!!! + p.push_back(path_system::back_t::value()); + } + else + p.pop_back(); + + if (!PathSystems::validate_abs_path(p.m_pathSystemId, p.m_elements)) + { + TL_PLAIN_FAIL("Invalid path"); + return abs_path(); + } + + return p; +} + +////////////////////////////////////////////////////////////////////////// + +template +abs_path abs_path::parent(size_t levels) const noexcept +{ + if (this->empty() || this->size() <= levels) + { + TL_PLAIN_FAIL("Invalid path"); + return abs_path(); + } + + abs_path p = *this; + + if (p.back() == path_system::back_t::value()) + { + //The parent of '/..' is '/../..'!!! + while (levels > 0) + { + levels--; + p.push_back(path_system::back_t::value()); + } + } + else + p.shrink(p.size() - levels); + + if (!PathSystems::validate_abs_path(p.m_pathSystemId, p.m_elements)) + { + TL_PLAIN_FAIL("Invalid path"); + return abs_path(); + } + + return p; +} + +////////////////////////////////////////////////////////////////////////// + +template +template +bool abs_path::is() const noexcept +{ + return PathSystems::template is(m_pathSystemId); +} + +////////////////////////////////////////////////////////////////////////// + +template +inline bool abs_path::is_valid() const noexcept +{ + return m_pathSystemId >= 0; +} + +////////////////////////////////////////////////////////////////////////// + +template +rel_path abs_path::path_to(const abs_path& to) const noexcept +{ + rel_path path; + path_to(path, to); + return path; +} + +////////////////////////////////////////////////////////////////////////// + +template +void abs_path::path_to(rel_path& dst, const abs_path& to) const noexcept +{ + dst.clear(); + + if (this->empty()) + { + dst.reserve(to.size()); + for (string const& element : to) + dst.push_element(element); + } + else if (to.empty()) + { + //ms relative path from an absolute path to an empty path should be an empty path + } + else + { + const size_t count = std::min(this->size(), to.size()); + size_t last = 0; + for (size_t i = 0; i < count; i++) + { + if (this->m_elements[i] != to.m_elements[i]) + break; + last = i + 1; + } + + dst.reserve((this->size() - last) + to.size() - last); + for (size_t i = last; i < this->size(); i++) + dst.push_element(path_system::back_t::value()); + + for (size_t i = last; i < to.size(); i++) + dst.push_element(to.m_elements[i]); + } +} + +////////////////////////////////////////////////////////////////////////// + +template +bool abs_path::is_prefix_of(const abs_path& path) const noexcept +{ + if (m_pathSystemId != path.m_pathSystemId) + return false; + + if (this->size() > path.size()) + return false; + + for (size_t i = 0, _count = this->size(); i < _count; ++i) + { + if (this->m_elements[i] != path[i]) + return false; + } + + return true; +} + +////////////////////////////////////////////////////////////////////////// + +template +void abs_path::take_elements(tl::vector&& elements) noexcept +{ + clear(); + if (elements.empty()) + return; + + m_pathSystemId = PathSystems::deduce_system_type(elements.front().data(), elements.front().size()); + if (m_pathSystemId < 0) + return; + + detail::path_system::path_base::take_elements(std::move(elements)); + + if (collapse_path() == false) + clear(); +} + +////////////////////////////////////////////////////////////////////////// + +template +bool abs_path::push_back(string element) noexcept +{ + if (m_pathSystemId < 0) + { + TL_PLAIN_ASSERT(this->empty()); + m_pathSystemId = PathSystems::deduce_system_type(element.data(), element.size()); + if (m_pathSystemId < 0) + return false; + } + + const bool is_back_element = element == path_system::back_t::value(); + + if (!detail::path_system::path_base::push_back(std::move(element))) + return false; + + if (is_back_element && collapse_path() == false) + { + clear(); + return false; + } + + return true; +} + +////////////////////////////////////////////////////////////////////////// + +template +const tl::string& abs_path::back() const noexcept +{ + TL_PLAIN_ASSERT(!this->empty()); + return this->m_elements.back(); +} + +////////////////////////////////////////////////////////////////////////// + +template +const tl::string& abs_path::front() const noexcept +{ + TL_PLAIN_ASSERT(!this->empty()); + return this->m_elements.front(); +} + +////////////////////////////////////////////////////////////////////////// + +template +tl::string& abs_path::front() noexcept +{ + TL_PLAIN_ASSERT(!this->empty()); + this->m_hash = 0; + return this->m_elements.front(); +} + +////////////////////////////////////////////////////////////////////////// + +template +tl::string& abs_path::back() noexcept +{ + TL_PLAIN_ASSERT(!this->empty()); + this->m_hash = 0; + return this->m_elements.back(); +} + +////////////////////////////////////////////////////////////////////////// + +template +template +abs_path abs_path::from_string(const Str& string, const abs_path& fallbackRoot) noexcept +{ + abs_path outPath; + if (outPath.parse(string.data(), string.size())) + return outPath; + else if (fallbackRoot.is_valid()) + return fallbackRoot + string; + else + return abs_path(); +} + +////////////////////////////////////////////////////////////////////////// + +template +template +abs_path abs_path::from_string(const Str& string) noexcept +{ + abs_path outPath; + if (outPath.parse(string.data(), string.size())) + return outPath; + + return abs_path(); +} + +////////////////////////////////////////////////////////////////////////// + +template +template +bool abs_path::is_valid_string(const Str& string) noexcept +{ + abs_path outPath; + return outPath.parse(string.data(), string.size()); +} + +template +template +bool abs_path::has_valid_tag(const Str& string) noexcept +{ + if (string.empty()) + return false; + + const int pathSystemId = PathSystems::deduce_system_type(string.data(), string.size()); + return pathSystemId >= 0; +} + +} + +////////////////////////////////////////////////////////////////////////// + +template +struct std::formatter> +{ + constexpr auto parse(format_parse_context& ctx) noexcept { return ctx.begin(); } + auto format(const tl::abs_path& p, std::format_context& ctx) const + { + return format_to(ctx.out(), "{}", p.str()); + } +}; + +template +void swap(tl::abs_path& a, tl::abs_path& b) noexcept +{ + a.swap(b); +} + +template +struct std::hash> +{ + std::size_t operator()(const tl::abs_path& p) const + { + return p.hash(); + } +}; + + +template +struct eastl::hash> +{ + std::size_t operator()(const tl::abs_path& p) const + { + return p.hash(); + } +}; + diff --git a/include/tl/adaptors.h b/include/tl/adaptors.h new file mode 100644 index 0000000..5fc3da8 --- /dev/null +++ b/include/tl/adaptors.h @@ -0,0 +1,3 @@ +#pragma once + +#include "tl/detail/prologue.h" \ No newline at end of file diff --git a/include/tl/algorithm.h b/include/tl/algorithm.h new file mode 100644 index 0000000..8434b8f --- /dev/null +++ b/include/tl/algorithm.h @@ -0,0 +1,9 @@ +#pragma once + +#include "tl/detail/prologue.h" +#include + +namespace tl +{ +using namespace eastl; +} \ No newline at end of file diff --git a/include/tl/algorithm/adjacent_find.h b/include/tl/algorithm/adjacent_find.h new file mode 100644 index 0000000..f6b3cba --- /dev/null +++ b/include/tl/algorithm/adjacent_find.h @@ -0,0 +1,30 @@ +#pragma once + +#include "tl/detail/prologue.h" +#include "tl/iterator.h" + +#include + + +namespace tl +{ + +////////////////////////////////////////////////////////////////////////// + +// adjacent_find() +// +// Container-based version of the `std::adjacent_find()` function to +// find equal adjacent elements within a container. +template +auto adjacent_find(Squence& sequence) noexcept -> decltype(tl::begin(sequence)); + +// Overload of adjacent_find() for using a predicate evaluation other than `==` as +// the function's test condition. +template +auto adjacent_find(Sequence& sequence, BinaryPredicate&& p) noexcept -> decltype(tl::begin(sequence)); + +////////////////////////////////////////////////////////////////////////// + +} // end namespace tl + +#include "tl/detail/algorithm/adjacent_find.inl" diff --git a/include/tl/algorithm/all_any_none_of.h b/include/tl/algorithm/all_any_none_of.h new file mode 100644 index 0000000..4bff451 --- /dev/null +++ b/include/tl/algorithm/all_any_none_of.h @@ -0,0 +1,30 @@ +#pragma once + +#include "tl/detail/prologue.h" +#include "tl/algorithm.h" +#include "tl/iterator.h" + +namespace tl +{ +////////////////////////////////////////////////////////////////////////// + +template +bool all_of(const C& c, Pred&& pred) noexcept +{ + return eastl::all_of(tl::begin(c), tl::end(c), std::forward(pred)); +} + +template +bool any_of(const C& c, Pred&& pred) noexcept +{ + return eastl::any_of(tl::begin(c), tl::end(c), std::forward(pred)); +} + +template +bool none_of(const C& c, Pred&& pred) noexcept +{ + return eastl::none_of(tl::begin(c), tl::end(c), std::forward(pred)); +} + +////////////////////////////////////////////////////////////////////////// +} diff --git a/include/tl/algorithm/append.h b/include/tl/algorithm/append.h new file mode 100644 index 0000000..4509a1b --- /dev/null +++ b/include/tl/algorithm/append.h @@ -0,0 +1,59 @@ +#pragma once + +#include "tl/detail/prologue.h" +#include "tl/type_traits.h" + +#include +#include +#include + +namespace tl +{ +namespace detail +{ +////////////////////////////////////////////////////////////////////////// + +// associative containers (ordered & unordered) [maps, unordered_maps, multimaps, unordered_multimaps ...] +template +void append(std::true_type, DstContainer& dst, const SrcContainer& src) noexcept +{ + dst.insert(std::begin(src), std::end(src)); +} +////////////////////////////////////////////////////////////////////////// + +// sequence containers [vector, dequeue, forward_list, list, ...] +template +void append(std::false_type, DstContainer& dst, const SrcContainer& src) noexcept +{ + dst.insert(std::end(dst), std::begin(src), std::end(src)); +} + +////////////////////////////////////////////////////////////////////////// + +// Obs: std::forward_list don't support fast traversal to the end +// thus going to std::prev(std::end(dst)) must be done manually +// Cost: O(N) +template +void append(std::false_type, std::forward_list& dst, const SrcContainer& src) noexcept +{ + auto before_end = dst.before_begin(); + for (auto& _ : dst) + ++before_end; + + dst.insert_after(before_end, std::begin(src), std::end(src)); +} + +////////////////////////////////////////////////////////////////////////// +} // namespace detail + + ////////////////////////////////////////////////////////////////////////// + +template +void append(DstContainer& dst, const SrcContainer& src) noexcept +{ + detail::append(tl::is_associative_container{}, dst, src); +} + +////////////////////////////////////////////////////////////////////////// + +} diff --git a/include/tl/algorithm/bits.h b/include/tl/algorithm/bits.h new file mode 100644 index 0000000..80510c1 --- /dev/null +++ b/include/tl/algorithm/bits.h @@ -0,0 +1,102 @@ +#pragma once + +#include "tl/detail/prologue.h" +#include "tl/functional.h" + +namespace tl +{ +namespace algorithm +{ + +////////////////////////////////////////////////////////////////////////// + +/** + * @brief Count the number of bit set for the given value. + */ +inline uint8_t bits_count(uint32_t i_value) noexcept +{ +#if defined(_MSC_VER) + return uint8_t(__popcnt(i_value)); +#elif defined(__clang__) || defined(__GNUC__) + return uint8_t(__builtin_popcount(i_value)); +#else + static_assert(!std::is_same_v, "no-builtin function found for this compiler"); +#endif +} + +////////////////////////////////////////////////////////////////////////// + +/** + * @brief Count the number of bit set for the given value. + */ +inline uint64_t bits_count(uint64_t i_value) noexcept +{ +#if defined(_MSC_VER) +#if defined(TL_PLATFORM_64) + return uint8_t(__popcnt64(i_value)); +#elif defined(TL_PLATFORM_32) + return bits_count(uint32_t(i_value >> 32)) + bits_count(uint32_t(i_value & 0xffffffff)); +#else + static_assert(!std::is_same_v, "no-builtin function found for this compiler);"); +#endif // TL_PLATFORM_XX +#elif defined(__clang__) || defined(__GNUC__) + return uint8_t(__builtin_popcountl(i_value)); +#else + static_assert(!std::is_same_v, "no-builtin function found for this compiler"); +#endif +} + +////////////////////////////////////////////////////////////////////////// + +/** + * @brief Position of the top bit set to 1. + * @param the given value + * @return -1 in case value is 0 otherwise the position. + */ +inline int8_t top_bit(uint32_t i_value) noexcept +{ +#if defined(_MSC_VER) + unsigned long index = 0; + return _BitScanReverse(&index, (unsigned long)i_value) ? uint8_t(index) : -1; +#elif defined(__clang__) || defined(__GNUC__) + return i_value ? 31u - __builtin_clzl(i_value) : -1; +#else + static_assert(!std::is_same_v, "no-builtin function found for this compiler);"); +#endif +} + +////////////////////////////////////////////////////////////////////////// + +/** + * @brief Position of the top bit set to 1. + * @param the given value + * @return -1 in case value is 0 otherwise the position. + */ +inline int8_t top_bit(uint64_t i_value) noexcept +{ +#if defined(_MSC_VER) + unsigned long index = 0; +#if defined(TL_PLATFORM_64) + return _BitScanReverse64(&index, (unsigned long long)i_value) ? uint8_t(index) : -1; +#elif defined(TL_PLATFORM_32) + const int8_t position = top_bit(uint32_t(i_value >> 32)); + if (position > -1) + { + return position + 32; + } + + return top_bit(uint32_t(i_value & 0xffffffff)); +#else + static_assert(!std::is_same_v, "no-builtin function found for this compiler);"); +#endif // TL_PLATFORM_XX +#elif defined(__clang__) || defined(__GNUC__) + return i_value ? 63u - __builtin_clzll(i_value) : -1; +#else + static_assert(!std::is_same_v, "no-builtin function found for this compiler);"); +#endif +} + +////////////////////////////////////////////////////////////////////////// + +} // namespace algorithm +} // namespace tl diff --git a/include/tl/algorithm/chained_sorting_predicate.h b/include/tl/algorithm/chained_sorting_predicate.h new file mode 100644 index 0000000..e718a9d --- /dev/null +++ b/include/tl/algorithm/chained_sorting_predicate.h @@ -0,0 +1,20 @@ +#pragma once + +#include "tl/detail/prologue.h" + +namespace tl +{ + +namespace algorithm +{ + +////////////////////////////////////////////////////////////////////////// + +template struct chained_sorting_predicate; + +template +chained_sorting_predicate make_chained_sorting_predicate(const Comparators&... args) noexcept; + +} + +} diff --git a/include/tl/algorithm/container.h b/include/tl/algorithm/container.h new file mode 100644 index 0000000..5ad9c01 --- /dev/null +++ b/include/tl/algorithm/container.h @@ -0,0 +1,36 @@ +#pragma once + +#include "tl/detail/prologue.h" + +// see https://en.cppreference.com/w/cpp/algorithm + +// ----------------------------------------------------------------------------- +// Non-modifiying sequence operations (STL specializations) +// ----------------------------------------------------------------------------- +#include "tl/algorithm/all_any_none_of.h" +#include "tl/algorithm/for_each.h" +#include "tl/algorithm/count.h" +#include "tl/algorithm/mismatch.h" +#include "tl/algorithm/find.h" +#include "tl/algorithm/find_end.h" +#include "tl/algorithm/find_first_of.h" +#include "tl/algorithm/adjacent_find.h" +#include "tl/algorithm/search.h" +// ----------------------------------------------------------------------------- +// Non-modifiying sequence operations (our own versions) +// ----------------------------------------------------------------------------- +#include "tl/algorithm/contains.h" + + +// ----------------------------------------------------------------------------- +// Modifiying sequence operations +// ----------------------------------------------------------------------------- +#include "tl/algorithm/copy.h" +#include "tl/algorithm/remove.h" +#include "tl/algorithm/unique.h" + + +// ----------------------------------------------------------------------------- +// Modifiying sequence operations (our own versions) +// ----------------------------------------------------------------------------- +#include "tl/algorithm/append.h" diff --git a/include/tl/algorithm/contains.h b/include/tl/algorithm/contains.h new file mode 100644 index 0000000..cc230c2 --- /dev/null +++ b/include/tl/algorithm/contains.h @@ -0,0 +1,39 @@ +#pragma once + +#include "tl/detail/prologue.h" +#include "tl/algorithm/find.h" +#include + + +namespace tl +{ + +////////////////////////////////////////////////////////////////////////// + +template +bool contains(C& c, T&& value) noexcept +{ + return tl::find(c, std::forward(value)) != tl::end(c); +} + +template +bool contains(const C& c, T&& value) noexcept +{ + return tl::cfind(c, std::forward(value)) != tl::end(c); +} + +template +bool contains_if(C& c, UnaryPredicate&& p) noexcept +{ + return tl::find_if(c, std::forward(p)) != tl::end(c); +} + +template +bool contains_if(const C& c, UnaryPredicate&& p) noexcept +{ + return tl::cfind_if(c, std::forward(p)) != tl::end(c); +} + +////////////////////////////////////////////////////////////////////////// + +} diff --git a/include/tl/algorithm/copy.h b/include/tl/algorithm/copy.h new file mode 100644 index 0000000..4a7c48c --- /dev/null +++ b/include/tl/algorithm/copy.h @@ -0,0 +1,14 @@ +#pragma once + +#include "tl/detail/prologue.h" + +namespace tl +{ + +////////////////////////////////////////////////////////////////////////// + +template auto copy(const Container& container, OutputIt d_first) noexcept -> OutputIt; +template auto copy_if(const Container& container, OutputIt d_first, const UnaryPredicate& predicate) noexcept -> OutputIt; + +////////////////////////////////////////////////////////////////////////// +} diff --git a/include/tl/algorithm/count.h b/include/tl/algorithm/count.h new file mode 100644 index 0000000..062acde --- /dev/null +++ b/include/tl/algorithm/count.h @@ -0,0 +1,30 @@ +#pragma once + +#include "tl/detail/prologue.h" +#include "tl/iterator.h" + +#include + +namespace tl +{ + +////////////////////////////////////////////////////////////////////////// + +// count() +// +// Container-based version of the `std::count()` function to +// count values that match within a container +template +auto count(const C& c, T&& value) noexcept -> typename std::iterator_traits::difference_type; + +// count_if() +// +// Container-based version of the `std::count()` function to +// count values that match within a container +template +auto count_if(const C& c, UnaryPredicate&& p) noexcept -> typename std::iterator_traits::difference_type; + +////////////////////////////////////////////////////////////////////////// + +} // end namespace tl + diff --git a/include/tl/algorithm/find.h b/include/tl/algorithm/find.h new file mode 100644 index 0000000..02ac73d --- /dev/null +++ b/include/tl/algorithm/find.h @@ -0,0 +1,155 @@ +#pragma once + +#include "tl/detail/prologue.h" +#include +#include +#include + +namespace tl +{ +namespace detail +{ +// associative containers (ordered & unordered) [maps, unordered_maps, multimaps, unordered_multimaps ...] +template +auto find(std::true_type, C& c, const Key& key) noexcept -> decltype(c.begin()) +{ + return c.find(key); +} + +// sequence containers [array, vector, dequeue, forward_list, list, ...] +template +auto find(std::false_type, C& c, const T& value) noexcept -> decltype(c.begin()) +{ + return eastl::find(tl::begin(c), tl::end(c), value); +} +} // namespace detail + + ////////////////////////////////////////////////////////////////////////// + +template +auto find(C& c, const T& value) noexcept -> decltype(c.begin()) +{ + return detail::find(is_associative_container{}, c, value); +} + +////////////////////////////////////////////////////////////////////////// + +namespace detail +{ +// associative containers (ordered & unordered) [maps, unordered_maps, multimaps, unordered_multimaps ...] +template +auto cfind(std::true_type, const C& c, const Key& key) noexcept -> decltype(c.cbegin()) +{ + return c.find(key); +} + +// sequence containers [array, vector, dequeue, forward_list, list, ...] +template +auto cfind(std::false_type, const C& c, const T& value) noexcept -> decltype(c.cbegin()) +{ + return eastl::find(tl::cbegin(c), tl::cend(c), value); +} +} // namespace detail + + +template +auto cfind(const C& c, const T& value) noexcept -> decltype(c.cbegin()) +{ + return detail::cfind(is_associative_container{}, c, value); +} + +////////////////////////////////////////////////////////////////////////// + +namespace detail +{ +// associative containers (ordered) [maps, multimaps ...] +template +auto rfind(std::true_type, C& c, const Key& key) noexcept -> decltype(c.rbegin()) +{ + auto forward_iterator = c.find(key); + return tl::make_reverse_iterator(forward_iterator); +} + +// sequence containers [array, vector, dequeue, list, ...] +template +auto rfind(std::false_type, C& c, const T& value) noexcept -> decltype(c.rbegin()) +{ + return eastl::find(tl::rbegin(c), tl::rend(c), value); +} +} // namespace detail + +template +auto rfind(C& c, const T& value) noexcept -> decltype(c.rbegin()) +{ + return detail::rfind(is_associative_container{}, c, value); +} + +////////////////////////////////////////////////////////////////////////// + +namespace detail +{ +// associative containers (ordered) [maps, multimaps ...] +template +auto crfind(std::true_type, const C& c, const Key& key) noexcept -> decltype(c.crbegin()) +{ + auto forward_iterator = c.find(key); + return tl::make_reverse_iterator(forward_iterator); +} + +// sequence containers [array, vector, dequeue, forward_list, list, ...] +template +auto crfind(std::false_type, const C& c, const T& value) noexcept -> decltype(c.crbegin()) +{ + return eastl::find(tl::rbegin(c), tl::rend(c), value); +} +} // namespace detail + +template +auto crfind(const C& c, const T& value) noexcept -> decltype(c.crbegin()) +{ + return detail::rfind(is_associative_container{}, c, value); +} + +////////////////////////////////////////////////////////////////////////// + +template +auto find_if(C& c, UnaryPredicate&& p) noexcept -> decltype(c.begin()) +{ + return eastl::find_if(tl::begin(c), tl::end(c), std::forward(p)); +} + +////////////////////////////////////////////////////////////////////////// + +template +auto cfind_if(const C& c, UnaryPredicate&& p) noexcept -> decltype(c.cbegin()) +{ + return eastl::find_if(tl::cbegin(c), tl::cend(c), std::forward(p)); +} + +////////////////////////////////////////////////////////////////////////// + +template +auto rfind_if(C& c, UnaryPredicate&& p) noexcept -> decltype(c.rbegin()) +{ + return eastl::find_if(tl::rbegin(c), tl::rend(c), std::forward(p)); +} + +////////////////////////////////////////////////////////////////////////// + +template +auto crfind_if(const C& c, UnaryPredicate&& p) noexcept -> decltype(c.crbegin()) +{ + return eastl::find_if(tl::crbegin(c), tl::crend(c), std::forward(p)); +} + +////////////////////////////////////////////////////////////////////////// + +template +auto find_if_not(C& c, UnaryPredicate&& q) noexcept -> decltype(tl::begin(c)) +{ + return eastl::find_if(tl::begin(c), tl::end(c), std::forward(q)); +} + +////////////////////////////////////////////////////////////////////////// + +} diff --git a/include/tl/algorithm/find_end.h b/include/tl/algorithm/find_end.h new file mode 100644 index 0000000..ab4dabb --- /dev/null +++ b/include/tl/algorithm/find_end.h @@ -0,0 +1,30 @@ +#pragma once + +#include "tl/detail/prologue.h" +#include "tl/iterator.h" + +#include + + +namespace tl +{ + +////////////////////////////////////////////////////////////////////////// + +// find_end() +// +// Container-based version of the `std::find_end()` function to +// find the last subsequence within a container. +template +auto find_end(SquenceA& sequence, SequenceB& subsequence) noexcept -> decltype(tl::begin(sequence)); + +// Overload of find_end() for using a predicate evaluation other than `==` as +// the function's test condition. +template +auto find_end(SequenceA& sequence, SequenceB& subsequence, BinaryPredicate&& p) noexcept -> decltype(tl::begin(sequence)); + +////////////////////////////////////////////////////////////////////////// + +} // end namespace tl + +#include "tl/detail/algorithm/find_end.inl" diff --git a/include/tl/algorithm/find_first_of.h b/include/tl/algorithm/find_first_of.h new file mode 100644 index 0000000..8859872 --- /dev/null +++ b/include/tl/algorithm/find_first_of.h @@ -0,0 +1,28 @@ +#pragma once + +#include "tl/detail/prologue.h" +#include "tl/iterator.h" + +#include + + +namespace tl +{ + +////////////////////////////////////////////////////////////////////////// + +// find_first_of() +// +// Container-based version of the `std::find_first_of()` function to +// find the first elements in an ordered set within a container. +template +auto find_first_of(SquenceA& sequence, SequenceB& subsequence) noexcept -> decltype(tl::begin(sequence)); + +// Overload of find_first_of() for using a predicate evaluation other than `==` as +// the function's test condition. +template +auto find_first_of(SequenceA& sequence, SequenceB& subsequence, BinaryPredicate&& p) noexcept -> decltype(tl::begin(sequence)); + +////////////////////////////////////////////////////////////////////////// + +} // end namespace tl diff --git a/include/tl/algorithm/for_each.h b/include/tl/algorithm/for_each.h new file mode 100644 index 0000000..0a6b706 --- /dev/null +++ b/include/tl/algorithm/for_each.h @@ -0,0 +1,24 @@ +#pragma once + +#include "tl/detail/prologue.h" +#include "tl/type_traits.h" +#include "tl/iterator.h" + +#include + + +namespace tl +{ + +////////////////////////////////////////////////////////////////////////// + +// for_each() +// +// Container-based version of the `std::for_each()` function to +// apply a function to a container's elements. +template +decay_t for_each(C&& c, Function&& f) noexcept; + +////////////////////////////////////////////////////////////////////////// + +} // end namespace tl diff --git a/include/tl/algorithm/mismatch.h b/include/tl/algorithm/mismatch.h new file mode 100644 index 0000000..f8493d0 --- /dev/null +++ b/include/tl/algorithm/mismatch.h @@ -0,0 +1,27 @@ +#pragma once + +#include "tl/detail/prologue.h" +#include "tl/iterator.h" + +#include + +namespace tl +{ + +////////////////////////////////////////////////////////////////////////// + +// mismatch() +// +// Container-based version of the `std::mismatch()` function to +// return the first element where two ordered containers differ +template +auto mismatch(C1& c1, C2& c2) noexcept -> std::pair; + +// Overload of mismatch() for using a predicate evaluation other than `==` as +// the function's test condition. +template +auto mismatch(C1& c1, C2& c2, BinaryPredicate&& p) noexcept -> std::pair; + +////////////////////////////////////////////////////////////////////////// + +} // end namespace tl diff --git a/include/tl/algorithm/move.h b/include/tl/algorithm/move.h new file mode 100644 index 0000000..493a8fe --- /dev/null +++ b/include/tl/algorithm/move.h @@ -0,0 +1,14 @@ +#pragma once + +#include "tl/detail/prologue.h" + +namespace tl +{ + +////////////////////////////////////////////////////////////////////////// + +template auto move(Container& container, OutputIt d_first) noexcept -> OutputIt; + +////////////////////////////////////////////////////////////////////////// +} + diff --git a/include/tl/algorithm/remove.h b/include/tl/algorithm/remove.h new file mode 100644 index 0000000..3fa059b --- /dev/null +++ b/include/tl/algorithm/remove.h @@ -0,0 +1,41 @@ +#pragma once + +#include "tl/detail/prologue.h" +#include "tl/algorithm.h" + +namespace tl +{ +////////////////////////////////////////////////////////////////////////// + +template +auto remove(Container& container, const T& value) noexcept -> decltype(container.begin()) +{ + return eastl::remove(container.begin(), container.end(), value); +} + +////////////////////////////////////////////////////////////////////////// + +template +auto remove_if(Container& container, const UnaryPredicate& predicate) noexcept -> decltype(container.begin()) +{ + return eastl::remove_if(container.begin(), container.end(), predicate); +} + +////////////////////////////////////////////////////////////////////////// + +template +auto erase(Container& container, const T& value) noexcept -> decltype(container.begin()) +{ + return container.erase(remove(container, value), container.end()); +} + +////////////////////////////////////////////////////////////////////////// + +template +auto erase_if(Container& container, const UnaryPredicate& predicate) noexcept -> decltype(container.begin()) +{ + return container.erase(remove_if(container, predicate), container.end()); +} + +////////////////////////////////////////////////////////////////////////// +} diff --git a/include/tl/algorithm/search.h b/include/tl/algorithm/search.h new file mode 100644 index 0000000..ecb1ef9 --- /dev/null +++ b/include/tl/algorithm/search.h @@ -0,0 +1,40 @@ +#pragma once + +#include "tl/detail/prologue.h" +#include "tl/iterator.h" + +#include + + +namespace tl +{ + +////////////////////////////////////////////////////////////////////////// + +// search() +// +// Container-based version of the `std::search()` function to search +// a container for a subsequence. +template +auto search(Sequence1& sequence, Sequence2& subsequence) noexcept -> decltype(tl::begin(sequence)); + +// Overload of search() for using a predicate evaluation other than +// `==` as the function's test condition. +template +auto search(Sequence1& sequence, Sequence2& subsequence, BinaryPredicate&& pred) noexcept -> decltype(tl::begin(sequence)); + +// search_n() +// +// Container-based version of the `std::search_n()` function to +// search a container for the first sequence of N elements. +template +auto search_n(Sequence& sequence, Size count, T&& value) noexcept -> decltype(tl::begin(sequence)); + +// Overload of search_n() for using a predicate evaluation other than +// `==` as the function's test condition. +template +auto search_n(Sequence& sequence, Size count, T&& value, BinaryPredicate&& pred) noexcept -> decltype(tl::begin(sequence)); + +////////////////////////////////////////////////////////////////////////// + +} // end namespace tl diff --git a/include/tl/algorithm/sort.h b/include/tl/algorithm/sort.h new file mode 100644 index 0000000..59fd49c --- /dev/null +++ b/include/tl/algorithm/sort.h @@ -0,0 +1,42 @@ +#pragma once + +#include "tl/detail/prologue.h" +#include +#include + +namespace tl +{ +////////////////////////////////////////////////////////////////////////// + +template +void sort(Container& container) noexcept +{ + eastl::sort(container.begin(), container.end()); +} + +////////////////////////////////////////////////////////////////////////// + +template +void sort(Container& container, const Compare& compare) noexcept +{ + eastl::sort(container.begin(), container.end(), compare); +} + +////////////////////////////////////////////////////////////////////////// + +template +void stable_sort(Container& container) noexcept +{ + eastl::stable_sort(container.begin(), container.end()); +} + +////////////////////////////////////////////////////////////////////////// + +template +void stable_sort(Container& container, const Compare& compare) noexcept +{ + eastl::stable_sort(container.begin(), container.end(), compare); +} + +////////////////////////////////////////////////////////////////////////// +} diff --git a/include/tl/algorithm/string.h b/include/tl/algorithm/string.h new file mode 100644 index 0000000..94d720b --- /dev/null +++ b/include/tl/algorithm/string.h @@ -0,0 +1,622 @@ +#pragma once + +#include "tl/detail/prologue.h" +#include "tl/functional.h" +#include +#include + +#include "tl/string.h" +#include "tl/optional.h" + +namespace tl +{ +namespace algorithm +{ +enum class empty_token_policy +{ + discard, + keep +}; + +template +DstContainer to_lower_ascii_copy(const SrcContainer& str) noexcept +{ + if constexpr (tl::is_same_v) + { + if (str.empty()) + return string(); + + const string::size_type s = str.size(); + fixed_vector b(s); + char const* string = str.data(); + for (string::size_type i = 0; i < s; i++) + b[i] = (char)ascii::tolower(string[i]); + + return { b.data(), s }; + } + else + { + DstContainer dst; + dst.resize(str.size()); + eastl::transform(tl::begin(str), tl::end(str), tl::begin(dst), ascii::tolower); + return dst; + }} + +template +Container to_lower_ascii_copy(const Container& str) noexcept +{ + return to_lower_ascii_copy(str); +} + + +template +DstContainer to_upper_ascii_copy(const SrcContainer& str) noexcept +{ + if constexpr (tl::is_same_v) + { + if (str.empty()) + return string(); + + const string::size_type s = str.size(); + fixed_vector b(s); + char const* string = str.data(); + for (string::size_type i = 0; i < s; i++) + b[i] = (char)ascii::toupper(string[i]); + + return { b.data(), s }; + } + else + { + DstContainer dst; + dst.resize(str.size()); + eastl::transform(tl::begin(str), tl::end(str), tl::begin(dst), ascii::toupper); + return dst; + } +} + +template +Container to_upper_ascii_copy(const Container& str) noexcept +{ + return to_upper_ascii_copy(str); +} + +template +void to_lower_ascii(Container& str) noexcept +{ + eastl::transform(tl::begin(str), tl::end(str), tl::begin(str), ascii::tolower); +} + +template <> +inline void to_lower_ascii(string& str) noexcept +{ + str = to_lower_ascii_copy(str); +} + +template +void to_upper_ascii(Container& str) noexcept +{ + eastl::transform(tl::begin(str), tl::end(str), tl::begin(str), ascii::toupper); +} + +template <> +inline void to_upper_ascii(string& str) noexcept +{ + str = to_upper_ascii_copy(str); +} + +template +tl::vector split_on_any(const String& str, const char* separators, empty_token_policy token_policy = empty_token_policy::discard) noexcept +{ + return split_on_any(str, String(separators), token_policy); +} + +template +tl::vector split_on_any(const String& str, const Delim& separators, empty_token_policy token_policy = empty_token_policy::discard) noexcept +{ + tl::vector dst; + dst.reserve(32); + split_on_any(str, + separators, + [&dst](const String& token) + { + dst.push_back(token); + }, + token_policy); + return dst; +} + +template +tl::vector split_on_all(const String& str, const Delim& separator, empty_token_policy token_policy = empty_token_policy::discard) noexcept +{ + tl::vector dst; + dst.reserve(32); + split_on_all(str, + separator, + [&dst](const String& token) + { + dst.push_back(token); + }, + token_policy); + return dst; +} + +template +void split_on_any(const String& str, const char* delimiters, const Func& functor, empty_token_policy token_policy = empty_token_policy::discard) noexcept +{ + return split_on_any(str, String(delimiters), functor, token_policy); +} + +template +void split_on_any(const String& str, char delimiter, const Func& functor, empty_token_policy token_policy = empty_token_policy::discard) noexcept +{ + String token; + const char* cstr = str.data(); + size_t offset = 0; + const size_t endOffset = str.size(); + while (offset <= endOffset) + { + const char* nextCstr = static_cast(::memchr(cstr + offset, delimiter, endOffset - offset)); + const size_t nextOffset = nextCstr ? nextCstr - cstr : endOffset; + + if (offset < nextOffset || token_policy == empty_token_policy::keep) + { + token.clear(); + token.append(cstr + offset, cstr + nextOffset); + functor(token); + } + offset = nextOffset + 1; + } +} + +template +void split_on_any(const String& str, const Delim& delimiters, const Func& functor, empty_token_policy token_policy = empty_token_policy::discard) noexcept +{ + const size_t k_delims_size = delimiters.size(); + if (k_delims_size == 1) + return split_on_any(str, delimiters[0], functor, token_policy); + + if (k_delims_size == 0) + { + TL_PLAIN_FAIL("'delimiters' cannot be empty"); + return; + } + + String token; + const char* cstr = str.data(); + const char* delimsStr = delimiters.data(); + size_t offset = 0; + const size_t endOffset = str.size(); + while (offset <= endOffset) + { + size_t nextOffset = offset; + for (; nextOffset < endOffset; ++nextOffset) + { + if (::memchr(delimsStr, cstr[nextOffset], k_delims_size) != nullptr) + break; + } + + if (offset < nextOffset || token_policy == empty_token_policy::keep) + { + token.clear(); + token.append(cstr + offset, cstr + nextOffset); + functor(token); + } + offset = nextOffset + 1; + } +} + +template +void split_on_all(const String& str, const Delim& sep, const Func& functor, empty_token_policy token_policy = empty_token_policy::discard) noexcept +{ + String separator(sep); + if (separator.empty()) + { + TL_PLAIN_FAIL("'delimiters' cannot be empty"); + return; + } + + size_t p0 = 0; + size_t p1 = 0; + String token; + while ((p1 = str.find(separator, p0)) != String::npos) + { + if (p1 > p0) + { + token.clear(); + token.append(str, p0, p1 - p0); + functor(token); + } + else if (token_policy == empty_token_policy::keep) + functor(String{}); + + p0 = p1 + separator.size(); + } + + if (str.size() > p0) + { + token.clear(); + token.append(str, p0, String::npos); + functor(token); + } + else if (token_policy == empty_token_policy::keep) + functor(String{}); +} + +template +String left(const String& text, size_t length) noexcept +{ + size_t textSize = text.size(); + return text.substr(0, length > textSize ? textSize : length); +} + +template +String right(const String& text, size_t length) noexcept +{ + const size_t textSize = text.size(); + size_t startPos = textSize >= length ? textSize - length : 0; + return text.substr(startPos); +} + +template +bool starts_with(const String& text, const String& prefix) noexcept +{ + const size_t textSize = text.size(); + const size_t prefixSize = prefix.size(); + if (textSize < prefixSize) + return false; + + return memcmp(text.data(), prefix.data(), prefixSize) == 0; +} + +template +bool starts_with_ci(const String& text, const String& prefix) noexcept +{ + const size_t textSize = text.size(); + const size_t prefixSize = prefix.size(); + if (textSize < prefixSize) + return false; + + return ascii::memicmp(text.data(), prefix.data(), prefixSize) == 0; +} + +template +bool ends_with(const String& text, const String& suffix) noexcept +{ + const size_t textSize = text.size(); + const size_t suffixSize = suffix.size(); + if (textSize < suffixSize) + return false; + + return memcmp(text.data() + (textSize - suffixSize), suffix.data(), suffixSize) == 0; +} + +template +bool ends_with_ci(const String& text, const String& suffix) noexcept +{ + const size_t textSize = text.size(); + const size_t suffixSize = suffix.size(); + if (textSize < suffixSize) + return false; + + return ascii::memicmp(text.data() + (textSize - suffixSize), suffix.data(), suffixSize) == 0; +} + +template +String remove_prefix(const String& text, const String& prefix) noexcept +{ + if (starts_with(text, prefix)) + return text.substr(prefix.size(), text.size() - prefix.size()); + + return text; +} + +template +String remove_prefix_ci(const String& text, const String& prefix) noexcept +{ + if (starts_with_ci(text, prefix)) + return text.substr(prefix.size(), text.size() - prefix.size()); + + return text; +} + +template +String remove_suffix(const String& text, const String& suffix) noexcept +{ + if (ends_with(text, suffix)) + return text.substr(0, text.size() - suffix.size()); + + return text; +} + +template +String remove_suffix_ci(const String& text, const String& suffix) noexcept +{ + if (ends_with_ci(text, suffix)) + return text.substr(0, text.size() - suffix.size()); + + return text; +} + +template +eastl::optional contains(const String& text, const String& subText) noexcept +{ + const size_t offset = text.find(subText); + if (offset != String::npos) + return offset; + + return nullopt; +} + +template +eastl::optional contains_ci(const String& text, const String& subText) noexcept +{ + const size_t offset = text.find_ci(subText); + if (offset != String::npos) + return offset; + + return nullopt; +} + +template +String quote(const String& text, const String& quote) noexcept +{ + String result = quote; + result += text; + result += quote; + return result; +} + +template +String unquote(const String& text, const String& quote) noexcept +{ + if (starts_with(text, quote) && ends_with(text, quote) && text.size() >= quote.size() * 2) + return text.substr(quote.size(), text.size() - quote.size() * 2); + + return text; +} + +static const char* s_defaultWhiteSpaceCStr = " \t\f\v\n\r"; + +template +String trim_left_any_of(const String& text, const String& characters) noexcept +{ + const typename String::size_type startPos = text.find_first_not_of(characters); + if (startPos == String::npos) + return String(); + + return text.substr(startPos); +} + +template +String trim_left_any_of_ci(const String& text, const String& characters) noexcept +{ + const typename String::size_type startPos = text.find_first_not_of_ci(characters); + if (startPos == String::npos) + return String(); + + return text.substr(startPos); +} + +template +String trim_left(const String& text) noexcept +{ + static String s_defaultWhiteSpace(s_defaultWhiteSpaceCStr); + return trim_left_any_of(text, s_defaultWhiteSpace); +} + +template +String trim_left_ci(const String& text) noexcept +{ + static String s_defaultWhiteSpace(s_defaultWhiteSpaceCStr); + return trim_left_any_of_ci(text, s_defaultWhiteSpace); +} + +template +String trim_right_any_of(const String& text, const String& characters) noexcept +{ + const typename String::size_type startPos = text.find_last_not_of(characters); + if (startPos == String::npos) + return String(); + + return text.substr(0, startPos + 1); +} + +template +String trim_right_any_of_ci(const String& text, const String& characters) noexcept +{ + const typename String::size_type startPos = text.find_last_not_of_ci(characters); + if (startPos == String::npos) + return String(); + + return text.substr(0, startPos + 1); +} + +template +String trim_right(const String& text) noexcept +{ + static String s_defaultWhiteSpace(s_defaultWhiteSpaceCStr); + return trim_right_any_of(text, String(s_defaultWhiteSpace)); +} + +template +String trim_right_ci(const String& text) noexcept +{ + static String s_defaultWhiteSpace(s_defaultWhiteSpaceCStr); + return trim_right_any_of_ci(text, String(s_defaultWhiteSpace)); +} + +template +String trim(const String& text) noexcept +{ + return trim_right(trim_left(text)); +} + +template +String trim_ci(const String& text) noexcept +{ + return trim_right_ci(trim_left_ci(text)); +} + +template +String trim_any_of(const String& text, const String& characters) noexcept +{ + return trim_right_any_of(trim_left_any_of(text, characters), characters); +} + +template +String trim_any_of_ci(const String& text, const String& characters) noexcept +{ + return trim_right_any_of_ci(trim_left_any_of_ci(text, characters), characters); +} + +template +String replace(const String& text, const String& target, const function(size_t, size_t)>& functor) noexcept +{ + if (target.empty()) + return text; + + eastl::fixed_vector positions; + + size_t position = 0; + while ((position = text.find(target, position)) != String::npos) + { + positions.push_back(position); + position += target.length(); + } + + String result; + result.reserve(text.size()); + + auto start = text.begin(); + for (auto it = positions.begin(); it != positions.end(); ++it) + { + const size_t position = *it; + size_t index = eastl::distance(positions.begin(), it); + + eastl::optional replacementString = functor(index, positions.size()); + if (!replacementString) + continue; + + auto end = text.begin() + position; + result.append(start, end); //copy up to the result + result.append(*replacementString); + start = end + target.size(); + } + + if (start != text.end()) + result.append(start, text.end()); //copy the end run + + return result; +} + +template +String replace(const String& text, const String& target, Fun f) noexcept +{ + return replace(text, target, function(size_t, size_t)>(f)); +} + +template +size_t replace_first(const string& text, const string& target, const string& replacement, size_t startingOffset, const Func& functor) noexcept +{ + if (target.empty()) + { + functor(text); + return string::npos; + } + + const size_t startPos = text.find(target, startingOffset); + if (startPos == string::npos) + { + functor(text); + return string::npos; + } + + eastl::string result(text.eastl_str()); + string res2(result.replace(startPos, target.length(), eastl::string(replacement.eastl_str()))); + + functor(res2); + return startPos + replacement.length(); +} + +namespace detail +{ +template +String join_worker(const Container& container, const String& delimiter) noexcept +{ + String dst; + //reserve some space based on some heuristics: + // We'll have size - 1 delimiters + // And an average string size of 2 chars. The small choice is made to help with the small string case only where the allocator overhead is significant. + dst.reserve((delimiter.size() + 2) * container.size()); + + auto end = std::end(container); + auto it = std::begin(container); + while (true) + { + dst += *it; + ++it; + if (it == end) + break; + + dst += delimiter; + } + return dst; +} + +template +string join_worker(const Container& container, const string& delimiter) noexcept +{ + eastl::string buffer; + //reserve some space based on some heuristics: + // We'll have size - 1 delimiters + // And an average string size of 2 chars. The small choice is made to help with the small string case only where the allocator overhead is significant. + buffer.reserve((delimiter.size() + 2) * container.size()); + + auto end = std::end(container); + auto it = std::begin(container); + while (true) + { + const string& s = *it; + buffer.append(s.data(), s.size()); + ++it; + if (it == end) + break; + + buffer.append(delimiter.data(), delimiter.size()); + } + return string(buffer); +} + +//joiner used when the Delim and String are of the same type +template +std::enable_if_t< + std::is_same_v< + std::remove_const_t>, + std::remove_const_t>>, + String> join(const Container& container, const Delim& delimiter) noexcept +{ + return join_worker(container, delimiter); +} + +//joiner used when the Delim and String are different types +template +std::enable_if_t< + !std::is_same_v< + std::remove_const_t>, + std::remove_const_t>>, + String> join(const Container& container, const Delim& delimiter) noexcept +{ + return join_worker(container, String(delimiter)); +} +} //namespace detail + +template +String join(const Container& container, const Delim& delimiter) noexcept +{ + if (container.empty()) + return String(); + + return detail::join(container, delimiter); +} +} +} diff --git a/include/tl/algorithm/unique.h b/include/tl/algorithm/unique.h new file mode 100644 index 0000000..fc18ddc --- /dev/null +++ b/include/tl/algorithm/unique.h @@ -0,0 +1,25 @@ +#pragma once + +#include "tl/detail/prologue.h" +#include + +namespace tl +{ +////////////////////////////////////////////////////////////////////////// + +template +auto unique(Container& container) noexcept -> decltype(container.begin()) +{ + return eastl::unique(container.begin(), container.end()); +} + +////////////////////////////////////////////////////////////////////////// + +template +auto unique(Container& container, const BinaryPredicate& predicate) noexcept -> decltype(container.begin()) +{ + return eastl::unique(container.begin(), container.end(), predicate); +} + +////////////////////////////////////////////////////////////////////////// +} diff --git a/include/tl/any.h b/include/tl/any.h new file mode 100644 index 0000000..a21e13d --- /dev/null +++ b/include/tl/any.h @@ -0,0 +1,3 @@ +#pragma once + +#include "tl/detail/prologue.h" diff --git a/include/tl/api.h b/include/tl/api.h new file mode 100644 index 0000000..da848ac --- /dev/null +++ b/include/tl/api.h @@ -0,0 +1,24 @@ +#pragma once + +#include "tl/platform.h" +#include "tl/toolchain.h" + +////////////////////////////////////////////////////////////////////////// + +#if defined TL_BUILD_SHARED_LIB +#if defined TL_TOOLCHAIN_MSC +# define TL_API __declspec(dllexport) +#else +# define TL_API +#endif +#elif defined TL_USE_SHARED_LIB +#if defined TL_TOOLCHAIN_MSC +# define TL_API __declspec(dllimport) +#else +# define TL_API +#endif // +#else +# define TL_API +#endif + +////////////////////////////////////////////////////////////////////////// \ No newline at end of file diff --git a/include/tl/array.h b/include/tl/array.h new file mode 100644 index 0000000..a21e13d --- /dev/null +++ b/include/tl/array.h @@ -0,0 +1,3 @@ +#pragma once + +#include "tl/detail/prologue.h" diff --git a/include/tl/assert.h b/include/tl/assert.h new file mode 100644 index 0000000..e92aadd --- /dev/null +++ b/include/tl/assert.h @@ -0,0 +1,6 @@ +#pragma once + +#include "tl/detail/prologue.h" +#include "tl/detail/internal_assert.h" +#include "tl/format.h" + diff --git a/include/tl/async_queue.h b/include/tl/async_queue.h new file mode 100644 index 0000000..177fc2e --- /dev/null +++ b/include/tl/async_queue.h @@ -0,0 +1,88 @@ +#pragma once + +#include +#include +#include +#include + +namespace tl +{ + +class async_queue +{ +public: + using Item = function; + + void enqueue(Item func) noexcept + { + std::lock_guard lg(m_mutex); + m_items.push_back(std::move(func)); + } + + bool run_one() noexcept + { + std::unique_lock lg(m_mutex); + if (m_items.empty()) + return false; + + const Item item = std::move(m_items.front()); + m_items.pop_front(); + lg.unlock(); + + item(); + return true; + } + + bool run() noexcept + { + std::unique_lock lg(m_mutex); + if (m_items.empty()) + return false; + + do + { + Item item = std::move(m_items.front()); + m_items.pop_front(); + lg.unlock(); + + item(); + + lg.lock(); + } while (!m_items.empty()); + + return true; + } + +private: + std::mutex m_mutex; + + deque m_items; +}; + + + +template +std::future async(async_queue& queue, Func&& func) noexcept +{ + shared_ptr> promise = make_shared>(); + auto future = promise->get_future(); + queue.enqueue([promise = std::move(promise), func = std::move(func)]() + { + if constexpr (tl::is_same_v) + { + func(); + promise->set_value(); + } + else + promise->set_value(func()); + }); + return future; +} + +template +bool is_ready(std::future const& f) noexcept +{ + return f.valid() && f.wait_until(std::chrono::system_clock::time_point::min()) == std::future_status::ready; +} + +} \ No newline at end of file diff --git a/include/tl/atomic.h b/include/tl/atomic.h new file mode 100644 index 0000000..547a35c --- /dev/null +++ b/include/tl/atomic.h @@ -0,0 +1,9 @@ +#pragma once + +#include "tl/detail/prologue.h" +#include + +namespace tl +{ +using namespace eastl; +} \ No newline at end of file diff --git a/include/tl/bitset.h b/include/tl/bitset.h new file mode 100644 index 0000000..adefd50 --- /dev/null +++ b/include/tl/bitset.h @@ -0,0 +1,9 @@ +#pragma once + +#include "tl/detail/prologue.h" +#include + +namespace tl +{ +using namespace eastl; +} \ No newline at end of file diff --git a/include/tl/bitvector.h b/include/tl/bitvector.h new file mode 100644 index 0000000..52fe665 --- /dev/null +++ b/include/tl/bitvector.h @@ -0,0 +1,9 @@ +#pragma once + +#include "tl/detail/prologue.h" +#include + +namespace tl +{ +using namespace eastl; +} \ No newline at end of file diff --git a/include/tl/blocking_queue.h b/include/tl/blocking_queue.h new file mode 100644 index 0000000..a9f4008 --- /dev/null +++ b/include/tl/blocking_queue.h @@ -0,0 +1,471 @@ +#pragma once + +#include "tl/detail/prologue.h" +#include "tl/result.h" +#include + +#include +#include +#include + +namespace tl +{ +////////////////////////////////////////////////////////////////////////// + +template +class blocking_queue +{ +public: + explicit blocking_queue(size_t max_size) noexcept; + ~blocking_queue() noexcept; + + void exit() noexcept; //stop all processing, returns Exited + void finish() noexcept; //once empty or full, stops all processing, returns Finished + + enum class push_error_code + { + Full, + Timeout, + Finished, + Exited, + }; + + using push_result = result; + + push_result push(T const& t) noexcept; + push_result try_push(T const& t) noexcept; + push_result push_for(T const& t, chrono::system_clock::duration duration) noexcept; + push_result push_until(T const& t, chrono::system_clock::time_point time_point) noexcept; + + push_result push(T&& t) noexcept; + push_result try_push(T&& t) noexcept; + push_result push_for(T&& t, chrono::system_clock::duration duration) noexcept; + push_result push_until(T&& t, chrono::system_clock::time_point time_point) noexcept; + + enum class pop_error_code + { + Empty, + Timeout, + Exited, + Finished, + }; + + using pop_result = result; + + pop_result pop() noexcept; + pop_result try_pop() noexcept; + pop_result pop_for(chrono::system_clock::duration duration) noexcept; + pop_result pop_until(chrono::system_clock::time_point time_point) noexcept; + + template + using pop_many_result = result; + + template + pop_many_result pop_many(size_t max) noexcept; + template + pop_many_result try_pop_many(size_t max) noexcept; + template + pop_many_result pop_many_for(size_t max, chrono::system_clock::duration duration) noexcept; + template + pop_many_result pop_many_until(size_t max, chrono::system_clock::time_point time_point) noexcept; + + using pop_many_emplace_result = result; + + template + pop_many_emplace_result pop_many_emplace(Container& dst, size_t max) noexcept; + template + pop_many_emplace_result try_pop_many_emplace(Container& dst, size_t max) noexcept; + template + pop_many_emplace_result pop_many_emplace_for(Container& dst, size_t max, chrono::system_clock::duration duration) noexcept; + template + pop_many_emplace_result pop_many_emplace_until(Container& dst, size_t max, chrono::system_clock::time_point time_point) noexcept; + + bool empty() const noexcept; + bool full() const noexcept; + size_t size() const noexcept; + size_t max_size() const noexcept; + size_t capacity() const noexcept; + +private: + push_result _push(T&& t, bool block, chrono::system_clock::time_point* time_point) noexcept; + pop_result _pop(bool block, chrono::system_clock::time_point* time_point) noexcept; + + template + pop_many_result _pop_many_emplace(Container& dst, size_t max, bool block, chrono::system_clock::time_point* time_point) noexcept; + + mutable std::mutex m_mutex; + std::condition_variable m_cv; + deque m_queue; + size_t m_max_size = 10; + bool m_exit = false; + bool m_finish = false; +}; + +////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////// + +template +blocking_queue::blocking_queue(size_t max_size) noexcept + : m_max_size(max_size) +{ + TL_ASSERT(m_max_size > 0); +} + +template +blocking_queue::~blocking_queue() noexcept +{ + exit(); +} + +template +void blocking_queue::exit() noexcept +{ + { + std::unique_lock lg(m_mutex); + m_exit = true; + } + m_cv.notify_all(); +} + +template +void blocking_queue::finish() noexcept +{ + { + std::unique_lock lg(m_mutex); + m_finish = true; + } + m_cv.notify_all(); +} + +template +bool blocking_queue::empty() const noexcept +{ + std::unique_lock lg(m_mutex); + return m_queue.empty(); +} + +template +bool blocking_queue::full() const noexcept +{ + std::unique_lock lg(m_mutex); + return m_queue.size() == m_max_size; +} + +template +size_t blocking_queue::size() const noexcept +{ + std::unique_lock lg(m_mutex); + return m_queue.size(); +} + +template +size_t blocking_queue::max_size() const noexcept +{ + return capacity(); +} + +template +size_t blocking_queue::capacity() const noexcept +{ + return m_max_size; +} + +template +typename blocking_queue::push_result blocking_queue::push(T const& t) noexcept +{ + return _push(T(t), true, nullptr); +} + +template +typename blocking_queue::push_result blocking_queue::try_push(T const& t) noexcept +{ + return _push(T(t), false, nullptr); +} + +template +typename blocking_queue::push_result blocking_queue::push(T&& t) noexcept +{ + return _push(std::move(t), true, nullptr); +} + +template +typename blocking_queue::push_result blocking_queue::try_push(T&& t) noexcept +{ + return _push(std::move(t), false, nullptr); +} + +template +typename blocking_queue::push_result blocking_queue::push_for(T const& t, chrono::system_clock::duration duration) noexcept +{ + chrono::system_clock::time_point until = chrono::system_clock::now() + duration; + return _push(T(t), true, &until); +} + +template +typename blocking_queue::push_result blocking_queue::push_until(T const& t, chrono::system_clock::time_point time_point) noexcept +{ + return _push(T(t), true, &time_point); +} + +template +typename blocking_queue::push_result blocking_queue::push_for(T&& t, chrono::system_clock::duration duration) noexcept +{ + chrono::system_clock::time_point until = chrono::system_clock::now() + duration; + return _push(std::move(t), true, &until); +} + +template +typename blocking_queue::push_result blocking_queue::push_until(T&& t, chrono::system_clock::time_point time_point) noexcept +{ + return _push(std::move(t), true, &time_point); +} + +template +typename blocking_queue::pop_result blocking_queue::pop() noexcept +{ + return _pop(true, nullptr); +} + +template +typename blocking_queue::pop_result blocking_queue::try_pop() noexcept +{ + return _pop(false, nullptr); +} + +template +typename blocking_queue::pop_result blocking_queue::pop_for(chrono::system_clock::duration duration) noexcept +{ + chrono::system_clock::time_point until = chrono::system_clock::now() + duration; + return _pop(true, &until); +} + +template +typename blocking_queue::pop_result blocking_queue::pop_until(chrono::system_clock::time_point time_point) noexcept +{ + return _pop(true, &time_point); +} + +template +template +result::pop_error_code> blocking_queue::pop_many(size_t max) noexcept +{ + Container container; + return _pop_many_emplace(container, max, true, nullptr); +} + +template +template +result::pop_error_code> blocking_queue::try_pop_many(size_t max) noexcept +{ + Container container; + return _pop_many_emplace(container, max, false, nullptr); +} + +template +template +result::pop_error_code> blocking_queue::pop_many_for(size_t max, chrono::system_clock::duration duration) noexcept +{ + Container container; + chrono::system_clock::time_point until = chrono::system_clock::now() + duration; + return _pop_many_emplace(container, max, true, &until); +} + +template +template +result::pop_error_code> blocking_queue::pop_many_until(size_t max, chrono::system_clock::time_point time_point) noexcept +{ + Container container; + return _pop_many_emplace(container, max, true, &time_point); +} + +template +template +typename blocking_queue::pop_many_emplace_result blocking_queue::pop_many_emplace(Container& dst, size_t max) noexcept +{ + pop_many_result result = _pop_many_emplace(dst, max, true, nullptr); + return (result.has_value()) ? success() : pop_many_emplace_result(result.error().code()); +} + +template +template +typename blocking_queue::pop_many_emplace_result blocking_queue::try_pop_many_emplace(Container& dst, size_t max) noexcept +{ + pop_many_result result = _pop_many_emplace(dst, max, false, nullptr); + return (result.has_value()) ? success() : pop_many_emplace_result(result.error().code()); +} + +template +template +typename blocking_queue::pop_many_emplace_result blocking_queue::pop_many_emplace_for(Container& dst, size_t max, chrono::system_clock::duration duration) noexcept +{ + chrono::system_clock::time_point until = chrono::system_clock::now() + duration; + pop_many_result result = _pop_many_emplace(dst, max, true, &until); + return (result.has_value()) ? success() : pop_many_emplace_result(result.error().code()); +} + +template +template +typename blocking_queue::pop_many_emplace_result blocking_queue::pop_many_emplace_until(Container& dst, size_t max, chrono::system_clock::time_point time_point) noexcept +{ + pop_many_result result = _pop_many_emplace(dst, max, true, &time_point); + return (result.has_value()) ? success() : pop_many_emplace_result(result.error().code()); +} + +////////////////////////////////////////////////////////////////////////// + +template +typename blocking_queue::push_result blocking_queue::_push(T&& t, bool block, chrono::system_clock::time_point* time_point) noexcept +{ + bool pushed_one = false; + bool timed_out = false; + + const auto system_tp = time_point ? std::chrono::system_clock::now() + std::chrono::nanoseconds(chrono::duration_cast(*time_point - chrono::system_clock::now()).count()) : std::chrono::system_clock::now(); + + //send the current datagram + { + std::unique_lock lg(m_mutex); + if (block) + { + while ((m_queue.size() >= m_max_size) & (!m_exit) & (!timed_out)) + { + if (m_finish) + return push_result(push_error_code::Finished); + + if (time_point) + { + m_cv.wait_until(lg, system_tp); + if (chrono::system_clock::now() >= *time_point) + timed_out = true; + } + else + m_cv.wait(lg); + } + + if (m_exit) + return push_result(push_error_code::Exited); + } + + if (m_queue.size() < m_max_size) + { + pushed_one = true; + m_queue.push_back(std::move(t)); + } + else if (m_finish) + return push_result(push_error_code::Finished); + } + + if (pushed_one) + { + m_cv.notify_all(); + return success(); + } + return timed_out ? push_result(push_error_code::Timeout) : push_result(push_error_code::Full); +} + +////////////////////////////////////////////////////////////////////////// + +template +template +result::pop_error_code> blocking_queue::_pop_many_emplace(Container& dst, size_t max, bool block, chrono::system_clock::time_point* time_point) noexcept +{ + const auto system_tp = time_point ? std::chrono::system_clock::now() + std::chrono::nanoseconds(chrono::duration_cast(*time_point - chrono::system_clock::now()).count()) : std::chrono::system_clock::now(); + + bool got_some = false; + bool timed_out = false; + { + //wait for data + std::unique_lock lg(m_mutex); + if (block) + { + while (m_queue.empty() & (!m_exit) & (!timed_out)) + { + if (m_finish) + return pop_many_result(pop_error_code::Finished); + + if (time_point) + { + m_cv.wait_until(lg, system_tp); + if (chrono::system_clock::now() >= *time_point) + timed_out = true; + } + else + m_cv.wait(lg); + } + + if (m_exit) + return pop_many_result(pop_error_code::Exited); + } + + if (m_queue.empty() && m_finish) + return pop_many_result(pop_error_code::Finished); + + while (!m_queue.empty()) + { + if (dst.size() >= max) + break; + + got_some = true; + dst.push_back(std::move(m_queue.front())); + m_queue.pop_front(); + } + } + if (got_some) + { + m_cv.notify_all(); + return pop_many_result(std::move(dst)); + } + return timed_out ? pop_many_result(pop_error_code::Timeout) : pop_many_result(pop_error_code::Empty); +} + +////////////////////////////////////////////////////////////////////////// + +template +typename blocking_queue::pop_result blocking_queue::_pop(bool block, chrono::system_clock::time_point* time_point) noexcept +{ + const auto system_tp = time_point ? std::chrono::system_clock::now() + std::chrono::nanoseconds(chrono::duration_cast(*time_point - chrono::system_clock::now()).count()) : std::chrono::system_clock::now(); + + optional opt_dst; + bool timed_out = false; + { + //wait for data + std::unique_lock lg(m_mutex); + if (block) + { + while (m_queue.empty() & (!m_exit) & (!timed_out)) + { + if (m_finish) + return pop_result(pop_error_code::Finished); + + if (time_point) + { + m_cv.wait_until(lg, system_tp); + if (chrono::system_clock::now() >= *time_point) + timed_out = true; + } + else + m_cv.wait(lg); + } + + if (m_exit) + return pop_result(pop_error_code::Exited); + } + + if (!m_queue.empty()) + { + opt_dst = std::move(m_queue.front()); + m_queue.pop_front(); + } + else if (m_finish) + return pop_result(pop_error_code::Finished); + } + + if (opt_dst.has_value()) + { + m_cv.notify_all(); + return std::move(opt_dst.value()); + } + return timed_out ? pop_result(pop_error_code::Timeout) : pop_result(pop_error_code::Empty); +} +} diff --git a/include/tl/call_traits.h b/include/tl/call_traits.h new file mode 100644 index 0000000..a21e13d --- /dev/null +++ b/include/tl/call_traits.h @@ -0,0 +1,3 @@ +#pragma once + +#include "tl/detail/prologue.h" diff --git a/include/tl/chrono.h b/include/tl/chrono.h new file mode 100644 index 0000000..322e815 --- /dev/null +++ b/include/tl/chrono.h @@ -0,0 +1,49 @@ +#pragma once + +#include "tl/detail/prologue.h" +#include + +namespace tl +{ +using namespace eastl; + + +namespace chrono +{ + +using namespace eastl::chrono; + +template +class TL_API chrono final +{ +public: + //! Reset the internal time stamp. If you specify an elapsed time offset it will affect directly to getElapsed. + void reset(typename Clock::duration elapsed_offset = Clock::duration::zero()) noexcept + { + m_start_time_point = Clock::now() + elapsed_offset; + } + + //! Return the time passed between the last Reset call and now. + typename Clock::duration get_elapsed() const noexcept + { + return Clock::now() - m_start_time_point; + } + + //! Return the time passed between the last Reset call and now and then Reset the Clock. + typename Clock::duration get_elapsed_and_reset() noexcept + { + auto now = Clock::now(); + auto d = now - m_start_time_point; + m_start_time_point = now; + return d; + } + +private: + typename Clock::time_point m_start_time_point = Clock::now(); +}; + +using system_chrono = chrono; +using high_resolution_chrono = chrono; + +} +} \ No newline at end of file diff --git a/include/tl/compact_signal.h b/include/tl/compact_signal.h new file mode 100644 index 0000000..0e1d180 --- /dev/null +++ b/include/tl/compact_signal.h @@ -0,0 +1,542 @@ +#pragma once + +#include // std::enable_if, std::is_constructible, etc +#include +#include "tl/unordered_map.h" +#include "tl/memory_buffer.h" +#include "tl/detail/internal_assert.h" + +namespace tl +{ + +template +class signal_system +{ +public: + class scoped_connection; + + class connection + { + public: + connection() noexcept = default; + connection(const connection& other) noexcept = default; + connection(connection&& other) noexcept = default; + connection& operator=(const connection& other) noexcept = default; + connection& operator=(connection&& other) noexcept = default; + + void disconnect() noexcept; + void lock() noexcept; + void unlock() noexcept; + [[nodiscard]] bool is_connected() const noexcept; + + protected: + friend class signal_system; + friend class scoped_connection; + + inline explicit connection(uint32_t id) noexcept; + + uint32_t id = 0; + }; + + class [[nodiscard]] scoped_connection : public connection + { + public: + scoped_connection() noexcept = default; + + scoped_connection(connection c) noexcept + : connection(c) {} + ~scoped_connection() noexcept; + + scoped_connection(const scoped_connection& other) = delete; + scoped_connection(scoped_connection&& other) noexcept + { + this->id = other.id; + other.id = 0; + } + scoped_connection& operator=(const scoped_connection& other) = delete; + scoped_connection& operator=(scoped_connection&& other) noexcept + { + if (this->id != 0) + this->disconnect(); + + this->id = other.id; + other.id = 0; + return *this; + } + + void detach() noexcept + { + this->id = 0; + } + }; + + //static inline void create(intptr_t owner, uint8_t signal_count, size_t delegate_count_hint = 0); + + template + static connection add(intptr_t owner, uint8_t signal_index, tl::function&& delegate) noexcept; + + template + static void invoke(intptr_t owner, uint8_t signal_index, Args&&... args) noexcept; + + static void disconnect(const connection& c) noexcept; + static void lock(const connection& c) noexcept; + static void unlock(const connection& c) noexcept; + static void remove_all() noexcept; + static void remove_all(intptr_t owner) noexcept; + static void remove_all(intptr_t owner, uint8_t signal_index) noexcept; + static size_t get_slot_count(intptr_t owner, uint8_t signal_index) noexcept; + +private: + //static inline uint32_t compute_owner_hash(void* owner); + + static inline constexpr size_t k_offset_bits = 25; + static inline constexpr size_t k_offset_mask = (1 << k_offset_bits) - 1; + + static inline constexpr size_t k_delegate_size_bits = 7; + static inline constexpr size_t k_delegate_size_mask = (1 << k_delegate_size_bits) - 1; + + static inline constexpr size_t k_signal_index_bits = 5; + static inline constexpr size_t k_signal_index_disconnected = (1 << k_signal_index_bits) - 1; //this is used to mark disconnected slots + + struct OwnerData + { + union + { + struct + { + uint32_t offset : k_offset_bits; //div 1 << k_offset_shift + uint32_t initialized : 1; + }; + uint32_t all = 0; + }; + }; + static_assert(sizeof(OwnerData) == 4); + + struct SlotHeader + { + union + { + struct + { + uint32_t delegate_size : k_delegate_size_bits; //div 1 << k_offset_shift + uint32_t prev_offset : k_offset_bits; //div 1 << k_offset_shift + uint32_t next_offset : k_offset_bits; //div 1 << k_offset_shift + uint32_t signal_index : k_signal_index_bits; //a certain value is used to signal disconnection (k_signal_index_disconnected) + uint32_t locked : 1; //if locked, don't emit + uint32_t __unused : 1; + }; + uint64_t all = 0; //8 bytes, to make sure alignment is fine + }; + void (*destructor)(void*) = nullptr; + }; + static_assert(sizeof(SlotHeader) == 16); + + struct ConnectionData + { + uint32_t offset : k_offset_bits; //div 1 << k_offset_shift + }; + + static typename ThreadPolicy::mutex_t m_mutex; + static tl::unordered_map m_owners; + static tl::unordered_map m_connections; + static typename ThreadPolicy::connection_id_counter_t m_last_connection_id; + static tl::memory_buffer m_slots; + static typename ThreadPolicy::data_ptr_t m_slots_data_ptr; + static size_t m_garbage; +}; + + +////////////////////////////////////////////////////////////////////////// + +template +signal_system::connection::connection(uint32_t id) noexcept + : id(id) +{ +} + +////////////////////////////////////////////////////////////////////////// + +namespace detail +{ +inline static constexpr size_t k_offset_shift = 3; +} + +template +template +auto signal_system::add(intptr_t owner, uint8_t signal_index, tl::function&& delegate) noexcept -> connection +{ + std::lock_guard lg(m_mutex); + + if constexpr ((sizeof(delegate) & ((1 << detail::k_offset_shift) - 1)) != 0) + TL_PLAIN_CRASH("delegate_size not property aligned"); + + if (signal_index >= k_signal_index_disconnected) + TL_PLAIN_CRASH("signal_index out of range"); + + using delegate_t = tl::function; + + OwnerData& od = m_owners[owner]; + const uint32_t delegate_size = static_cast(sizeof(delegate)); + if ((delegate_size >> detail::k_offset_shift) > k_delegate_size_mask) + TL_PLAIN_CRASH("delegate_size out of range"); + + TL_PLAIN_ASSERT((delegate_size & ((1 << detail::k_offset_shift) - 1)) == 0); + TL_PLAIN_ASSERT(delegate_size < 128 * (1 << detail::k_offset_shift)); + + const uint32_t offset = static_cast(m_slots.size()) >> detail::k_offset_shift; + if (offset > k_offset_mask) + TL_PLAIN_CRASH("offset out of range"); + + //TODO: this is not safe!!!! while resizing, the atomic still points to the old deleted data before it's updated. + // Here I have to double buffer stuff, so that the atomic either points to old, but valid memory or to new memory. Never to old but deleted memory + // Effectively this is what should happen: + // auto new_mem = allocate(new_size); + // memcpy(new_mem, old_mem, old_size); + // m_slots_data_ptr = new_mem; + // delete old_mem; + m_slots.resize_uninitialized(m_slots.size() + (size_t)delegate_size + sizeof(SlotHeader)); + m_slots_data_ptr = reinterpret_cast(m_slots.data()); + uint64_t* data = m_slots_data_ptr; + + auto& header = *reinterpret_cast(data + offset); + header = {}; + header.delegate_size = delegate_size >> detail::k_offset_shift; + header.signal_index = signal_index; + header.destructor = [](void* memory) { static_cast(memory)->~delegate_t(); }; + + if (od.initialized == 0) //store the index + { + od.initialized = 1; + od.offset = offset; + header.prev_offset = offset; + } + else //here, we already have one slot (could be marked as deleted though!) + { + SlotHeader& first_header = *reinterpret_cast(data + od.offset); + + const uint32_t last_offset = first_header.prev_offset;//first_header->prev_offset points to the last header + SlotHeader& last_header = *reinterpret_cast(data + last_offset); + + last_header.next_offset = offset; //link our header after the last one + header.prev_offset = last_offset; + first_header.prev_offset = offset; //this is used to point to the last header, always + } + + //note - data is uint64_t, so any byte offset has to be divided by 8 (or shifted by k_offset_shift) + new(data + offset + (sizeof(SlotHeader) >> detail::k_offset_shift)) delegate_t(std::move(delegate)); + + const uint32_t connection_id = ++m_last_connection_id; + m_connections[connection_id] = { offset }; + + return connection(connection_id); +} + +template +template +void signal_system::invoke(intptr_t owner, uint8_t signal_index, Args&&... args) noexcept +{ + //we're locked from here --- + std::unique_lock lg(m_mutex); + + auto it = m_owners.find(owner); + if (it == m_owners.end()) + return; + + OwnerData od = it->second; + + lg.unlock(); + // --- to here + + + // UNLOCKED ACCESS FOLLOWING THIS + // The only things safe are local data (copies) and atomics + // Any member should be atomic and not cached across the invoke, as the user might connect/disconnect stuff and invalidate the slots + + using delegate_t = tl::function; + size_t next_offset = od.offset; + const size_t last_offset = (reinterpret_cast(m_slots_data_ptr + od.offset))->prev_offset; + bool is_last; + do + { + const SlotHeader* header = reinterpret_cast(m_slots_data_ptr + next_offset); + next_offset = header->next_offset; + is_last = (next_offset == 0) | (next_offset > last_offset); + if ((header->signal_index == signal_index) & (header->locked == 0)) + { + auto delegate = reinterpret_cast(const_cast(header) + 1); + if (is_last) //last slot? we can move potentially + (*delegate)(std::forward(args)...); + else + (*delegate)(args...); + } + } while (!is_last); +} + + +template +typename ThreadPolicy::mutex_t signal_system::m_mutex; + +template +tl::unordered_map::OwnerData> signal_system::m_owners; + +template +tl::unordered_map::ConnectionData> signal_system::m_connections; + +template +typename ThreadPolicy::connection_id_counter_t signal_system::m_last_connection_id = 0; + +template +tl::memory_buffer signal_system::m_slots = tl::memory_buffer(1024); + +template +typename ThreadPolicy::data_ptr_t signal_system::m_slots_data_ptr = nullptr; + +template +size_t signal_system::m_garbage = 0; + +template +void signal_system::connection::disconnect() noexcept +{ + signal_system::disconnect(*this); + this->id = 0; +} +template +void signal_system::connection::lock() noexcept +{ + signal_system::lock(*this); +} +template +void signal_system::connection::unlock() noexcept +{ + signal_system::unlock(*this); +} +template +bool signal_system::connection::is_connected() const noexcept +{ + return this->id != 0; +} + +template +signal_system::scoped_connection::~scoped_connection() noexcept +{ + if (this->id > 0) + this->disconnect(); +} + +template +void signal_system::disconnect(const connection& c) noexcept +{ + std::lock_guard lg(m_mutex); + + auto it = m_connections.find(c.id); + if (it == m_connections.end()) + return; + + ConnectionData cd = it->second; + m_connections.erase(it); + + uint64_t* data = m_slots_data_ptr; + + SlotHeader* header = reinterpret_cast(data + cd.offset); + if (header->signal_index == k_signal_index_disconnected) + return; //already removed + + header->destructor(header + 1); + header->signal_index = k_signal_index_disconnected; //mark it so that traversal will not call it + + if (header->prev_offset > cd.offset)//first slot + { + //we cannot remove the head slot!!! The OwnerData points to it + } + else if (header->next_offset == 0) //last slot? + { + //we cannot remove the tail slot!!! The OwnerData points to it (using the prev_offset) + } + else //middle slot + { + auto* prev_header = reinterpret_cast(data + header->prev_offset); + auto* next_header = reinterpret_cast(data + header->next_offset); + prev_header->next_offset = header->next_offset; + next_header->prev_offset = header->prev_offset; + } + + m_garbage += sizeof(SlotHeader) + (static_cast(header->delegate_size) << detail::k_offset_shift); +} + +template +void signal_system::lock(const connection& c) noexcept +{ + std::lock_guard lg(m_mutex); + + auto it = m_connections.find(c.id); + if (it == m_connections.end()) + return; + + ConnectionData cd = it->second; + + uint64_t* data = m_slots_data_ptr; + + SlotHeader* header = reinterpret_cast(data + cd.offset); + TL_PLAIN_ASSERT(header->locked == 0); + header->locked = 1; +} + +template +void signal_system::unlock(const connection& c) noexcept +{ + std::lock_guard lg(m_mutex); + + auto it = m_connections.find(c.id); + if (it == m_connections.end()) + return; + + ConnectionData cd = it->second; + + uint64_t* data = m_slots_data_ptr; + + SlotHeader* header = reinterpret_cast(data + cd.offset); + TL_PLAIN_ASSERT(header->locked != 0); + header->locked = 0; +} + +template +void signal_system::remove_all() noexcept +{ + std::lock_guard lg(m_mutex); + + uint64_t* data = m_slots_data_ptr; + for (auto& p : m_owners) + { + OwnerData& od = p.second; + if (od.initialized == 0) + continue; + + size_t next_offset = od.offset; + do + { + SlotHeader* header = reinterpret_cast(data + next_offset); + next_offset = header->next_offset; + if (header->signal_index != k_signal_index_disconnected) + { + header->destructor(header + 1); + header->signal_index = k_signal_index_disconnected; + m_garbage += sizeof(SlotHeader) + (static_cast(header->delegate_size) << detail::k_offset_shift); + } + } while (next_offset != 0); + + od.all = {}; + } +} + +template +void signal_system::remove_all(intptr_t owner) noexcept +{ + std::lock_guard lg(m_mutex); + + auto it = m_owners.find(owner); + if (it == m_owners.end()) + return; + + OwnerData& od = it->second; + uint64_t* data = m_slots_data_ptr; + + if (od.initialized != 0) + { + size_t next_offset = od.offset; + do + { + SlotHeader* header = reinterpret_cast(data + next_offset); + next_offset = header->next_offset; + if (header->signal_index != k_signal_index_disconnected) + { + header->destructor(header + 1); + header->signal_index = k_signal_index_disconnected; + m_garbage += sizeof(SlotHeader) + (static_cast(header->delegate_size) << detail::k_offset_shift); + } + } while (next_offset != 0); + } + m_owners.erase(it); +} + +template +void signal_system::remove_all(intptr_t owner, uint8_t signal_index) noexcept +{ + std::lock_guard lg(m_mutex); + + if (signal_index >= k_signal_index_disconnected) + { + TL_PLAIN_FAIL(); + return; + } + + auto it = m_owners.find(owner); + if (it == m_owners.end()) + return; + + OwnerData& od = it->second; + uint64_t* data = m_slots_data_ptr; + + if (od.initialized == 0) + { + m_owners.erase(it); + return; + } + + bool has_other_signals = false; + size_t next_offset = od.offset; + do + { + SlotHeader* header = reinterpret_cast(data + next_offset); + next_offset = header->next_offset; + if (header->signal_index == signal_index) + { + header->destructor(header + 1); + header->signal_index = k_signal_index_disconnected; + m_garbage += sizeof(SlotHeader) + (static_cast(header->delegate_size) << detail::k_offset_shift); + } + else + has_other_signals = true; + } while (next_offset != 0); + + if (!has_other_signals) + m_owners.erase(it); +} + +template +size_t signal_system::get_slot_count(intptr_t owner, uint8_t signal_index) noexcept +{ + std::lock_guard lg(m_mutex); + + if (signal_index >= k_signal_index_disconnected) + { + TL_PLAIN_FAIL(); + return 0; + } + + auto it = m_owners.find(owner); + if (it == m_owners.end()) + return 0; + + OwnerData& od = it->second; + uint64_t* data = m_slots_data_ptr; + + if (od.initialized == 0) + { + m_owners.erase(it); + return 0; + } + + size_t count = 0; + size_t next_offset = od.offset; + do + { + SlotHeader* header = reinterpret_cast(data + next_offset); + next_offset = header->next_offset; + if (header->signal_index == signal_index) + count++; + } while (next_offset != 0); + + return count; +} + +} \ No newline at end of file diff --git a/include/tl/compact_signal_mt.h b/include/tl/compact_signal_mt.h new file mode 100644 index 0000000..2b40361 --- /dev/null +++ b/include/tl/compact_signal_mt.h @@ -0,0 +1,72 @@ +#pragma once + +#include // std::enable_if, std::is_constructible, etc +#include // std::invoke +#include "tl/functional.h" +#include "tl/vector_map.h" +#include "tl/unordered_map.h" +#include "tl/hash_and_combine.h" +#include "tl/memory_buffer.h" +#include "tl/detail/internal_assert.h" +#include "tl/atomic.h" +#include +#include "compact_signal.h" + +namespace tl +{ + +namespace detail +{ + +struct signal_mt_thread_policy +{ + using mutex_t = std::mutex; + using connection_id_counter_t = tl::atomic; + using data_ptr_t = tl::atomic; +}; + +} + +using signal_system_mt = signal_system; +using connection_mt = signal_system_mt::scoped_connection; + +template +class signal_mt; + +template +class signal_mt +{ +public: + signal_mt() noexcept = default; + signal_mt(const signal_mt&) noexcept = delete; + signal_mt(signal_mt&&) noexcept = delete; + ~signal_mt() noexcept + { + disconnect_all_slots(); + } + + signal_mt& operator=(const signal_mt&) = delete; + signal_mt& operator=(signal_mt&&) = delete; + + connection_mt connect(tl::function delegate) noexcept + { + return signal_system_mt::add((intptr_t)this, 0, std::move(delegate)); + } + + void invoke(Args... args) const noexcept + { + signal_system_mt::invoke((intptr_t)this, 0, std::forward(args)...); + } + + void disconnect_all_slots() const noexcept + { + signal_system_mt::remove_all((intptr_t)this); + } + + bool has_slots() const + { + return signal_system_mt::get_slot_count((intptr_t)this, 0) > 0; + } +}; + +} \ No newline at end of file diff --git a/include/tl/compact_signal_st.h b/include/tl/compact_signal_st.h new file mode 100644 index 0000000..bf03b83 --- /dev/null +++ b/include/tl/compact_signal_st.h @@ -0,0 +1,70 @@ +#pragma once + +#include +#include "tl/memory_buffer.h" +#include "compact_signal.h" + +namespace tl +{ + +namespace detail +{ + +struct signal_st_null_mutex +{ + inline void lock() noexcept {}; + inline void unlock() noexcept {}; +}; + +struct signal_st_thread_policy +{ + using mutex_t = signal_st_null_mutex; + using connection_id_counter_t = uint32_t; + using data_ptr_t = uint64_t*; +}; + +} + +using signal_system_st = signal_system; +using connection_st = signal_system_st::scoped_connection; + +template +class signal_st; + +template +class signal_st +{ +public: + signal_st() noexcept = default; + signal_st(const signal_st&) noexcept = delete; + signal_st(signal_st&&) noexcept = delete; + ~signal_st() noexcept + { + disconnect_all_slots(); + } + + signal_st& operator=(const signal_st&) = delete; + signal_st& operator=(signal_st&&) = delete; + + connection_st connect(tl::function delegate) noexcept + { + return signal_system_st::add((intptr_t)this, 0, std::move(delegate)); + } + + void invoke(Args... args) const noexcept + { + signal_system_st::invoke((intptr_t)this, 0, std::forward(args)...); + } + + void disconnect_all_slots() const noexcept + { + signal_system_st::remove_all((intptr_t)this); + } + + bool has_slots() const + { + return signal_system_st::get_slot_count((intptr_t)this, 0) > 0; + } +}; + +} \ No newline at end of file diff --git a/include/tl/compressed_pair.h b/include/tl/compressed_pair.h new file mode 100644 index 0000000..a21e13d --- /dev/null +++ b/include/tl/compressed_pair.h @@ -0,0 +1,3 @@ +#pragma once + +#include "tl/detail/prologue.h" diff --git a/include/tl/crash.h b/include/tl/crash.h new file mode 100644 index 0000000..e5892e8 --- /dev/null +++ b/include/tl/crash.h @@ -0,0 +1,15 @@ +#pragma once + +#include "tl/detail/prologue.h" +#include "tl/detail/internal_crash.h" +#include "tl/format.h" +#include "tl/string.h" + +#define TL_CRASH(__fmt__, ...) \ + do \ + { \ + const eastl::string __msg__ = tl::format(__fmt__, ##__VA_ARGS__); \ + tl::crash::response res = tl::crash::detail::invoke_crash_handler(__FILE__, __LINE__, __msg__.c_str()); \ + if (res == tl::crash::response::CRASH) \ + *reinterpret_cast(0) = 0xDEAD; \ + } while(false) diff --git a/include/tl/debug.h b/include/tl/debug.h new file mode 100644 index 0000000..c47a25e --- /dev/null +++ b/include/tl/debug.h @@ -0,0 +1,64 @@ +#pragma once + +#include "tl/platform.h" + +#if defined(TL_FORCE_DEBUG) && defined(TL_RETAIL) +# error "TL_FORCE_DEBUG and TL_RETAIL are incompatible!" +#endif + +#if defined(TL_DEBUG) +# error "Do not pass TL_DEBUG to the compiler. If you want to force debug, pass TL_FORCE_DEBUG" +#endif + + +#if !defined(NDEBUG) || defined(TL_FORCE_DEBUG) +# define TL_DEBUG +#endif + +#if defined(TL_PLATFORM_ANDROID_FAMILY) + +#if defined(TL_PLATFORM_ARCHITECTURE_AMD64) || defined(TL_PLATFORM_ARCHITECTURE_X86) + extern __attribute__((gnu_inline, always_inline)) + __inline__ void trap_instruction(void) noexcept + { + __asm__ volatile("int $0x03"); + } +#elif defined(TL_PLATFORM_ARCHITECTURE_ARM32) + extern __attribute__((gnu_inline, always_inline)) + __inline__ void trap_instruction(void) noexcept + { + /* See 'arm-linux-tdep.c' in GDB source, + * 'eabi_linux_arm_le_breakpoint' */ + __asm__ volatile(".inst 0xe7f001f0"); + /* Has same known problem and workaround + * as Thumb mode */ + } +#elif defined(TL_PLATFORM_ARCHITECTURE_ARM64) + extern __attribute__((gnu_inline, always_inline)) + __inline__ void trap_instruction(void) noexcept + { + /* See 'aarch64-tdep.c' in GDB source, + * 'aarch64_default_breakpoint' */ + __asm__ volatile(".inst 0xd4200000"); + } +#endif + +#endif + +#ifdef TL_DEBUG +# if defined(TL_PLATFORM_WINDOWS_FAMILY) +# define TL_BREAK() __debugbreak() //better than assert(0) as it will break on this exact line instead of deep in the assert function. +# elif defined(TL_PLATFORM_OSX_FAMILY) || defined(TL_PLATFORM_LINUX_FAMILY) +# include +# define TL_BREAK() ::raise(SIGTRAP) +# elif defined(TL_PLATFORM_ANDROID_FAMILY) +# include +# define TL_BREAK() ::raise(SIGSTOP) +# endif +#else +# if defined(TL_PLATFORM_WINDOWS_FAMILY) +# define TL_BREAK() do {} while (false) +# else +# define TL_BREAK() do {} while (false) +# endif +#endif diff --git a/include/tl/deque.h b/include/tl/deque.h new file mode 100644 index 0000000..107186f --- /dev/null +++ b/include/tl/deque.h @@ -0,0 +1,9 @@ +#pragma once + +#include "tl/detail/prologue.h" +#include + +namespace tl +{ +using namespace eastl; +} \ No newline at end of file diff --git a/include/tl/detail/internal_assert.h b/include/tl/detail/internal_assert.h new file mode 100644 index 0000000..ba6bbaa --- /dev/null +++ b/include/tl/detail/internal_assert.h @@ -0,0 +1,278 @@ +#pragma once + +#include "tl/detail/prologue.h" +#include + +/////////////////////////////////////////////////////////////////////////////// + +namespace tl +{ +namespace assert +{ + + //The assert handler is called whenever an assert fails and should present a message to the user. + //If it returns BREAK, the code will dbgbreak and allow you to connect with the debugger. Returning CONTINUE will continue the code normally + // condition contains a stringified condition that failed + // msg is any user supplied message. Can be NULL + // file is the name of the file the assert failed + // line is the line where the assert failed + + enum class response + { + BREAK, + CONTINUE + }; + + using handler = response (*)(const char *, const char *, int, const char *); + TL_API handler set_handler(handler) noexcept; + + TL_API response default_handler(const char* condition, const char* file, int line, const char* msg) noexcept; + + inline response null_handler(const char*, const char*, int, const char*) noexcept + { + return response::CONTINUE; + } + + //Use this to temporarily change the assert handler for the current scope + class handler_guard + { + public: + explicit handler_guard(handler new_handler) noexcept + { + m_prev_handler = set_handler(new_handler); + } + ~handler_guard() noexcept + { + set_handler(m_prev_handler); + } + + private: + handler m_prev_handler; + }; + + + /////////////////////////////////////////////////////////////////////// + //for internal use only + namespace detail + { + //helper for checking if a description is used instead of an assert condition + template struct _description_in_condition { static const bool value = false; }; + template<> struct _description_in_condition { static const bool value = true; }; + template struct _description_in_condition { static const bool value = true; }; + + template + struct description_in_condition + { + static const bool value = _description_in_condition>>::value; + }; + + //this is responsible of formatting the messages and calling the handler + TL_API response do_assert(const char* _condition, const char* _file, int _line, const char* msg) noexcept; + TL_API response do_assert(const char* _condition, const char* _file, int _line) noexcept; + + //used to mark/test once asserts as being taken or not + TL_API bool is_assert_enabled(const char* _file, int _line) noexcept; + TL_API void set_assert_enabled(const char* _file, int _line, bool _enabled) noexcept; + + inline const char* get_plain_message() noexcept { return ""; } + inline const char* get_plain_message(const char* msg) noexcept { return msg; } + + TL_API handler& get_static_handler() noexcept; + } +} + +} + +#define __TL_ASSERT_TEST_DESCRIPTION_IN_CONDITION__(condition) \ + const auto& __cond_result__ = (condition); \ + (void)(__cond_result__); \ + static_assert(tl::assert::detail::description_in_condition::value == false, \ + "It seems you used the assert description instead of the assert condition.") + +//ASSERT +#define __TL_ASSERT_ENABLED_IMPL__(condition, ...) \ + do \ + { \ + __TL_ASSERT_TEST_DESCRIPTION_IN_CONDITION__(condition); \ + if (!(__cond_result__)) [[unlikely]] \ + { \ + eastl::string __msg__ = tl::format(__VA_ARGS__); \ + if (tl::assert::detail::do_assert(#condition, __FILE__, __LINE__, __msg__.data()) == tl::assert::response::BREAK) \ + TL_BREAK(); \ + } \ + } while(false) + + + +#define __TL_ASSERT_ENABLED_IMPL_ONCE__(condition, ...) \ + do \ + { \ + __TL_ASSERT_TEST_DESCRIPTION_IN_CONDITION__(condition); \ + /* checking the condition first and then the GetAssertOnce, as chances are that the condition is cheaper */ \ + if (!(__cond_result__) && tl::assert::detail::is_assert_enabled(__FILE__, __LINE__)) [[unlikely]] \ + { \ + tl::assert::detail::set_assert_enabled(__FILE__, __LINE__, false); \ + eastl::string __msg__ = tl::format(__VA_ARGS__); \ + if (tl::assert::detail::do_assert(#condition, __FILE__, __LINE__, __msg__.data()) == tl::assert::response::BREAK) \ + TL_BREAK(); \ + } \ + } while(false) + +//PLAIN ASSERT +#define __TL_PLAIN_ASSERT_ENABLED_IMPL__(condition, ...) \ + do \ + { \ + __TL_ASSERT_TEST_DESCRIPTION_IN_CONDITION__(condition); \ + if (!(__cond_result__)) [[unlikely]] \ + { \ + if (tl::assert::detail::do_assert(#condition, __FILE__, __LINE__, ##__VA_ARGS__) == tl::assert::response::BREAK) \ + TL_BREAK(); \ + } \ + } while(false) + +#define __TL_PLAIN_ASSERT_ENABLED_IMPL_ONCE__(condition, ...) \ + do \ + { \ + __TL_ASSERT_TEST_DESCRIPTION_IN_CONDITION__(condition); \ + /* checking the condition first and then the GetAssertOnce, as chances are that the condition is cheaper */ \ + if (!(__cond_result__) && tl::assert::detail::is_assert_enabled(__FILE__, __LINE__)) [[unlikely]] \ + { \ + tl::assert::detail::set_assert_enabled(__FILE__, __LINE__, false); \ + if (tl::assert::detail::do_assert(#condition, __FILE__, __LINE__, ##__VA_ARGS__) == tl::assert::response::BREAK) \ + TL_BREAK(); \ + } \ + } while(false) + +//FAIL +#define __TL_FAIL_ENABLED_IMPL__(...) \ + do \ + { \ + eastl::string __msg__ = tl::format(__VA_ARGS__); \ + if (tl::assert::detail::do_assert("fail", __FILE__, __LINE__, __msg__.data()) == tl::assert::response::BREAK) \ + TL_BREAK(); \ + } while(false) + + +#define __TL_FAIL_ENABLED_IMPL_ONCE__(...) \ + do \ + { \ + if (tl::assert::detail::is_assert_enabled(__FILE__, __LINE__)) [[likely]] \ + { \ + tl::assert::detail::set_assert_enabled(__FILE__, __LINE__, false); \ + eastl::string __msg__ = tl::format(__VA_ARGS__); \ + if (tl::assert::detail::do_assert("fail", __FILE__, __LINE__, __msg__.data()) == tl::assert::response::BREAK) \ + TL_BREAK(); \ + } \ + } while(false) + +//PLAIN FAIL +#define __TL_PLAIN_FAIL_ENABLED_IMPL__(...) \ + do \ + { \ + const char* __msg__ = tl::assert::detail::get_plain_message(__VA_ARGS__); \ + if (tl::assert::detail::do_assert("fail", __FILE__, __LINE__, __msg__) == tl::assert::response::BREAK) \ + TL_BREAK(); \ + } while(false) + +#define __TL_PLAIN_FAIL_ENABLED_IMPL_ONCE__(...) \ + do \ + { \ + if (tl::assert::detail::is_assert_enabled(__FILE__, __LINE__)) [[likely]] \ + { \ + tl::assert::detail::set_assert_enabled(__FILE__, __LINE__, false); \ + const char* __msg__ = tl::assert::detail::get_plain_message(__VA_ARGS__); \ + if (tl::assert::detail::do_assert("fail", __FILE__, __LINE__, __msg__) == tl::assert::response::BREAK) \ + TL_BREAK(); \ + } \ + } while(false) + + +#define __TL_ASSERT_DISABLED_IMPL__(condition, ...) do {} while (false) +#define __TL_ASSERT_DISABLED_IMPL_ONCE__(condition, ...) do {} while (false) +#define __TL_PLAIN_ASSERT_DISABLED_IMPL__(condition, ...) do {} while (false) +#define __TL_PLAIN_ASSERT_DISABLED_IMPL_ONCE__(condition, ...) do {} while (false) + +#define __TL_FAIL_DISABLED_IMPL__(...) do {} while (false) +#define __TL_FAIL_DISABLED_IMPL_ONCE__(...) do {} while (false) +#define __TL_PLAIN_FAIL_DISABLED_IMPL__(...) do {} while (false) +#define __TL_PLAIN_FAIL_DISABLED_IMPL_ONCE__(...) do {} while (false) + +#ifdef TL_DEBUG + + //assert + #define TL_ASSERT __TL_ASSERT_ENABLED_IMPL__ + #define TL_ASSERT_ONCE __TL_ASSERT_ENABLED_IMPL_ONCE__ + + #define TL_ASSERT_RELEASE __TL_ASSERT_ENABLED_IMPL__ + #define TL_ASSERT_RELEASE_ONCE __TL_ASSERT_ENABLED_IMPL_ONCE__ + + //plain assert + #define TL_PLAIN_ASSERT __TL_PLAIN_ASSERT_ENABLED_IMPL__ + #define TL_PLAIN_ASSERT_ONCE __TL_PLAIN_ASSERT_ENABLED_IMPL_ONCE__ + + #define TL_PLAIN_ASSERT_RELEASE __TL_PLAIN_ASSERT_ENABLED_IMPL__ + #define TL_PLAIN_ASSERT_RELEASE_ONCE __TL_PLAIN_ASSERT_ENABLED_IMPL_ONCE__ + + //fail + #define TL_FAIL __TL_FAIL_ENABLED_IMPL__ + #define TL_FAIL_ONCE __TL_FAIL_ENABLED_IMPL_ONCE__ + + #define TL_FAIL_RELEASE __TL_FAIL_ENABLED_IMPL__ + #define TL_FAIL_RELEASE_ONCE __TL_FAIL_ENABLED_IMPL_ONCE__ + + //plain fail + #define TL_PLAIN_FAIL __TL_PLAIN_FAIL_ENABLED_IMPL__ + #define TL_PLAIN_FAIL_ONCE __TL_PLAIN_FAIL_ENABLED_IMPL_ONCE__ + + #define TL_PLAIN_FAIL_RELEASE __TL_PLAIN_FAIL_ENABLED_IMPL__ + #define TL_PLAIN_FAIL_RELEASE_ONCE __TL_PLAIN_FAIL_ENABLED_IMPL_ONCE__ + +#else + + //assert + #define TL_ASSERT __TL_ASSERT_DISABLED_IMPL__ + #define TL_ASSERT_ONCE __TL_ASSERT_DISABLED_IMPL_ONCE__ + +#ifdef TL_RETAIL + + #define TL_ASSERT_RELEASE __TL_ASSERT_DISABLED_IMPL__ + #define TL_ASSERT_RELEASE_ONCE __TL_ASSERT_DISABLED_IMPL_ONCE__ + + #define TL_PLAIN_ASSERT_RELEASE __TL_ASSERT_DISABLED_IMPL__ + #define TL_PLAIN_ASSERT_RELEASE_ONCE __TL_ASSERT_DISABLED_IMPL_ONCE__ + + #define TL_FAIL_RELEASE __TL_ASSERT_DISABLED_IMPL__ + #define TL_FAIL_RELEASE_ONCE __TL_ASSERT_DISABLED_IMPL_ONCE__ + + #define TL_PLAIN_FAIL_RELEASE __TL_ASSERT_DISABLED_IMPL__ + #define TL_PLAIN_FAIL_RELEASE_ONCE __TL_ASSERT_DISABLED_IMPL_ONCE__ + +#else + #define TL_ASSERT_RELEASE __TL_ASSERT_ENABLED_IMPL__ + #define TL_ASSERT_RELEASE_ONCE __TL_ASSERT_ENABLED_IMPL_ONCE__ + + #define TL_PLAIN_ASSERT_RELEASE __TL_PLAIN_ASSERT_ENABLED_IMPL__ + #define TL_PLAIN_ASSERT_RELEASE_ONCE __TL_PLAIN_ASSERT_ENABLED_IMPL_ONCE__ + + #define TL_FAIL_RELEASE __TL_FAIL_ENABLED_IMPL__ + #define TL_FAIL_RELEASE_ONCE __TL_FAIL_ENABLED_IMPL_ONCE__ + + #define TL_PLAIN_FAIL_RELEASE __TL_PLAIN_FAIL_ENABLED_IMPL__ + #define TL_PLAIN_FAIL_RELEASE_ONCE __TL_PLAIN_FAIL_ENABLED_IMPL_ONCE__ + +#endif + + //plain assert + #define TL_PLAIN_ASSERT __TL_PLAIN_ASSERT_DISABLED_IMPL__ + #define TL_PLAIN_ASSERT_ONCE __TL_PLAIN_ASSERT_DISABLED_IMPL_ONCE__ + + //fail + #define TL_FAIL __TL_FAIL_DISABLED_IMPL__ + #define TL_FAIL_ONCE __TL_FAIL_DISABLED_IMPL_ONCE__ + + //plain fail + #define TL_PLAIN_FAIL __TL_PLAIN_FAIL_DISABLED_IMPL__ + #define TL_PLAIN_FAIL_ONCE __TL_PLAIN_FAIL_DISABLED_IMPL_ONCE__ + + +#endif diff --git a/include/tl/detail/internal_crash.h b/include/tl/detail/internal_crash.h new file mode 100644 index 0000000..86b9806 --- /dev/null +++ b/include/tl/detail/internal_crash.h @@ -0,0 +1,66 @@ +#pragma once + +#include "tl/detail/prologue.h" + +/////////////////////////////////////////////////////////////////////////////// +// + +namespace tl +{ +namespace crash +{ + //use this to specify a function to be called when a crash is requested. + //Note!!! File and msg can be NULL + //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + // + // It's very risky to implement this function so do it only if you REALLY REALLY have to do something before the crash. + // This might be called because the system is out of memory - so any allocation will fail. Same for file handles or any other resources. + // + //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + + enum class response + { + CRASH, + CONTINUE + }; + + using handler = response (*)(const char *, int, const char *); + TL_API handler set_handler(handler) noexcept; + + TL_API response default_handler(const char* file, int line, const char* msg) noexcept; + + //Use this to temporarily change the assert handler for the current scope + class handler_guard + { + public: + explicit handler_guard(handler new_handler) noexcept + { + m_prev_handler = set_handler(new_handler); + } + ~handler_guard() noexcept + { + set_handler(m_prev_handler); + } + + private: + handler m_prev_handler; + }; + + /////////////////////////////////////////////////////////////////////// + //for internal use only +namespace detail +{ +TL_API handler& get_static_handler() noexcept; +//this is responsible of formatting the messages and calling the handler +inline response invoke_crash_handler(const char* file, int line, const char* msg) noexcept +{ + const handler& handler = get_static_handler(); + return handler(file, line, msg); +} +} +} +} + +#include "tl/detail/internal_assert.h" + + diff --git a/include/tl/detail/magic_enum.h b/include/tl/detail/magic_enum.h new file mode 100644 index 0000000..89c19a4 --- /dev/null +++ b/include/tl/detail/magic_enum.h @@ -0,0 +1,1139 @@ +// __ __ _ ______ _____ +// | \/ | (_) | ____| / ____|_ _ +// | \ / | __ _ __ _ _ ___ | |__ _ __ _ _ _ __ ___ | | _| |_ _| |_ +// | |\/| |/ _` |/ _` | |/ __| | __| | '_ \| | | | '_ ` _ \ | | |_ _|_ _| +// | | | | (_| | (_| | | (__ | |____| | | | |_| | | | | | | | |____|_| |_| +// |_| |_|\__,_|\__, |_|\___| |______|_| |_|\__,_|_| |_| |_| \_____| +// __/ | https://github.com/Neargye/magic_enum +// |___/ version 0.6.5 +// +// Licensed under the MIT License . +// SPDX-License-Identifier: MIT +// Copyright (c) 2019 - 2020 Daniil Goncharov . +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#ifndef NEARGYE_MAGIC_ENUM_HPP +#define NEARGYE_MAGIC_ENUM_HPP + +#define MAGIC_ENUM_VERSION_MAJOR 0 +#define MAGIC_ENUM_VERSION_MINOR 7 +#define MAGIC_ENUM_VERSION_PATCH 3 + +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined(MAGIC_ENUM_CONFIG_FILE) +#include MAGIC_ENUM_CONFIG_FILE +#endif + +#if !defined(MAGIC_ENUM_USING_ALIAS_OPTIONAL) +#include +#endif +#if !defined(MAGIC_ENUM_USING_ALIAS_STRING) +#include +#endif +#if !defined(MAGIC_ENUM_USING_ALIAS_STRING_VIEW) +#include +#endif + +#if defined(__clang__) +# pragma clang diagnostic push +#elif defined(__GNUC__) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wmaybe-uninitialized" // May be used uninitialized 'return {};'. +#elif defined(_MSC_VER) +# pragma warning(push) +# pragma warning(disable : 26495) // Variable 'static_string::chars_' is uninitialized. +# pragma warning(disable : 28020) // Arithmetic overflow: Using operator '-' on a 4 byte value and then casting the result to a 8 byte value. +# pragma warning(disable : 26451) // The expression '0<=_Param_(1)&&_Param_(1)<=1-1' is not true at this call. +#endif + +// Checks magic_enum compiler compatibility. +#if defined(__clang__) && __clang_major__ >= 5 || defined(__GNUC__) && __GNUC__ >= 9 || defined(_MSC_VER) && _MSC_VER >= 1910 +# undef MAGIC_ENUM_SUPPORTED +# define MAGIC_ENUM_SUPPORTED 1 +#endif + +// Checks magic_enum compiler aliases compatibility. +#if defined(__clang__) && __clang_major__ >= 5 || defined(__GNUC__) && __GNUC__ >= 9 || defined(_MSC_VER) && _MSC_VER >= 1920 +# undef MAGIC_ENUM_SUPPORTED_ALIASES +# define MAGIC_ENUM_SUPPORTED_ALIASES 1 +#endif + +// Enum value must be greater or equals than MAGIC_ENUM_RANGE_MIN. By default MAGIC_ENUM_RANGE_MIN = -128. +// If need another min range for all enum types by default, redefine the macro MAGIC_ENUM_RANGE_MIN. +#if !defined(MAGIC_ENUM_RANGE_MIN) +# define MAGIC_ENUM_RANGE_MIN -128 +#endif + +// Enum value must be less or equals than MAGIC_ENUM_RANGE_MAX. By default MAGIC_ENUM_RANGE_MAX = 128. +// If need another max range for all enum types by default, redefine the macro MAGIC_ENUM_RANGE_MAX. +#if !defined(MAGIC_ENUM_RANGE_MAX) +# define MAGIC_ENUM_RANGE_MAX 128 +#endif + +namespace magic_enum { + +// If need another optional type, define the macro MAGIC_ENUM_USING_ALIAS_OPTIONAL. +#if defined(MAGIC_ENUM_USING_ALIAS_OPTIONAL) +MAGIC_ENUM_USING_ALIAS_OPTIONAL +#else +using std::optional; +#endif + +// If need another string_view type, define the macro MAGIC_ENUM_USING_ALIAS_STRING_VIEW. +#if defined(MAGIC_ENUM_USING_ALIAS_STRING_VIEW) +MAGIC_ENUM_USING_ALIAS_STRING_VIEW +#else +using std::string_view; +#endif + +// If need another string type, define the macro MAGIC_ENUM_USING_ALIAS_STRING. +#if defined(MAGIC_ENUM_USING_ALIAS_STRING) +MAGIC_ENUM_USING_ALIAS_STRING +#else +using std::string; +#endif + +namespace customize { + +// Enum value must be in range [MAGIC_ENUM_RANGE_MIN, MAGIC_ENUM_RANGE_MAX]. By default MAGIC_ENUM_RANGE_MIN = -128, MAGIC_ENUM_RANGE_MAX = 128. +// If need another range for all enum types by default, redefine the macro MAGIC_ENUM_RANGE_MIN and MAGIC_ENUM_RANGE_MAX. +// If need another range for specific enum type, add specialization enum_range for necessary enum type. +template +struct enum_range { + static_assert(std::is_enum_v, "magic_enum::customize::enum_range requires enum type."); + inline static constexpr int min = MAGIC_ENUM_RANGE_MIN; + inline static constexpr int max = MAGIC_ENUM_RANGE_MAX; + static_assert(max > min, "magic_enum::customize::enum_range requires max > min."); +}; + +static_assert(MAGIC_ENUM_RANGE_MIN <= 0, "MAGIC_ENUM_RANGE_MIN must be less or equals than 0."); +static_assert(MAGIC_ENUM_RANGE_MIN > (std::numeric_limits::min)(), "MAGIC_ENUM_RANGE_MIN must be greater than INT16_MIN."); + +static_assert(MAGIC_ENUM_RANGE_MAX > 0, "MAGIC_ENUM_RANGE_MAX must be greater than 0."); +static_assert(MAGIC_ENUM_RANGE_MAX < (std::numeric_limits::max)(), "MAGIC_ENUM_RANGE_MAX must be less than INT16_MAX."); + +static_assert(MAGIC_ENUM_RANGE_MAX > MAGIC_ENUM_RANGE_MIN, "MAGIC_ENUM_RANGE_MAX must be greater than MAGIC_ENUM_RANGE_MIN."); + +// If need custom names for enum, add specialization enum_name for necessary enum type. +template +constexpr string_view enum_name(E) noexcept { + static_assert(std::is_enum_v, "magic_enum::customize::enum_name requires enum type."); + + return {}; +} + +} // namespace magic_enum::customize + +namespace detail { + +template +struct supported +#if defined(MAGIC_ENUM_SUPPORTED) && MAGIC_ENUM_SUPPORTED || defined(MAGIC_ENUM_NO_CHECK_SUPPORT) + : std::true_type {}; +#else + : std::false_type {}; +#endif + +struct char_equal_to { + constexpr bool operator()(char lhs, char rhs) const noexcept { + return lhs == rhs; + } +}; + +template +class static_string { + public: + constexpr explicit static_string(string_view str) noexcept : static_string{str, std::make_index_sequence{}} { + assert(str.size() == N); + } + + constexpr const char* data() const noexcept { return chars_; } + + constexpr std::size_t size() const noexcept { return N; } + + constexpr operator string_view() const noexcept { return {data(), size()}; } + + private: + template + constexpr static_string(string_view str, std::index_sequence) noexcept : chars_{str[I]..., '\0'} {} + + char chars_[N + 1]; +}; + +template <> +class static_string<0> { + public: + constexpr explicit static_string(string_view) noexcept {} + + constexpr const char* data() const noexcept { return nullptr; } + + constexpr std::size_t size() const noexcept { return 0; } + + constexpr operator string_view() const noexcept { return {}; } +}; + +constexpr string_view pretty_name(string_view name) noexcept { + for (std::size_t i = name.size(); i > 0; --i) { + if (!((name[i - 1] >= '0' && name[i - 1] <= '9') || + (name[i - 1] >= 'a' && name[i - 1] <= 'z') || + (name[i - 1] >= 'A' && name[i - 1] <= 'Z') || + (name[i - 1] == '_'))) { + name.remove_prefix(i); + break; + } + } + + if (name.size() > 0 && ((name.front() >= 'a' && name.front() <= 'z') || + (name.front() >= 'A' && name.front() <= 'Z') || + (name.front() == '_'))) { + return name; + } + + return {}; // Invalid name. +} + +constexpr std::size_t find(string_view str, char c) noexcept { +#if defined(__clang__) && __clang_major__ < 9 && defined(__GLIBCXX__) || defined(_MSC_VER) && _MSC_VER < 1920 && !defined(__clang__) +// https://stackoverflow.com/questions/56484834/constexpr-stdstring-viewfind-last-of-doesnt-work-on-clang-8-with-libstdc +// https://developercommunity.visualstudio.com/content/problem/360432/vs20178-regression-c-failed-in-test.html + constexpr bool workaround = true; +#else + constexpr bool workaround = false; +#endif + if constexpr (workaround) { + for (std::size_t i = 0; i < str.size(); ++i) { + if (str[i] == c) { + return i; + } + } + + return string_view::npos; + } else { + return str.find_first_of(c); + } +} + +template +constexpr std::array, N> to_array(T (&a)[N], std::index_sequence) { + return {{a[I]...}}; +} + +template +constexpr bool cmp_equal(string_view lhs, string_view rhs, BinaryPredicate&& p) noexcept(std::is_nothrow_invocable_r_v) { +#if defined(_MSC_VER) && _MSC_VER < 1920 && !defined(__clang__) + // https://developercommunity.visualstudio.com/content/problem/360432/vs20178-regression-c-failed-in-test.html + // https://developercommunity.visualstudio.com/content/problem/232218/c-constexpr-string-view.html + constexpr bool workaround = true; +#else + constexpr bool workaround = false; +#endif + constexpr bool default_predicate = std::is_same_v, char_equal_to>; + + if constexpr (default_predicate && !workaround) { + static_cast(p); + return lhs == rhs; + } else { + if (lhs.size() != rhs.size()) { + return false; + } + + const auto size = lhs.size(); + for (std::size_t i = 0; i < size; ++i) { + if (!p(lhs[i], rhs[i])) { + return false; + } + } + + return true; + } +} + +template +constexpr bool cmp_less(L lhs, R rhs) noexcept { + static_assert(std::is_integral_v && std::is_integral_v, "magic_enum::detail::cmp_less requires integral type."); + + if constexpr (std::is_signed_v == std::is_signed_v) { + // If same signedness (both signed or both unsigned). + return lhs < rhs; + } else if constexpr (std::is_signed_v) { + // If 'right' is negative, then result is 'false', otherwise cast & compare. + return rhs > 0 && lhs < static_cast>(rhs); + } else { + // If 'left' is negative, then result is 'true', otherwise cast & compare. + return lhs < 0 || static_cast>(lhs) < rhs; + } +} + +template +constexpr I log2(I value) noexcept { + static_assert(std::is_integral_v, "magic_enum::detail::log2 requires integral type."); + + auto ret = I{0}; + for (; value > I{1}; value >>= I{1}, ++ret) {} + + return ret; +} + +template +constexpr bool is_pow2(I x) noexcept { + static_assert(std::is_integral_v, "magic_enum::detail::is_pow2 requires integral type."); + + return x != 0 && (x & (x - 1)) == 0; +} + +template +inline constexpr bool is_enum_v = std::is_enum_v && std::is_same_v>; + +template +constexpr auto n() noexcept { + static_assert(is_enum_v, "magic_enum::detail::n requires enum type."); +#if defined(MAGIC_ENUM_SUPPORTED) && MAGIC_ENUM_SUPPORTED +# if defined(__clang__) + constexpr string_view name{__PRETTY_FUNCTION__ + 34, sizeof(__PRETTY_FUNCTION__) - 36}; +# elif defined(__GNUC__) + constexpr string_view name{__PRETTY_FUNCTION__ + 49, sizeof(__PRETTY_FUNCTION__) - 51}; +# elif defined(_MSC_VER) + constexpr string_view name{__FUNCSIG__ + 40, sizeof(__FUNCSIG__) - 57}; +# endif + return static_string{name}; +#else + return string_view{}; // Unsupported compiler. +#endif +} + +template +inline constexpr auto type_name_v = n(); + +template +constexpr auto n() noexcept { + static_assert(is_enum_v, "magic_enum::detail::n requires enum type."); + constexpr auto custom_name = customize::enum_name(V); + + if constexpr (custom_name.empty()) { + static_cast(custom_name); +#if defined(MAGIC_ENUM_SUPPORTED) && MAGIC_ENUM_SUPPORTED +# if defined(__clang__) || defined(__GNUC__) + constexpr auto name = pretty_name({__PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 2}); +# elif defined(_MSC_VER) + constexpr auto name = pretty_name({__FUNCSIG__, sizeof(__FUNCSIG__) - 17}); +# endif + return static_string{name}; +#else + return string_view{}; // Unsupported compiler. +#endif + } else { + return static_string{custom_name}; + } +} + +template +inline constexpr auto enum_name_v = n(); + +template +constexpr bool is_valid() noexcept { + static_assert(is_enum_v, "magic_enum::detail::is_valid requires enum type."); + + return n(V)>().size() != 0; +} + +template > +constexpr E value(std::size_t i) noexcept { + static_assert(is_enum_v, "magic_enum::detail::value requires enum type."); + + if constexpr (IsFlags) { + return static_cast(U{1} << static_cast(static_cast(i) + O)); + } else { + return static_cast(static_cast(i) + O); + } +} + +template > +constexpr int reflected_min() noexcept { + static_assert(is_enum_v, "magic_enum::detail::reflected_min requires enum type."); + + if constexpr (IsFlags) { + return 0; + } else { + constexpr auto lhs = customize::enum_range::min; + static_assert(lhs > (std::numeric_limits::min)(), "magic_enum::enum_range requires min must be greater than INT16_MIN."); + constexpr auto rhs = (std::numeric_limits::min)(); + + if constexpr (cmp_less(lhs, rhs)) { + return rhs; + } else { + static_assert(!is_valid(0)>(), "magic_enum::enum_range detects enum value smaller than min range size."); + return lhs; + } + } +} + +template > +constexpr int reflected_max() noexcept { + static_assert(is_enum_v, "magic_enum::detail::reflected_max requires enum type."); + + if constexpr (IsFlags) { + return std::numeric_limits::digits - 1; + } else { + constexpr auto lhs = customize::enum_range::max; + static_assert(lhs < (std::numeric_limits::max)(), "magic_enum::enum_range requires max must be less than INT16_MAX."); + constexpr auto rhs = (std::numeric_limits::max)(); + + if constexpr (cmp_less(lhs, rhs)) { + static_assert(!is_valid(0)>(), "magic_enum::enum_range detects enum value larger than max range size."); + return lhs; + } else { + return rhs; + } + } +} + +template +inline constexpr auto reflected_min_v = reflected_min(); + +template +inline constexpr auto reflected_max_v = reflected_max(); + +template +constexpr std::size_t values_count(const bool (&valid)[N]) noexcept { + auto count = std::size_t{0}; + for (std::size_t i = 0; i < N; ++i) { + if (valid[i]) { + ++count; + } + } + + return count; +} + +template +constexpr auto values(std::index_sequence) noexcept { + static_assert(is_enum_v, "magic_enum::detail::values requires enum type."); + constexpr bool valid[sizeof...(I)] = {is_valid(I)>()...}; + constexpr std::size_t count = values_count(valid); + + if constexpr (count > 0) { + E values[count] = {}; + for (std::size_t i = 0, v = 0; v < count; ++i) { + if (valid[i]) { + values[v++] = value(i); + } + } + + return to_array(values, std::make_index_sequence{}); + } else { + return std::array{}; + } +} + +template > +constexpr auto values() noexcept { + static_assert(is_enum_v, "magic_enum::detail::values requires enum type."); + constexpr auto min = reflected_min_v; + constexpr auto max = reflected_max_v; + constexpr auto range_size = max - min + 1; + static_assert(range_size > 0, "magic_enum::enum_range requires valid size."); + static_assert(range_size < (std::numeric_limits::max)(), "magic_enum::enum_range requires valid size."); + + return values>(std::make_index_sequence{}); +} + +template +inline constexpr auto values_v = values(); + +template > +using values_t = decltype((values_v)); + +template +inline constexpr auto count_v = values_v.size(); + +template > +inline constexpr auto min_v = (count_v > 0) ? static_cast(values_v.front()) : U{0}; + +template > +inline constexpr auto max_v = (count_v > 0) ? static_cast(values_v.back()) : U{0}; + +template > +constexpr std::size_t range_size() noexcept { + static_assert(is_enum_v, "magic_enum::detail::range_size requires enum type."); + constexpr auto max = IsFlags ? log2(max_v) : max_v; + constexpr auto min = IsFlags ? log2(min_v) : min_v; + constexpr auto range_size = max - min + U{1}; + static_assert(range_size > 0, "magic_enum::enum_range requires valid size."); + static_assert(range_size < (std::numeric_limits::max)(), "magic_enum::enum_range requires valid size."); + + return static_cast(range_size); +} + +template +inline constexpr auto range_size_v = range_size(); + +template +using index_t = std::conditional_t < (std::numeric_limits::max)(), std::uint8_t, std::uint16_t>; + +template +inline constexpr auto invalid_index_v = (std::numeric_limits>::max)(); + +template +constexpr auto indexes(std::index_sequence) noexcept { + static_assert(is_enum_v, "magic_enum::detail::indexes requires enum type."); + constexpr auto min = IsFlags ? log2(min_v) : min_v; + [[maybe_unused]] auto i = index_t{0}; + + return std::array{{(is_valid(I)>() ? i++ : invalid_index_v)...}}; +} + +template +inline constexpr auto indexes_v = indexes(std::make_index_sequence>{}); + +template +constexpr auto names(std::index_sequence) noexcept { + static_assert(is_enum_v, "magic_enum::detail::names requires enum type."); + + return std::array{{enum_name_v[I]>...}}; +} + +template +inline constexpr auto names_v = names(std::make_index_sequence>{}); + +template > +using names_t = decltype((names_v)); + +template +constexpr auto entries(std::index_sequence) noexcept { + static_assert(is_enum_v, "magic_enum::detail::entries requires enum type."); + + return std::array, sizeof...(I)>{{{values_v[I], enum_name_v[I]>}...}}; +} + +template +inline constexpr auto entries_v = entries(std::make_index_sequence>{}); + +template > +using entries_t = decltype((entries_v)); + +template > +constexpr bool is_sparse() noexcept { + static_assert(is_enum_v, "magic_enum::detail::is_sparse requires enum type."); + + return range_size_v != count_v; +} + +template +inline constexpr bool is_sparse_v = is_sparse(); + +template > +constexpr std::size_t undex(U value) noexcept { + static_assert(is_enum_v, "magic_enum::detail::undex requires enum type."); + + if (const auto i = static_cast(value - min_v); value >= min_v && value <= max_v) { + if constexpr (is_sparse_v) { + if (const auto idx = indexes_v[i]; idx != invalid_index_v) { + return idx; + } + } else { + return i; + } + } + + return invalid_index_v; // Value out of range. +} + +template > +constexpr std::size_t endex(E value) noexcept { + static_assert(is_enum_v, "magic_enum::detail::endex requires enum type."); + + return undex(static_cast(value)); +} + +template > +constexpr U value_ors() noexcept { + static_assert(is_enum_v, "magic_enum::detail::endex requires enum type."); + + auto value = U{0}; + for (std::size_t i = 0; i < count_v; ++i) { + value |= static_cast(values_v[i]); + } + + return value; +} + +template +struct enable_if_enum {}; + +template +struct enable_if_enum { + using type = R; + using D = std::decay_t; + static_assert(supported::value, "magic_enum unsupported compiler (https://github.com/Neargye/magic_enum#compiler-compatibility)."); +}; + +template +using enable_if_enum_t = std::enable_if_t>, R>; + +template >>> +using enum_concept = T; + +template > +struct is_scoped_enum : std::false_type {}; + +template +struct is_scoped_enum : std::bool_constant>> {}; + +template > +struct is_unscoped_enum : std::false_type {}; + +template +struct is_unscoped_enum : std::bool_constant>> {}; + +template >> +struct underlying_type {}; + +template +struct underlying_type : std::underlying_type> {}; + +} // namespace magic_enum::detail + +// Checks is magic_enum supported compiler. +inline constexpr bool is_magic_enum_supported = detail::supported::value; + +template +using Enum = detail::enum_concept; + +// Checks whether T is an Unscoped enumeration type. +// Provides the member constant value which is equal to true, if T is an [Unscoped enumeration](https://en.cppreference.com/w/cpp/language/enum#Unscoped_enumeration) type. Otherwise, value is equal to false. +template +struct is_unscoped_enum : detail::is_unscoped_enum {}; + +template +inline constexpr bool is_unscoped_enum_v = is_unscoped_enum::value; + +// Checks whether T is an Scoped enumeration type. +// Provides the member constant value which is equal to true, if T is an [Scoped enumeration](https://en.cppreference.com/w/cpp/language/enum#Scoped_enumerations) type. Otherwise, value is equal to false. +template +struct is_scoped_enum : detail::is_scoped_enum {}; + +template +inline constexpr bool is_scoped_enum_v = is_scoped_enum::value; + +// If T is a complete enumeration type, provides a member typedef type that names the underlying type of T. +// Otherwise, if T is not an enumeration type, there is no member type. Otherwise (T is an incomplete enumeration type), the program is ill-formed. +template +struct underlying_type : detail::underlying_type {}; + +template +using underlying_type_t = typename underlying_type::type; + +// Returns type name of enum. +template +[[nodiscard]] constexpr auto enum_type_name() noexcept -> detail::enable_if_enum_t { + using D = std::decay_t; + constexpr string_view name = detail::type_name_v; + static_assert(name.size() > 0, "Enum type does not have a name."); + + return name; +} + +// Returns number of enum values. +template +[[nodiscard]] constexpr auto enum_count() noexcept -> detail::enable_if_enum_t { + using D = std::decay_t; + + return detail::count_v; +} + +// Returns enum value at specified index. +// No bounds checking is performed: the behavior is undefined if index >= number of enum values. +template +[[nodiscard]] constexpr auto enum_value(std::size_t index) noexcept -> detail::enable_if_enum_t> { + using D = std::decay_t; + static_assert(detail::count_v > 0, "magic_enum requires enum implementation and valid max and min."); + + if constexpr (detail::is_sparse_v) { + return assert((index < detail::count_v)), detail::values_v[index]; + } else { + return assert((index < detail::count_v)), detail::value>(index); + } +} + +// Returns std::array with enum values, sorted by enum value. +template +[[nodiscard]] constexpr auto enum_values() noexcept -> detail::enable_if_enum_t> { + using D = std::decay_t; + static_assert(detail::count_v > 0, "magic_enum requires enum implementation and valid max and min."); + + return detail::values_v; +} + +// Returns name from static storage enum variable. +// This version is much lighter on the compile times and is not restricted to the enum_range limitation. +template +[[nodiscard]] constexpr auto enum_name() noexcept -> detail::enable_if_enum_t { + using D = std::decay_t; + constexpr string_view name = detail::enum_name_v; + static_assert(name.size() > 0, "Enum value does not have a name."); + + return name; +} + +// Returns name from enum value. +// If enum value does not have name or value out of range, returns empty string. +template +[[nodiscard]] constexpr auto enum_name(E value) noexcept -> detail::enable_if_enum_t { + using D = std::decay_t; + + if (const auto i = detail::endex(value); i != detail::invalid_index_v) { + return detail::names_v[i]; + } + + return {}; // Invalid value or out of range. +} + +// Returns std::array with names, sorted by enum value. +template +[[nodiscard]] constexpr auto enum_names() noexcept -> detail::enable_if_enum_t> { + using D = std::decay_t; + static_assert(detail::count_v > 0, "magic_enum requires enum implementation and valid max and min."); + + return detail::names_v; +} + +// Returns std::array with pairs (value, name), sorted by enum value. +template +[[nodiscard]] constexpr auto enum_entries() noexcept -> detail::enable_if_enum_t> { + using D = std::decay_t; + static_assert(detail::count_v > 0, "magic_enum requires enum implementation and valid max and min."); + + return detail::entries_v; +} + +// Obtains enum value from integer value. +// Returns optional with enum value. +template +[[nodiscard]] constexpr auto enum_cast(underlying_type_t value) noexcept -> detail::enable_if_enum_t>> { + using D = std::decay_t; + + if (detail::undex(value) != detail::invalid_index_v) { + return static_cast(value); + } + + return {}; // Invalid value or out of range. +} + +// Obtains enum value from name. +// Returns optional with enum value. +template +[[nodiscard]] constexpr auto enum_cast(string_view value, BinaryPredicate p) noexcept(std::is_nothrow_invocable_r_v) -> detail::enable_if_enum_t>> { + static_assert(std::is_invocable_r_v, "magic_enum::enum_cast requires bool(char, char) invocable predicate."); + using D = std::decay_t; + + for (std::size_t i = 0; i < detail::count_v; ++i) { + if (detail::cmp_equal(value, detail::names_v[i], p)) { + return enum_value(i); + } + } + + return {}; // Invalid value or out of range. +} + +// Obtains enum value from name. +// Returns optional with enum value. +template +[[nodiscard]] constexpr auto enum_cast(string_view value) noexcept -> detail::enable_if_enum_t>> { + using D = std::decay_t; + + return enum_cast(value, detail::char_equal_to{}); +} + +// Returns integer value from enum value. +template +[[nodiscard]] constexpr auto enum_integer(E value) noexcept -> detail::enable_if_enum_t> { + return static_cast>(value); +} + +// Obtains index in enum values from enum value. +// Returns optional with index. +template +[[nodiscard]] constexpr auto enum_index(E value) noexcept -> detail::enable_if_enum_t> { + using D = std::decay_t; + + if (const auto i = detail::endex(value); i != detail::invalid_index_v) { + return i; + } + + return {}; // Invalid value or out of range. +} + +// Checks whether enum contains enumerator with such enum value. +template +[[nodiscard]] constexpr auto enum_contains(E value) noexcept -> detail::enable_if_enum_t { + using D = std::decay_t; + + return detail::endex(value) != detail::invalid_index_v; +} + +// Checks whether enum contains enumerator with such integer value. +template +[[nodiscard]] constexpr auto enum_contains(underlying_type_t value) noexcept -> detail::enable_if_enum_t { + using D = std::decay_t; + + return detail::undex(value) != detail::invalid_index_v; +} + +// Checks whether enum contains enumerator with such name. +template +[[nodiscard]] constexpr auto enum_contains(string_view value, BinaryPredicate p) noexcept(std::is_nothrow_invocable_r_v) -> detail::enable_if_enum_t { + static_assert(std::is_invocable_r_v, "magic_enum::enum_contains requires bool(char, char) invocable predicate."); + using D = std::decay_t; + + return enum_cast(value, std::move_if_noexcept(p)).has_value(); +} + +// Checks whether enum contains enumerator with such name. +template +[[nodiscard]] constexpr auto enum_contains(string_view value) noexcept -> detail::enable_if_enum_t { + using D = std::decay_t; + + return enum_cast(value).has_value(); +} + +namespace ostream_operators { + +template , int> = 0> +std::basic_ostream& operator<<(std::basic_ostream& os, E value) { + using D = std::decay_t; + using U = underlying_type_t; +#if defined(MAGIC_ENUM_SUPPORTED) && MAGIC_ENUM_SUPPORTED + if (const auto name = magic_enum::enum_name(value); !name.empty()) { + for (const auto c : name) { + os.put(c); + } + return os; + } +#endif + return (os << static_cast(value)); +} + +template , int> = 0> +std::basic_ostream& operator<<(std::basic_ostream& os, optional value) { + return value.has_value() ? (os << value.value()) : os; +} + +} // namespace magic_enum::ostream_operators + +namespace bitwise_operators { + +template , int> = 0> +constexpr E operator~(E rhs) noexcept { + return static_cast(~static_cast>(rhs)); +} + +template , int> = 0> +constexpr E operator|(E lhs, E rhs) noexcept { + return static_cast(static_cast>(lhs) | static_cast>(rhs)); +} + +template , int> = 0> +constexpr E operator&(E lhs, E rhs) noexcept { + return static_cast(static_cast>(lhs) & static_cast>(rhs)); +} + +template , int> = 0> +constexpr E operator^(E lhs, E rhs) noexcept { + return static_cast(static_cast>(lhs) ^ static_cast>(rhs)); +} + +template , int> = 0> +constexpr E& operator|=(E& lhs, E rhs) noexcept { + return lhs = (lhs | rhs); +} + +template , int> = 0> +constexpr E& operator&=(E& lhs, E rhs) noexcept { + return lhs = (lhs & rhs); +} + +template , int> = 0> +constexpr E& operator^=(E& lhs, E rhs) noexcept { + return lhs = (lhs ^ rhs); +} + +} // namespace magic_enum::bitwise_operators + +namespace flags { + +// Returns type name of enum. +using magic_enum::enum_type_name; + +// Returns number of enum-flags values. +template +[[nodiscard]] constexpr auto enum_count() noexcept -> detail::enable_if_enum_t { + using D = std::decay_t; + + return detail::count_v; +} + +// Returns enum-flags value at specified index. +// No bounds checking is performed: the behavior is undefined if index >= number of enum-flags values. +template +[[nodiscard]] constexpr auto enum_value(std::size_t index) noexcept -> detail::enable_if_enum_t> { + using D = std::decay_t; + static_assert(detail::count_v > 0, "magic_enum::flags requires enum-flags implementation."); + + if constexpr (detail::is_sparse_v) { + return assert((index < detail::count_v)), detail::values_v[index]; + } else { + constexpr auto min = detail::log2(detail::min_v); + + return assert((index < detail::count_v)), detail::value(index); + } +} + +// Returns std::array with enum-flags values, sorted by enum-flags value. +template +[[nodiscard]] constexpr auto enum_values() noexcept -> detail::enable_if_enum_t> { + using D = std::decay_t; + static_assert(detail::count_v > 0, "magic_enum::flags requires enum-flags implementation."); + + return detail::values_v; +} + +// Returns name from enum-flags value. +// If enum-flags value does not have name or value out of range, returns empty string. +template +[[nodiscard]] auto enum_name(E value) -> detail::enable_if_enum_t { + using D = std::decay_t; + using U = underlying_type_t; + + string name; + auto check_value = U{0}; + for (std::size_t i = 0; i < detail::count_v; ++i) { + if (const auto v = static_cast(enum_value(i)); (static_cast(value) & v) != 0) { + check_value |= v; + const auto n = detail::names_v[i]; + if (!name.empty()) { + name.append(1, '|'); + } + name.append(n.data(), n.size()); + } + } + + if (check_value != 0 && check_value == static_cast(value)) { + return name; + } + + return {}; // Invalid value or out of range. +} + +// Returns std::array with string names, sorted by enum-flags value. +template +[[nodiscard]] constexpr auto enum_names() noexcept -> detail::enable_if_enum_t> { + using D = std::decay_t; + static_assert(detail::count_v > 0, "magic_enum::flags requires enum-flags implementation."); + + return detail::names_v; +} + +// Returns std::array with pairs (value, name), sorted by enum-flags value. +template +[[nodiscard]] constexpr auto enum_entries() noexcept -> detail::enable_if_enum_t> { + using D = std::decay_t; + static_assert(detail::count_v > 0, "magic_enum::flags requires enum-flags implementation."); + + return detail::entries_v; +} + +// Obtains enum-flags value from integer value. +// Returns optional with enum-flags value. +template +[[nodiscard]] constexpr auto enum_cast(underlying_type_t value) noexcept -> detail::enable_if_enum_t>> { + using D = std::decay_t; + using U = underlying_type_t; + + if constexpr (detail::is_sparse_v) { + auto check_value = U{0}; + for (std::size_t i = 0; i < detail::count_v; ++i) { + if (const auto v = static_cast(enum_value(i)); (value & v) != 0) { + check_value |= v; + } + } + + if (check_value != 0 && check_value == value) { + return static_cast(value); + } + } else { + constexpr auto min = detail::min_v; + constexpr auto max = detail::value_ors(); + + if (value >= min && value <= max) { + return static_cast(value); + } + } + + return {}; // Invalid value or out of range. +} + +// Obtains enum-flags value from name. +// Returns optional with enum-flags value. +template +[[nodiscard]] constexpr auto enum_cast(string_view value, BinaryPredicate p) noexcept(std::is_nothrow_invocable_r_v) -> detail::enable_if_enum_t>> { + static_assert(std::is_invocable_r_v, "magic_enum::flags::enum_cast requires bool(char, char) invocable predicate."); + using D = std::decay_t; + using U = underlying_type_t; + + auto result = U{0}; + while (!value.empty()) { + const auto d = detail::find(value, '|'); + const auto s = (d == string_view::npos) ? value : value.substr(0, d); + auto f = U{0}; + for (std::size_t i = 0; i < detail::count_v; ++i) { + if (detail::cmp_equal(s, detail::names_v[i], p)) { + f = static_cast(enum_value(i)); + result |= f; + break; + } + } + if (f == U{0}) { + return {}; // Invalid value or out of range. + } + value.remove_prefix((d == string_view::npos) ? value.size() : d + 1); + } + + if (result == U{0}) { + return {}; // Invalid value or out of range. + } else { + return static_cast(result); + } +} + +// Obtains enum-flags value from name. +// Returns optional with enum-flags value. +template +[[nodiscard]] constexpr auto enum_cast(string_view value) noexcept -> detail::enable_if_enum_t>> { + using D = std::decay_t; + + return enum_cast(value, detail::char_equal_to{}); +} + +// Returns integer value from enum value. +using magic_enum::enum_integer; + +// Obtains index in enum-flags values from enum-flags value. +// Returns optional with index. +template +[[nodiscard]] constexpr auto enum_index(E value) noexcept -> detail::enable_if_enum_t> { + using D = std::decay_t; + using U = underlying_type_t; + + if (detail::is_pow2(static_cast(value))) { + for (std::size_t i = 0; i < detail::count_v; ++i) { + if (enum_value(i) == value) { + return i; + } + } + } + + return {}; // Invalid value or out of range. +} + +// Checks whether enum-flags contains enumerator with such enum-flags value. +template +[[nodiscard]] constexpr auto enum_contains(E value) noexcept -> detail::enable_if_enum_t { + using D = std::decay_t; + using U = underlying_type_t; + + return enum_cast(static_cast(value)).has_value(); +} + +// Checks whether enum-flags contains enumerator with such integer value. +template +[[nodiscard]] constexpr auto enum_contains(underlying_type_t value) noexcept -> detail::enable_if_enum_t { + using D = std::decay_t; + + return enum_cast(value).has_value(); +} + +// Checks whether enum-flags contains enumerator with such name. +template +[[nodiscard]] constexpr auto enum_contains(string_view value, BinaryPredicate p) noexcept(std::is_nothrow_invocable_r_v) -> detail::enable_if_enum_t { + static_assert(std::is_invocable_r_v, "magic_enum::flags::enum_contains requires bool(char, char) invocable predicate."); + using D = std::decay_t; + + return enum_cast(value, std::move_if_noexcept(p)).has_value(); +} + +// Checks whether enum-flags contains enumerator with such name. +template +[[nodiscard]] constexpr auto enum_contains(string_view value) noexcept -> detail::enable_if_enum_t { + using D = std::decay_t; + + return enum_cast(value).has_value(); +} + +} // namespace magic_enum::flags + +namespace flags::ostream_operators { + +template , int> = 0> +std::basic_ostream& operator<<(std::basic_ostream& os, E value) { + using D = std::decay_t; + using U = underlying_type_t; +#if defined(MAGIC_ENUM_SUPPORTED) && MAGIC_ENUM_SUPPORTED + if (const auto name = magic_enum::flags::enum_name(value); !name.empty()) { + for (const auto c : name) { + os.put(c); + } + return os; + } +#endif + return (os << static_cast(value)); +} + +template , int> = 0> +std::basic_ostream& operator<<(std::basic_ostream& os, optional value) { + return value.has_value() ? (os << value.value()) : os; +} + +} // namespace magic_enum::flags::ostream_operators + +namespace flags::bitwise_operators { + +using namespace magic_enum::bitwise_operators; + +} // namespace magic_enum::flags::bitwise_operators + +} // namespace magic_enum + +#if defined(__clang__) +# pragma clang diagnostic pop +#elif defined(__GNUC__) +# pragma GCC diagnostic pop +#elif defined(_MSC_VER) +# pragma warning(pop) +#endif + +#endif // NEARGYE_MAGIC_ENUM_HPP \ No newline at end of file diff --git a/include/tl/detail/outcome.hpp b/include/tl/detail/outcome.hpp new file mode 100644 index 0000000..b908b21 --- /dev/null +++ b/include/tl/detail/outcome.hpp @@ -0,0 +1,7903 @@ +/* Include the default amount of outcome +(C) 2018-2019 Niall Douglas (4 commits) +File Created: Mar 2018 + + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License in the accompanying file +Licence.txt or at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + + +Distributed under the Boost Software License, Version 1.0. + (See accompanying file Licence.txt or copy at + http://www.boost.org/LICENSE_1_0.txt) +*/ +#if !0L || defined(GENERATING_OUTCOME_MODULE_INTERFACE) || OUTCOME_DISABLE_CXX_MODULES +/* Tells C++ coroutines about Outcome's result +(C) 2019 Niall Douglas (12 commits) +File Created: Oct 2019 + + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License in the accompanying file +Licence.txt or at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + + +Distributed under the Boost Software License, Version 1.0. + (See accompanying file Licence.txt or copy at + http://www.boost.org/LICENSE_1_0.txt) +*/ +#ifndef OUTCOME_COROUTINE_SUPPORT_HPP +#define OUTCOME_COROUTINE_SUPPORT_HPP +/* Configure Outcome with QuickCppLib +(C) 2015-2021 Niall Douglas (24 commits) +File Created: August 2015 + + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License in the accompanying file +Licence.txt or at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + + +Distributed under the Boost Software License, Version 1.0. + (See accompanying file Licence.txt or copy at + http://www.boost.org/LICENSE_1_0.txt) +*/ +#ifndef OUTCOME_V2_CONFIG_HPP +#define OUTCOME_V2_CONFIG_HPP +/* Sets Outcome version +(C) 2017-2019 Niall Douglas (4 commits) + + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License in the accompanying file +Licence.txt or at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + + +Distributed under the Boost Software License, Version 1.0. + (See accompanying file Licence.txt or copy at + http://www.boost.org/LICENSE_1_0.txt) +*/ +/*! AWAITING HUGO JSON CONVERSION TOOL */ +#define OUTCOME_VERSION_MAJOR 2 +/*! AWAITING HUGO JSON CONVERSION TOOL */ +#define OUTCOME_VERSION_MINOR 2 +/*! AWAITING HUGO JSON CONVERSION TOOL */ +#define OUTCOME_VERSION_PATCH 0 +/*! AWAITING HUGO JSON CONVERSION TOOL */ +#define OUTCOME_VERSION_REVISION 0 // Revision version for cmake and DLL version stamping +/*! AWAITING HUGO JSON CONVERSION TOOL */ +#ifndef OUTCOME_DISABLE_ABI_PERMUTATION +#define OUTCOME_UNSTABLE_VERSION +#endif +// Pull in detection of __MINGW64_VERSION_MAJOR +#if defined(__MINGW32__) && !0L +#include <_mingw.h> +#endif +/* Configure QuickCppLib +(C) 2016-2017 Niall Douglas (8 commits) + + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License in the accompanying file +Licence.txt or at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + + +Distributed under the Boost Software License, Version 1.0. + (See accompanying file Licence.txt or copy at + http://www.boost.org/LICENSE_1_0.txt) +*/ +#ifndef QUICKCPPLIB_CONFIG_HPP +#define QUICKCPPLIB_CONFIG_HPP +/* Provides SG-10 feature checking for all C++ compilers +(C) 2014-2017 Niall Douglas (13 commits) +File Created: Nov 2014 + + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License in the accompanying file +Licence.txt or at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + + +Distributed under the Boost Software License, Version 1.0. + (See accompanying file Licence.txt or copy at + http://www.boost.org/LICENSE_1_0.txt) +*/ +#ifndef QUICKCPPLIB_HAS_FEATURE_H +#define QUICKCPPLIB_HAS_FEATURE_H +#if __cplusplus >= 201103L +// Some of these macros ended up getting removed by ISO standards, +// they are prefixed with //// +////#if !defined(__cpp_alignas) +////#define __cpp_alignas 190000 +////#endif +////#if !defined(__cpp_default_function_template_args) +////#define __cpp_default_function_template_args 190000 +////#endif +////#if !defined(__cpp_defaulted_functions) +////#define __cpp_defaulted_functions 190000 +////#endif +////#if !defined(__cpp_deleted_functions) +////#define __cpp_deleted_functions 190000 +////#endif +////#if !defined(__cpp_generalized_initializers) +////#define __cpp_generalized_initializers 190000 +////#endif +////#if !defined(__cpp_implicit_moves) +////#define __cpp_implicit_moves 190000 +////#endif +////#if !defined(__cpp_inline_namespaces) +////#define __cpp_inline_namespaces 190000 +////#endif +////#if !defined(__cpp_local_type_template_args) +////#define __cpp_local_type_template_args 190000 +////#endif +////#if !defined(__cpp_noexcept) +////#define __cpp_noexcept 190000 +////#endif +////#if !defined(__cpp_nonstatic_member_init) +////#define __cpp_nonstatic_member_init 190000 +////#endif +////#if !defined(__cpp_nullptr) +////#define __cpp_nullptr 190000 +////#endif +////#if !defined(__cpp_override_control) +////#define __cpp_override_control 190000 +////#endif +////#if !defined(__cpp_thread_local) +////#define __cpp_thread_local 190000 +////#endif +////#if !defined(__cpp_auto_type) +////#define __cpp_auto_type 190000 +////#endif +////#if !defined(__cpp_strong_enums) +////#define __cpp_strong_enums 190000 +////#endif +////#if !defined(__cpp_trailing_return) +////#define __cpp_trailing_return 190000 +////#endif +////#if !defined(__cpp_unrestricted_unions) +////#define __cpp_unrestricted_unions 190000 +////#endif +#if !defined(__cpp_alias_templates) +#define __cpp_alias_templates 190000 +#endif +#if !defined(__cpp_attributes) +#define __cpp_attributes 190000 +#endif +#if !defined(__cpp_constexpr) +#if __cplusplus >= 201402L +#define __cpp_constexpr 201304 // relaxed constexpr +#else +#define __cpp_constexpr 190000 +#endif +#endif +#if !defined(__cpp_decltype) +#define __cpp_decltype 190000 +#endif +#if !defined(__cpp_delegating_constructors) +#define __cpp_delegating_constructors 190000 +#endif +#if !defined(__cpp_explicit_conversion) //// renamed from __cpp_explicit_conversions +#define __cpp_explicit_conversion 190000 +#endif +#if !defined(__cpp_inheriting_constructors) +#define __cpp_inheriting_constructors 190000 +#endif +#if !defined(__cpp_initializer_lists) //// NEW +#define __cpp_initializer_lists 190000 +#endif +#if !defined(__cpp_lambdas) +#define __cpp_lambdas 190000 +#endif +#if !defined(__cpp_nsdmi) +#define __cpp_nsdmi 190000 //// NEW +#endif +#if !defined(__cpp_range_based_for) //// renamed from __cpp_range_for +#define __cpp_range_based_for 190000 +#endif +#if !defined(__cpp_raw_strings) +#define __cpp_raw_strings 190000 +#endif +#if !defined(__cpp_ref_qualifiers) //// renamed from __cpp_reference_qualified_functions +#define __cpp_ref_qualifiers 190000 +#endif +#if !defined(__cpp_rvalue_references) +#define __cpp_rvalue_references 190000 +#endif +#if !defined(__cpp_static_assert) +#define __cpp_static_assert 190000 +#endif +#if !defined(__cpp_unicode_characters) //// NEW +#define __cpp_unicode_characters 190000 +#endif +#if !defined(__cpp_unicode_literals) +#define __cpp_unicode_literals 190000 +#endif +#if !defined(__cpp_user_defined_literals) +#define __cpp_user_defined_literals 190000 +#endif +#if !defined(__cpp_variadic_templates) +#define __cpp_variadic_templates 190000 +#endif +#endif +#if __cplusplus >= 201402L +// Some of these macros ended up getting removed by ISO standards, +// they are prefixed with //// +////#if !defined(__cpp_contextual_conversions) +////#define __cpp_contextual_conversions 190000 +////#endif +////#if !defined(__cpp_digit_separators) +////#define __cpp_digit_separators 190000 +////#endif +////#if !defined(__cpp_relaxed_constexpr) +////#define __cpp_relaxed_constexpr 190000 +////#endif +////#if !defined(__cpp_runtime_arrays) +////# define __cpp_runtime_arrays 190000 +////#endif +#if !defined(__cpp_aggregate_nsdmi) +#define __cpp_aggregate_nsdmi 190000 +#endif +#if !defined(__cpp_binary_literals) +#define __cpp_binary_literals 190000 +#endif +#if !defined(__cpp_decltype_auto) +#define __cpp_decltype_auto 190000 +#endif +#if !defined(__cpp_generic_lambdas) +#define __cpp_generic_lambdas 190000 +#endif +#if !defined(__cpp_init_captures) +#define __cpp_init_captures 190000 +#endif +#if !defined(__cpp_return_type_deduction) +#define __cpp_return_type_deduction 190000 +#endif +#if !defined(__cpp_sized_deallocation) +#define __cpp_sized_deallocation 190000 +#endif +#if !defined(__cpp_variable_templates) +#define __cpp_variable_templates 190000 +#endif +#endif +// VS2010: _MSC_VER=1600 +// VS2012: _MSC_VER=1700 +// VS2013: _MSC_VER=1800 +// VS2015: _MSC_VER=1900 +// VS2017: _MSC_VER=1910 +#if defined(_MSC_VER) && !defined(__clang__) +#if !defined(__cpp_exceptions) && defined(_CPPUNWIND) +#define __cpp_exceptions 190000 +#endif +#if !defined(__cpp_rtti) && defined(_CPPRTTI) +#define __cpp_rtti 190000 +#endif +// C++ 11 +#if !defined(__cpp_alias_templates) && _MSC_VER >= 1800 +#define __cpp_alias_templates 190000 +#endif +#if !defined(__cpp_attributes) +#define __cpp_attributes 190000 +#endif +#if !defined(__cpp_constexpr) && _MSC_FULL_VER >= 190023506 /* VS2015 */ +#define __cpp_constexpr 190000 +#endif +#if !defined(__cpp_decltype) && _MSC_VER >= 1600 +#define __cpp_decltype 190000 +#endif +#if !defined(__cpp_delegating_constructors) && _MSC_VER >= 1800 +#define __cpp_delegating_constructors 190000 +#endif +#if !defined(__cpp_explicit_conversion) && _MSC_VER >= 1800 +#define __cpp_explicit_conversion 190000 +#endif +#if !defined(__cpp_inheriting_constructors) && _MSC_VER >= 1900 +#define __cpp_inheriting_constructors 190000 +#endif +#if !defined(__cpp_initializer_lists) && _MSC_VER >= 1900 +#define __cpp_initializer_lists 190000 +#endif +#if !defined(__cpp_lambdas) && _MSC_VER >= 1600 +#define __cpp_lambdas 190000 +#endif +#if !defined(__cpp_nsdmi) && _MSC_VER >= 1900 +#define __cpp_nsdmi 190000 +#endif +#if !defined(__cpp_range_based_for) && _MSC_VER >= 1700 +#define __cpp_range_based_for 190000 +#endif +#if !defined(__cpp_raw_strings) && _MSC_VER >= 1800 +#define __cpp_raw_strings 190000 +#endif +#if !defined(__cpp_ref_qualifiers) && _MSC_VER >= 1900 +#define __cpp_ref_qualifiers 190000 +#endif +#if !defined(__cpp_rvalue_references) && _MSC_VER >= 1600 +#define __cpp_rvalue_references 190000 +#endif +#if !defined(__cpp_static_assert) && _MSC_VER >= 1600 +#define __cpp_static_assert 190000 +#endif +//#if !defined(__cpp_unicode_literals) +//# define __cpp_unicode_literals 190000 +//#endif +#if !defined(__cpp_user_defined_literals) && _MSC_VER >= 1900 +#define __cpp_user_defined_literals 190000 +#endif +#if !defined(__cpp_variadic_templates) && _MSC_VER >= 1800 +#define __cpp_variadic_templates 190000 +#endif +// C++ 14 +//#if !defined(__cpp_aggregate_nsdmi) +//#define __cpp_aggregate_nsdmi 190000 +//#endif +#if !defined(__cpp_binary_literals) && _MSC_VER >= 1900 +#define __cpp_binary_literals 190000 +#endif +#if !defined(__cpp_decltype_auto) && _MSC_VER >= 1900 +#define __cpp_decltype_auto 190000 +#endif +#if !defined(__cpp_generic_lambdas) && _MSC_VER >= 1900 +#define __cpp_generic_lambdas 190000 +#endif +#if !defined(__cpp_init_captures) && _MSC_VER >= 1900 +#define __cpp_init_captures 190000 +#endif +#if !defined(__cpp_return_type_deduction) && _MSC_VER >= 1900 +#define __cpp_return_type_deduction 190000 +#endif +#if !defined(__cpp_sized_deallocation) && _MSC_VER >= 1900 +#define __cpp_sized_deallocation 190000 +#endif +#if !defined(__cpp_variable_templates) && _MSC_FULL_VER >= 190023506 +#define __cpp_variable_templates 190000 +#endif +#endif // _MSC_VER +// Much to my surprise, GCC's support of these is actually incomplete, so fill in the gaps +#if (defined(__GNUC__) && !defined(__clang__)) +#define QUICKCPPLIB_GCC (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) +#if !defined(__cpp_exceptions) && defined(__EXCEPTIONS) +#define __cpp_exceptions 190000 +#endif +#if !defined(__cpp_rtti) && defined(__GXX_RTTI) +#define __cpp_rtti 190000 +#endif +// C++ 11 +#if defined(__GXX_EXPERIMENTAL_CXX0X__) +#if !defined(__cpp_alias_templates) && (QUICKCPPLIB_GCC >= 40700) +#define __cpp_alias_templates 190000 +#endif +#if !defined(__cpp_attributes) && (QUICKCPPLIB_GCC >= 40800) +#define __cpp_attributes 190000 +#endif +#if !defined(__cpp_constexpr) && (QUICKCPPLIB_GCC >= 40600) +#define __cpp_constexpr 190000 +#endif +#if !defined(__cpp_decltype) && (QUICKCPPLIB_GCC >= 40300) +#define __cpp_decltype 190000 +#endif +#if !defined(__cpp_delegating_constructors) && (QUICKCPPLIB_GCC >= 40700) +#define __cpp_delegating_constructors 190000 +#endif +#if !defined(__cpp_explicit_conversion) && (QUICKCPPLIB_GCC >= 40500) +#define __cpp_explicit_conversion 190000 +#endif +#if !defined(__cpp_inheriting_constructors) && (QUICKCPPLIB_GCC >= 40800) +#define __cpp_inheriting_constructors 190000 +#endif +#if !defined(__cpp_initializer_lists) && (QUICKCPPLIB_GCC >= 40800) +#define __cpp_initializer_lists 190000 +#endif +#if !defined(__cpp_lambdas) && (QUICKCPPLIB_GCC >= 40500) +#define __cpp_lambdas 190000 +#endif +#if !defined(__cpp_nsdmi) && (QUICKCPPLIB_GCC >= 40700) +#define __cpp_nsdmi 190000 +#endif +#if !defined(__cpp_range_based_for) && (QUICKCPPLIB_GCC >= 40600) +#define __cpp_range_based_for 190000 +#endif +#if !defined(__cpp_raw_strings) && (QUICKCPPLIB_GCC >= 40500) +#define __cpp_raw_strings 190000 +#endif +#if !defined(__cpp_ref_qualifiers) && (QUICKCPPLIB_GCC >= 40801) +#define __cpp_ref_qualifiers 190000 +#endif +// __cpp_rvalue_reference deviation +#if !defined(__cpp_rvalue_references) && defined(__cpp_rvalue_reference) +#define __cpp_rvalue_references __cpp_rvalue_reference +#endif +#if !defined(__cpp_static_assert) && (QUICKCPPLIB_GCC >= 40300) +#define __cpp_static_assert 190000 +#endif +#if !defined(__cpp_unicode_characters) && (QUICKCPPLIB_GCC >= 40500) +#define __cpp_unicode_characters 190000 +#endif +#if !defined(__cpp_unicode_literals) && (QUICKCPPLIB_GCC >= 40500) +#define __cpp_unicode_literals 190000 +#endif +#if !defined(__cpp_user_defined_literals) && (QUICKCPPLIB_GCC >= 40700) +#define __cpp_user_defined_literals 190000 +#endif +#if !defined(__cpp_variadic_templates) && (QUICKCPPLIB_GCC >= 40400) +#define __cpp_variadic_templates 190000 +#endif +// C++ 14 +// Every C++ 14 supporting GCC does the right thing here +#endif // __GXX_EXPERIMENTAL_CXX0X__ +#endif // GCC +// clang deviates in some places from the present SG-10 draft, plus older +// clangs are quite incomplete +#if defined(__clang__) +#define QUICKCPPLIB_CLANG (__clang_major__ * 10000 + __clang_minor__ * 100 + __clang_patchlevel__) +#if !defined(__cpp_exceptions) && (defined(__EXCEPTIONS) || defined(_CPPUNWIND)) +#define __cpp_exceptions 190000 +#endif +#if !defined(__cpp_rtti) && (defined(__GXX_RTTI) || defined(_CPPRTTI)) +#define __cpp_rtti 190000 +#endif +// C++ 11 +#if defined(__GXX_EXPERIMENTAL_CXX0X__) +#if !defined(__cpp_alias_templates) && (QUICKCPPLIB_CLANG >= 30000) +#define __cpp_alias_templates 190000 +#endif +#if !defined(__cpp_attributes) && (QUICKCPPLIB_CLANG >= 30300) +#define __cpp_attributes 190000 +#endif +#if !defined(__cpp_constexpr) && (QUICKCPPLIB_CLANG >= 30100) +#define __cpp_constexpr 190000 +#endif +#if !defined(__cpp_decltype) && (QUICKCPPLIB_CLANG >= 20900) +#define __cpp_decltype 190000 +#endif +#if !defined(__cpp_delegating_constructors) && (QUICKCPPLIB_CLANG >= 30000) +#define __cpp_delegating_constructors 190000 +#endif +#if !defined(__cpp_explicit_conversion) && (QUICKCPPLIB_CLANG >= 30000) +#define __cpp_explicit_conversion 190000 +#endif +#if !defined(__cpp_inheriting_constructors) && (QUICKCPPLIB_CLANG >= 30300) +#define __cpp_inheriting_constructors 190000 +#endif +#if !defined(__cpp_initializer_lists) && (QUICKCPPLIB_CLANG >= 30100) +#define __cpp_initializer_lists 190000 +#endif +#if !defined(__cpp_lambdas) && (QUICKCPPLIB_CLANG >= 30100) +#define __cpp_lambdas 190000 +#endif +#if !defined(__cpp_nsdmi) && (QUICKCPPLIB_CLANG >= 30000) +#define __cpp_nsdmi 190000 +#endif +#if !defined(__cpp_range_based_for) && (QUICKCPPLIB_CLANG >= 30000) +#define __cpp_range_based_for 190000 +#endif +// __cpp_raw_string_literals deviation +#if !defined(__cpp_raw_strings) && defined(__cpp_raw_string_literals) +#define __cpp_raw_strings __cpp_raw_string_literals +#endif +#if !defined(__cpp_raw_strings) && (QUICKCPPLIB_CLANG >= 30000) +#define __cpp_raw_strings 190000 +#endif +#if !defined(__cpp_ref_qualifiers) && (QUICKCPPLIB_CLANG >= 20900) +#define __cpp_ref_qualifiers 190000 +#endif +// __cpp_rvalue_reference deviation +#if !defined(__cpp_rvalue_references) && defined(__cpp_rvalue_reference) +#define __cpp_rvalue_references __cpp_rvalue_reference +#endif +#if !defined(__cpp_rvalue_references) && (QUICKCPPLIB_CLANG >= 20900) +#define __cpp_rvalue_references 190000 +#endif +#if !defined(__cpp_static_assert) && (QUICKCPPLIB_CLANG >= 20900) +#define __cpp_static_assert 190000 +#endif +#if !defined(__cpp_unicode_characters) && (QUICKCPPLIB_CLANG >= 30000) +#define __cpp_unicode_characters 190000 +#endif +#if !defined(__cpp_unicode_literals) && (QUICKCPPLIB_CLANG >= 30000) +#define __cpp_unicode_literals 190000 +#endif +// __cpp_user_literals deviation +#if !defined(__cpp_user_defined_literals) && defined(__cpp_user_literals) +#define __cpp_user_defined_literals __cpp_user_literals +#endif +#if !defined(__cpp_user_defined_literals) && (QUICKCPPLIB_CLANG >= 30100) +#define __cpp_user_defined_literals 190000 +#endif +#if !defined(__cpp_variadic_templates) && (QUICKCPPLIB_CLANG >= 20900) +#define __cpp_variadic_templates 190000 +#endif +// C++ 14 +// Every C++ 14 supporting clang does the right thing here +#endif // __GXX_EXPERIMENTAL_CXX0X__ +#endif // clang +#endif +#ifndef QUICKCPPLIB_DISABLE_ABI_PERMUTATION +// Note the second line of this file must ALWAYS be the git SHA, third line ALWAYS the git SHA update time +#define QUICKCPPLIB_PREVIOUS_COMMIT_REF 9a151aab3abaf1ced6b0b82f9328beb536386254 +#define QUICKCPPLIB_PREVIOUS_COMMIT_DATE "2021-02-23 11:25:30 +00:00" +#define QUICKCPPLIB_PREVIOUS_COMMIT_UNIQUE 9a151aab +#endif +#define QUICKCPPLIB_VERSION_GLUE2(a, b) a##b +#define QUICKCPPLIB_VERSION_GLUE(a, b) QUICKCPPLIB_VERSION_GLUE2(a, b) +// clang-format off +#if defined(QUICKCPPLIB_DISABLE_ABI_PERMUTATION) +#define QUICKCPPLIB_NAMESPACE quickcpplib +#define QUICKCPPLIB_NAMESPACE_BEGIN namespace quickcpplib { +#define QUICKCPPLIB_NAMESPACE_END } +#else +#define QUICKCPPLIB_NAMESPACE quickcpplib::QUICKCPPLIB_VERSION_GLUE(_, QUICKCPPLIB_PREVIOUS_COMMIT_UNIQUE) +#define QUICKCPPLIB_NAMESPACE_BEGIN namespace quickcpplib { namespace QUICKCPPLIB_VERSION_GLUE(_, QUICKCPPLIB_PREVIOUS_COMMIT_UNIQUE) { +#define QUICKCPPLIB_NAMESPACE_END } } +#endif +// clang-format on +#ifdef _MSC_VER +#define QUICKCPPLIB_BIND_MESSAGE_PRAGMA2(x) __pragma(message(x)) +#define QUICKCPPLIB_BIND_MESSAGE_PRAGMA(x) QUICKCPPLIB_BIND_MESSAGE_PRAGMA2(x) +#define QUICKCPPLIB_BIND_MESSAGE_PREFIX(type) __FILE__ "(" QUICKCPPLIB_BIND_STRINGIZE2(__LINE__) "): " type ": " +#define QUICKCPPLIB_BIND_MESSAGE_(type, prefix, msg) QUICKCPPLIB_BIND_MESSAGE_PRAGMA(prefix msg) +#else +#define QUICKCPPLIB_BIND_MESSAGE_PRAGMA2(x) _Pragma(#x) +#define QUICKCPPLIB_BIND_MESSAGE_PRAGMA(type, x) QUICKCPPLIB_BIND_MESSAGE_PRAGMA2(type x) +#define QUICKCPPLIB_BIND_MESSAGE_(type, prefix, msg) QUICKCPPLIB_BIND_MESSAGE_PRAGMA(type, msg) +#endif +//! Have the compiler output a message +#define QUICKCPPLIB_MESSAGE(msg) QUICKCPPLIB_BIND_MESSAGE_(message, QUICKCPPLIB_BIND_MESSAGE_PREFIX("message"), msg) +//! Have the compiler output a note +#define QUICKCPPLIB_NOTE(msg) QUICKCPPLIB_BIND_MESSAGE_(message, QUICKCPPLIB_BIND_MESSAGE_PREFIX("note"), msg) +//! Have the compiler output a warning +#define QUICKCPPLIB_WARNING(msg) QUICKCPPLIB_BIND_MESSAGE_(GCC warning, QUICKCPPLIB_BIND_MESSAGE_PREFIX("warning"), msg) +//! Have the compiler output an error +#define QUICKCPPLIB_ERROR(msg) QUICKCPPLIB_BIND_MESSAGE_(GCC error, QUICKCPPLIB_BIND_MESSAGE_PREFIX("error"), msg) +#define QUICKCPPLIB_ANNOTATE_RWLOCK_CREATE(p) +#define QUICKCPPLIB_ANNOTATE_RWLOCK_DESTROY(p) +#define QUICKCPPLIB_ANNOTATE_RWLOCK_ACQUIRED(p, s) +#define QUICKCPPLIB_ANNOTATE_RWLOCK_RELEASED(p, s) +#define QUICKCPPLIB_ANNOTATE_IGNORE_READS_BEGIN() +#define QUICKCPPLIB_ANNOTATE_IGNORE_READS_END() +#define QUICKCPPLIB_ANNOTATE_IGNORE_WRITES_BEGIN() +#define QUICKCPPLIB_ANNOTATE_IGNORE_WRITES_END() +#define QUICKCPPLIB_DRD_IGNORE_VAR(x) +#define QUICKCPPLIB_DRD_STOP_IGNORING_VAR(x) +#define QUICKCPPLIB_RUNNING_ON_VALGRIND (0) +#ifndef QUICKCPPLIB_IN_THREAD_SANITIZER +#if defined(__has_feature) +#if __has_feature(thread_sanitizer) +#define QUICKCPPLIB_IN_THREAD_SANITIZER 1 +#endif +#elif defined(__SANITIZE_THREAD__) +#define QUICKCPPLIB_IN_THREAD_SANITIZER 1 +#endif +#endif +#ifndef QUICKCPPLIB_IN_THREAD_SANITIZER +#define QUICKCPPLIB_IN_THREAD_SANITIZER 0 +#endif +#if QUICKCPPLIB_IN_THREAD_SANITIZER +#define QUICKCPPLIB_DISABLE_THREAD_SANITIZE __attribute__((no_sanitize_thread)) +#else +#define QUICKCPPLIB_DISABLE_THREAD_SANITIZE +#endif +#ifndef QUICKCPPLIB_SMT_PAUSE +#if !defined(__clang__) && defined(_MSC_VER) && _MSC_VER >= 1310 && (defined(_M_IX86) || defined(_M_X64)) +extern "C" void _mm_pause(); +#pragma intrinsic(_mm_pause) +#define QUICKCPPLIB_SMT_PAUSE _mm_pause(); +#elif !defined(__c2__) && defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__)) +#define QUICKCPPLIB_SMT_PAUSE __asm__ __volatile__("rep; nop" : : : "memory"); +#endif +#endif +#ifndef QUICKCPPLIB_FORCEINLINE +#if defined(_MSC_VER) +#define QUICKCPPLIB_FORCEINLINE __forceinline +#elif defined(__GNUC__) +#define QUICKCPPLIB_FORCEINLINE __attribute__((always_inline)) +#else +#define QUICKCPPLIB_FORCEINLINE +#endif +#endif +#ifndef QUICKCPPLIB_NOINLINE +#if defined(_MSC_VER) +#define QUICKCPPLIB_NOINLINE __declspec(noinline) +#elif defined(__GNUC__) +#define QUICKCPPLIB_NOINLINE __attribute__((noinline)) +#else +#define QUICKCPPLIB_NOINLINE +#endif +#endif +#ifdef __has_cpp_attribute +#define QUICKCPPLIB_HAS_CPP_ATTRIBUTE(attr) __has_cpp_attribute(attr) +#else +#define QUICKCPPLIB_HAS_CPP_ATTRIBUTE(attr) (0) +#endif +#if !defined(QUICKCPPLIB_NORETURN) +#if QUICKCPPLIB_HAS_CPP_ATTRIBUTE(noreturn) +#define QUICKCPPLIB_NORETURN [[noreturn]] +#elif defined(_MSC_VER) +#define QUICKCPPLIB_NORETURN __declspec(noreturn) +#elif defined(__GNUC__) +#define QUICKCPPLIB_NORETURN __attribute__((__noreturn__)) +#else +#define QUICKCPPLIB_NORETURN +#endif +#endif +#ifndef QUICKCPPLIB_NODISCARD +#if 0L || (_HAS_CXX17 && _MSC_VER >= 1911 /* VS2017.3 */) +#define QUICKCPPLIB_NODISCARD [[nodiscard]] +#endif +#endif +#ifndef QUICKCPPLIB_NODISCARD +#if QUICKCPPLIB_HAS_CPP_ATTRIBUTE(nodiscard) +#define QUICKCPPLIB_NODISCARD [[nodiscard]] +#elif defined(__clang__) // deliberately not GCC +#define QUICKCPPLIB_NODISCARD __attribute__((warn_unused_result)) +#elif defined(_MSC_VER) +// _Must_inspect_result_ expands into this +#define QUICKCPPLIB_NODISCARD __declspec("SAL_name" "(" "\"_Must_inspect_result_\"" "," "\"\"" "," "\"2\"" ")") __declspec("SAL_begin") __declspec("SAL_post") __declspec("SAL_mustInspect") __declspec("SAL_post") __declspec("SAL_checkReturn") __declspec("SAL_end") +#endif +#endif +#ifndef QUICKCPPLIB_NODISCARD +#define QUICKCPPLIB_NODISCARD +#endif +#ifndef QUICKCPPLIB_SYMBOL_VISIBLE +#if defined(_MSC_VER) +#define QUICKCPPLIB_SYMBOL_VISIBLE +#elif defined(__GNUC__) +#define QUICKCPPLIB_SYMBOL_VISIBLE __attribute__((visibility("default"))) +#else +#define QUICKCPPLIB_SYMBOL_VISIBLE +#endif +#endif +#ifndef QUICKCPPLIB_SYMBOL_EXPORT +#if defined(_MSC_VER) +#define QUICKCPPLIB_SYMBOL_EXPORT __declspec(dllexport) +#elif defined(__GNUC__) +#define QUICKCPPLIB_SYMBOL_EXPORT __attribute__((visibility("default"))) +#else +#define QUICKCPPLIB_SYMBOL_EXPORT +#endif +#endif +#ifndef QUICKCPPLIB_SYMBOL_IMPORT +#if defined(_MSC_VER) +#define QUICKCPPLIB_SYMBOL_IMPORT __declspec(dllimport) +#elif defined(__GNUC__) +#define QUICKCPPLIB_SYMBOL_IMPORT +#else +#define QUICKCPPLIB_SYMBOL_IMPORT +#endif +#endif +#ifndef QUICKCPPLIB_THREAD_LOCAL +#if _MSC_VER >= 1800 +#define QUICKCPPLIB_THREAD_LOCAL_IS_CXX11 1 +#elif __cplusplus >= 201103L +#if __GNUC__ >= 5 && !defined(__clang__) +#define QUICKCPPLIB_THREAD_LOCAL_IS_CXX11 1 +#elif defined(__has_feature) +#if __has_feature(cxx_thread_local) +#define QUICKCPPLIB_THREAD_LOCAL_IS_CXX11 1 +#endif +#endif +#endif +#ifdef QUICKCPPLIB_THREAD_LOCAL_IS_CXX11 +#define QUICKCPPLIB_THREAD_LOCAL thread_local +#endif +#ifndef QUICKCPPLIB_THREAD_LOCAL +#if defined(_MSC_VER) +#define QUICKCPPLIB_THREAD_LOCAL __declspec(thread) +#elif defined(__GNUC__) +#define QUICKCPPLIB_THREAD_LOCAL __thread +#else +#error Unknown compiler, cannot set QUICKCPPLIB_THREAD_LOCAL +#endif +#endif +#endif +/* MSVC capable preprocessor macro overloading +(C) 2014-2017 Niall Douglas (3 commits) +File Created: Aug 2014 + + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License in the accompanying file +Licence.txt or at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + + +Distributed under the Boost Software License, Version 1.0. + (See accompanying file Licence.txt or copy at + http://www.boost.org/LICENSE_1_0.txt) +*/ +#ifndef QUICKCPPLIB_PREPROCESSOR_MACRO_OVERLOAD_H +#define QUICKCPPLIB_PREPROCESSOR_MACRO_OVERLOAD_H +#define QUICKCPPLIB_GLUE(x, y) x y +#define QUICKCPPLIB_RETURN_ARG_COUNT(_1_, _2_, _3_, _4_, _5_, _6_, _7_, _8_, count, ...) count +#define QUICKCPPLIB_EXPAND_ARGS(args) QUICKCPPLIB_RETURN_ARG_COUNT args +#define QUICKCPPLIB_COUNT_ARGS_MAX8(...) QUICKCPPLIB_EXPAND_ARGS((__VA_ARGS__, 8, 7, 6, 5, 4, 3, 2, 1, 0)) +#define QUICKCPPLIB_OVERLOAD_MACRO2(name, count) name##count +#define QUICKCPPLIB_OVERLOAD_MACRO1(name, count) QUICKCPPLIB_OVERLOAD_MACRO2(name, count) +#define QUICKCPPLIB_OVERLOAD_MACRO(name, count) QUICKCPPLIB_OVERLOAD_MACRO1(name, count) +#define QUICKCPPLIB_CALL_OVERLOAD(name, ...) QUICKCPPLIB_GLUE(QUICKCPPLIB_OVERLOAD_MACRO(name, QUICKCPPLIB_COUNT_ARGS_MAX8(__VA_ARGS__)), (__VA_ARGS__)) +#define QUICKCPPLIB_GLUE_(x, y) x y +#define QUICKCPPLIB_RETURN_ARG_COUNT_(_1_, _2_, _3_, _4_, _5_, _6_, _7_, _8_, count, ...) count +#define QUICKCPPLIB_EXPAND_ARGS_(args) QUICKCPPLIB_RETURN_ARG_COUNT_ args +#define QUICKCPPLIB_COUNT_ARGS_MAX8_(...) QUICKCPPLIB_EXPAND_ARGS_((__VA_ARGS__, 8, 7, 6, 5, 4, 3, 2, 1, 0)) +#define QUICKCPPLIB_OVERLOAD_MACRO2_(name, count) name##count +#define QUICKCPPLIB_OVERLOAD_MACRO1_(name, count) QUICKCPPLIB_OVERLOAD_MACRO2_(name, count) +#define QUICKCPPLIB_OVERLOAD_MACRO_(name, count) QUICKCPPLIB_OVERLOAD_MACRO1_(name, count) +#define QUICKCPPLIB_CALL_OVERLOAD_(name, ...) QUICKCPPLIB_GLUE_(QUICKCPPLIB_OVERLOAD_MACRO_(name, QUICKCPPLIB_COUNT_ARGS_MAX8_(__VA_ARGS__)), (__VA_ARGS__)) +#endif +#if defined(__cpp_concepts) && !defined(QUICKCPPLIB_DISABLE_CONCEPTS_SUPPORT) +#define QUICKCPPLIB_TREQUIRES_EXPAND8(a, b, c, d, e, f, g, h) a &&QUICKCPPLIB_TREQUIRES_EXPAND7(b, c, d, e, f, g, h) +#define QUICKCPPLIB_TREQUIRES_EXPAND7(a, b, c, d, e, f, g) a &&QUICKCPPLIB_TREQUIRES_EXPAND6(b, c, d, e, f, g) +#define QUICKCPPLIB_TREQUIRES_EXPAND6(a, b, c, d, e, f) a &&QUICKCPPLIB_TREQUIRES_EXPAND5(b, c, d, e, f) +#define QUICKCPPLIB_TREQUIRES_EXPAND5(a, b, c, d, e) a &&QUICKCPPLIB_TREQUIRES_EXPAND4(b, c, d, e) +#define QUICKCPPLIB_TREQUIRES_EXPAND4(a, b, c, d) a &&QUICKCPPLIB_TREQUIRES_EXPAND3(b, c, d) +#define QUICKCPPLIB_TREQUIRES_EXPAND3(a, b, c) a &&QUICKCPPLIB_TREQUIRES_EXPAND2(b, c) +#define QUICKCPPLIB_TREQUIRES_EXPAND2(a, b) a &&QUICKCPPLIB_TREQUIRES_EXPAND1(b) +#define QUICKCPPLIB_TREQUIRES_EXPAND1(a) a +//! Expands into a && b && c && ... +#define QUICKCPPLIB_TREQUIRES(...) requires QUICKCPPLIB_CALL_OVERLOAD(QUICKCPPLIB_TREQUIRES_EXPAND, __VA_ARGS__) +#define QUICKCPPLIB_TEMPLATE(...) template <__VA_ARGS__> +#define QUICKCPPLIB_TEXPR(...) requires { (__VA_ARGS__); } +#define QUICKCPPLIB_TPRED(...) (__VA_ARGS__) +#if !defined(_MSC_VER) || _MSC_FULL_VER >= 192400000 // VS 2019 16.3 is broken here +#define QUICKCPPLIB_REQUIRES(...) requires(__VA_ARGS__) +#else +#define QUICKCPPLIB_REQUIRES(...) +#endif +#else +#define QUICKCPPLIB_TEMPLATE(...) template <__VA_ARGS__ +#define QUICKCPPLIB_TREQUIRES(...) , __VA_ARGS__ > +#define QUICKCPPLIB_TEXPR(...) typename = decltype(__VA_ARGS__) +#ifdef _MSC_VER +// MSVC gives an error if every specialisation of a template is always ill-formed, so +// the more powerful SFINAE form below causes pukeage :( +#define QUICKCPPLIB_TPRED(...) typename = typename std::enable_if<(__VA_ARGS__)>::type +#else +#define QUICKCPPLIB_TPRED(...) typename std::enable_if<(__VA_ARGS__), bool>::type = true +#endif +#define QUICKCPPLIB_REQUIRES(...) +#endif +#endif +#ifndef __cpp_variadic_templates +#error Outcome needs variadic template support in the compiler +#endif +#if __cpp_constexpr < 201304 && _MSC_FULL_VER < 191100000 +#error Outcome needs constexpr (C++ 14) support in the compiler +#endif +#ifndef __cpp_variable_templates +#error Outcome needs variable template support in the compiler +#endif +#if !defined(__clang__) && defined(__GNUC__) && __GNUC__ < 6 +#error Due to a bug in nested template variables parsing, Outcome does not work on GCCs earlier than v6. +#endif +#ifndef OUTCOME_SYMBOL_VISIBLE +#define OUTCOME_SYMBOL_VISIBLE QUICKCPPLIB_SYMBOL_VISIBLE +#endif +#ifndef OUTCOME_FORCEINLINE +#define OUTCOME_FORCEINLINE QUICKCPPLIB_FORCEINLINE +#endif +#ifndef OUTCOME_NODISCARD +#define OUTCOME_NODISCARD QUICKCPPLIB_NODISCARD +#endif +#ifndef OUTCOME_THREAD_LOCAL +#define OUTCOME_THREAD_LOCAL QUICKCPPLIB_THREAD_LOCAL +#endif +#ifndef OUTCOME_TEMPLATE +#define OUTCOME_TEMPLATE(...) QUICKCPPLIB_TEMPLATE(__VA_ARGS__) +#endif +#ifndef OUTCOME_TREQUIRES +#define OUTCOME_TREQUIRES(...) QUICKCPPLIB_TREQUIRES(__VA_ARGS__) +#endif +#ifndef OUTCOME_TEXPR +#define OUTCOME_TEXPR(...) QUICKCPPLIB_TEXPR(__VA_ARGS__) +#endif +#ifndef OUTCOME_TPRED +#define OUTCOME_TPRED(...) QUICKCPPLIB_TPRED(__VA_ARGS__) +#endif +#ifndef OUTCOME_REQUIRES +#define OUTCOME_REQUIRES(...) QUICKCPPLIB_REQUIRES(__VA_ARGS__) +#endif +/* Convenience macros for importing local namespace binds +(C) 2014-2017 Niall Douglas (9 commits) +File Created: Aug 2014 + + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License in the accompanying file +Licence.txt or at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + + +Distributed under the Boost Software License, Version 1.0. + (See accompanying file Licence.txt or copy at + http://www.boost.org/LICENSE_1_0.txt) +*/ +#ifndef QUICKCPPLIB_BIND_IMPORT_HPP +#define QUICKCPPLIB_BIND_IMPORT_HPP +/* 2014-10-9 ned: I lost today figuring out the below. I really hate the C preprocessor now. + * + * Anyway, infinity = 8. It's easy to expand below if needed. + */ +#define QUICKCPPLIB_BIND_STRINGIZE(a) #a +#define QUICKCPPLIB_BIND_STRINGIZE2(a) QUICKCPPLIB_BIND_STRINGIZE(a) +#define QUICKCPPLIB_BIND_NAMESPACE_VERSION8(a, b, c, d, e, f, g, h) a##_##b##_##c##_##d##_##e##_##f##_##g##_##h +#define QUICKCPPLIB_BIND_NAMESPACE_VERSION7(a, b, c, d, e, f, g) a##_##b##_##c##_##d##_##e##_##f##_##g +#define QUICKCPPLIB_BIND_NAMESPACE_VERSION6(a, b, c, d, e, f) a##_##b##_##c##_##d##_##e##_##f +#define QUICKCPPLIB_BIND_NAMESPACE_VERSION5(a, b, c, d, e) a##_##b##_##c##_##d##_##e +#define QUICKCPPLIB_BIND_NAMESPACE_VERSION4(a, b, c, d) a##_##b##_##c##_##d +#define QUICKCPPLIB_BIND_NAMESPACE_VERSION3(a, b, c) a##_##b##_##c +#define QUICKCPPLIB_BIND_NAMESPACE_VERSION2(a, b) a##_##b +#define QUICKCPPLIB_BIND_NAMESPACE_VERSION1(a) a +//! Concatenates each parameter with _ +#define QUICKCPPLIB_BIND_NAMESPACE_VERSION(...) QUICKCPPLIB_CALL_OVERLOAD(QUICKCPPLIB_BIND_NAMESPACE_VERSION, __VA_ARGS__) +#define QUICKCPPLIB_BIND_NAMESPACE_SELECT_2(name, modifier) name +#define QUICKCPPLIB_BIND_NAMESPACE_SELECT2(name, modifier) ::name +#define QUICKCPPLIB_BIND_NAMESPACE_SELECT_1(name) name +#define QUICKCPPLIB_BIND_NAMESPACE_SELECT1(name) ::name +#define QUICKCPPLIB_BIND_NAMESPACE_SELECT_(...) QUICKCPPLIB_CALL_OVERLOAD_(QUICKCPPLIB_BIND_NAMESPACE_SELECT_, __VA_ARGS__) +#define QUICKCPPLIB_BIND_NAMESPACE_SELECT(...) QUICKCPPLIB_CALL_OVERLOAD_(QUICKCPPLIB_BIND_NAMESPACE_SELECT, __VA_ARGS__) +#define QUICKCPPLIB_BIND_NAMESPACE_EXPAND8(a, b, c, d, e, f, g, h) QUICKCPPLIB_BIND_NAMESPACE_SELECT_ a QUICKCPPLIB_BIND_NAMESPACE_SELECT b QUICKCPPLIB_BIND_NAMESPACE_SELECT c QUICKCPPLIB_BIND_NAMESPACE_SELECT d QUICKCPPLIB_BIND_NAMESPACE_SELECT e QUICKCPPLIB_BIND_NAMESPACE_SELECT f QUICKCPPLIB_BIND_NAMESPACE_SELECT g QUICKCPPLIB_BIND_NAMESPACE_SELECT h +#define QUICKCPPLIB_BIND_NAMESPACE_EXPAND7(a, b, c, d, e, f, g) QUICKCPPLIB_BIND_NAMESPACE_SELECT_ a QUICKCPPLIB_BIND_NAMESPACE_SELECT b QUICKCPPLIB_BIND_NAMESPACE_SELECT c QUICKCPPLIB_BIND_NAMESPACE_SELECT d QUICKCPPLIB_BIND_NAMESPACE_SELECT e QUICKCPPLIB_BIND_NAMESPACE_SELECT f QUICKCPPLIB_BIND_NAMESPACE_SELECT g +#define QUICKCPPLIB_BIND_NAMESPACE_EXPAND6(a, b, c, d, e, f) QUICKCPPLIB_BIND_NAMESPACE_SELECT_ a QUICKCPPLIB_BIND_NAMESPACE_SELECT b QUICKCPPLIB_BIND_NAMESPACE_SELECT c QUICKCPPLIB_BIND_NAMESPACE_SELECT d QUICKCPPLIB_BIND_NAMESPACE_SELECT e QUICKCPPLIB_BIND_NAMESPACE_SELECT f +#define QUICKCPPLIB_BIND_NAMESPACE_EXPAND5(a, b, c, d, e) QUICKCPPLIB_BIND_NAMESPACE_SELECT_ a QUICKCPPLIB_BIND_NAMESPACE_SELECT b QUICKCPPLIB_BIND_NAMESPACE_SELECT c QUICKCPPLIB_BIND_NAMESPACE_SELECT d QUICKCPPLIB_BIND_NAMESPACE_SELECT e +#define QUICKCPPLIB_BIND_NAMESPACE_EXPAND4(a, b, c, d) QUICKCPPLIB_BIND_NAMESPACE_SELECT_ a QUICKCPPLIB_BIND_NAMESPACE_SELECT b QUICKCPPLIB_BIND_NAMESPACE_SELECT c QUICKCPPLIB_BIND_NAMESPACE_SELECT d +#define QUICKCPPLIB_BIND_NAMESPACE_EXPAND3(a, b, c) QUICKCPPLIB_BIND_NAMESPACE_SELECT_ a QUICKCPPLIB_BIND_NAMESPACE_SELECT b QUICKCPPLIB_BIND_NAMESPACE_SELECT c +#define QUICKCPPLIB_BIND_NAMESPACE_EXPAND2(a, b) QUICKCPPLIB_BIND_NAMESPACE_SELECT_ a QUICKCPPLIB_BIND_NAMESPACE_SELECT b +#define QUICKCPPLIB_BIND_NAMESPACE_EXPAND1(a) QUICKCPPLIB_BIND_NAMESPACE_SELECT_ a +//! Expands into a::b::c:: ... +#define QUICKCPPLIB_BIND_NAMESPACE(...) QUICKCPPLIB_CALL_OVERLOAD(QUICKCPPLIB_BIND_NAMESPACE_EXPAND, __VA_ARGS__) +#define QUICKCPPLIB_BIND_NAMESPACE_BEGIN_NAMESPACE_SELECT2(name, modifier) modifier namespace name { +#define QUICKCPPLIB_BIND_NAMESPACE_BEGIN_NAMESPACE_SELECT1(name) namespace name { +#define QUICKCPPLIB_BIND_NAMESPACE_BEGIN_NAMESPACE_SELECT(...) QUICKCPPLIB_CALL_OVERLOAD_(QUICKCPPLIB_BIND_NAMESPACE_BEGIN_NAMESPACE_SELECT, __VA_ARGS__) +#define QUICKCPPLIB_BIND_NAMESPACE_BEGIN_EXPAND8(a, b, c, d, e, f, g, h) QUICKCPPLIB_BIND_NAMESPACE_BEGIN_NAMESPACE_SELECT a QUICKCPPLIB_BIND_NAMESPACE_BEGIN_EXPAND7(b, c, d, e, f, g, h) +#define QUICKCPPLIB_BIND_NAMESPACE_BEGIN_EXPAND7(a, b, c, d, e, f, g) QUICKCPPLIB_BIND_NAMESPACE_BEGIN_NAMESPACE_SELECT a QUICKCPPLIB_BIND_NAMESPACE_BEGIN_EXPAND6(b, c, d, e, f, g) +#define QUICKCPPLIB_BIND_NAMESPACE_BEGIN_EXPAND6(a, b, c, d, e, f) QUICKCPPLIB_BIND_NAMESPACE_BEGIN_NAMESPACE_SELECT a QUICKCPPLIB_BIND_NAMESPACE_BEGIN_EXPAND5(b, c, d, e, f) +#define QUICKCPPLIB_BIND_NAMESPACE_BEGIN_EXPAND5(a, b, c, d, e) QUICKCPPLIB_BIND_NAMESPACE_BEGIN_NAMESPACE_SELECT a QUICKCPPLIB_BIND_NAMESPACE_BEGIN_EXPAND4(b, c, d, e) +#define QUICKCPPLIB_BIND_NAMESPACE_BEGIN_EXPAND4(a, b, c, d) QUICKCPPLIB_BIND_NAMESPACE_BEGIN_NAMESPACE_SELECT a QUICKCPPLIB_BIND_NAMESPACE_BEGIN_EXPAND3(b, c, d) +#define QUICKCPPLIB_BIND_NAMESPACE_BEGIN_EXPAND3(a, b, c) QUICKCPPLIB_BIND_NAMESPACE_BEGIN_NAMESPACE_SELECT a QUICKCPPLIB_BIND_NAMESPACE_BEGIN_EXPAND2(b, c) +#define QUICKCPPLIB_BIND_NAMESPACE_BEGIN_EXPAND2(a, b) QUICKCPPLIB_BIND_NAMESPACE_BEGIN_NAMESPACE_SELECT a QUICKCPPLIB_BIND_NAMESPACE_BEGIN_EXPAND1(b) +#define QUICKCPPLIB_BIND_NAMESPACE_BEGIN_EXPAND1(a) QUICKCPPLIB_BIND_NAMESPACE_BEGIN_NAMESPACE_SELECT a +//! Expands into namespace a { namespace b { namespace c ... +#define QUICKCPPLIB_BIND_NAMESPACE_BEGIN(...) QUICKCPPLIB_CALL_OVERLOAD(QUICKCPPLIB_BIND_NAMESPACE_BEGIN_EXPAND, __VA_ARGS__) +#define QUICKCPPLIB_BIND_NAMESPACE_EXPORT_BEGIN_NAMESPACE_SELECT2(name, modifier) modifier namespace name { +#define QUICKCPPLIB_BIND_NAMESPACE_EXPORT_BEGIN_NAMESPACE_SELECT1(name) export namespace name { +#define QUICKCPPLIB_BIND_NAMESPACE_EXPORT_BEGIN_NAMESPACE_SELECT(...) QUICKCPPLIB_CALL_OVERLOAD_(QUICKCPPLIB_BIND_NAMESPACE_EXPORT_BEGIN_NAMESPACE_SELECT, __VA_ARGS__) +#define QUICKCPPLIB_BIND_NAMESPACE_EXPORT_BEGIN_EXPAND8(a, b, c, d, e, f, g, h) QUICKCPPLIB_BIND_NAMESPACE_EXPORT_BEGIN_NAMESPACE_SELECT a QUICKCPPLIB_BIND_NAMESPACE_EXPORT_BEGIN_EXPAND7(b, c, d, e, f, g, h) +#define QUICKCPPLIB_BIND_NAMESPACE_EXPORT_BEGIN_EXPAND7(a, b, c, d, e, f, g) QUICKCPPLIB_BIND_NAMESPACE_EXPORT_BEGIN_NAMESPACE_SELECT a QUICKCPPLIB_BIND_NAMESPACE_EXPORT_BEGIN_EXPAND6(b, c, d, e, f, g) +#define QUICKCPPLIB_BIND_NAMESPACE_EXPORT_BEGIN_EXPAND6(a, b, c, d, e, f) QUICKCPPLIB_BIND_NAMESPACE_EXPORT_BEGIN_NAMESPACE_SELECT a QUICKCPPLIB_BIND_NAMESPACE_EXPORT_BEGIN_EXPAND5(b, c, d, e, f) +#define QUICKCPPLIB_BIND_NAMESPACE_EXPORT_BEGIN_EXPAND5(a, b, c, d, e) QUICKCPPLIB_BIND_NAMESPACE_EXPORT_BEGIN_NAMESPACE_SELECT a QUICKCPPLIB_BIND_NAMESPACE_EXPORT_BEGIN_EXPAND4(b, c, d, e) +#define QUICKCPPLIB_BIND_NAMESPACE_EXPORT_BEGIN_EXPAND4(a, b, c, d) QUICKCPPLIB_BIND_NAMESPACE_EXPORT_BEGIN_NAMESPACE_SELECT a QUICKCPPLIB_BIND_NAMESPACE_EXPORT_BEGIN_EXPAND3(b, c, d) +#define QUICKCPPLIB_BIND_NAMESPACE_EXPORT_BEGIN_EXPAND3(a, b, c) QUICKCPPLIB_BIND_NAMESPACE_EXPORT_BEGIN_NAMESPACE_SELECT a QUICKCPPLIB_BIND_NAMESPACE_EXPORT_BEGIN_EXPAND2(b, c) +#define QUICKCPPLIB_BIND_NAMESPACE_EXPORT_BEGIN_EXPAND2(a, b) QUICKCPPLIB_BIND_NAMESPACE_EXPORT_BEGIN_NAMESPACE_SELECT a QUICKCPPLIB_BIND_NAMESPACE_EXPORT_BEGIN_EXPAND1(b) +#define QUICKCPPLIB_BIND_NAMESPACE_EXPORT_BEGIN_EXPAND1(a) QUICKCPPLIB_BIND_NAMESPACE_EXPORT_BEGIN_NAMESPACE_SELECT a +//! Expands into export namespace a { namespace b { namespace c ... +#define QUICKCPPLIB_BIND_NAMESPACE_EXPORT_BEGIN(...) QUICKCPPLIB_CALL_OVERLOAD(QUICKCPPLIB_BIND_NAMESPACE_EXPORT_BEGIN_EXPAND, __VA_ARGS__) +#define QUICKCPPLIB_BIND_NAMESPACE_END_NAMESPACE_SELECT2(name, modifier) } +#define QUICKCPPLIB_BIND_NAMESPACE_END_NAMESPACE_SELECT1(name) } +#define QUICKCPPLIB_BIND_NAMESPACE_END_NAMESPACE_SELECT(...) QUICKCPPLIB_CALL_OVERLOAD_(QUICKCPPLIB_BIND_NAMESPACE_END_NAMESPACE_SELECT, __VA_ARGS__) +#define QUICKCPPLIB_BIND_NAMESPACE_END_EXPAND8(a, b, c, d, e, f, g, h) QUICKCPPLIB_BIND_NAMESPACE_END_NAMESPACE_SELECT a QUICKCPPLIB_BIND_NAMESPACE_END_EXPAND7(b, c, d, e, f, g, h) +#define QUICKCPPLIB_BIND_NAMESPACE_END_EXPAND7(a, b, c, d, e, f, g) QUICKCPPLIB_BIND_NAMESPACE_END_NAMESPACE_SELECT a QUICKCPPLIB_BIND_NAMESPACE_END_EXPAND6(b, c, d, e, f, g) +#define QUICKCPPLIB_BIND_NAMESPACE_END_EXPAND6(a, b, c, d, e, f) QUICKCPPLIB_BIND_NAMESPACE_END_NAMESPACE_SELECT a QUICKCPPLIB_BIND_NAMESPACE_END_EXPAND5(b, c, d, e, f) +#define QUICKCPPLIB_BIND_NAMESPACE_END_EXPAND5(a, b, c, d, e) QUICKCPPLIB_BIND_NAMESPACE_END_NAMESPACE_SELECT a QUICKCPPLIB_BIND_NAMESPACE_END_EXPAND4(b, c, d, e) +#define QUICKCPPLIB_BIND_NAMESPACE_END_EXPAND4(a, b, c, d) QUICKCPPLIB_BIND_NAMESPACE_END_NAMESPACE_SELECT a QUICKCPPLIB_BIND_NAMESPACE_END_EXPAND3(b, c, d) +#define QUICKCPPLIB_BIND_NAMESPACE_END_EXPAND3(a, b, c) QUICKCPPLIB_BIND_NAMESPACE_END_NAMESPACE_SELECT a QUICKCPPLIB_BIND_NAMESPACE_END_EXPAND2(b, c) +#define QUICKCPPLIB_BIND_NAMESPACE_END_EXPAND2(a, b) QUICKCPPLIB_BIND_NAMESPACE_END_NAMESPACE_SELECT a QUICKCPPLIB_BIND_NAMESPACE_END_EXPAND1(b) +#define QUICKCPPLIB_BIND_NAMESPACE_END_EXPAND1(a) QUICKCPPLIB_BIND_NAMESPACE_END_NAMESPACE_SELECT a +//! Expands into } } ... +#define QUICKCPPLIB_BIND_NAMESPACE_END(...) QUICKCPPLIB_CALL_OVERLOAD(QUICKCPPLIB_BIND_NAMESPACE_END_EXPAND, __VA_ARGS__) +//! Expands into a static const char string array used to mark BindLib compatible namespaces +#define QUICKCPPLIB_BIND_DECLARE(decl, desc) static const char *quickcpplib_out[] = {#decl, desc}; +#endif +#ifndef OUTCOME_ENABLE_LEGACY_SUPPORT_FOR +#define OUTCOME_ENABLE_LEGACY_SUPPORT_FOR 220 // the v2.2 Outcome release +#endif +#if defined(OUTCOME_UNSTABLE_VERSION) +/* UPDATED BY SCRIPT +(C) 2017-2019 Niall Douglas (225 commits) + + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License in the accompanying file +Licence.txt or at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + + +Distributed under the Boost Software License, Version 1.0. + (See accompanying file Licence.txt or copy at + http://www.boost.org/LICENSE_1_0.txt) +*/ +// Note the second line of this file must ALWAYS be the git SHA, third line ALWAYS the git SHA update time +#define OUTCOME_PREVIOUS_COMMIT_REF 35644f5cdeb90f6f29d80714a371098d311cbd47 +#define OUTCOME_PREVIOUS_COMMIT_DATE "2021-02-26 10:38:25 +00:00" +#define OUTCOME_PREVIOUS_COMMIT_UNIQUE 35644f5c +#define OUTCOME_V2 (QUICKCPPLIB_BIND_NAMESPACE_VERSION(outcome_v2, OUTCOME_PREVIOUS_COMMIT_UNIQUE)) +#ifdef _DEBUG +#define OUTCOME_V2_CXX_MODULE_NAME QUICKCPPLIB_BIND_NAMESPACE((QUICKCPPLIB_BIND_NAMESPACE_VERSION(outcome_v2d, OUTCOME_PREVIOUS_COMMIT_UNIQUE))) +#else +#define OUTCOME_V2_CXX_MODULE_NAME QUICKCPPLIB_BIND_NAMESPACE((QUICKCPPLIB_BIND_NAMESPACE_VERSION(outcome_v2, OUTCOME_PREVIOUS_COMMIT_UNIQUE))) +#endif +#else +#define OUTCOME_V2 (QUICKCPPLIB_BIND_NAMESPACE_VERSION(outcome_v2)) +#ifdef _DEBUG +#define OUTCOME_V2_CXX_MODULE_NAME QUICKCPPLIB_BIND_NAMESPACE((QUICKCPPLIB_BIND_NAMESPACE_VERSION(outcome_v2d))) +#else +#define OUTCOME_V2_CXX_MODULE_NAME QUICKCPPLIB_BIND_NAMESPACE((QUICKCPPLIB_BIND_NAMESPACE_VERSION(outcome_v2))) +#endif +#endif +#if defined(GENERATING_OUTCOME_MODULE_INTERFACE) +#define OUTCOME_V2_NAMESPACE QUICKCPPLIB_BIND_NAMESPACE(OUTCOME_V2) +#define OUTCOME_V2_NAMESPACE_BEGIN QUICKCPPLIB_BIND_NAMESPACE_BEGIN(OUTCOME_V2) +#define OUTCOME_V2_NAMESPACE_EXPORT_BEGIN QUICKCPPLIB_BIND_NAMESPACE_EXPORT_BEGIN(OUTCOME_V2) +#define OUTCOME_V2_NAMESPACE_END QUICKCPPLIB_BIND_NAMESPACE_END(OUTCOME_V2) +#else +#define OUTCOME_V2_NAMESPACE QUICKCPPLIB_BIND_NAMESPACE(OUTCOME_V2) +#define OUTCOME_V2_NAMESPACE_BEGIN QUICKCPPLIB_BIND_NAMESPACE_BEGIN(OUTCOME_V2) +#define OUTCOME_V2_NAMESPACE_EXPORT_BEGIN QUICKCPPLIB_BIND_NAMESPACE_BEGIN(OUTCOME_V2) +#define OUTCOME_V2_NAMESPACE_END QUICKCPPLIB_BIND_NAMESPACE_END(OUTCOME_V2) +#endif +#include // for uint32_t etc +#include +#include // for future serialisation +#include // for placement in moves etc +#include +#ifndef OUTCOME_USE_STD_IN_PLACE_TYPE +#if defined(_MSC_VER) && _HAS_CXX17 +#define OUTCOME_USE_STD_IN_PLACE_TYPE 1 // MSVC always has std::in_place_type +#elif __cplusplus >= 201700 +// libstdc++ before GCC 6 doesn't have it, despite claiming C++ 17 support +#ifdef __has_include +#if !__has_include() +#define OUTCOME_USE_STD_IN_PLACE_TYPE 0 // must have it if is present +#endif +#endif +#ifndef OUTCOME_USE_STD_IN_PLACE_TYPE +#define OUTCOME_USE_STD_IN_PLACE_TYPE 1 +#endif +#else +#define OUTCOME_USE_STD_IN_PLACE_TYPE 0 +#endif +#endif +#if OUTCOME_USE_STD_IN_PLACE_TYPE +#include // for in_place_type_t +OUTCOME_V2_NAMESPACE_BEGIN +template using in_place_type_t = std::in_place_type_t; +using std::in_place_type; +OUTCOME_V2_NAMESPACE_END +#else +OUTCOME_V2_NAMESPACE_BEGIN +/*! AWAITING HUGO JSON CONVERSION TOOL +type definition template in_place_type_t. Potential doc page: `in_place_type_t` +*/ +template struct in_place_type_t +{ + explicit in_place_type_t() = default; +}; +/*! AWAITING HUGO JSON CONVERSION TOOL +SIGNATURE NOT RECOGNISED +*/ +template constexpr in_place_type_t in_place_type{}; +OUTCOME_V2_NAMESPACE_END +#endif +#ifndef OUTCOME_TRIVIAL_ABI +#if 0L || __clang_major__ >= 7 +//! Defined to be `[[clang::trivial_abi]]` when on a new enough clang compiler. Usually automatic, can be overriden. +#define OUTCOME_TRIVIAL_ABI [[clang::trivial_abi]] +#else +#define OUTCOME_TRIVIAL_ABI +#endif +#endif +OUTCOME_V2_NAMESPACE_BEGIN +namespace detail +{ + // Test if type is an in_place_type_t + template struct is_in_place_type_t + { + static constexpr bool value = false; + }; + template struct is_in_place_type_t> + { + static constexpr bool value = true; + }; + // Replace void with constructible void_type + struct empty_type + { + }; + struct void_type + { + // We always compare true to another instance of me + constexpr bool operator==(void_type /*unused*/) const noexcept { return true; } + constexpr bool operator!=(void_type /*unused*/) const noexcept { return false; } + }; + template using devoid = std::conditional_t::value, void_type, T>; + template using rebind_type5 = Output; + template + using rebind_type4 = std::conditional_t< // + std::is_volatile::value, // + std::add_volatile_t>>, // + rebind_type5>; + template + using rebind_type3 = std::conditional_t< // + std::is_const::value, // + std::add_const_t>>, // + rebind_type4>; + template + using rebind_type2 = std::conditional_t< // + std::is_lvalue_reference::value, // + std::add_lvalue_reference_t>>, // + rebind_type3>; + template + using rebind_type = std::conditional_t< // + std::is_rvalue_reference::value, // + std::add_rvalue_reference_t>>, // + rebind_type2>; + // static_assert(std::is_same_v, volatile const int &&>, ""); + /* True if type is the same or constructible. Works around a bug where clang + libstdc++ + pukes on std::is_constructible (this bug is fixed upstream). + */ + template struct _is_explicitly_constructible + { + static constexpr bool value = std::is_constructible::value; + }; + template struct _is_explicitly_constructible + { + static constexpr bool value = false; + }; + template <> struct _is_explicitly_constructible + { + static constexpr bool value = false; + }; + template static constexpr bool is_explicitly_constructible = _is_explicitly_constructible::value; + template struct _is_implicitly_constructible + { + static constexpr bool value = std::is_convertible::value; + }; + template struct _is_implicitly_constructible + { + static constexpr bool value = false; + }; + template <> struct _is_implicitly_constructible + { + static constexpr bool value = false; + }; + template static constexpr bool is_implicitly_constructible = _is_implicitly_constructible::value; + template struct _is_nothrow_constructible + { + static constexpr bool value = std::is_nothrow_constructible::value; + }; + template struct _is_nothrow_constructible + { + static constexpr bool value = false; + }; + template <> struct _is_nothrow_constructible + { + static constexpr bool value = false; + }; + template static constexpr bool is_nothrow_constructible = _is_nothrow_constructible::value; + template struct _is_constructible + { + static constexpr bool value = std::is_constructible::value; + }; + template struct _is_constructible + { + static constexpr bool value = false; + }; + template <> struct _is_constructible + { + static constexpr bool value = false; + }; + template static constexpr bool is_constructible = _is_constructible::value; +#ifndef OUTCOME_USE_STD_IS_NOTHROW_SWAPPABLE +#if defined(_MSC_VER) && _HAS_CXX17 +#define OUTCOME_USE_STD_IS_NOTHROW_SWAPPABLE 1 // MSVC always has std::is_nothrow_swappable +#elif __cplusplus >= 201700 +// libstdc++ before GCC 6 doesn't have it, despite claiming C++ 17 support +#ifdef __has_include +#if !__has_include() +#define OUTCOME_USE_STD_IS_NOTHROW_SWAPPABLE 0 +#endif +#endif +#ifndef OUTCOME_USE_STD_IS_NOTHROW_SWAPPABLE +#define OUTCOME_USE_STD_IS_NOTHROW_SWAPPABLE 1 +#endif +#else +#define OUTCOME_USE_STD_IS_NOTHROW_SWAPPABLE 0 +#endif +#endif +// True if type is nothrow swappable +#if !0L && OUTCOME_USE_STD_IS_NOTHROW_SWAPPABLE + template using is_nothrow_swappable = std::is_nothrow_swappable; +#else + template struct is_nothrow_swappable + { + static constexpr bool value = std::is_nothrow_move_constructible::value && std::is_nothrow_move_assignable::value; + }; +#endif +} // namespace detail +OUTCOME_V2_NAMESPACE_END +#ifndef OUTCOME_THROW_EXCEPTION +#ifdef __cpp_exceptions +#define OUTCOME_THROW_EXCEPTION(expr) throw expr +#else +#ifdef __ANDROID__ +#define OUTCOME_DISABLE_EXECINFO +#endif +#ifndef OUTCOME_DISABLE_EXECINFO +#ifdef _WIN32 +/* Implements backtrace() et al from glibc on win64 +(C) 2016-2017 Niall Douglas (4 commits) +File Created: Mar 2016 + + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License in the accompanying file +Licence.txt or at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + + +Distributed under the Boost Software License, Version 1.0. + (See accompanying file Licence.txt or copy at + http://www.boost.org/LICENSE_1_0.txt) +*/ +#ifndef BOOST_BINDLIB_EXECINFO_WIN64_H +#define BOOST_BINDLIB_EXECINFO_WIN64_H +#ifndef _WIN32 +#error Can only be included on Windows +#endif +#include +#include +#ifdef QUICKCPPLIB_EXPORTS +#define EXECINFO_DECL extern __declspec(dllexport) +#else +#if defined(__cplusplus) && (!defined(QUICKCPPLIB_HEADERS_ONLY) || QUICKCPPLIB_HEADERS_ONLY == 1) && !0L +#define EXECINFO_DECL inline +#elif defined(QUICKCPPLIB_DYN_LINK) && !defined(QUICKCPPLIB_STATIC_LINK) +#define EXECINFO_DECL extern __declspec(dllimport) +#else +#define EXECINFO_DECL extern +#endif +#endif +#ifdef __cplusplus +extern "C" { +#endif +//! Fill the array of void * at bt with up to len entries, returning entries filled. +EXECINFO_DECL _Check_return_ size_t backtrace(_Out_writes_(len) void **bt, _In_ size_t len); +//! Returns a malloced block of string representations of the input backtrace. +EXECINFO_DECL _Check_return_ _Ret_writes_maybenull_(len) char **backtrace_symbols(_In_reads_(len) void *const *bt, _In_ size_t len); +// extern void backtrace_symbols_fd(void *const *bt, size_t len, int fd); +#ifdef __cplusplus +} +#if (!defined(QUICKCPPLIB_HEADERS_ONLY) || QUICKCPPLIB_HEADERS_ONLY == 1) && !0L +#define QUICKCPPLIB_INCLUDED_BY_HEADER 1 +/* Implements backtrace() et al from glibc on win64 +(C) 2016-2017 Niall Douglas (14 commits) +File Created: Mar 2016 + + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License in the accompanying file +Licence.txt or at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + + +Distributed under the Boost Software License, Version 1.0. + (See accompanying file Licence.txt or copy at + http://www.boost.org/LICENSE_1_0.txt) +*/ +/* Implements backtrace() et al from glibc on win64 +(C) 2016-2017 Niall Douglas (4 commits) +File Created: Mar 2016 + + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License in the accompanying file +Licence.txt or at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + + +Distributed under the Boost Software License, Version 1.0. + (See accompanying file Licence.txt or copy at + http://www.boost.org/LICENSE_1_0.txt) +*/ +#include +#include // for abort +#include +// To avoid including windows.h, this source has been macro expanded and win32 function shimmed for C++ only +#if defined(__cplusplus) && !defined(__clang__) +namespace win32 +{ + extern _Ret_maybenull_ void *__stdcall LoadLibraryA(_In_ const char *lpLibFileName); + typedef int(__stdcall *GetProcAddress_returntype)(); + extern GetProcAddress_returntype __stdcall GetProcAddress(_In_ void *hModule, _In_ const char *lpProcName); + extern _Success_(return != 0) unsigned short __stdcall RtlCaptureStackBackTrace(_In_ unsigned long FramesToSkip, _In_ unsigned long FramesToCapture, + _Out_writes_to_(FramesToCapture, return ) void **BackTrace, + _Out_opt_ unsigned long *BackTraceHash); + extern _Success_(return != 0) + _When_((cchWideChar == -1) && (cbMultiByte != 0), + _Post_equal_to_(_String_length_(lpMultiByteStr) + + 1)) int __stdcall WideCharToMultiByte(_In_ unsigned int CodePage, _In_ unsigned long dwFlags, const wchar_t *lpWideCharStr, + _In_ int cchWideChar, _Out_writes_bytes_to_opt_(cbMultiByte, return ) char *lpMultiByteStr, + _In_ int cbMultiByte, _In_opt_ const char *lpDefaultChar, _Out_opt_ int *lpUsedDefaultChar); +#pragma comment(lib, "kernel32.lib") +#if (defined(__x86_64__) || defined(_M_X64)) || (defined(__aarch64__) || defined(_M_ARM64)) +#pragma comment(linker, "/alternatename:?LoadLibraryA@win32@@YAPEAXPEBD@Z=LoadLibraryA") +#pragma comment(linker, "/alternatename:?GetProcAddress@win32@@YAP6AHXZPEAXPEBD@Z=GetProcAddress") +#pragma comment(linker, "/alternatename:?RtlCaptureStackBackTrace@win32@@YAGKKPEAPEAXPEAK@Z=RtlCaptureStackBackTrace") +#pragma comment(linker, "/alternatename:?WideCharToMultiByte@win32@@YAHIKPEB_WHPEADHPEBDPEAH@Z=WideCharToMultiByte") +#elif defined(__x86__) || defined(_M_IX86) || defined(__i386__) +#pragma comment(linker, "/alternatename:?LoadLibraryA@win32@@YGPAXPBD@Z=__imp__LoadLibraryA@4") +#pragma comment(linker, "/alternatename:?GetProcAddress@win32@@YGP6GHXZPAXPBD@Z=__imp__GetProcAddress@8") +#pragma comment(linker, "/alternatename:?RtlCaptureStackBackTrace@win32@@YGGKKPAPAXPAK@Z=__imp__RtlCaptureStackBackTrace@16") +#pragma comment(linker, "/alternatename:?WideCharToMultiByte@win32@@YGHIKPB_WHPADHPBDPAH@Z=__imp__WideCharToMultiByte@32") +#elif defined(__arm__) || defined(_M_ARM) +#pragma comment(linker, "/alternatename:?LoadLibraryA@win32@@YAPAXPBD@Z=LoadLibraryA") +#pragma comment(linker, "/alternatename:?GetProcAddress@win32@@YAP6AHXZPAXPBD@Z=GetProcAddress") +#pragma comment(linker, "/alternatename:?RtlCaptureStackBackTrace@win32@@YAGKKPAPAXPAK@Z=RtlCaptureStackBackTrace") +#pragma comment(linker, "/alternatename:?WideCharToMultiByte@win32@@YAHIKPB_WHPADHPBDPAH@Z=WideCharToMultiByte") +#else +#error Unknown architecture +#endif +} // namespace win32 +#else +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#endif +#ifndef NOMINMAX +#define NOMINMAX +#endif +#include +#endif +#ifdef __cplusplus +namespace +{ +#endif + typedef struct _IMAGEHLP_LINE64 + { + unsigned long SizeOfStruct; + void *Key; + unsigned long LineNumber; + wchar_t *FileName; + unsigned long long int Address; + } IMAGEHLP_LINE64, *PIMAGEHLP_LINE64; + typedef int(__stdcall *SymInitialize_t)(_In_ void *hProcess, _In_opt_ const wchar_t *UserSearchPath, _In_ int fInvadeProcess); + typedef int(__stdcall *SymGetLineFromAddr64_t)(_In_ void *hProcess, _In_ unsigned long long int dwAddr, _Out_ unsigned long *pdwDisplacement, + _Out_ PIMAGEHLP_LINE64 Line); + static std::atomic dbghelp_init_lock; +#if defined(__cplusplus) && !defined(__clang__) + static void *dbghelp; +#else +static HMODULE dbghelp; +#endif + static SymInitialize_t SymInitialize; + static SymGetLineFromAddr64_t SymGetLineFromAddr64; + static void load_dbghelp() + { +#if defined(__cplusplus) && !defined(__clang__) + using win32::GetProcAddress; + using win32::LoadLibraryA; +#endif + while(dbghelp_init_lock.exchange(1, std::memory_order_acq_rel)) + ; + if(dbghelp) + { + dbghelp_init_lock.store(0, std::memory_order_release); + return; + } + dbghelp = LoadLibraryA("DBGHELP.DLL"); + if(dbghelp) + { + SymInitialize = (SymInitialize_t) GetProcAddress(dbghelp, "SymInitializeW"); + if(!SymInitialize) + abort(); + if(!SymInitialize((void *) (size_t) -1 /*GetCurrentProcess()*/, NULL, 1)) + abort(); + SymGetLineFromAddr64 = (SymGetLineFromAddr64_t) GetProcAddress(dbghelp, "SymGetLineFromAddrW64"); + if(!SymGetLineFromAddr64) + abort(); + } + dbghelp_init_lock.store(0, std::memory_order_release); + } +#ifdef __cplusplus +} +#endif +#ifdef __cplusplus +extern "C" +{ +#endif + _Check_return_ size_t backtrace(_Out_writes_(len) void **bt, _In_ size_t len) + { +#if defined(__cplusplus) && !defined(__clang__) + using win32::RtlCaptureStackBackTrace; +#endif + return RtlCaptureStackBackTrace(1, (unsigned long) len, bt, NULL); + } +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable : 6385 6386) // MSVC static analyser can't grok this function. clang's analyser gives it thumbs up. +#endif + _Check_return_ _Ret_writes_maybenull_(len) char **backtrace_symbols(_In_reads_(len) void *const *bt, _In_ size_t len) + { +#if defined(__cplusplus) && !defined(__clang__) + using win32::WideCharToMultiByte; +#endif + size_t bytes = (len + 1) * sizeof(void *) + 256, n; + if(!len) + return NULL; + else + { + char **ret = (char **) malloc(bytes); + char *p = (char *) (ret + len + 1), *end = (char *) ret + bytes; + if(!ret) + return NULL; + for(n = 0; n < len + 1; n++) + ret[n] = NULL; + load_dbghelp(); + for(n = 0; n < len; n++) + { + unsigned long displ; + IMAGEHLP_LINE64 ihl; + memset(&ihl, 0, sizeof(ihl)); + ihl.SizeOfStruct = sizeof(IMAGEHLP_LINE64); + int please_realloc = 0; + if(!bt[n]) + { + ret[n] = NULL; + } + else + { + // Keep offset till later + ret[n] = (char *) ((char *) p - (char *) ret); + if(!SymGetLineFromAddr64 || !SymGetLineFromAddr64((void *) (size_t) -1 /*GetCurrentProcess()*/, (size_t) bt[n], &displ, &ihl)) + { + if(n == 0) + { + free(ret); + return NULL; + } + ihl.FileName = (wchar_t *) L"unknown"; + ihl.LineNumber = 0; + } + retry: + if(please_realloc) + { + char **temp = (char **) realloc(ret, bytes + 256); + if(!temp) + { + free(ret); + return NULL; + } + p = (char *) temp + (p - (char *) ret); + ret = temp; + bytes += 256; + end = (char *) ret + bytes; + } + if(ihl.FileName && ihl.FileName[0]) + { + int plen = WideCharToMultiByte(65001 /*CP_UTF8*/, 0, ihl.FileName, -1, p, (int) (end - p), NULL, NULL); + if(!plen) + { + please_realloc = 1; + goto retry; + } + p[plen - 1] = 0; + p += plen - 1; + } + else + { + if(end - p < 16) + { + please_realloc = 1; + goto retry; + } + _ui64toa_s((size_t) bt[n], p, end - p, 16); + p = strchr(p, 0); + } + if(end - p < 16) + { + please_realloc = 1; + goto retry; + } + *p++ = ':'; + _itoa_s(ihl.LineNumber, p, end - p, 10); + p = strchr(p, 0) + 1; + } + } + for(n = 0; n < len; n++) + { + if(ret[n]) + ret[n] = (char *) ret + (size_t) ret[n]; + } + return ret; + } + } +#ifdef _MSC_VER +#pragma warning(pop) +#endif + // extern void backtrace_symbols_fd(void *const *bt, size_t len, int fd); +#ifdef __cplusplus +} +#endif +#undef QUICKCPPLIB_INCLUDED_BY_HEADER +#endif +#endif +#endif +#else +#include +#endif +#endif // OUTCOME_DISABLE_EXECINFO +#include +#include +OUTCOME_V2_NAMESPACE_BEGIN +namespace detail +{ + QUICKCPPLIB_NORETURN inline void do_fatal_exit(const char *expr) + { +#if !defined(OUTCOME_DISABLE_EXECINFO) + void *bt[16]; + size_t btlen = backtrace(bt, sizeof(bt) / sizeof(bt[0])); // NOLINT +#endif + fprintf(stderr, "FATAL: Outcome throws exception %s with exceptions disabled\n", expr); // NOLINT +#if !defined(OUTCOME_DISABLE_EXECINFO) + char **bts = backtrace_symbols(bt, btlen); // NOLINT + if(bts != nullptr) + { + for(size_t n = 0; n < btlen; n++) + { + fprintf(stderr, " %s\n", bts[n]); // NOLINT + } + free(bts); // NOLINT + } +#endif + abort(); + } +} // namespace detail +OUTCOME_V2_NAMESPACE_END +#define OUTCOME_THROW_EXCEPTION(expr) OUTCOME_V2_NAMESPACE::detail::do_fatal_exit(#expr), (void) (expr) +#endif +#endif +#ifndef BOOST_OUTCOME_AUTO_TEST_CASE +#define BOOST_OUTCOME_AUTO_TEST_CASE(a, b) BOOST_AUTO_TEST_CASE(a, b) +#endif +#endif +#define OUTCOME_COROUTINE_SUPPORT_NAMESPACE_BEGIN OUTCOME_V2_NAMESPACE_BEGIN namespace awaitables { +// +#define OUTCOME_COROUTINE_SUPPORT_NAMESPACE_EXPORT_BEGIN OUTCOME_V2_NAMESPACE_EXPORT_BEGIN namespace awaitables { +// +#define OUTCOME_COROUTINE_SUPPORT_NAMESPACE_END } OUTCOME_V2_NAMESPACE_END +#ifdef __cpp_exceptions +/* Tries to convert an exception ptr into its equivalent error code +(C) 2017-2019 Niall Douglas (11 commits) +File Created: July 2017 + + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License in the accompanying file +Licence.txt or at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + + +Distributed under the Boost Software License, Version 1.0. + (See accompanying file Licence.txt or copy at + http://www.boost.org/LICENSE_1_0.txt) +*/ +#ifndef OUTCOME_UTILS_HPP +#define OUTCOME_UTILS_HPP +#include +#include +#include +OUTCOME_V2_NAMESPACE_BEGIN +#ifdef __cpp_exceptions +/*! AWAITING HUGO JSON CONVERSION TOOL +SIGNATURE NOT RECOGNISED +*/ +inline std::error_code error_from_exception(std::exception_ptr &&ep = std::current_exception(), std::error_code not_matched = std::make_error_code(std::errc::resource_unavailable_try_again)) noexcept +{ + if(!ep) + { + return {}; + } + try + { + std::rethrow_exception(ep); + } + catch(const std::invalid_argument & /*unused*/) + { + ep = std::exception_ptr(); + return std::make_error_code(std::errc::invalid_argument); + } + catch(const std::domain_error & /*unused*/) + { + ep = std::exception_ptr(); + return std::make_error_code(std::errc::argument_out_of_domain); + } + catch(const std::length_error & /*unused*/) + { + ep = std::exception_ptr(); + return std::make_error_code(std::errc::argument_list_too_long); + } + catch(const std::out_of_range & /*unused*/) + { + ep = std::exception_ptr(); + return std::make_error_code(std::errc::result_out_of_range); + } + catch(const std::logic_error & /*unused*/) /* base class for this group */ + { + ep = std::exception_ptr(); + return std::make_error_code(std::errc::invalid_argument); + } + catch(const std::system_error &e) /* also catches ios::failure */ + { + ep = std::exception_ptr(); + return e.code(); + } + catch(const std::overflow_error & /*unused*/) + { + ep = std::exception_ptr(); + return std::make_error_code(std::errc::value_too_large); + } + catch(const std::range_error & /*unused*/) + { + ep = std::exception_ptr(); + return std::make_error_code(std::errc::result_out_of_range); + } + catch(const std::runtime_error & /*unused*/) /* base class for this group */ + { + ep = std::exception_ptr(); + return std::make_error_code(std::errc::resource_unavailable_try_again); + } + catch(const std::bad_alloc & /*unused*/) + { + ep = std::exception_ptr(); + return std::make_error_code(std::errc::not_enough_memory); + } + catch(...) + { + } + return not_matched; +} +/*! AWAITING HUGO JSON CONVERSION TOOL +SIGNATURE NOT RECOGNISED +*/ +inline void try_throw_std_exception_from_error(std::error_code ec, const std::string &msg = std::string{}) +{ + if(!ec || (ec.category() != std::generic_category() +#ifndef _WIN32 + && ec.category() != std::system_category() +#endif + )) + { + return; + } + switch(ec.value()) + { + case EINVAL: + throw msg.empty() ? std::invalid_argument("invalid argument") : std::invalid_argument(msg); + case EDOM: + throw msg.empty() ? std::domain_error("domain error") : std::domain_error(msg); + case E2BIG: + throw msg.empty() ? std::length_error("length error") : std::length_error(msg); + case ERANGE: + throw msg.empty() ? std::out_of_range("out of range") : std::out_of_range(msg); + case EOVERFLOW: + throw msg.empty() ? std::overflow_error("overflow error") : std::overflow_error(msg); + case ENOMEM: + throw std::bad_alloc(); + } +} +#endif +OUTCOME_V2_NAMESPACE_END +#endif +OUTCOME_V2_NAMESPACE_BEGIN +namespace awaitables +{ + namespace detail + { + inline bool error_is_set(std::error_code ec) noexcept { return !!ec; } + inline std::error_code error_from_exception(std::exception_ptr &&ep, std::error_code not_matched) noexcept { return OUTCOME_V2_NAMESPACE::error_from_exception(static_cast(ep), not_matched); } + } // namespace detail +} // namespace awaitables +OUTCOME_V2_NAMESPACE_END +#endif +/* Tells C++ coroutines about Outcome's result +(C) 2019-2020 Niall Douglas (12 commits) +File Created: Oct 2019 + + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License in the accompanying file +Licence.txt or at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + + +Distributed under the Boost Software License, Version 1.0. + (See accompanying file Licence.txt or copy at + http://www.boost.org/LICENSE_1_0.txt) +*/ +#ifndef OUTCOME_DETAIL_COROUTINE_SUPPORT_HPP +#define OUTCOME_DETAIL_COROUTINE_SUPPORT_HPP +#include +#include +#if __cpp_impl_coroutine || (defined(_MSC_VER) && __cpp_coroutines) || (defined(__clang__) && __cpp_coroutines) +#ifndef OUTCOME_HAVE_NOOP_COROUTINE +#if defined(__has_builtin) +#if __has_builtin(__builtin_coro_noop) +#define OUTCOME_HAVE_NOOP_COROUTINE 1 +#endif +#endif +#endif +#ifndef OUTCOME_HAVE_NOOP_COROUTINE +#if _MSC_VER >= 1928 +#define OUTCOME_HAVE_NOOP_COROUTINE 1 +#else +#define OUTCOME_HAVE_NOOP_COROUTINE 0 +#endif +#endif +#if __has_include() +#include +OUTCOME_V2_NAMESPACE_BEGIN +namespace awaitables +{ + template using coroutine_handle = std::coroutine_handle; + template using coroutine_traits = std::coroutine_traits; + using std::suspend_always; + using std::suspend_never; +#if OUTCOME_HAVE_NOOP_COROUTINE + using std::noop_coroutine; +#endif +} // namespace awaitables +OUTCOME_V2_NAMESPACE_END +#define OUTCOME_FOUND_COROUTINE_HEADER 1 +#elif __has_include() +#include +OUTCOME_V2_NAMESPACE_BEGIN +namespace awaitables +{ + template using coroutine_handle = std::experimental::coroutine_handle; + template using coroutine_traits = std::experimental::coroutine_traits; + using std::experimental::suspend_always; + using std::experimental::suspend_never; +#if OUTCOME_HAVE_NOOP_COROUTINE + using std::experimental::noop_coroutine; +#endif +} // namespace awaitables +OUTCOME_V2_NAMESPACE_END +#define OUTCOME_FOUND_COROUTINE_HEADER 1 +#endif +#endif +OUTCOME_V2_NAMESPACE_EXPORT_BEGIN +namespace awaitables +{ + namespace detail + { + struct error_type_not_found + { + }; + struct exception_type_not_found + { + }; + template struct type_found + { + using type = T; + }; + template constexpr inline type_found extract_error_type(int /*unused*/) { return {}; } + template constexpr inline type_found extract_error_type(...) { return {}; } + template constexpr inline type_found extract_exception_type(int /*unused*/) { return {}; } + template constexpr inline type_found extract_exception_type(...) { return {}; } + OUTCOME_TEMPLATE(class T, class U) + OUTCOME_TREQUIRES(OUTCOME_TPRED(OUTCOME_V2_NAMESPACE::detail::is_constructible)) + inline bool try_set_error(T &&e, U *result) + { + new(result) U(static_cast(e)); + return true; + } + template inline bool try_set_error(T && /*unused*/, ...) { return false; } + OUTCOME_TEMPLATE(class T, class U) + OUTCOME_TREQUIRES(OUTCOME_TPRED(OUTCOME_V2_NAMESPACE::detail::is_constructible)) + inline void set_or_rethrow(T &e, U *result) { new(result) U(e); } + template inline void set_or_rethrow(T &e, ...) { rethrow_exception(e); } + template class fake_atomic + { + T _v; + public: + constexpr fake_atomic(T v) + : _v(v) + { + } + T load(std::memory_order /*unused*/) { return _v; } + void store(T v, std::memory_order /*unused*/) { _v = v; } + }; +#ifdef OUTCOME_FOUND_COROUTINE_HEADER + template struct outcome_promise_type + { + using container_type = typename Awaitable::container_type; + using result_set_type = std::conditional_t, fake_atomic>; + union + { + OUTCOME_V2_NAMESPACE::detail::empty_type _default{}; + container_type result; + }; + result_set_type result_set{false}; + coroutine_handle<> continuation; + outcome_promise_type() noexcept {} + outcome_promise_type(const outcome_promise_type &) = delete; + outcome_promise_type(outcome_promise_type &&) = delete; + outcome_promise_type &operator=(const outcome_promise_type &) = delete; + outcome_promise_type &operator=(outcome_promise_type &&) = delete; + ~outcome_promise_type() + { + if(result_set.load(std::memory_order_acquire)) + { + result.~container_type(); // could throw + } + } + auto get_return_object() + { + return Awaitable{*this}; // could throw bad_alloc + } + void return_value(container_type &&value) + { + assert(!result_set.load(std::memory_order_acquire)); + if(result_set.load(std::memory_order_acquire)) + { + result.~container_type(); // could throw + } + new(&result) container_type(static_cast(value)); // could throw + result_set.store(true, std::memory_order_release); + } + void return_value(const container_type &value) + { + assert(!result_set.load(std::memory_order_acquire)); + if(result_set.load(std::memory_order_acquire)) + { + result.~container_type(); // could throw + } + new(&result) container_type(value); // could throw + result_set.store(true, std::memory_order_release); + } + void unhandled_exception() + { + assert(!result_set.load(std::memory_order_acquire)); + if(result_set.load(std::memory_order_acquire)) + { + result.~container_type(); + } +#ifdef __cpp_exceptions + auto e = std::current_exception(); + auto ec = detail::error_from_exception(static_cast(e), {}); + // Try to set error code first + if(!detail::error_is_set(ec) || !detail::try_set_error(static_cast(ec), &result)) + { + detail::set_or_rethrow(e, &result); // could throw + } +#else + std::terminate(); +#endif + result_set.store(true, std::memory_order_release); + } + auto initial_suspend() noexcept + { + struct awaiter + { + bool await_ready() noexcept { return !suspend_initial; } + void await_resume() noexcept {} + void await_suspend(coroutine_handle<> /*unused*/) noexcept {} + }; + return awaiter{}; + } + auto final_suspend() noexcept + { + struct awaiter + { + bool await_ready() noexcept { return false; } + void await_resume() noexcept {} +#if OUTCOME_HAVE_NOOP_COROUTINE + coroutine_handle<> await_suspend(coroutine_handle self) noexcept + { + return self.promise().continuation ? self.promise().continuation : noop_coroutine(); + } +#else + void await_suspend(coroutine_handle self) + { + if(self.promise().continuation) + { + return self.promise().continuation.resume(); + } + } +#endif + }; + return awaiter{}; + } + }; + template struct outcome_promise_type + { + using container_type = void; + using result_set_type = std::conditional_t, fake_atomic>; + result_set_type result_set{false}; + coroutine_handle<> continuation; + outcome_promise_type() {} + outcome_promise_type(const outcome_promise_type &) = delete; + outcome_promise_type(outcome_promise_type &&) = delete; + outcome_promise_type &operator=(const outcome_promise_type &) = delete; + outcome_promise_type &operator=(outcome_promise_type &&) = delete; + ~outcome_promise_type() = default; + auto get_return_object() + { + return Awaitable{*this}; // could throw bad_alloc + } + void return_void() noexcept + { + assert(!result_set.load(std::memory_order_acquire)); + result_set.store(true, std::memory_order_release); + } + void unhandled_exception() + { + assert(!result_set.load(std::memory_order_acquire)); + std::rethrow_exception(std::current_exception()); // throws + } + auto initial_suspend() noexcept + { + struct awaiter + { + bool await_ready() noexcept { return !suspend_initial; } + void await_resume() noexcept {} + void await_suspend(coroutine_handle<> /*unused*/) noexcept {} + }; + return awaiter{}; + } + auto final_suspend() noexcept + { + struct awaiter + { + bool await_ready() noexcept { return false; } + void await_resume() noexcept {} +#if OUTCOME_HAVE_NOOP_COROUTINE + coroutine_handle<> await_suspend(coroutine_handle self) noexcept + { + return self.promise().continuation ? self.promise().continuation : noop_coroutine(); + } +#else + void await_suspend(coroutine_handle self) + { + if(self.promise().continuation) + { + return self.promise().continuation.resume(); + } + } +#endif + }; + return awaiter{}; + } + }; + template + constexpr inline auto move_result_from_promise_if_not_void(outcome_promise_type &p) + { + return static_cast(p.result); + } + template + constexpr inline void move_result_from_promise_if_not_void(outcome_promise_type & /*unused*/) + { + } + template struct OUTCOME_NODISCARD awaitable + { + using container_type = Cont; + using promise_type = outcome_promise_type::value>; + coroutine_handle _h; + awaitable(awaitable &&o) noexcept + : _h(static_cast &&>(o._h)) + { + o._h = nullptr; + } + awaitable(const awaitable &o) = delete; + awaitable &operator=(awaitable &&) = delete; // as per P1056 + awaitable &operator=(const awaitable &) = delete; + ~awaitable() + { + if(_h) + { + _h.destroy(); + } + } + explicit awaitable(promise_type &p) // could throw + : _h(coroutine_handle::from_promise(p)) + { + } + bool await_ready() noexcept { return _h.promise().result_set.load(std::memory_order_acquire); } + container_type await_resume() + { + assert(_h.promise().result_set.load(std::memory_order_acquire)); + if(!_h.promise().result_set.load(std::memory_order_acquire)) + { + std::terminate(); + } + return detail::move_result_from_promise_if_not_void(_h.promise()); + } +#if OUTCOME_HAVE_NOOP_COROUTINE + coroutine_handle<> await_suspend(coroutine_handle<> cont) noexcept + { + _h.promise().continuation = cont; + return _h; + } +#else + void await_suspend(coroutine_handle<> cont) + { + _h.promise().continuation = cont; + _h.resume(); + } +#endif + }; +#endif + } // namespace detail +} // namespace awaitables +OUTCOME_V2_NAMESPACE_END +#endif +#ifdef OUTCOME_FOUND_COROUTINE_HEADER +OUTCOME_V2_NAMESPACE_EXPORT_BEGIN namespace awaitables { +/*! AWAITING HUGO JSON CONVERSION TOOL +SIGNATURE NOT RECOGNISED +*/ +template using eager = OUTCOME_V2_NAMESPACE::awaitables::detail::awaitable; +/*! AWAITING HUGO JSON CONVERSION TOOL +SIGNATURE NOT RECOGNISED +*/ +template using atomic_eager = OUTCOME_V2_NAMESPACE::awaitables::detail::awaitable; +/*! AWAITING HUGO JSON CONVERSION TOOL +SIGNATURE NOT RECOGNISED +*/ +template using lazy = OUTCOME_V2_NAMESPACE::awaitables::detail::awaitable; +/*! AWAITING HUGO JSON CONVERSION TOOL +SIGNATURE NOT RECOGNISED +*/ +template using atomic_lazy = OUTCOME_V2_NAMESPACE::awaitables::detail::awaitable; +} OUTCOME_V2_NAMESPACE_END +#endif +#undef OUTCOME_COROUTINE_SUPPORT_NAMESPACE_BEGIN +#undef OUTCOME_COROUTINE_SUPPORT_NAMESPACE_EXPORT_BEGIN +#undef OUTCOME_COROUTINE_SUPPORT_NAMESPACE_END +#endif +/* iostream specialisations for result and outcome +(C) 2017-2019 Niall Douglas (21 commits) +File Created: July 2017 + + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License in the accompanying file +Licence.txt or at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + + +Distributed under the Boost Software License, Version 1.0. + (See accompanying file Licence.txt or copy at + http://www.boost.org/LICENSE_1_0.txt) +*/ +#ifndef OUTCOME_IOSTREAM_SUPPORT_HPP +#define OUTCOME_IOSTREAM_SUPPORT_HPP +/* A less simple result type +(C) 2017-2019 Niall Douglas (79 commits) +File Created: June 2017 + + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License in the accompanying file +Licence.txt or at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + + +Distributed under the Boost Software License, Version 1.0. + (See accompanying file Licence.txt or copy at + http://www.boost.org/LICENSE_1_0.txt) +*/ +#ifndef OUTCOME_OUTCOME_HPP +#define OUTCOME_OUTCOME_HPP +/* A very simple result type +(C) 2017-2019 Niall Douglas (99 commits) +File Created: June 2017 + + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License in the accompanying file +Licence.txt or at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + + +Distributed under the Boost Software License, Version 1.0. + (See accompanying file Licence.txt or copy at + http://www.boost.org/LICENSE_1_0.txt) +*/ +#ifndef OUTCOME_RESULT_HPP +#define OUTCOME_RESULT_HPP +/* A very simple result type +(C) 2017-2019 Niall Douglas (8 commits) +File Created: June 2017 + + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License in the accompanying file +Licence.txt or at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + + +Distributed under the Boost Software License, Version 1.0. + (See accompanying file Licence.txt or copy at + http://www.boost.org/LICENSE_1_0.txt) +*/ +#ifndef OUTCOME_STD_RESULT_HPP +#define OUTCOME_STD_RESULT_HPP +/* A very simple result type +(C) 2017-2021 Niall Douglas (14 commits) +File Created: June 2017 + + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License in the accompanying file +Licence.txt or at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + + +Distributed under the Boost Software License, Version 1.0. + (See accompanying file Licence.txt or copy at + http://www.boost.org/LICENSE_1_0.txt) +*/ +#ifndef OUTCOME_BASIC_RESULT_HPP +#define OUTCOME_BASIC_RESULT_HPP +/* Says how to convert value, error and exception types +(C) 2017-2019 Niall Douglas (12 commits) +File Created: Nov 2017 + + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License in the accompanying file +Licence.txt or at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + + +Distributed under the Boost Software License, Version 1.0. + (See accompanying file Licence.txt or copy at + http://www.boost.org/LICENSE_1_0.txt) +*/ +#ifndef OUTCOME_CONVERT_HPP +#define OUTCOME_CONVERT_HPP +/* Storage for a very simple basic_result type +(C) 2017-2019 Niall Douglas (6 commits) +File Created: Oct 2017 + + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License in the accompanying file +Licence.txt or at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + + +Distributed under the Boost Software License, Version 1.0. + (See accompanying file Licence.txt or copy at + http://www.boost.org/LICENSE_1_0.txt) +*/ +#ifndef OUTCOME_BASIC_RESULT_STORAGE_HPP +#define OUTCOME_BASIC_RESULT_STORAGE_HPP +/* Type sugar for success and failure +(C) 2017-2019 Niall Douglas (25 commits) +File Created: July 2017 + + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License in the accompanying file +Licence.txt or at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + + +Distributed under the Boost Software License, Version 1.0. + (See accompanying file Licence.txt or copy at + http://www.boost.org/LICENSE_1_0.txt) +*/ +#ifndef OUTCOME_SUCCESS_FAILURE_HPP +#define OUTCOME_SUCCESS_FAILURE_HPP +OUTCOME_V2_NAMESPACE_BEGIN +/*! AWAITING HUGO JSON CONVERSION TOOL +type definition template success_type. Potential doc page: `success_type` +*/ +template struct OUTCOME_NODISCARD success_type +{ + using value_type = T; +private: + value_type _value; + uint16_t _spare_storage{0}; +public: + success_type() = default; + success_type(const success_type &) = default; + success_type(success_type &&) = default; // NOLINT + success_type &operator=(const success_type &) = default; + success_type &operator=(success_type &&) = default; // NOLINT + ~success_type() = default; + OUTCOME_TEMPLATE(class U) + OUTCOME_TREQUIRES(OUTCOME_TPRED(!std::is_same>::value)) + constexpr explicit success_type(U &&v, uint16_t spare_storage = 0) + : _value(static_cast(v)) // NOLINT + , _spare_storage(spare_storage) + { + } + constexpr value_type &value() & { return _value; } + constexpr const value_type &value() const & { return _value; } + constexpr value_type &&value() && { return static_cast(_value); } + constexpr const value_type &&value() const && { return static_cast(_value); } + constexpr uint16_t spare_storage() const { return _spare_storage; } +}; +template <> struct OUTCOME_NODISCARD success_type +{ + using value_type = void; + constexpr uint16_t spare_storage() const { return 0; } +}; +/*! Returns type sugar for implicitly constructing a `basic_result` with a successful state, +default constructing `T` if necessary. +*/ +inline constexpr success_type success() noexcept +{ + return success_type{}; +} +/*! Returns type sugar for implicitly constructing a `basic_result` with a successful state. +\effects Copies or moves the successful state supplied into the returned type sugar. +*/ +template inline constexpr success_type> success(T &&v, uint16_t spare_storage = 0) +{ + return success_type>{static_cast(v), spare_storage}; +} +/*! AWAITING HUGO JSON CONVERSION TOOL +type definition template failure_type. Potential doc page: `failure_type` +*/ +template struct OUTCOME_NODISCARD failure_type +{ + using error_type = EC; + using exception_type = E; +private: + error_type _error; + exception_type _exception; + bool _have_error{false}, _have_exception{false}; + uint16_t _spare_storage{0}; + struct error_init_tag + { + }; + struct exception_init_tag + { + }; +public: + failure_type() = default; + failure_type(const failure_type &) = default; + failure_type(failure_type &&) = default; // NOLINT + failure_type &operator=(const failure_type &) = default; + failure_type &operator=(failure_type &&) = default; // NOLINT + ~failure_type() = default; + template + constexpr explicit failure_type(U &&u, V &&v, uint16_t spare_storage = 0) + : _error(static_cast(u)) + , _exception(static_cast(v)) + , _have_error(true) + , _have_exception(true) + , _spare_storage(spare_storage) + { + } + template + constexpr explicit failure_type(in_place_type_t /*unused*/, U &&u, uint16_t spare_storage = 0, error_init_tag /*unused*/ = error_init_tag()) + : _error(static_cast(u)) + , _exception() + , _have_error(true) + , _spare_storage(spare_storage) + { + } + template + constexpr explicit failure_type(in_place_type_t /*unused*/, U &&u, uint16_t spare_storage = 0, + exception_init_tag /*unused*/ = exception_init_tag()) + : _error() + , _exception(static_cast(u)) + , _have_exception(true) + , _spare_storage(spare_storage) + { + } + constexpr bool has_error() const { return _have_error; } + constexpr bool has_exception() const { return _have_exception; } + constexpr error_type &error() & { return _error; } + constexpr const error_type &error() const & { return _error; } + constexpr error_type &&error() && { return static_cast(_error); } + constexpr const error_type &&error() const && { return static_cast(_error); } + constexpr exception_type &exception() & { return _exception; } + constexpr const exception_type &exception() const & { return _exception; } + constexpr exception_type &&exception() && { return static_cast(_exception); } + constexpr const exception_type &&exception() const && { return static_cast(_exception); } + constexpr uint16_t spare_storage() const { return _spare_storage; } +}; +template struct OUTCOME_NODISCARD failure_type +{ + using error_type = EC; + using exception_type = void; +private: + error_type _error; + uint16_t _spare_storage{0}; +public: + failure_type() = default; + failure_type(const failure_type &) = default; + failure_type(failure_type &&) = default; // NOLINT + failure_type &operator=(const failure_type &) = default; + failure_type &operator=(failure_type &&) = default; // NOLINT + ~failure_type() = default; + OUTCOME_TEMPLATE(class U) + OUTCOME_TREQUIRES(OUTCOME_TPRED(!std::is_same>::value)) + constexpr explicit failure_type(U &&u, uint16_t spare_storage = 0) + : _error(static_cast(u)) // NOLINT + , _spare_storage(spare_storage) + { + } + constexpr error_type &error() & { return _error; } + constexpr const error_type &error() const & { return _error; } + constexpr error_type &&error() && { return static_cast(_error); } + constexpr const error_type &&error() const && { return static_cast(_error); } + constexpr uint16_t spare_storage() const { return _spare_storage; } +}; +template struct OUTCOME_NODISCARD failure_type +{ + using error_type = void; + using exception_type = E; +private: + exception_type _exception; + uint16_t _spare_storage{0}; +public: + failure_type() = default; + failure_type(const failure_type &) = default; + failure_type(failure_type &&) = default; // NOLINT + failure_type &operator=(const failure_type &) = default; + failure_type &operator=(failure_type &&) = default; // NOLINT + ~failure_type() = default; + OUTCOME_TEMPLATE(class V) + OUTCOME_TREQUIRES(OUTCOME_TPRED(!std::is_same>::value)) + constexpr explicit failure_type(V &&v, uint16_t spare_storage = 0) + : _exception(static_cast(v)) // NOLINT + , _spare_storage(spare_storage) + { + } + constexpr exception_type &exception() & { return _exception; } + constexpr const exception_type &exception() const & { return _exception; } + constexpr exception_type &&exception() && { return static_cast(_exception); } + constexpr const exception_type &&exception() const && { return static_cast(_exception); } + constexpr uint16_t spare_storage() const { return _spare_storage; } +}; +/*! AWAITING HUGO JSON CONVERSION TOOL +SIGNATURE NOT RECOGNISED +*/ +template inline constexpr failure_type> failure(EC &&v, uint16_t spare_storage = 0) +{ + return failure_type>{static_cast(v), spare_storage}; +} +/*! AWAITING HUGO JSON CONVERSION TOOL +SIGNATURE NOT RECOGNISED +*/ +template inline constexpr failure_type, std::decay_t> failure(EC &&v, E &&w, uint16_t spare_storage = 0) +{ + return failure_type, std::decay_t>{static_cast(v), static_cast(w), spare_storage}; +} +namespace detail +{ + template struct is_success_type + { + static constexpr bool value = false; + }; + template struct is_success_type> + { + static constexpr bool value = true; + }; + template struct is_failure_type + { + static constexpr bool value = false; + }; + template struct is_failure_type> + { + static constexpr bool value = true; + }; +} // namespace detail +/*! AWAITING HUGO JSON CONVERSION TOOL +SIGNATURE NOT RECOGNISED +*/ +template static constexpr bool is_success_type = detail::is_success_type>::value; +/*! AWAITING HUGO JSON CONVERSION TOOL +SIGNATURE NOT RECOGNISED +*/ +template static constexpr bool is_failure_type = detail::is_failure_type>::value; +OUTCOME_V2_NAMESPACE_END +#endif +/* Traits for Outcome +(C) 2018-2019 Niall Douglas (8 commits) +File Created: March 2018 + + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License in the accompanying file +Licence.txt or at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + + +Distributed under the Boost Software License, Version 1.0. + (See accompanying file Licence.txt or copy at + http://www.boost.org/LICENSE_1_0.txt) +*/ +#ifndef OUTCOME_TRAIT_HPP +#define OUTCOME_TRAIT_HPP +OUTCOME_V2_NAMESPACE_EXPORT_BEGIN +namespace trait +{ + /*! AWAITING HUGO JSON CONVERSION TOOL +SIGNATURE NOT RECOGNISED +*/ + template // + static constexpr bool type_can_be_used_in_basic_result = // + (!std::is_reference::value // + && !OUTCOME_V2_NAMESPACE::detail::is_in_place_type_t>::value // + && !is_success_type // + && !is_failure_type // + && !std::is_array::value // + && (std::is_void::value || (std::is_object::value // + && std::is_destructible::value)) // + ); + /*! AWAITING HUGO JSON CONVERSION TOOL +type definition is_error_type. Potential doc page: NOT FOUND +*/ + template struct is_move_bitcopying + { + static constexpr bool value = false; + }; + /*! AWAITING HUGO JSON CONVERSION TOOL +type definition is_error_type. Potential doc page: NOT FOUND +*/ + template struct is_error_type + { + static constexpr bool value = false; + }; + /*! AWAITING HUGO JSON CONVERSION TOOL +type definition is_error_type_enum. Potential doc page: NOT FOUND +*/ + template struct is_error_type_enum + { + static constexpr bool value = false; + }; + namespace detail + { + template using devoid = OUTCOME_V2_NAMESPACE::detail::devoid; + template std::add_rvalue_reference_t> declval() noexcept; + // From http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4436.pdf + namespace detector_impl + { + template using void_t = void; + template class Op, class... Args> struct detector + { + static constexpr bool value = false; + using type = Default; + }; + template class Op, class... Args> struct detector>, Op, Args...> + { + static constexpr bool value = true; + using type = Op; + }; + } // namespace detector_impl + template