diff --git a/.github/release-please-manifest.json b/.github/release-please-manifest.json index 6119427..9896626 100644 --- a/.github/release-please-manifest.json +++ b/.github/release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "2.0.15" + ".": "2.0.16" } diff --git a/CHANGELOG.md b/CHANGELOG.md index e19ac7f..903de33 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,12 @@ # Changelog +## [2.0.16](https://github.com/untrustedmodders/plugify-module-cpp/compare/v2.0.15...v2.0.16) (2025-09-10) + + +### Bug Fixes + +* simplify build scripts ([d33fc8e](https://github.com/untrustedmodders/plugify-module-cpp/commit/d33fc8ec60c0c1d06f614602d427d9555fdad343)) + ## [2.0.15](https://github.com/untrustedmodders/plugify-module-cpp/compare/v2.0.14...v2.0.15) (2025-09-09) diff --git a/CMakeLists.txt b/CMakeLists.txt index 0d7ccab..32b0e79 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -8,7 +8,7 @@ endif() file(READ "${CMAKE_CURRENT_SOURCE_DIR}/version.txt" VERSION_FILE_CONTENTS) string(STRIP "${VERSION_FILE_CONTENTS}" VERSION_FILE_CONTENTS) set(CPPLM_VERSION "${VERSION_FILE_CONTENTS}" CACHE STRING "Set version name") -set(CPPLM_PACKAGE "plugify-module-cpp" CACHE STRING "Set package name") +set(CPPLM_PACKAGE "cpp_module" CACHE STRING "Set package name") string(REPLACE "v" "" CPPLM_VERSION "${CPPLM_VERSION}") string(REGEX REPLACE "[.+-]" ";" CPPLM_VERSION_LIST ${CPPLM_VERSION}) list(GET CPPLM_VERSION_LIST 0 CPPLM_VERSION_MAJOR) @@ -44,7 +44,7 @@ set(PLUGIFY_BUILD_SHARED_LIB ON CACHE INTERNAL "") set(PLUGIFY_BUILD_TESTS OFF CACHE INTERNAL "") if(LINUX) set(PLUGIFY_USE_STATIC_STDLIB ON CACHE INTERNAL "") - set(PLUGIFY_USE_ABI0 ON CACHE INTERNAL "") + set(PLUGIFY_USE_ABI0 OFF CACHE INTERNAL "") endif() add_subdirectory(external/plugify) diff --git a/conda/bld.bat b/conda/bld.bat index c729cb7..6d9c4c9 100644 --- a/conda/bld.bat +++ b/conda/bld.bat @@ -3,11 +3,12 @@ REM bld.bat - For Windows builds REM Create the target directories if not exist "%PREFIX%\bin" mkdir "%PREFIX%\bin" -if not exist "%PREFIX%" mkdir "%PREFIX%" -REM Copy the DLL and module file -copy bin\plugify-module-cpp.dll "%PREFIX%\bin\" || exit 1 -copy plugify-module-cpp.pmodule "%PREFIX%\" || exit 1 +REM Copy entire folders into %PREFIX +xcopy bin "%PREFIX%\bin" /E /Y /I + +REM Copy all .pmodule files into %PREFIX% +copy *.pmodule "%PREFIX%\" || exit 1 REM Create activation scripts if not exist "%PREFIX%\etc\conda\activate.d" mkdir "%PREFIX%\etc\conda\activate.d" diff --git a/conda/build.sh b/conda/build.sh index 7741d14..ceb2573 100644 --- a/conda/build.sh +++ b/conda/build.sh @@ -3,17 +3,16 @@ set -ex -# Create the target directories +# Copy entire bin/ folder into $PREFIX/bin/ mkdir -p $PREFIX/bin -mkdir -p $PREFIX +cp -r bin/* $PREFIX/bin/ -# Copy the shared library and module file -cp bin/libplugify-module-cpp.so $PREFIX/bin/ -cp plugify-module-cpp.pmodule $PREFIX/ +# Copy all .pmodule files into $PREFIX/ +cp -r *.pmodule $PREFIX/ -# Set proper permissions -chmod 755 $PREFIX/bin/libplugify-module-cpp.so -chmod 644 $PREFIX/plugify-module-cpp.pmodule +# Fix permissions (recursively for bin, selective for modules) +chmod -R 755 $PREFIX/bin +chmod 644 $PREFIX/*.pmodule # Create activation scripts for proper library path mkdir -p $PREFIX/etc/conda/activate.d diff --git a/external/plugify b/external/plugify index e2ef25f..672952c 160000 --- a/external/plugify +++ b/external/plugify @@ -1 +1 @@ -Subproject commit e2ef25fcd9fc63232829c2ca79425a3b414523f3 +Subproject commit 672952c8932b86f1a53a66d92c54a2ab85fa2815 diff --git a/generator/generator.py b/generator/generator.py index c972ec4..1a52c4c 100644 --- a/generator/generator.py +++ b/generator/generator.py @@ -584,7 +584,15 @@ def main(manifest_path: str, output_dir: str, override: bool): print(f'Output directory does not exist: {output_dir}') return 1 - plugin_name = os.path.basename(manifest_path).rsplit('.', 3)[0] + try: + with open(manifest_path, 'r', encoding='utf-8') as file: + pplugin = json.load(file) + + except Exception as e: + print(f'An error occurred: {e}') + return 1 + + plugin_name = pplugin.get('name', os.path.basename(manifest_path).rsplit('.', 3)[0]) output_path = os.path.join(output_dir, 'pps', f'{plugin_name}.hpp') os.makedirs(os.path.dirname(output_path), exist_ok=True) @@ -593,9 +601,6 @@ def main(manifest_path: str, output_dir: str, override: bool): return 1 try: - with open(manifest_path, 'r', encoding='utf-8') as file: - pplugin = json.load(file) - content = generate_header(plugin_name, pplugin) with open(output_path, 'w', encoding='utf-8') as file: diff --git a/include/plg/enum.hpp b/include/plg/enum.hpp index 5a2b373..84f6d23 100644 --- a/include/plg/enum.hpp +++ b/include/plg/enum.hpp @@ -55,7 +55,7 @@ namespace plg { template constexpr auto value(std::size_t v) { - return static_cast(ENUM_MIN_VALUE + v); + return static_cast(ENUM_MIN_VALUE + static_cast(v)); } template diff --git a/include/plg/expected.hpp b/include/plg/expected.hpp new file mode 100644 index 0000000..d6bc137 --- /dev/null +++ b/include/plg/expected.hpp @@ -0,0 +1,1430 @@ +#pragma once + +#include "plg/macro.hpp" + +#ifdef __cpp_lib_expected +#include +namespace plg { + using std::expected; + using std::unexpected; +} +#else +#include +#include +#include +#include +#include +#include +#include +#include + +// from https://github.com/RishabhRD +namespace plg { + template + class unexpected { + public: + using value_type = E; + constexpr unexpected(unexpected const&) = default; + constexpr unexpected(unexpected&&) = default;// NOLINT + constexpr auto operator=(unexpected const&) -> unexpected& = default; + constexpr auto operator=(unexpected&&) -> unexpected& = default;// NOLINT + ~unexpected() = default; + + template + requires std::constructible_from + constexpr explicit unexpected(std::in_place_t /*unused*/, Args&&... args) + : val(std::forward(args)...) {} + + template + requires std::constructible_from&, Args...> + constexpr explicit unexpected(std::in_place_t /*unused*/, + std::initializer_list i_list, + Args&&... args) + : val(i_list, std::forward(args)...) {} + + template + requires(!std::same_as, unexpected>) && + (!std::same_as, std::in_place_t>) && + std::constructible_from + constexpr explicit unexpected(Err&& err)// NOLINT + : val(std::forward(err)) {} + + constexpr auto value() const& noexcept -> E const& { return val; } + constexpr auto value() & noexcept -> E& { return val; } + constexpr auto value() const&& noexcept -> E const&& { + return std::move(val); + } + constexpr auto value() && noexcept -> E&& { return std::move(val); } + + constexpr void swap(unexpected& other) noexcept( + std::is_nothrow_swappable_v) + requires(std::is_swappable_v) + { + using std::swap; + swap(val, other.val); + } + + template + requires(requires(E const& x, E2 const& y) { + { x == y } -> std::convertible_to; + }) + friend constexpr auto operator==(unexpected const& x, unexpected const& y) -> bool { + return x.value() == y.value(); + } + + friend constexpr void swap(unexpected& x, unexpected& y) noexcept(noexcept(x.swap(y))) + requires(std::is_swappable_v) + { + x.swap(y); + } + + private: + E val; + }; + + template + unexpected(E) -> unexpected; + + template + class bad_expected_access; + + template<> + class bad_expected_access : public std::exception { + protected: + bad_expected_access() noexcept = default; + bad_expected_access(bad_expected_access const&) = default; + bad_expected_access(bad_expected_access&&) = default; + auto operator=(bad_expected_access const&) -> bad_expected_access& = default; + auto operator=(bad_expected_access&&) -> bad_expected_access& = default; + ~bad_expected_access() override = default; + + public: + auto what() const noexcept -> char const* override {// NOLINT + return "bad expected access"; + } + }; + + template + class bad_expected_access : public bad_expected_access { + public: + explicit bad_expected_access(E e) : val(std::move(e)) {} + auto what() const noexcept -> char const* override {// NOLINT + return "bad expected access"; + } + + auto error() & noexcept -> E& { return val; } + auto error() const& noexcept -> E const& { return val; } + auto error() && noexcept -> E&& { return std::move(val); } + auto error() const&& noexcept -> const E&& { return std::move(val); } + + private: + E val; + }; + + struct unexpect_t {}; + inline constexpr unexpect_t unexpect{}; + + namespace detail { + template + concept non_void_destructible = std::same_as || std::destructible; + } + + template + class expected; + + namespace detail { + template + concept expected_constructible_from_other = + std::constructible_from && + std::constructible_from && + (!std::constructible_from&>) && + (!std::constructible_from>) && + (!std::constructible_from const&>) && + (!std::constructible_from const>) && + (!std::convertible_to&, T>) && + (!std::convertible_to&&, T>) && + (!std::convertible_to const&, T>) && + (!std::convertible_to const&&, T>) && + (!std::constructible_from, expected&>) && + (!std::constructible_from, expected>) && + (!std::constructible_from, expected const&>) && + (!std::constructible_from, expected const>); + + template + concept is_unexpected = + std::same_as, unexpected>; + + template + concept is_expected = + std::same_as, + expected>; + + // This function makes sure expected doesn't get into valueless_by_exception + // state due to any exception while assignment + template + constexpr void reinit_expected(T& newval, U& oldval, Args&&... args) { + if constexpr (std::is_nothrow_constructible_v) { + std::destroy_at(std::addressof(oldval)); + std::construct_at(std::addressof(newval), std::forward(args)...); + } else if constexpr (std::is_nothrow_move_constructible_v) { + T tmp(std::forward(args)...); + std::destroy_at(std::addressof(oldval)); + std::construct_at(std::addressof(newval), std::move(tmp)); + } else { + U tmp(std::move(oldval)); + std::destroy_at(std::addressof(oldval)); + try { + std::construct_at(std::addressof(newval), std::forward(args)...); + } catch (...) { + std::construct_at(std::addressof(oldval), std::move(tmp)); + throw; + } + } + } + + }// namespace detail + + template + class expected { + public: + using value_type = T; + using error_type = E; + using unexpected_type = unexpected; + + template + using rebind = expected; + + // constructors + // postcondition: has_value() = true + constexpr expected() + requires std::default_initializable + : val{} {}; + + // postcondition: has_value() = rhs.has_value() + constexpr expected(expected const& rhs) + requires std::copy_constructible && std::copy_constructible && + std::is_trivially_copy_constructible_v && + std::is_trivially_copy_constructible_v + = default; + + // postcondition: has_value() = rhs.has_value() + constexpr expected(expected const& rhs) + requires std::copy_constructible && std::copy_constructible + : has_val(rhs.has_val) { + if (rhs.has_value()) { + std::construct_at(std::addressof(this->val), *rhs); + } else { + std::construct_at(std::addressof(this->unex), rhs.error()); + } + } + + constexpr expected(expected&&) noexcept( + std::is_nothrow_move_constructible_v && + std::is_nothrow_move_constructible_v) + requires std::move_constructible && std::move_constructible && + std::is_trivially_move_constructible_v && + std::is_trivially_move_constructible_v + = default; + + constexpr expected(expected&& rhs) noexcept( + std::is_nothrow_move_constructible_v && + std::is_nothrow_move_constructible_v) + requires std::move_constructible && std::move_constructible + : has_val(rhs.has_value()) { + if (rhs.has_value()) { + std::construct_at(std::addressof(this->val), std::move(*rhs)); + } else { + std::construct_at(std::addressof(this->unex), std::move(rhs.error())); + } + } + + template + requires detail::expected_constructible_from_other + constexpr explicit(!std::convertible_to || + !std::convertible_to) + expected(expected const& rhs)// NOLINT + : has_val(rhs.has_value()) { + using UF = U const&; + using GF = G const&; + if (rhs.has_value()) { + std::construct_at(std::addressof(this->val), std::forward(*rhs)); + } else { + std::construct_at(std::addressof(this->unex), + std::forward(rhs.error())); + } + } + + template + requires detail::expected_constructible_from_other + constexpr explicit(!std::convertible_to || !std::convertible_to) + expected(expected&& rhs)// NOLINT + : has_val(rhs.has_value()) { + using UF = U const&; + using GF = G const&; + if (rhs.has_value()) { + std::construct_at(std::addressof(this->val), std::forward(*rhs)); + } else { + std::construct_at(std::addressof(this->unex), + std::forward(rhs.error())); + } + } + + template + requires(!std::same_as, std::in_place_t>) && + (!std::same_as, std::remove_cvref_t>) && + (!detail::is_unexpected) && + std::constructible_from + constexpr explicit(!std::convertible_to) expected(U&& v)// NOLINT + : val(std::forward(v)) {} + + template + requires std::constructible_from + constexpr explicit(!std::convertible_to) + expected(unexpected const& e)// NOLINT + : has_val{false}, unex(std::forward(e.value())) {} + + template + requires std::constructible_from + constexpr explicit(!std::convertible_to) + expected(unexpected&& e)// NOLINT + : has_val{false}, unex(std::forward(e.value())) {} + + template + requires std::constructible_from + constexpr explicit expected(std::in_place_t /*unused*/, Args&&... args) + : val(std::forward(args)...) {} + + template + requires std::constructible_from&, Args...> + constexpr explicit expected(std::in_place_t /*unused*/, + std::initializer_list il, + Args&&... args) + : val(il, std::forward(args)...) {} + + template + requires std::constructible_from + constexpr explicit expected(unexpect_t /*unused*/, Args&&... args) + : has_val{false}, unex(std::forward(args)...) {} + + template + requires std::constructible_from&, + Args...> + constexpr explicit expected(unexpect_t /*unused*/, + std::initializer_list il, + Args&&... args) + : has_val(false), + unex(il, std::forward(args)...) {} + + // destructor + constexpr ~expected() { + if constexpr (std::is_trivially_destructible_v and + std::is_trivially_destructible_v) { + } else if constexpr (std::is_trivially_destructible_v) { + if (!has_val) { + std::destroy_at(std::addressof(this->unex)); + } + } else if constexpr (std::is_trivially_destructible_v) { + if (has_val) { + std::destroy_at(std::addressof(this->val)); + } + } else { + if (has_val) { + std::destroy_at(std::addressof(this->val)); + } else { + std::destroy_at(std::addressof(this->unex)); + } + } + } + + // assignment + constexpr auto operator=(expected const& rhs)// NOLINT + -> expected& + requires std::is_copy_assignable_v && + std::is_copy_constructible_v && + std::is_copy_assignable_v && + std::is_copy_constructible_v && + (std::is_nothrow_move_constructible_v || + std::is_nothrow_move_constructible_v) + { + if (this->has_value() and rhs.has_value()) { + this->val = *rhs; + } else if (this->has_value()) { + detail::reinit_expected(this->unex, this->val, rhs.error()); + } else if (rhs.has_value()) { + detail::reinit_expected(this->val, this->unex, *rhs); + } else { + this->unex = rhs.error(); + } + has_val = rhs.has_value(); + return *this; + } + + constexpr auto operator=(expected&& rhs)// + noexcept(std::is_nothrow_move_assignable_v && + std::is_nothrow_move_constructible_v && + std::is_nothrow_move_assignable_v && + std::is_nothrow_move_constructible_v) + -> expected& + requires std::is_move_constructible_v && + std::is_move_assignable_v && + std::is_move_constructible_v && + std::is_move_assignable_v && + (std::is_nothrow_move_constructible_v || std::is_nothrow_move_constructible_v) + { + if (this->has_value() and rhs.has_value()) { + this->val = std::move(*rhs); + } else if (this->has_value()) { + detail::reinit_expected(this->unex, this->val, std::move(rhs.error())); + } else if (rhs.has_value()) { + detail::reinit_expected(this->val, this->unex, std::move(*rhs)); + } else { + this->unex = std::move(rhs.error()); + } + has_val = rhs.has_value(); + return *this; + } + + template + constexpr auto operator=(U&& rhs) -> expected& requires(!std::same_as>) && + (!detail::is_unexpected>) && + std::constructible_from&& std::is_assignable_v && + (std::is_nothrow_constructible_v || + std::is_nothrow_move_constructible_v || + std::is_nothrow_move_constructible_v) { + if (this->has_value()) { + this->val = std::forward(rhs); + return *this; + } + detail::reinit_expected(this->val, this->unex, std::forward(rhs)); + has_val = true; + return *this; + } + + template + requires std::constructible_from && + std::is_assignable_v && + (std::is_nothrow_constructible_v || + std::is_nothrow_move_constructible_v || + std::is_nothrow_move_constructible_v) + constexpr auto operator=(unexpected const& e) -> expected& { + using GF = G const&; + if (has_value()) { + detail::reinit_expected(this->unex, this->val, + std::forward(e.value())); + } else { + this->unex = std::forward(e.value()); + } + has_val = false; + return *this; + } + + template + requires std::constructible_from && + std::is_assignable_v && + (std::is_nothrow_constructible_v || + std::is_nothrow_move_constructible_v || + std::is_nothrow_move_constructible_v) + constexpr auto operator=(unexpected&& e) -> expected& { + using GF = G; + if (has_value()) { + detail::reinit_expected(this->unex, this->val, + std::forward(e.value())); + } else { + this->unex = std::forward(e.value()); + } + has_val = false; + return *this; + } + + // modifiers + template + requires std::is_nothrow_constructible_v + constexpr auto emplace(Args&&... args) noexcept -> T& { + if (has_value()) { + std::destroy_at(std::addressof(this->val)); + } else { + std::destroy_at(std::addressof(this->unex)); + has_val = true; + } + return *std::construct_at(std::addressof(this->val), + std::forward(args)...); + } + + template + requires std::is_nothrow_constructible_v&, Args...> + constexpr auto emplace(std::initializer_list il, + Args&&... args) noexcept -> T& { + if (has_value()) { + std::destroy_at(std::addressof(this->val)); + } else { + std::destroy_at(std::addressof(this->unex)); + has_val = true; + } + return *std::construct_at(std::addressof(this->val), il, + std::forward(args)...); + } + + // swap + constexpr void swap(expected& rhs) noexcept( + std::is_nothrow_constructible_v && + std::is_nothrow_swappable_v && + std::is_nothrow_move_constructible_v && + std::is_nothrow_swappable_v) + requires std::is_swappable_v && + std::is_swappable_v && + std::is_move_constructible_v && + std::is_move_constructible_v && + (std::is_nothrow_constructible_v || + std::is_nothrow_constructible_v) + { + if (rhs.has_value()) { + if (has_value()) { + using std::swap; + swap(this->val, rhs.val); + } else { + rhs.swap(*this); + } + } else { + if (has_value()) { + if constexpr (std::is_nothrow_move_constructible_v) { + E tmp(std::move(rhs.unex)); + std::destroy_at(std::addressof(rhs.unex)); + try { + std::construct_at(std::addressof(rhs.val), std::move(this->val)); + std::destroy_at(std::addressof(this->val)); + std::construct_at(std::addressof(this->unex), std::move(tmp)); + } catch (...) { + std::construct_at(std::addressof(rhs.unex), std::move(tmp)); + throw; + } + } else { + T tmp(std::move(this->val)); + std::destroy_at(std::addressof(this->val)); + try { + std::construct_at(std::addressof(this->unex), std::move(rhs.unex)); + std::destroy_at(std::addressof(rhs.unex)); + std::construct_at(std::addressof(rhs.val), std::move(tmp)); + } catch (...) { + std::construct_at(std::addressof(this->val), std::move(tmp)); + throw; + } + } + has_val = false; + rhs.has_val = true; + } else { + using std::swap; + swap(this->unex, rhs.unex); + } + } + } + + // observers + + // precondition: has_value() = true + constexpr auto operator->() const noexcept -> T const* { + return std::addressof(this->val); + } + + // precondition: has_value() = true + constexpr auto operator->() noexcept -> T* { + return std::addressof(this->val); + } + + // precondition: has_value() = true + constexpr auto operator*() const& noexcept -> T const& { return this->val; } + + // precondition: has_value() = true + constexpr auto operator*() & noexcept -> T& { return this->val; } + + // precondition: has_value() = true + constexpr auto operator*() const&& noexcept -> T const&& { + return std::move(this->val); + } + + // precondition: has_value() = true + constexpr auto operator*() && noexcept -> T&& { return std::move(this->val); } + + constexpr explicit operator bool() const noexcept { return has_val; } + + [[nodiscard]] constexpr auto has_value() const noexcept -> bool { + return has_val; + } + + constexpr auto value() const& -> T const& { + if (has_value()) { + return this->val; + } + throw bad_expected_access(error()); + } + + constexpr auto value() & -> T& { + if (has_value()) { + return this->val; + } + throw bad_expected_access(error()); + } + + constexpr auto value() const&& -> T const&& { + if (has_value()) { + return std::move(this->val); + } + throw bad_expected_access(std::move(error())); + } + + constexpr auto value() && -> T&& { + if (has_value()) { + return std::move(this->val); + } + throw bad_expected_access(std::move(error())); + } + + // precondition: has_value() = false + constexpr auto error() const& -> E const& { return this->unex; } + + // precondition: has_value() = false + constexpr auto error() & -> E& { return this->unex; } + + // precondition: has_value() = false + constexpr auto error() const&& -> E const&& { return std::move(this->unex); } + + // precondition: has_value() = false + constexpr auto error() && -> E&& { return std::move(this->unex); } + + template + requires std::is_copy_constructible_v && std::is_convertible_v + constexpr auto value_or(U&& v) const& -> T { + return has_value() ? **this : static_cast(std::forward(v)); + } + + template + requires std::is_move_constructible_v && std::is_convertible_v + constexpr auto value_or(U&& v) && -> T { + return has_value() ? std::move(**this) : static_cast(std::forward(v)); + } + + template>> + requires detail::is_expected && + std::is_same_v && + std::is_copy_constructible_v && std::is_copy_constructible_v + constexpr auto and_then(F&& f) & { + if (has_value()) { + return std::invoke(std::forward(f), **this); + } + return U(unexpect, error()); + } + + template>> + requires detail::is_expected && + std::is_same_v && + std::is_copy_constructible_v && + std::is_copy_constructible_v + constexpr auto and_then(F&& f) const& { + if (has_value()) { + return std::invoke(std::forward(f), **this); + } + return U(unexpect, error()); + } + + template>> + requires detail::is_expected && + std::is_same_v && + std::is_move_constructible_v && + std::is_move_constructible_v + constexpr auto and_then(F&& f) && { + if (has_value()) { + return std::invoke(std::forward(f), std::move(**this)); + } + return U(unexpect, std::move(error())); + } + + template>> + requires detail::is_expected && + std::is_same_v && + std::is_move_constructible_v && + std::is_move_constructible_v + constexpr auto and_then(F&& f) const&& { + if (has_value()) { + return std::invoke(std::forward(f), std::move(**this)); + } + return U(unexpect, std::move(error())); + } + + template>> + requires detail::is_expected && + std::is_same_v && + std::is_copy_constructible_v && + std::is_copy_constructible_v + constexpr auto or_else(F&& f) & { + if (has_value()) { + return G(**this); + } + return std::invoke(std::forward(f), error()); + } + + template>> + requires detail::is_expected && + std::is_same_v && + std::is_copy_constructible_v && + std::is_copy_constructible_v + constexpr auto or_else(F&& f) const& { + if (has_value()) { + return G(**this); + } + return std::invoke(std::forward(f), error()); + } + + template>> + requires detail::is_expected && + std::is_same_v && + std::is_move_constructible_v && + std::is_move_constructible_v + constexpr auto or_else(F&& f) && { + if (has_value()) { + return G(std::move(**this)); + } + return std::invoke(std::forward(f), std::move(error())); + } + + template>> + requires detail::is_expected && + std::is_same_v && + std::is_move_constructible_v && + std::is_move_constructible_v + constexpr auto or_else(F&& f) const&& { + if (has_value()) { + return G(std::move(**this)); + } + return std::invoke(std::forward(f), std::move(error())); + } + + template>> + requires std::is_void_v && + std::is_copy_constructible_v && + std::is_copy_constructible_v + constexpr auto or_else(F&& f) & { + if (has_value()) { + return expected(*this); + } + std::invoke(std::forward(f), error()); + return expected(*this); + } + + template>> + requires std::is_void_v && + std::is_copy_constructible_v && + std::is_copy_constructible_v + constexpr auto or_else(F&& f) const& { + if (has_value()) { + return expected(*this); + } + std::invoke(std::forward(f), error()); + return expected(*this); + } + + template>> + requires std::is_void_v && + std::is_move_constructible_v && + std::is_move_constructible_v && + std::is_copy_constructible_v + constexpr auto or_else(F&& f) && { + if (has_value()) { + return expected(std::move(*this)); + } + // TODO: is this copy necessary, as f can be just read argument function + std::invoke(std::forward(f), error()); + return expected(std::move(*this)); + } + + template>> + requires std::is_void_v && + std::is_move_constructible_v && + std::is_move_constructible_v && + std::is_copy_constructible_v + constexpr auto or_else(F&& f) const&& { + if (!has_value()) { + return expected(std::move(*this)); + } + // TODO: is this copy necessary, as f can be just read argument function + std::invoke(std::forward(f), error()); + return expected(std::move(*this)); + } + + template>> + requires std::is_copy_constructible_v && + std::is_copy_constructible_v + constexpr auto transform(F&& f) & { + if (has_value()) { + if constexpr (!std::same_as) { + return expected(std::invoke(std::forward(f), **this)); + } else { + std::invoke(std::forward(f), std::move(**this)); + return expected(); + } + } + return expected(unexpect, error()); + } + + template>> + requires std::is_copy_constructible_v && + std::is_copy_constructible_v + constexpr auto transform(F&& f) const& { + if (has_value()) { + if constexpr (!std::same_as) { + return expected(std::invoke(std::forward(f), **this)); + } else { + std::invoke(std::forward(f), std::move(**this)); + return expected(); + } + } + return expected(unexpect, error()); + } + + template>> + requires std::is_move_constructible_v && + std::is_move_constructible_v + constexpr auto transform(F&& f) && { + if (has_value()) { + if constexpr (!std::same_as) { + return expected( + std::invoke(std::forward(f), std::move(**this))); + } else { + std::invoke(std::forward(f), std::move(**this)); + return expected(); + } + } + return expected(unexpect, std::move(error())); + } + + template>> + requires std::is_move_constructible_v && + std::is_move_constructible_v + constexpr auto transform(F&& f) const&& { + if (has_value()) { + if constexpr (!std::same_as) { + return expected( + std::invoke(std::forward(f), std::move(**this))); + } else { + std::invoke(std::forward(f), std::move(**this)); + return expected(); + } + } + return expected(unexpect, std::move(error())); + } + + template>> + requires std::is_copy_constructible_v && + std::is_copy_constructible_v + constexpr auto transform_error(F&& f) & { + if (has_value()) { + return expected(**this); + } + return expected(unexpect, std::invoke(std::forward(f), error())); + } + + template>> + requires std::is_copy_constructible_v && + std::is_copy_constructible_v + constexpr auto transform_error(F&& f) const& { + if (has_value()) { + return expected(**this); + } + return expected(unexpect, std::invoke(std::forward(f), error())); + } + + template>> + requires std::is_move_constructible_v && + std::is_move_constructible_v + constexpr auto transform_error(F&& f) && { + if (has_value()) { + return expected(std::move(**this)); + } + return expected(unexpect, + std::invoke(std::forward(f), std::move(error()))); + } + + template>> + requires std::is_move_constructible_v && + std::is_move_constructible_v + constexpr auto transform_error(F&& f) const&& { + if (has_value()) { + return expected(std::move(**this)); + } + return expected(unexpect, + std::invoke(std::forward(f), std::move(error()))); + } + + // equality operators + template + requires(!std::is_void_v) && + requires(T const& t1, T2 const& t2, E const& e1, E2 const& e2) { + { t1 == t2 } -> std::convertible_to; + { e1 == e2 } -> std::convertible_to; + } + friend constexpr auto operator==(expected const& x, expected const& y) + -> bool { + if (x.has_value() != y.has_value()) { + return false; + } + return x.has_value() ? (*x == *y) : (x.error() == y.error()); + } + + template + requires(!detail::is_expected) && requires(T const& x, T2 const& v) { + { x == v } -> std::convertible_to; + } + friend constexpr auto operator==(expected const& x, T2 const& v) -> bool { + return x.has_value() && static_cast(*x == v); + } + + template + requires requires(E const& x, unexpected const& e) { + { x == e.value() } -> std::convertible_to; + } + friend constexpr auto operator==(expected const& x, unexpected const& e) + -> bool { + return !x.has_value() && static_cast(x.error() == e.value()); + } + + // specialized algorithms + friend constexpr void swap(expected& x, + expected& y) noexcept(noexcept(x.swap(y))) { + x.swap(y); + } + + private: + bool has_val{true}; + union { + T val; + E unex; + }; + }; + + template + class expected { + public: + using value_type = void; + using error_type = E; + using unexpected_type = unexpected; + + template + using rebind = expected; + + // constructors + + // postcondition: has_value() = true + constexpr expected() noexcept {}// NOLINT + + constexpr expected( + expected const& rhs) + requires std::is_copy_constructible_v && + std::is_trivially_copy_constructible_v + = default; + + constexpr expected( + expected const& rhs) + requires std::is_copy_constructible_v + : has_val(rhs.has_value()) { + if (!rhs.has_value()) { + std::construct_at(std::addressof(this->unex), rhs.error()); + } + } + + constexpr expected(expected&&) noexcept(std::is_nothrow_move_constructible_v) + requires std::is_move_constructible_v && + std::is_trivially_move_constructible_v + = default; + + constexpr expected(expected&& rhs) noexcept(std::is_nothrow_move_constructible_v) + requires std::is_move_constructible_v + : has_val(rhs.has_value()) { + if (!rhs.has_value()) { + std::construct_at(std::addressof(this->unex), std::move(rhs.error())); + } + } + + template + requires std::is_void_v && std::is_constructible_v && (!std::is_constructible_v, expected&>) && (!std::is_constructible_v, expected>) && (!std::is_constructible_v, expected const&>) && (!std::is_constructible_v, expected const&>) + constexpr explicit(!std::is_convertible_v) + expected(expected const& rhs)// NOLINT + : has_val(rhs.has_value()) { + if (!rhs.has_value()) { + std::construct_at(std::addressof(this->unex), + std::forward(rhs.error())); + } + } + + template + requires std::is_void_v && std::is_constructible_v && (!std::is_constructible_v, expected&>) && (!std::is_constructible_v, expected>) && (!std::is_constructible_v, expected const&>) && (!std::is_constructible_v, expected const&>) + constexpr explicit(!std::is_convertible_v) + expected(expected&& rhs)// NOLINT + : has_val(rhs.has_value()) { + if (!rhs.has_value()) { + std::construct_at(std::addressof(this->unex), + std::forward(rhs.error())); + } + } + + template + requires std::is_constructible_v + constexpr explicit(!std::is_convertible_v) + expected(unexpected const& e)// NOLINT + : has_val(false), unex(std::forward(e.value())) {} + + template + requires std::is_constructible_v + constexpr explicit(!std::is_convertible_v) + expected(unexpected&& e)// NOLINT + : has_val(false), unex(std::forward(e.value())) {} + + constexpr explicit expected(std::in_place_t /*unused*/) noexcept {} + + template + requires std::is_constructible_v + constexpr explicit expected(unexpect_t /*unused*/, Args&&... args) + : has_val(false), unex(std::forward(args)...) {} + + template + requires std::is_constructible_v&, Args...> + constexpr explicit expected(unexpect_t /*unused*/, std::initializer_list il, Args... args) + : has_val(false), + unex(il, std::forward(args)...) {} + + // destructor + constexpr ~expected() { + if constexpr (std::is_trivially_destructible_v) { + } else { + if (!has_value()) std::destroy_at(std::addressof(this->unex)); + } + } + + // assignment + constexpr auto operator=(expected const& rhs) -> expected&// NOLINT + requires std::is_copy_assignable_v && + std::is_copy_constructible_v + { + if (has_value() && rhs.has_value()) { + } else if (has_value()) { + std::construct_at(std::addressof(this->unex), rhs.unex); + has_val = false; + } else if (rhs.has_value()) { + std::destroy_at(std::addressof(this->unex)); + has_val = true; + } else { + this->unex = rhs.error(); + } + return *this; + } + + constexpr auto operator=(expected&& rhs) noexcept(std::is_nothrow_move_constructible_v && + std::is_nothrow_move_assignable_v) -> expected& + requires std::is_move_constructible_v && + std::is_move_assignable_v + { + if (has_value() && rhs.has_value()) { + } else if (has_value()) { + std::construct_at(std::addressof(this->unex), std::move(rhs.unex)); + has_val = false; + } else if (rhs.has_value()) { + std::destroy_at(std::addressof(this->unex)); + has_val = true; + } else { + this->unex = std::move(rhs.error()); + } + return *this; + } + + template + requires std::is_constructible_v and + std::is_assignable_v + constexpr auto operator=(unexpected const& e) -> expected& { + if (has_value()) { + std::construct_at(std::addressof(this->unex), + std::forward(e.value())); + has_val = false; + } else { + this->unex = std::forward(e.value()); + } + return *this; + } + + template + requires std::is_constructible_v && + std::is_assignable_v + constexpr auto operator=(unexpected&& e) -> expected& { + if (has_value()) { + std::construct_at(std::addressof(this->unex), std::forward(e.value())); + has_val = false; + } else { + this->unex = std::forward(e.value()); + } + return *this; + } + + // modifiers + constexpr void emplace() noexcept { + if (!has_value()) { + std::destroy_at(std::addressof(this->unex)); + has_val = true; + } + } + + // swap + constexpr void swap(expected& rhs) noexcept(std::is_nothrow_move_constructible_v && + std::is_nothrow_swappable_v) + requires std::is_swappable_v && + std::is_move_constructible_v + { + if (rhs.has_value()) { + if (has_value()) { + } else { + rhs.swap(*this); + } + } else { + if (has_value()) { + std::construct_at(std::addressof(this->unex), std::move(rhs.unex)); + std::destroy_at(std::addressof(rhs.unex)); + has_val = false; + rhs.has_val = true; + } else { + using std::swap; + swap(this->unex, rhs.unex); + } + } + } + + // observers + constexpr explicit operator bool() const noexcept { return has_val; } + + [[nodiscard]] constexpr auto has_value() const noexcept -> bool { + return has_val; + } + + // precondition: has_value() = true + constexpr void operator*() const noexcept {} + + constexpr void value() const& { + if (!has_value()) { + throw bad_expected_access(error()); + } + } + + constexpr void value() && { + if (!has_value()) { + throw bad_expected_access(std::move(error())); + } + } + + // precondition: has_value() = false + constexpr auto error() const& -> E const& { return this->unex; } + + // precondition: has_value() = false + constexpr auto error() & -> E& { return this->unex; } + + // precondition: has_value() = false + constexpr auto error() const&& -> E const&& { return std::move(this->unex); } + + // precondition: has_value() = false + constexpr auto error() && -> E&& { return std::move(this->unex); } + + // monadic + template>> + requires detail::is_expected && + std::is_same_v && + std::is_copy_constructible_v + constexpr auto and_then(F&& f) & { + if (has_value()) { + return std::invoke(std::forward(f)); + } + return U(unexpect, error()); + } + + template>> + requires detail::is_expected && + std::is_same_v && + std::is_copy_constructible_v + constexpr auto and_then(F&& f) const& { + if (has_value()) { + return std::invoke(std::forward(f)); + } + return U(unexpect, error()); + } + + template>> + requires detail::is_expected && + std::is_same_v && + std::is_move_constructible_v + constexpr auto and_then(F&& f) && { + if (has_value()) { + return std::invoke(std::forward(f)); + } + return U(unexpect, std::move(error())); + } + + template>> + requires detail::is_expected && + std::is_same_v && + std::is_move_constructible_v + constexpr auto and_then(F&& f) const&& { + if (has_value()) { + return std::invoke(std::forward(f)); + } + return U(unexpect, std::move(error())); + } + + template>> + requires detail::is_expected && + std::is_same_v && + std::is_copy_constructible_v + constexpr auto or_else(F&& f) & { + if (has_value()) { + return G{}; + } + return std::invoke(std::forward(f), error()); + } + + template>> + requires detail::is_expected && + std::is_same_v && + std::is_copy_constructible_v + constexpr auto or_else(F&& f) const& { + if (has_value()) { + return G{}; + } + return std::invoke(std::forward(f), error()); + } + + template>> + requires detail::is_expected && + std::is_same_v && + std::is_move_constructible_v + constexpr auto or_else(F&& f) && { + if (has_value()) { + return G{}; + } + return std::invoke(std::forward(f), std::move(error())); + } + + template>> + requires detail::is_expected && + std::is_same_v && + std::is_move_constructible_v + constexpr auto or_else(F&& f) const&& { + if (has_value()) { + return G{}; + } + return std::invoke(std::forward(f), std::move(error())); + } + + template>> + requires std::is_void_v && + std::is_copy_constructible_v + constexpr auto or_else(F&& f) & { + if (has_value()) { + return expected(*this); + } + std::invoke(std::forward(f), error()); + return expected(*this); + } + + template>> + requires std::is_void_v && + std::is_copy_constructible_v + constexpr auto or_else(F&& f) const& { + if (has_value()) { + return expected(*this); + } + std::invoke(std::forward(f), error()); + return expected(*this); + } + + template>> + requires std::is_void_v && + std::is_move_constructible_v && + std::is_copy_constructible_v + constexpr auto or_else(F&& f) && { + if (has_value()) { + return expected(std::move(*this)); + } + // TODO: is this copy necessary, as f can be just read argument function + std::invoke(std::forward(f), error()); + return expected(std::move(*this)); + } + + template>> + requires std::is_void_v && + std::is_move_constructible_v && + std::is_copy_constructible_v + constexpr auto or_else(F&& f) const&& { + if (!has_value()) { + return expected(std::move(*this)); + } + // TODO: is this copy necessary, as f can be just read argument function + std::invoke(std::forward(f), error()); + return expected(std::move(*this)); + } + + template>> + requires std::is_copy_constructible_v + constexpr auto transform(F&& f) & { + if (has_value()) { + if constexpr (!std::same_as) { + return expected(std::invoke(std::forward(f))); + } else { + std::invoke(std::forward(f)); + return expected(); + } + } + return expected(unexpect, error()); + } + + template>> + requires std::is_copy_constructible_v + constexpr auto transform(F&& f) const& { + if (has_value()) { + if constexpr (!std::same_as) { + return expected(std::invoke(std::forward(f))); + } else { + std::invoke(std::forward(f)); + return expected(); + } + } + return expected(unexpect, error()); + } + + template>> + requires std::is_move_constructible_v + constexpr auto transform(F&& f) && { + if (has_value()) { + if constexpr (!std::same_as) { + return expected(std::invoke(std::forward(f))); + } else { + std::invoke(std::forward(f)); + return expected(); + } + } + return expected(unexpect, std::move(error())); + } + + template>> + requires std::is_move_constructible_v + constexpr auto transform(F&& f) const&& { + if (has_value()) { + if constexpr (!std::same_as) { + return expected(std::invoke(std::forward(f))); + } else { + std::invoke(std::forward(f)); + return expected(); + } + } + return expected(unexpect, std::move(error())); + } + + template>> + requires std::is_copy_constructible_v + constexpr auto transform_error(F&& f) & { + if (has_value()) { + return expected{}; + } + return expected(unexpect, + std::invoke(std::forward(f), error())); + } + + template>> + requires std::is_copy_constructible_v + constexpr auto transform_error(F&& f) const& { + if (has_value()) { + return expected{}; + } + return expected(unexpect, + std::invoke(std::forward(f), error())); + } + + template>> + requires std::is_move_constructible_v + constexpr auto transform_error(F&& f) && { + if (has_value()) { + return expected{}; + } + return expected( + unexpect, std::invoke(std::forward(f), std::move(error()))); + } + + template>> + requires std::is_move_constructible_v + constexpr auto transform_error(F&& f) const&& { + if (has_value()) { + return expected{}; + } + return expected( + unexpect, std::invoke(std::forward(f), std::move(error()))); + } + + // expected equality operators + template + requires std::is_void_v && requires(E e, E2 e2) { + { e == e2 } -> std::convertible_to; + } + friend constexpr auto operator==(expected const& x, expected const& y) + -> bool { + if (x.has_value() != y.has_value()) return false; + return x.has_value() or static_cast(x.error() == y.error()); + } + + template + requires requires(expected const& x, unexpected const& e) { + { x.error() == e.value() } -> std::convertible_to; + } + friend constexpr auto operator==(expected const& x, unexpected const& e) + -> bool { + return !x.has_value() && static_cast(x.error() == e.value()); + } + + // specialized algorithms + friend constexpr void swap(expected& x, + expected& y) noexcept(noexcept(x.swap(y))) { + x.swap(y); + } + + private: + bool has_val{true}; + union { + E unex; + }; + }; + +}// namespace plg +#endif \ No newline at end of file diff --git a/include/plg/flat_map.hpp b/include/plg/flat_map.hpp new file mode 100644 index 0000000..9912431 --- /dev/null +++ b/include/plg/flat_map.hpp @@ -0,0 +1,780 @@ +#pragma once + +#include "plg/macro.hpp" + +#ifdef __cpp_lib_flat_map +#include +namespace plg { + template> + using flat_map = std::flat_map; +} +#else +#include "plg/vector.hpp" + +namespace plg { + namespace detail { + template < typename T, typename U, typename = void > + struct is_transparent + : std::false_type {}; + + template < typename T, typename U > + struct is_transparent> + : std::true_type {}; + + template < typename T, typename U > + inline constexpr bool is_transparent_v = is_transparent::value; + + template + constexpr bool is_sorted(Iter first, Iter last, Compare comp) { + if (first != last) { + Iter next = first; + while (++next != last) { + if (comp(*next, *first)) { + return false; + } + ++first; + } + } + return true; + } + + template + constexpr bool is_sorted_unique(Iter first, Iter last, Compare comp) { + if (first != last) { + Iter next = first; + while (++next != last) { + if (!comp(*first, *next)) { + return false; + } + ++first; + } + } + return true; + } + + template + struct pair_compare : public Compare { + pair_compare() = default; + + explicit pair_compare(const Compare& compare) + : Compare(compare) {} + + bool operator()( + const typename Pair::first_type& l, + const typename Pair::first_type& r) const { + return Compare::operator()(l, r); + } + + bool operator()(const Pair& l, const Pair& r) const { + return Compare::operator()(l.first, r.first); + } + + bool operator()( + const typename Pair::first_type& l, + const Pair& r) const { + return Compare::operator()(l, r.first); + } + + bool operator()( + const Pair& l, + const typename Pair::first_type& r) const { + return Compare::operator()(l.first, r); + } + + template + requires (is_transparent_v) + bool operator()(const K& l, const Pair& r) const { + return Compare::operator()(l, r.first); + } + + template + requires (is_transparent_v) + bool operator()(const Pair& l, const K& r) const { + return Compare::operator()(l.first, r); + } + }; + + template + struct eq_compare : public Compare { + eq_compare() = default; + + explicit eq_compare(const Compare& compare) + : Compare(compare) {} + + template + bool operator()(const L& l, const R& r) const { + return !Compare::operator()(l, r) && !Compare::operator()(r, l); + } + }; + } // namespace detail + + struct sorted_range_t {}; + inline constexpr sorted_range_t sorted_range = sorted_range_t(); + + struct sorted_unique_range_t : public sorted_range_t {}; + inline constexpr sorted_unique_range_t sorted_unique_range = sorted_unique_range_t(); + + template, typename Container = std::vector>> + class flat_map { + public: + using key_type = Key; + using mapped_type = Value; + using value_type = typename Container::value_type; + + using size_type = typename Container::size_type; + using difference_type = typename Container::difference_type; + + using key_compare = Compare; + using container_type = Container; + + using reference = typename Container::reference; + using const_reference = typename Container::const_reference; + using pointer = typename Container::pointer; + using const_pointer = typename Container::const_pointer; + + using iterator = typename Container::iterator; + using const_iterator = typename Container::const_iterator; + using reverse_iterator = typename Container::reverse_iterator; + using const_reverse_iterator = typename Container::const_reverse_iterator; + + struct value_compare : private key_compare { + value_compare() = default; + + explicit value_compare(key_compare compare) + : key_compare(std::move(compare)) {} + + bool operator()(const value_type& l, const value_type& r) const { + return key_compare::operator()(l.first, r.first); + } + }; + + public: + flat_map() = default; + ~flat_map() = default; + + explicit flat_map(const Compare& c) + : _compare(c) {} + + template + explicit flat_map(const Allocator& a) + : _data(a) {} + + template + flat_map(const Compare& c, const Allocator& a) + : _compare(c), _data(a) {} + + template + flat_map(Iterator first, Iterator last) { + from_range(first, last); + } + + template + flat_map(sorted_range_t, Iterator first, Iterator last) { + from_range(sorted_range, first, last); + } + + template + flat_map(sorted_unique_range_t, Iterator first, Iterator last) { + from_range(sorted_unique_range, first, last); + } + + template + flat_map(Iterator first, Iterator last, const Compare& c) + : _compare(c) { + from_range(first, last); + } + + template + flat_map(sorted_range_t, Iterator first, Iterator last, const Compare& c) + : _compare(c) { + from_range(sorted_range, first, last); + } + + template + flat_map(sorted_unique_range_t, Iterator first, Iterator last, const Compare& c) + : _compare(c) { + from_range(sorted_unique_range, first, last); + } + + template + flat_map(Iterator first, Iterator last, const Allocator& a) + : _data(a) { + from_range(first, last); + } + + template + flat_map(sorted_range_t, Iterator first, Iterator last, const Allocator& a) + : _data(a) { + from_range(sorted_range, first, last); + } + + template + flat_map(sorted_unique_range_t, Iterator first, Iterator last, const Allocator& a) + : _data(a) { + from_range(sorted_unique_range, first, last); + } + + template + flat_map(Iterator first, Iterator last, const Compare& c, const Allocator& a) + : _compare(c), _data(a) { + from_range(first, last); + } + + template + flat_map(sorted_range_t, Iterator first, Iterator last, const Compare& c, const Allocator& a) + : _compare(c), _data(a) { + from_range(sorted_range, first, last); + } + + template + flat_map(sorted_unique_range_t, Iterator first, Iterator last, const Compare& c, const Allocator& a) + : _compare(c), _data(a) { + from_range(sorted_unique_range, first, last); + } + + flat_map(std::initializer_list list) { + from_range(list.begin(), list.end()); + } + + flat_map(sorted_range_t, std::initializer_list list) { + from_range(sorted_range, list.begin(), list.end()); + } + + flat_map(sorted_unique_range_t, std::initializer_list list) { + from_range(sorted_unique_range, list.begin(), list.end()); + } + + flat_map(std::initializer_list list, const Compare& c) + : _compare(c) { + from_range(list.begin(), list.end()); + } + + flat_map(sorted_range_t, std::initializer_list list, const Compare& c) + : _compare(c) { + from_range(sorted_range, list.begin(), list.end()); + } + + flat_map(sorted_unique_range_t, std::initializer_list list, const Compare& c) + : _compare(c) { + from_range(sorted_unique_range, list.begin(), list.end()); + } + + template + flat_map(std::initializer_list list, const Allocator& a) + : _data(a) { + from_range(list.begin(), list.end()); + } + + template + flat_map(sorted_range_t, std::initializer_list list, const Allocator& a) + : _data(a) { + from_range(sorted_range, list.begin(), list.end()); + } + + template + flat_map(sorted_unique_range_t, std::initializer_list list, const Allocator& a) + : _data(a) { + from_range(sorted_unique_range, list.begin(), list.end()); + } + + template + flat_map(std::initializer_list list, const Compare& c, const Allocator& a) + : _compare(c), _data(a) { + from_range(list.begin(), list.end()); + } + + template + flat_map(sorted_range_t, std::initializer_list list, const Compare& c, const Allocator& a) + : _compare(c), _data(a) { + from_range(sorted_range, list.begin(), list.end()); + } + + template + flat_map(sorted_unique_range_t, std::initializer_list list, const Compare& c, const Allocator& a) + : _compare(c), _data(a) { + from_range(sorted_unique_range, list.begin(), list.end()); + } + + template + flat_map(flat_map&& other, const Allocator& a) + : _compare(std::move(other._compare)), _data(std::move(other._data), a) {} + + template + flat_map(const flat_map& other, const Allocator& a) + : _compare(other._compare), _data(other._data, a) {} + + flat_map(flat_map&& other) noexcept = default; + flat_map(const flat_map& other) = default; + + flat_map& operator=(flat_map&& other) noexcept = default; + flat_map& operator=(const flat_map& other) = default; + + flat_map& operator=(std::initializer_list list) { + flat_map(list).swap(*this); + return *this; + } + + iterator begin() noexcept(noexcept(std::declval().begin())) { + return _data.begin(); + } + + const_iterator begin() const + noexcept(noexcept(std::declval().begin())) { + return _data.begin(); + } + + const_iterator cbegin() const + noexcept(noexcept(std::declval().cbegin())) { + return _data.cbegin(); + } + + iterator end() noexcept(noexcept(std::declval().end())) { + return _data.end(); + } + + const_iterator end() const + noexcept(noexcept(std::declval().end())) { + return _data.end(); + } + + const_iterator cend() const + noexcept(noexcept(std::declval().cend())) { + return _data.cend(); + } + + reverse_iterator rbegin() noexcept(noexcept(std::declval().rbegin())) { + return _data.rbegin(); + } + + const_reverse_iterator rbegin() const + noexcept(noexcept(std::declval().rbegin())) { + return _data.rbegin(); + } + + const_reverse_iterator crbegin() const + noexcept(noexcept(std::declval().crbegin())) { + return _data.crbegin(); + } + + reverse_iterator rend() noexcept(noexcept(std::declval().rend())) { + return _data.rend(); + } + + const_reverse_iterator rend() const + noexcept(noexcept(std::declval().rend())) { + return _data.rend(); + } + + const_reverse_iterator crend() const + noexcept(noexcept(std::declval().crend())) { + return _data.crend(); + } + + bool empty() const + noexcept(noexcept(std::declval().empty())) { + return _data.empty(); + } + + size_type size() const + noexcept(noexcept(std::declval().size())) { + return _data.size(); + } + + size_type max_size() const + noexcept(noexcept(std::declval().max_size())) { + return _data.max_size(); + } + + size_type capacity() const + noexcept(noexcept(std::declval().capacity())) { + return _data.capacity(); + } + + void reserve(size_type capacity) { + _data.reserve(capacity); + } + + void shrink_to_fit() { + _data.shrink_to_fit(); + } + + mapped_type& operator[](key_type&& key) { + const iterator iter = find(key); + return iter != end() + ? iter->second + : emplace(std::move(key), mapped_type()).first->second; + } + + mapped_type& operator[](const key_type& key) { + const iterator iter = find(key); + return iter != end() + ? iter->second + : emplace(key, mapped_type()).first->second; + } + + mapped_type& at(const key_type& key) { + const iterator iter = find(key); + PLUGIFY_ASSERT(iter != end(), "plg::flat_map::at(): key not found", std::out_of_range); + return iter->second; + } + + const mapped_type& at(const key_type& key) const { + const const_iterator iter = find(key); + PLUGIFY_ASSERT(iter != end(), "plg::flat_map::at(): key not found", std::out_of_range); + return iter->second; + } + + template + requires (detail::is_transparent_v) + mapped_type& at(const K& key) { + const iterator iter = find(key); + PLUGIFY_ASSERT(iter != end(), "plg::flat_map::at(): key not found", std::out_of_range); + return iter->second; + } + + template + requires (detail::is_transparent_v) + const mapped_type& at(const K& key) const { + const const_iterator iter = find(key); + PLUGIFY_ASSERT(iter != end(), "plg::flat_map::at(): key not found", std::out_of_range); + return iter->second; + } + + std::pair insert(value_type&& value) { + const iterator iter = lower_bound(value.first); + return iter == end() || _compare(value, *iter) + ? std::make_pair(_data.insert(iter, std::move(value)), true) + : std::make_pair(iter, false); + } + + std::pair insert(const value_type& value) { + const iterator iter = lower_bound(value.first); + return iter == end() || _compare(value, *iter) + ? std::make_pair(_data.insert(iter, value), true) + : std::make_pair(iter, false); + } + + iterator insert(const_iterator hint, value_type&& value) { + return (hint == begin() || _compare(*(hint - 1), value)) && (hint == end() || _compare(value, *hint)) + ? _data.insert(hint, std::move(value)) + : insert(std::move(value)).first; + } + + iterator insert(const_iterator hint, const value_type& value) { + return (hint == begin() || _compare(*(hint - 1), value)) && (hint == end() || _compare(value, *hint)) + ? _data.insert(hint, value) + : insert(value).first; + } + + template + std::pair insert_or_assign(key_type&& key, V&& value) { + iterator iter = lower_bound(key); + if (iter == end() || _compare(key, *iter)) { + iter = emplace_hint(iter, std::move(key), std::forward(value)); + return {iter, true}; + } + (*iter).second = std::forward(value); + return {iter, false}; + } + + template + std::pair insert_or_assign(const key_type& key, V&& value) { + iterator iter = lower_bound(key); + if (iter == end() || _compare(key, *iter)) { + iter = emplace_hint(iter, key, std::forward(value)); + return {iter, true}; + } + (*iter).second = std::forward(value); + return {iter, false}; + } + + template + void insert(Iterator first, Iterator last) { + insert_range(first, last); + } + + template + void insert(sorted_range_t, Iterator first, Iterator last) { + insert_range(sorted_range, first, last); + } + + void insert(std::initializer_list list) { + insert_range(list.begin(), list.end()); + } + + void insert(sorted_range_t, std::initializer_list list) { + insert_range(sorted_range, list.begin(), list.end()); + } + + template + std::pair emplace(Args&&... args) { + return insert(value_type(std::forward(args)...)); + } + + template + iterator emplace_hint(const_iterator hint, Args&&... args) { + return insert(hint, value_type(std::forward(args)...)); + } + + template + std::pair try_emplace(key_type&& key, Args&&... args) { + iterator iter = lower_bound(key); + if (iter == end() || _compare(key, *iter)) { + iter = emplace_hint(iter, std::move(key), std::forward(args)...); + return {iter, true}; + } + return {iter, false}; + } + + template + std::pair try_emplace(const key_type& key, Args&&... args) { + iterator iter = lower_bound(key); + if (iter == end() || _compare(key, *iter)) { + iter = emplace_hint(iter, key, std::forward(args)...); + return {iter, true}; + } + return {iter, false}; + } + + void clear() noexcept(noexcept(std::declval().clear())) { + _data.clear(); + } + + iterator erase(const_iterator iter) { + return _data.erase(iter); + } + + iterator erase(const_iterator first, const_iterator last) { + return _data.erase(first, last); + } + + size_type erase(const key_type& key) { + const const_iterator iter = find(key); + return iter != end() + ? (erase(iter), 1) + : 0; + } + + void swap(flat_map& other) noexcept(std::is_nothrow_swappable_v && std::is_nothrow_swappable_v) { + using std::swap; + swap(_compare, other._compare); + swap(_data, other._data); + } + + size_type count(const key_type& key) const { + const const_iterator iter = find(key); + return iter != end() ? 1 : 0; + } + + template + requires (detail::is_transparent_v) + size_type count(const K& key) const { + const const_iterator iter = find(key); + return iter != end() ? 1 : 0; + } + + iterator find(const key_type& key) { + const iterator iter = lower_bound(key); + return iter != end() && !_compare(key, *iter) + ? iter + : end(); + } + + const_iterator find(const key_type& key) const { + const const_iterator iter = lower_bound(key); + return iter != end() && !_compare(key, *iter) + ? iter + : end(); + } + + template + requires (detail::is_transparent_v) + iterator find(const K& key) { + const iterator iter = lower_bound(key); + return iter != end() && !_compare(key, *iter) + ? iter + : end(); + } + + template + requires (detail::is_transparent_v) + const_iterator find(const K& key) const { + const const_iterator iter = lower_bound(key); + return iter != end() && !_compare(key, *iter) + ? iter + : end(); + } + + bool contains(const key_type& key) const { + return find(key) != end(); + } + + template + requires (detail::is_transparent_v) + bool contains(const K& key) const { + return find(key) != end(); + } + + std::pair equal_range(const key_type& key) { + return std::equal_range(begin(), end(), key, _compare); + } + + std::pair equal_range(const key_type& key) const { + return std::equal_range(begin(), end(), key, _compare); + } + + template + requires (detail::is_transparent_v) + std::pair equal_range(const K& key) { + return std::equal_range(begin(), end(), key, _compare); + } + + template + requires (detail::is_transparent_v) + std::pair equal_range(const K& key) const { + return std::equal_range(begin(), end(), key, _compare); + } + + iterator lower_bound(const key_type& key) { + return std::lower_bound(begin(), end(), key, _compare); + } + + const_iterator lower_bound(const key_type& key) const { + return std::lower_bound(begin(), end(), key, _compare); + } + + template + requires (detail::is_transparent_v) + iterator lower_bound(const K& key) { + return std::lower_bound(begin(), end(), key, _compare); + } + + template + requires (detail::is_transparent_v) + const_iterator lower_bound(const K& key) const { + return std::lower_bound(begin(), end(), key, _compare); + } + + iterator upper_bound(const key_type& key) { + return std::upper_bound(begin(), end(), key, _compare); + } + + const_iterator upper_bound(const key_type& key) const { + return std::upper_bound(begin(), end(), key, _compare); + } + + template + requires (detail::is_transparent_v) + iterator upper_bound(const K& key) { + return std::upper_bound(begin(), end(), key, _compare); + } + + template + requires (detail::is_transparent_v) + const_iterator upper_bound(const K& key) const { + return std::upper_bound(begin(), end(), key, _compare); + } + + key_compare key_comp() const { + return _compare; + } + + value_compare value_comp() const { + return value_compare(key_comp()); + } + + private: + template + void from_range(Iter first, Iter last) { + assert(_data.empty()); + _data.insert(_data.end(), first, last); + std::sort(_data.begin(), _data.end(), value_comp()); + _data.erase( + std::unique(_data.begin(), _data.end(), + detail::eq_compare(value_comp())), + _data.end()); + } + + template + void from_range(sorted_range_t, Iter first, Iter last) { + assert(_data.empty()); + assert(detail::is_sorted(first, last, value_comp())); + _data.insert(_data.end(), first, last); + _data.erase( + std::unique(_data.begin(), _data.end(), + detail::eq_compare(value_comp())), + _data.end()); + } + + template + void from_range(sorted_unique_range_t, Iter first, Iter last) { + assert(_data.empty()); + assert(detail::is_sorted_unique(first, last, value_comp())); + _data.insert(_data.end(), first, last); + } + + private: + template + void insert_range(Iter first, Iter last) { + const auto mid_iter = _data.insert(_data.end(), first, last); + std::sort(mid_iter, _data.end(), value_comp()); + std::inplace_merge(_data.begin(), mid_iter, _data.end(), value_comp()); + _data.erase( + std::unique(_data.begin(), _data.end(), + detail::eq_compare(value_comp())), + _data.end()); + } + + template + void insert_range(sorted_range_t, Iter first, Iter last) { + assert(detail::is_sorted(first, last, value_comp())); + const auto mid_iter = _data.insert(_data.end(), first, last); + std::inplace_merge(_data.begin(), mid_iter, _data.end(), value_comp()); + _data.erase( + std::unique(_data.begin(), _data.end(), + detail::eq_compare(value_comp())), + _data.end()); + } + + private: + PLUGIFY_NO_UNIQUE_ADDRESS + detail::pair_compare _compare; + container_type _data; + }; + + template + void swap( + flat_map& l, + flat_map& r) noexcept(noexcept(l.swap(r))) { + l.swap(r); + } + + template + bool operator==( + const flat_map& l, + const flat_map& r) { + return l.size() == r.size() && std::equal(l.begin(), l.end(), r.begin()); + } + + template + auto operator<=>( + const flat_map& l, + const flat_map& r) { + if (l.size() < r.size()) { + return std::partial_ordering::less; + } else if (l.size() > r.size()) { + return std::partial_ordering::greater; + } else { + if (std::lexicographical_compare(l.cbegin(), l.cend(), r.cbegin(), r.cend())) { + return std::partial_ordering::less; + } else { + return std::partial_ordering::greater; + } + } + } + + template, typename Container = plg::vector>> + using map = flat_map; + +}// namespace plg +#endif \ No newline at end of file diff --git a/include/plg/format.hpp b/include/plg/format.hpp index 1a013a7..94e5bdb 100644 --- a/include/plg/format.hpp +++ b/include/plg/format.hpp @@ -24,100 +24,3 @@ namespace std { } #endif // __cpp_lib_format - -#include -#include - -namespace plg { - namespace detail { - // Concept to match string-like types including char* and const char* - template - concept is_string_like = requires(T v) { - { std::string_view(v) }; - }; - } - - template - constexpr std::string join(const Range& range, std::string_view separator) { - std::string result; - - auto it = range.begin(); - auto end = range.end(); - - if (it == end) return result; - - // First pass: compute total size - size_t total_size = 0; - size_t count = 0; - - for (auto tmp = it; tmp != end; ++tmp) { - using Elem = std::decay_t; - if constexpr (detail::is_string_like) { - total_size += std::string_view(*tmp).size(); - } else { - total_size += std::formatted_size("{}", *tmp); - } - ++count; - } - if (count > 1) { - total_size += (count - 1) * separator.size(); - } - result.reserve(total_size); - - auto in = std::back_inserter(result); - - // Second pass: actual formatting - /*if (it != end)*/ { - std::format_to(in, "{}", *it++); - } - while (it != end) { - std::format_to(in, "{}{}", separator, *it++); - } - - return result; - } - - template - constexpr std::string join(const Range& range, Proj&& proj, std::string_view separator) { - std::string result; - - auto it = range.begin(); - auto end = range.end(); - - if (it == end) return result; - - // First pass: compute total size - size_t total_size = 0; - size_t count = 0; - - for (auto tmp = it; tmp != end; ++tmp) { - auto&& projected = std::invoke(std::forward(proj), *tmp); - using Elem = std::decay_t; - - if constexpr (detail::is_string_like) { - total_size += std::string_view(*projected).size(); - } else { - total_size += std::formatted_size("{}", projected); - } - ++count; - } - if (count > 1) { - total_size += (count - 1) * separator.size(); - } - result.reserve(total_size); - - auto out = std::back_inserter(result); - - // Second pass: actual formatting - { - auto&& projected = std::invoke(std::forward(proj), *it++); - std::format_to(out, "{}", projected); - } - while (it != end) { - auto&& projected = std::invoke(std::forward(proj), *it++); - std::format_to(out, "{}{}", separator, projected); - } - - return result; - } -} diff --git a/include/plg/hash.hpp b/include/plg/hash.hpp index de2e050..53f37bf 100644 --- a/include/plg/hash.hpp +++ b/include/plg/hash.hpp @@ -76,4 +76,12 @@ namespace plg { (hash_combine(seed, args), ...); // fold expression return seed; } + + template + struct pair_hash { + size_t operator()(std::pair const& p) const { + return hash_combine_all(p.first, p.second); + } + }; + } diff --git a/include/plg/macro.hpp b/include/plg/macro.hpp index 6008e31..a072fec 100644 --- a/include/plg/macro.hpp +++ b/include/plg/macro.hpp @@ -257,7 +257,7 @@ # define PLUGIFY_NOINLINE [[gnu::noinline]] #elif PLUGIFY_COMPILER_MSVC # pragma warning(error: 4714) -# define PLUGIFY_FORCE_INLINE __forceinline +# define PLUGIFY_FORCE_INLINE [[msvc::forceinline]] # define PLUGIFY_NOINLINE __declspec(noinline) #else # define PLUGIFY_FORCE_INLINE inline diff --git a/include/plg/path.hpp b/include/plg/path.hpp new file mode 100644 index 0000000..61343f3 --- /dev/null +++ b/include/plg/path.hpp @@ -0,0 +1,52 @@ +#pragma once + +#include +#include +#include + +namespace plg { + using path_view = std::basic_string_view; + using path_string = std::filesystem::path::string_type; + using path_char = path_string::value_type; + using path_diff_t = path_string::difference_type; + +#if _WIN32 +#define PLUGIFY_PATH_LITERAL(x) L##x +#else +#define PLUGIFY_PATH_LITERAL(x) x +#endif + + template + bool insensitive_equals(const path_char lhs, const path_char rhs) { + if constexpr (std::is_same_v) { + return std::towlower(lhs) == rhs; // NOLINT + } else { + return std::tolower(lhs) == rhs; + } + } + + inline bool insensitive_equals(const path_view lhs, const path_view rhs) { + return lhs.size() == rhs.size() && std::equal(lhs.cbegin(), lhs.cend(), rhs.cbegin(), insensitive_equals); + } + + inline path_view extension_view(const std::filesystem::path& path) { + constexpr path_diff_t extension_size = 4; + if (!path.has_extension()) { return {}; } + const path_string& path_str = path.native(); + const auto offset = static_cast(path_str.size()) - extension_size; + if (offset <= 0) { return {}; } + return { path_str.cbegin() + offset, path_str.cend() }; + } + + inline bool has_extension(const std::filesystem::path& path, const path_view extension) { + return insensitive_equals(extension_view(path), extension); + } + + inline auto as_string(const std::filesystem::path& p) { +#if _WIN32 + return p.string(); // returns std::string by value +#else + return p.native(); // returns const std::string& +#endif + } +} diff --git a/include/plg/string.hpp b/include/plg/string.hpp index 17b4b29..175bed2 100644 --- a/include/plg/string.hpp +++ b/include/plg/string.hpp @@ -44,20 +44,63 @@ namespace plg { namespace detail { - template - struct is_allocator : std::false_type {}; - - template - struct is_allocator().allocate(std::size_t{}))>> : std::true_type {}; - - template - constexpr bool is_allocator_v = is_allocator::value; - - template - using is_traits = std::conjunction, std::is_integral>; - - template - constexpr bool is_traits_v = is_traits::value; + template + concept is_allocator = requires(Alloc& a, std::size_t n) { + typename std::allocator_traits::value_type; + typename std::allocator_traits::pointer; + typename std::allocator_traits::const_pointer; + typename std::allocator_traits::size_type; + typename std::allocator_traits::difference_type; + + { std::allocator_traits::allocate(a, n) } + -> std::convertible_to::pointer>; + + requires requires(typename std::allocator_traits::pointer p) { + std::allocator_traits::deallocate(a, p, n); + }; + }; + + template + concept is_char_traits = requires { + // Required type definitions + typename Traits::char_type; + typename Traits::int_type; + typename Traits::off_type; + typename Traits::pos_type; + typename Traits::state_type; + } && requires( + typename Traits::char_type c1, + typename Traits::char_type c2, + typename Traits::char_type& cr, + const typename Traits::char_type& ccr, + typename Traits::char_type* p, + const typename Traits::char_type* cp, + typename Traits::int_type i1, + typename Traits::int_type i2, + std::size_t n + ) { + // Character operations + { Traits::assign(cr, ccr) } -> std::same_as; + { Traits::eq(ccr, ccr) } -> std::convertible_to; + { Traits::lt(ccr, ccr) } -> std::convertible_to; + + // String operations + { Traits::compare(cp, cp, n) } -> std::convertible_to; + { Traits::length(cp) } -> std::convertible_to; + { Traits::find(cp, n, ccr) } -> std::convertible_to; + + // Memory operations + { Traits::move(p, cp, n) } -> std::same_as; + { Traits::copy(p, cp, n) } -> std::same_as; + { Traits::assign(p, n, c1) } -> std::same_as; + + // int_type operations + { Traits::not_eof(i1) } -> std::same_as; + { Traits::to_char_type(i1) } -> std::same_as; + { Traits::to_int_type(c1) } -> std::same_as; + { Traits::eq_int_type(i1, i2) } -> std::convertible_to; + { Traits::eof() } -> std::same_as; + }; struct uninitialized_size_tag {}; @@ -68,11 +111,12 @@ namespace plg { template concept string_compatible_range = std::ranges::input_range && std::convertible_to, Type>; #endif // PLUGIFY_STRING_CONTAINERS_RANGES - }// namespace detail + + } // namespace detail // basic_string // based on implementations from libc++, libstdc++ and Microsoft STL - template, typename Allocator = plg::allocator> requires (detail::is_traits_v && detail::is_allocator_v) + template, detail::is_allocator Allocator = plg::allocator> class basic_string { private: using allocator_traits = std::allocator_traits; @@ -1926,3 +1970,107 @@ namespace std { struct formatter, Allocator>> : plg::detail::string_formatter_base {}; }// namespace std #endif // PLUGIFY_STRING_NO_STD_FORMAT + +template +std::ostream& operator<<(std::ostream& os, const plg::basic_string& str) { + os << str.c_str(); + return os; +} + +#ifndef PLUGIFY_STRING_NO_STD_FORMAT +#include + +namespace plg { + namespace detail { + // Concept to match string-like types including char* and const char* + template + concept is_string_like = requires(T v) { + { std::string_view(v) }; + }; + } + + template + constexpr string join(const Range& range, std::string_view separator) { + string result; + + auto it = range.cbegin(); + auto end = range.cend(); + + if (it == end) return result; + + // First pass: compute total size + size_t total_size = 0; + size_t count = 0; + + for (auto tmp = it; tmp != end; ++tmp) { + using Elem = std::decay_t; + if constexpr (detail::is_string_like) { + total_size += std::string_view(*tmp).size(); + } else { + total_size += std::formatted_size("{}", *tmp); + } + ++count; + } + if (count > 1) { + total_size += (count - 1) * separator.size(); + } + result.reserve(total_size); + + auto in = std::back_inserter(result); + + // Second pass: actual formatting + /*if (it != end)*/ { + std::format_to(in, "{}", *it++); + } + while (it != end) { + std::format_to(in, "{}{}", separator, *it++); + } + + return result; + } + + template + constexpr string join(const Range& range, Proj&& proj, std::string_view separator) { + string result; + + auto it = range.cbegin(); + auto end = range.cend(); + + if (it == end) return result; + + // First pass: compute total size + size_t total_size = 0; + size_t count = 0; + + for (auto tmp = it; tmp != end; ++tmp) { + auto&& projected = std::invoke(std::forward(proj), *tmp); + using Elem = std::decay_t; + + if constexpr (detail::is_string_like) { + total_size += std::string_view(*projected).size(); + } else { + total_size += std::formatted_size("{}", projected); + } + ++count; + } + if (count > 1) { + total_size += (count - 1) * separator.size(); + } + result.reserve(total_size); + + auto out = std::back_inserter(result); + + // Second pass: actual formatting + { + auto&& projected = std::invoke(std::forward(proj), *it++); + std::format_to(out, "{}", projected); + } + while (it != end) { + auto&& projected = std::invoke(std::forward(proj), *it++); + std::format_to(out, "{}{}", separator, projected); + } + + return result; + } +} // namespace plugify +#endif // PLUGIFY_STRING_NO_STD_FORMAT \ No newline at end of file diff --git a/include/plg/vector.hpp b/include/plg/vector.hpp index b340a61..3ec8750 100644 --- a/include/plg/vector.hpp +++ b/include/plg/vector.hpp @@ -27,7 +27,32 @@ #include "plg/allocator.hpp" namespace plg { - template + namespace detail { + template + concept is_alloc = requires(Alloc& a, std::size_t n) { + typename std::allocator_traits::value_type; + typename std::allocator_traits::pointer; + typename std::allocator_traits::const_pointer; + typename std::allocator_traits::size_type; + typename std::allocator_traits::difference_type; + + { std::allocator_traits::allocate(a, n) } + -> std::convertible_to::pointer>; + + requires requires(typename std::allocator_traits::pointer p) { + std::allocator_traits::deallocate(a, p, n); + }; + }; + + struct initialized_value_tag {}; + +#if PLUGIFY_VECTOR_CONTAINERS_RANGES + template + concept vector_compatible_range = std::ranges::input_range && std::convertible_to, Type>; +#endif + } // namespace detail + + template struct vector_iterator { using allocator_traits = std::allocator_traits; public: @@ -119,7 +144,7 @@ namespace plg { } #endif // __cpp_impl_three_way_comparison - template + template struct vector_const_iterator { using allocator_traits = std::allocator_traits; public: @@ -152,14 +177,14 @@ namespace plg { ++_current; return *this; } - constexpr vector_const_iterator operator++(int) const noexcept { + constexpr vector_const_iterator operator++(int) noexcept { return vector_const_iterator(_current++); } constexpr vector_const_iterator& operator--() noexcept { --_current; return *this; } - constexpr vector_const_iterator operator--(int) const noexcept { + constexpr vector_const_iterator operator--(int) noexcept { return vector_const_iterator(_current--); } constexpr vector_const_iterator& operator+=(const difference_type n) noexcept { @@ -232,18 +257,9 @@ namespace plg { return lhs.base() <=> rhs.base(); } - namespace detail { - struct initialized_value_tag {}; - -#if PLUGIFY_VECTOR_CONTAINERS_RANGES - template - concept vector_compatible_range = std::ranges::input_range && std::convertible_to, Type>; -#endif - } // namespace detail - // vector // based on implementations from libc++, libstdc++ and Microsoft STL - template> + template> class vector { using allocator_traits = std::allocator_traits; public: diff --git a/include/plg/version.hpp b/include/plg/version.hpp index 355d33f..b12ac87 100644 --- a/include/plg/version.hpp +++ b/include/plg/version.hpp @@ -6,10 +6,7 @@ #include #include #include -#include -#include #include -#include #if __has_include() #include #else @@ -22,11 +19,12 @@ #include "plg/hash.hpp" #include "plg/macro.hpp" +#include "plg/string.hpp" +#include "plg/vector.hpp" // from https://github.com/Neargye/semver namespace plg { namespace detail { - template struct resize_uninitialized { constexpr static auto resize(T& str, std::size_t size) -> std::void_t { @@ -67,7 +65,7 @@ namespace plg { struct prerelease_identifier { prerelease_identifier_type type; - std::string identifier; + string identifier; }; class version_parser; @@ -92,19 +90,19 @@ namespace plg { constexpr I2 minor() const noexcept { return minor_; } constexpr I3 patch() const noexcept { return patch_; } - constexpr const std::string& prerelease_tag() const { return prerelease_tag_; } - constexpr const std::string& build_metadata() const { return build_metadata_; } + constexpr const string& prerelease_tag() const { return prerelease_tag_; } + constexpr const string& build_metadata() const { return build_metadata_; } - constexpr std::string to_string() const; + constexpr string to_string() const; private: I1 major_ = 0; I2 minor_ = 1; I3 patch_ = 0; - std::string prerelease_tag_; - std::string build_metadata_; + string prerelease_tag_; + string build_metadata_; - std::vector prerelease_identifiers; + vector prerelease_identifiers; constexpr std::size_t length() const noexcept { return detail::length(major_) + detail::length(minor_) + detail::length(patch_) + 2 @@ -124,9 +122,9 @@ namespace plg { }; template - constexpr std::string version::to_string() const { - std::string result; - detail::resize_uninitialized{}.resize(result, length()); + constexpr string version::to_string() const { + string result; + detail::resize_uninitialized{}.resize(result, length()); auto it = result.end(); if (!build_metadata_.empty()) { @@ -169,7 +167,6 @@ namespace plg { }; namespace detail { - constexpr from_chars_result success(const char* ptr) noexcept { return from_chars_result{ ptr, std::errc{} }; } @@ -295,28 +292,28 @@ namespace plg { class token_stream { public: constexpr token_stream() = default; - constexpr explicit token_stream(std::vector tokens) noexcept : tokens(std::move(tokens)) {} + constexpr explicit token_stream(vector tokens) noexcept : tokens_(std::move(tokens)) {} constexpr void push(const token& token) noexcept { - tokens.push_back(token); + tokens_.push_back(token); } constexpr token advance() noexcept { - const token token = get(current); - ++current; + const token token = get(current_); + ++current_; return token; } constexpr token peek(std::size_t k = 0) const noexcept { - return get(current + k); + return get(current_ + k); } constexpr token previous() const noexcept { - return get(current - 1); + return get(current_ - 1); } constexpr bool advanceIfMatch(token& token, token_type type) noexcept { - if (get(current).type != type) { + if (get(current_).type != type) { return false; } @@ -338,11 +335,11 @@ namespace plg { } private: - std::size_t current = 0; - std::vector tokens; + std::size_t current_ = 0; + vector tokens_; constexpr token get(std::size_t i) const noexcept { - return tokens[i]; + return tokens_[i]; } }; @@ -481,7 +478,7 @@ namespace plg { class version_parser { public: - constexpr explicit version_parser(token_stream& stream) : stream{stream} { + constexpr explicit version_parser(token_stream& stream) : stream_{stream} { } template @@ -493,8 +490,8 @@ namespace plg { return result; } - if (!stream.consume(token_type::dot)) { - return failure(stream.previous().lexeme); + if (!stream_.consume(token_type::dot)) { + return failure(stream_.previous().lexeme); } result = parse_number(out.minor_); @@ -502,8 +499,8 @@ namespace plg { return result; } - if (!stream.consume(token_type::dot)) { - return failure(stream.previous().lexeme); + if (!stream_.consume(token_type::dot)) { + return failure(stream_.previous().lexeme); } result = parse_number(out.patch_); @@ -511,14 +508,14 @@ namespace plg { return result; } - if (stream.advanceIfMatch(token_type::hyphen)) { + if (stream_.advanceIfMatch(token_type::hyphen)) { result = parse_prerelease_tag(out.prerelease_tag_, out.prerelease_identifiers); if (!result) { return result; } } - if (stream.advanceIfMatch(token_type::plus)) { + if (stream_.advanceIfMatch(token_type::plus)) { result = parse_build_metadata(out.build_metadata_); if (!result) { return result; @@ -530,11 +527,11 @@ namespace plg { private: - token_stream& stream; + token_stream& stream_; template constexpr from_chars_result parse_number(Int& out) { - token token = stream.advance(); + token token = stream_.advance(); if (!is_digit(token)) { return failure(token.lexeme); @@ -545,30 +542,30 @@ namespace plg { if (first_digit == 0) { out = static_cast(result); - return success(stream.peek().lexeme); + return success(stream_.peek().lexeme); } - while (stream.advanceIfMatch(token, token_type::digit)) { + while (stream_.advanceIfMatch(token, token_type::digit)) { result = result * 10 + std::get(token.value); } if (detail::number_in_range(result)) { out = static_cast(result); - return success(stream.peek().lexeme); + return success(stream_.peek().lexeme); } return failure(token.lexeme, std::errc::result_out_of_range); } - constexpr from_chars_result parse_prerelease_tag(std::string& out, std::vector& out_identifiers) { - std::string result; + constexpr from_chars_result parse_prerelease_tag(string& out, vector& out_identifiers) { + string result; do { if (!result.empty()) { result.push_back('.'); } - std::string identifier; + string identifier; if (const auto res = parse_prerelease_identifier(identifier); !res) { return res; } @@ -576,35 +573,35 @@ namespace plg { result.append(identifier); out_identifiers.push_back(make_prerelease_identifier(identifier)); - } while (stream.advanceIfMatch(token_type::dot)); + } while (stream_.advanceIfMatch(token_type::dot)); out = result; - return success(stream.peek().lexeme); + return success(stream_.peek().lexeme); } - constexpr from_chars_result parse_build_metadata(std::string& out) { - std::string result; + constexpr from_chars_result parse_build_metadata(string& out) { + string result; do { if (!result.empty()) { result.push_back('.'); } - std::string identifier; + string identifier; if (const auto res = parse_build_identifier(identifier); !res) { return res; } result.append(identifier); - } while (stream.advanceIfMatch(token_type::dot)); + } while (stream_.advanceIfMatch(token_type::dot)); out = result; - return success(stream.peek().lexeme); + return success(stream_.peek().lexeme); } - constexpr from_chars_result parse_prerelease_identifier(std::string& out) { - std::string result; - token token = stream.advance(); + constexpr from_chars_result parse_prerelease_identifier(string& out) { + string result; + token token = stream_.advance(); do { switch (token.type) { @@ -635,13 +632,13 @@ namespace plg { default: return failure(token.lexeme); } - } while (stream.advanceIfMatch(token, token_type::hyphen) || stream.advanceIfMatch(token, token_type::letter) || stream.advanceIfMatch(token, token_type::digit)); + } while (stream_.advanceIfMatch(token, token_type::hyphen) || stream_.advanceIfMatch(token, token_type::letter) || stream_.advanceIfMatch(token, token_type::digit)); out = result; - return success(stream.peek().lexeme); + return success(stream_.peek().lexeme); } - constexpr detail::prerelease_identifier make_prerelease_identifier(const std::string& identifier) { + constexpr detail::prerelease_identifier make_prerelease_identifier(const string& identifier) { auto type = detail::prerelease_identifier_type::numeric; for (char c : identifier) { if (c == '-' || detail::is_letter(c)) { @@ -652,9 +649,9 @@ namespace plg { return detail::prerelease_identifier{ type, identifier }; } - constexpr from_chars_result parse_build_identifier(std::string& out) { - std::string result; - token token = stream.advance(); + constexpr from_chars_result parse_build_identifier(string& out) { + string result; + token token = stream_.advance(); do { switch (token.type) { @@ -673,10 +670,10 @@ namespace plg { default: return failure(token.lexeme); } - } while (stream.advanceIfMatch(token, token_type::hyphen) || stream.advanceIfMatch(token, token_type::letter) || stream.advanceIfMatch(token, token_type::digit)); + } while (stream_.advanceIfMatch(token, token_type::hyphen) || stream_.advanceIfMatch(token, token_type::letter) || stream_.advanceIfMatch(token, token_type::digit)); out = result; - return success(stream.peek().lexeme); + return success(stream_.peek().lexeme); } constexpr bool is_leading_zero(int digit) noexcept { @@ -684,12 +681,12 @@ namespace plg { return false; } - int k = 0; + size_t k = 0; int alpha_numerics = 0; int digits = 0; while (true) { - const token token = stream.peek(k); + const token token = stream_.peek(k); if (!is_alphanumeric(token)) { break; @@ -827,31 +824,44 @@ namespace plg { template class range_comparator { public: - constexpr range_comparator(const version& v, range_operator op) noexcept : v(v), op(op) {} + constexpr range_comparator(const version& v, range_operator op) noexcept : v_(v), op_(op) {} constexpr bool contains(const version& other) const noexcept { - switch (op) { + switch (op_) { case range_operator::less: - return detail::compare_parsed(other, v, version_compare_option::include_prerelease) < 0; + return detail::compare_parsed(other, v_, version_compare_option::include_prerelease) < 0; case range_operator::less_or_equal: - return detail::compare_parsed(other, v, version_compare_option::include_prerelease) <= 0; + return detail::compare_parsed(other, v_, version_compare_option::include_prerelease) <= 0; case range_operator::greater: - return detail::compare_parsed(other, v, version_compare_option::include_prerelease) > 0; + return detail::compare_parsed(other, v_, version_compare_option::include_prerelease) > 0; case range_operator::greater_or_equal: - return detail::compare_parsed(other, v, version_compare_option::include_prerelease) >= 0; + return detail::compare_parsed(other, v_, version_compare_option::include_prerelease) >= 0; case range_operator::equal: - return detail::compare_parsed(other, v, version_compare_option::include_prerelease) == 0; + return detail::compare_parsed(other, v_, version_compare_option::include_prerelease) == 0; } return false; } - constexpr const version& get_version() const noexcept { return v; } + constexpr const version& get_version() const noexcept { return v_; } + + constexpr range_operator get_operator() const noexcept { return op_; } - constexpr range_operator get_operator() const noexcept { return op; } + constexpr string to_string() const { + string result; + switch (op_) { + case range_operator::less: result += "<"; break; + case range_operator::less_or_equal: result += "<="; break; + case range_operator::greater: result += ">"; break; + case range_operator::greater_or_equal: result += ">="; break; + case range_operator::equal: result += "="; break; + } + result += v_.to_string(); + return result; + } private: - version v; - range_operator op; + version v_; + range_operator op_; }; class range_parser; @@ -868,36 +878,40 @@ namespace plg { } } - return std::all_of(ranges_comparators.begin(), ranges_comparators.end(), [&](const auto& ranges_comparator) { + return std::all_of(ranges_comparators_.begin(), ranges_comparators_.end(), [&](const auto& ranges_comparator) { return ranges_comparator.contains(v); }); } - constexpr const auto begin() const noexcept { - return ranges_comparators.begin(); + constexpr auto begin() const noexcept { + return ranges_comparators_.begin(); } - constexpr const auto end() const noexcept { - return ranges_comparators.end(); + constexpr auto end() const noexcept { + return ranges_comparators_.end(); } constexpr std::size_t size() const noexcept { - return ranges_comparators.size(); + return ranges_comparators_.size(); } constexpr bool empty() const noexcept { - return ranges_comparators.empty(); + return ranges_comparators_.empty(); + } + + constexpr string to_string() const { + return join(ranges_comparators_, " "); } private: - std::vector> ranges_comparators; + vector> ranges_comparators_; constexpr bool match_at_least_one_comparator_with_prerelease(const version& v) const noexcept { if (v.prerelease_tag().empty()) { return true; } - return std::any_of(ranges_comparators.begin(), ranges_comparators.end(), [&](const auto& ranges_comparator) { + return std::any_of(ranges_comparators_.begin(), ranges_comparators_.end(), [&](const auto& ranges_comparator) { const bool has_prerelease = !ranges_comparator.get_version().prerelease_tag().empty(); const bool equal_without_prerelease = detail::compare_parsed(v, ranges_comparator.get_version(), version_compare_option::exclude_prerelease) == 0; return has_prerelease && equal_without_prerelease; @@ -912,39 +926,43 @@ namespace plg { friend class detail::range_parser; constexpr bool contains(const version& v, version_compare_option option = version_compare_option::exclude_prerelease) const noexcept { - return std::any_of(ranges.begin(), ranges.end(), [&](const auto& range) { + return std::any_of(ranges_.begin(), ranges_.end(), [&](const auto& range) { return range.contains(v, option); }); } - constexpr const auto begin() const noexcept { - return ranges.begin(); + constexpr auto begin() const noexcept { + return ranges_.begin(); } - constexpr const auto end() const noexcept { - return ranges.end(); + constexpr auto end() const noexcept { + return ranges_.end(); } constexpr std::size_t size() const noexcept { - return ranges.size(); + return ranges_.size(); } constexpr bool empty() const noexcept { - return ranges.empty(); + return ranges_.empty(); + } + + constexpr string to_string() const { + return join(ranges_, " "); } private: - std::vector> ranges; + vector> ranges_; }; namespace detail { class range_parser { public: - constexpr explicit range_parser(token_stream stream) noexcept : stream(std::move(stream)) {} + constexpr explicit range_parser(token_stream stream) noexcept : stream_(std::move(stream)) {} template constexpr from_chars_result parse(range_set& out) noexcept { - std::vector> ranges; + vector> ranges; do { @@ -956,54 +974,54 @@ namespace plg { ranges.push_back(range); skip_whitespaces(); - } while (stream.advanceIfMatch(token_type::logical_or)); + } while (stream_.advanceIfMatch(token_type::logical_or)); - out.ranges = std::move(ranges); + out.ranges_ = std::move(ranges); - return success(stream.peek().lexeme); + return success(stream_.peek().lexeme); } private: - token_stream stream; + token_stream stream_; template constexpr from_chars_result parse_range(detail::range& out) noexcept { do { skip_whitespaces(); - if (const auto res = parse_range_comparator(out.ranges_comparators); !res) { + if (const auto res = parse_range_comparator(out.ranges_comparators_); !res) { return res; } skip_whitespaces(); - } while (stream.check(token_type::range_operator) || stream.check(token_type::digit)); + } while (stream_.check(token_type::range_operator) || stream_.check(token_type::digit)); - return success(stream.peek().lexeme); + return success(stream_.peek().lexeme); } template - constexpr from_chars_result parse_range_comparator(std::vector>& out) noexcept { + constexpr from_chars_result parse_range_comparator(vector>& out) noexcept { range_operator op = range_operator::equal; token token; - if (stream.advanceIfMatch(token, token_type::range_operator)) { + if (stream_.advanceIfMatch(token, token_type::range_operator)) { op = std::get(token.value); } skip_whitespaces(); version ver; - version_parser parser{ stream }; + version_parser parser{ stream_ }; if (const auto res = parser.parse(ver); !res) { return res; } out.emplace_back(ver, op); - return success(stream.peek().lexeme); + return success(stream_.peek().lexeme); } constexpr void skip_whitespaces() noexcept { - while (stream.advanceIfMatch(token_type::space)) { + while (stream_.advanceIfMatch(token_type::space)) { ; } } @@ -1060,5 +1078,38 @@ namespace std { return std::format_to(ctx.out(), "{}", ver.to_string()); } }; + template + struct formatter> { + constexpr auto parse(std::format_parse_context& ctx) { + return ctx.begin(); + } + + template + auto format(const plg::range_set& ver, FormatContext& ctx) const { + return std::format_to(ctx.out(), "{}", ver.to_string()); + } + }; + template + struct formatter> { + constexpr auto parse(std::format_parse_context& ctx) { + return ctx.begin(); + } + + template + auto format(const plg::detail::range& ver, FormatContext& ctx) const { + return std::format_to(ctx.out(), "{}", ver.to_string()); + } + }; + template + struct formatter> { + constexpr auto parse(std::format_parse_context& ctx) { + return ctx.begin(); + } + + template + auto format(const plg::detail::range_comparator& ver, FormatContext& ctx) const { + return std::format_to(ctx.out(), "{}", ver.to_string()); + } + }; }// namespace std #endif // PLUGIFY_VECTOR_NO_STD_FORMAT diff --git a/plugify-module-cpp.pmodule.in b/plugify-module-cpp.pmodule.in index b36bd60..8347419 100644 --- a/plugify-module-cpp.pmodule.in +++ b/plugify-module-cpp.pmodule.in @@ -1,7 +1,7 @@ { "$schema": "https://raw.githubusercontent.com/untrustedmodders/plugify/refs/heads/main/schemas/language-module.schema.json", "version": "${CPPLM_VERSION}", - "name": "plugify-module-cpp", + "name": "${CPPLM_PACKAGE}", "language": "cpp", "description": "Adds support for C++ plugins", "author": "untrustedmodders", diff --git a/src/module.cpp b/src/module.cpp index 7f1093e..2a33b23 100644 --- a/src/module.cpp +++ b/src/module.cpp @@ -30,6 +30,7 @@ void CppLanguageModule::Shutdown() { } void CppLanguageModule::OnUpdate([[maybe_unused]] std::chrono::milliseconds dt) { + } bool CppLanguageModule::IsDebugBuild() { diff --git a/test/cross_call_master/CMakeLists.txt b/test/cross_call_master/CMakeLists.txt index 88875ad..6b45aae 100644 --- a/test/cross_call_master/CMakeLists.txt +++ b/test/cross_call_master/CMakeLists.txt @@ -33,7 +33,7 @@ include(cmake/CompatFormat.cmake) set(PLUGIN_SOURCES "plugin.cpp" "simple_tests.hpp" "simple_tests.cpp") add_library(${PROJECT_NAME} SHARED ${PLUGIN_SOURCES}) -target_include_directories(${PROJECT_NAME} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}) +target_include_directories(${PROJECT_NAME} PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/external/plugify/include") if(MSVC) target_compile_options(${PROJECT_NAME} PRIVATE /W4 /WX) diff --git a/test/cross_call_master/plg/allocator.hpp b/test/cross_call_master/external/plugify/include/plg/allocator.hpp similarity index 100% rename from test/cross_call_master/plg/allocator.hpp rename to test/cross_call_master/external/plugify/include/plg/allocator.hpp diff --git a/test/cross_call_master/plg/any.hpp b/test/cross_call_master/external/plugify/include/plg/any.hpp similarity index 100% rename from test/cross_call_master/plg/any.hpp rename to test/cross_call_master/external/plugify/include/plg/any.hpp diff --git a/test/cross_call_master/plg/api.hpp b/test/cross_call_master/external/plugify/include/plg/api.hpp similarity index 100% rename from test/cross_call_master/plg/api.hpp rename to test/cross_call_master/external/plugify/include/plg/api.hpp diff --git a/test/cross_call_master/plg/debugging.hpp b/test/cross_call_master/external/plugify/include/plg/debugging.hpp similarity index 100% rename from test/cross_call_master/plg/debugging.hpp rename to test/cross_call_master/external/plugify/include/plg/debugging.hpp diff --git a/test/cross_call_master/plg/enum.hpp b/test/cross_call_master/external/plugify/include/plg/enum.hpp similarity index 98% rename from test/cross_call_master/plg/enum.hpp rename to test/cross_call_master/external/plugify/include/plg/enum.hpp index 5a2b373..84f6d23 100644 --- a/test/cross_call_master/plg/enum.hpp +++ b/test/cross_call_master/external/plugify/include/plg/enum.hpp @@ -55,7 +55,7 @@ namespace plg { template constexpr auto value(std::size_t v) { - return static_cast(ENUM_MIN_VALUE + v); + return static_cast(ENUM_MIN_VALUE + static_cast(v)); } template diff --git a/test/cross_call_master/external/plugify/include/plg/expected.hpp b/test/cross_call_master/external/plugify/include/plg/expected.hpp new file mode 100644 index 0000000..d6bc137 --- /dev/null +++ b/test/cross_call_master/external/plugify/include/plg/expected.hpp @@ -0,0 +1,1430 @@ +#pragma once + +#include "plg/macro.hpp" + +#ifdef __cpp_lib_expected +#include +namespace plg { + using std::expected; + using std::unexpected; +} +#else +#include +#include +#include +#include +#include +#include +#include +#include + +// from https://github.com/RishabhRD +namespace plg { + template + class unexpected { + public: + using value_type = E; + constexpr unexpected(unexpected const&) = default; + constexpr unexpected(unexpected&&) = default;// NOLINT + constexpr auto operator=(unexpected const&) -> unexpected& = default; + constexpr auto operator=(unexpected&&) -> unexpected& = default;// NOLINT + ~unexpected() = default; + + template + requires std::constructible_from + constexpr explicit unexpected(std::in_place_t /*unused*/, Args&&... args) + : val(std::forward(args)...) {} + + template + requires std::constructible_from&, Args...> + constexpr explicit unexpected(std::in_place_t /*unused*/, + std::initializer_list i_list, + Args&&... args) + : val(i_list, std::forward(args)...) {} + + template + requires(!std::same_as, unexpected>) && + (!std::same_as, std::in_place_t>) && + std::constructible_from + constexpr explicit unexpected(Err&& err)// NOLINT + : val(std::forward(err)) {} + + constexpr auto value() const& noexcept -> E const& { return val; } + constexpr auto value() & noexcept -> E& { return val; } + constexpr auto value() const&& noexcept -> E const&& { + return std::move(val); + } + constexpr auto value() && noexcept -> E&& { return std::move(val); } + + constexpr void swap(unexpected& other) noexcept( + std::is_nothrow_swappable_v) + requires(std::is_swappable_v) + { + using std::swap; + swap(val, other.val); + } + + template + requires(requires(E const& x, E2 const& y) { + { x == y } -> std::convertible_to; + }) + friend constexpr auto operator==(unexpected const& x, unexpected const& y) -> bool { + return x.value() == y.value(); + } + + friend constexpr void swap(unexpected& x, unexpected& y) noexcept(noexcept(x.swap(y))) + requires(std::is_swappable_v) + { + x.swap(y); + } + + private: + E val; + }; + + template + unexpected(E) -> unexpected; + + template + class bad_expected_access; + + template<> + class bad_expected_access : public std::exception { + protected: + bad_expected_access() noexcept = default; + bad_expected_access(bad_expected_access const&) = default; + bad_expected_access(bad_expected_access&&) = default; + auto operator=(bad_expected_access const&) -> bad_expected_access& = default; + auto operator=(bad_expected_access&&) -> bad_expected_access& = default; + ~bad_expected_access() override = default; + + public: + auto what() const noexcept -> char const* override {// NOLINT + return "bad expected access"; + } + }; + + template + class bad_expected_access : public bad_expected_access { + public: + explicit bad_expected_access(E e) : val(std::move(e)) {} + auto what() const noexcept -> char const* override {// NOLINT + return "bad expected access"; + } + + auto error() & noexcept -> E& { return val; } + auto error() const& noexcept -> E const& { return val; } + auto error() && noexcept -> E&& { return std::move(val); } + auto error() const&& noexcept -> const E&& { return std::move(val); } + + private: + E val; + }; + + struct unexpect_t {}; + inline constexpr unexpect_t unexpect{}; + + namespace detail { + template + concept non_void_destructible = std::same_as || std::destructible; + } + + template + class expected; + + namespace detail { + template + concept expected_constructible_from_other = + std::constructible_from && + std::constructible_from && + (!std::constructible_from&>) && + (!std::constructible_from>) && + (!std::constructible_from const&>) && + (!std::constructible_from const>) && + (!std::convertible_to&, T>) && + (!std::convertible_to&&, T>) && + (!std::convertible_to const&, T>) && + (!std::convertible_to const&&, T>) && + (!std::constructible_from, expected&>) && + (!std::constructible_from, expected>) && + (!std::constructible_from, expected const&>) && + (!std::constructible_from, expected const>); + + template + concept is_unexpected = + std::same_as, unexpected>; + + template + concept is_expected = + std::same_as, + expected>; + + // This function makes sure expected doesn't get into valueless_by_exception + // state due to any exception while assignment + template + constexpr void reinit_expected(T& newval, U& oldval, Args&&... args) { + if constexpr (std::is_nothrow_constructible_v) { + std::destroy_at(std::addressof(oldval)); + std::construct_at(std::addressof(newval), std::forward(args)...); + } else if constexpr (std::is_nothrow_move_constructible_v) { + T tmp(std::forward(args)...); + std::destroy_at(std::addressof(oldval)); + std::construct_at(std::addressof(newval), std::move(tmp)); + } else { + U tmp(std::move(oldval)); + std::destroy_at(std::addressof(oldval)); + try { + std::construct_at(std::addressof(newval), std::forward(args)...); + } catch (...) { + std::construct_at(std::addressof(oldval), std::move(tmp)); + throw; + } + } + } + + }// namespace detail + + template + class expected { + public: + using value_type = T; + using error_type = E; + using unexpected_type = unexpected; + + template + using rebind = expected; + + // constructors + // postcondition: has_value() = true + constexpr expected() + requires std::default_initializable + : val{} {}; + + // postcondition: has_value() = rhs.has_value() + constexpr expected(expected const& rhs) + requires std::copy_constructible && std::copy_constructible && + std::is_trivially_copy_constructible_v && + std::is_trivially_copy_constructible_v + = default; + + // postcondition: has_value() = rhs.has_value() + constexpr expected(expected const& rhs) + requires std::copy_constructible && std::copy_constructible + : has_val(rhs.has_val) { + if (rhs.has_value()) { + std::construct_at(std::addressof(this->val), *rhs); + } else { + std::construct_at(std::addressof(this->unex), rhs.error()); + } + } + + constexpr expected(expected&&) noexcept( + std::is_nothrow_move_constructible_v && + std::is_nothrow_move_constructible_v) + requires std::move_constructible && std::move_constructible && + std::is_trivially_move_constructible_v && + std::is_trivially_move_constructible_v + = default; + + constexpr expected(expected&& rhs) noexcept( + std::is_nothrow_move_constructible_v && + std::is_nothrow_move_constructible_v) + requires std::move_constructible && std::move_constructible + : has_val(rhs.has_value()) { + if (rhs.has_value()) { + std::construct_at(std::addressof(this->val), std::move(*rhs)); + } else { + std::construct_at(std::addressof(this->unex), std::move(rhs.error())); + } + } + + template + requires detail::expected_constructible_from_other + constexpr explicit(!std::convertible_to || + !std::convertible_to) + expected(expected const& rhs)// NOLINT + : has_val(rhs.has_value()) { + using UF = U const&; + using GF = G const&; + if (rhs.has_value()) { + std::construct_at(std::addressof(this->val), std::forward(*rhs)); + } else { + std::construct_at(std::addressof(this->unex), + std::forward(rhs.error())); + } + } + + template + requires detail::expected_constructible_from_other + constexpr explicit(!std::convertible_to || !std::convertible_to) + expected(expected&& rhs)// NOLINT + : has_val(rhs.has_value()) { + using UF = U const&; + using GF = G const&; + if (rhs.has_value()) { + std::construct_at(std::addressof(this->val), std::forward(*rhs)); + } else { + std::construct_at(std::addressof(this->unex), + std::forward(rhs.error())); + } + } + + template + requires(!std::same_as, std::in_place_t>) && + (!std::same_as, std::remove_cvref_t>) && + (!detail::is_unexpected) && + std::constructible_from + constexpr explicit(!std::convertible_to) expected(U&& v)// NOLINT + : val(std::forward(v)) {} + + template + requires std::constructible_from + constexpr explicit(!std::convertible_to) + expected(unexpected const& e)// NOLINT + : has_val{false}, unex(std::forward(e.value())) {} + + template + requires std::constructible_from + constexpr explicit(!std::convertible_to) + expected(unexpected&& e)// NOLINT + : has_val{false}, unex(std::forward(e.value())) {} + + template + requires std::constructible_from + constexpr explicit expected(std::in_place_t /*unused*/, Args&&... args) + : val(std::forward(args)...) {} + + template + requires std::constructible_from&, Args...> + constexpr explicit expected(std::in_place_t /*unused*/, + std::initializer_list il, + Args&&... args) + : val(il, std::forward(args)...) {} + + template + requires std::constructible_from + constexpr explicit expected(unexpect_t /*unused*/, Args&&... args) + : has_val{false}, unex(std::forward(args)...) {} + + template + requires std::constructible_from&, + Args...> + constexpr explicit expected(unexpect_t /*unused*/, + std::initializer_list il, + Args&&... args) + : has_val(false), + unex(il, std::forward(args)...) {} + + // destructor + constexpr ~expected() { + if constexpr (std::is_trivially_destructible_v and + std::is_trivially_destructible_v) { + } else if constexpr (std::is_trivially_destructible_v) { + if (!has_val) { + std::destroy_at(std::addressof(this->unex)); + } + } else if constexpr (std::is_trivially_destructible_v) { + if (has_val) { + std::destroy_at(std::addressof(this->val)); + } + } else { + if (has_val) { + std::destroy_at(std::addressof(this->val)); + } else { + std::destroy_at(std::addressof(this->unex)); + } + } + } + + // assignment + constexpr auto operator=(expected const& rhs)// NOLINT + -> expected& + requires std::is_copy_assignable_v && + std::is_copy_constructible_v && + std::is_copy_assignable_v && + std::is_copy_constructible_v && + (std::is_nothrow_move_constructible_v || + std::is_nothrow_move_constructible_v) + { + if (this->has_value() and rhs.has_value()) { + this->val = *rhs; + } else if (this->has_value()) { + detail::reinit_expected(this->unex, this->val, rhs.error()); + } else if (rhs.has_value()) { + detail::reinit_expected(this->val, this->unex, *rhs); + } else { + this->unex = rhs.error(); + } + has_val = rhs.has_value(); + return *this; + } + + constexpr auto operator=(expected&& rhs)// + noexcept(std::is_nothrow_move_assignable_v && + std::is_nothrow_move_constructible_v && + std::is_nothrow_move_assignable_v && + std::is_nothrow_move_constructible_v) + -> expected& + requires std::is_move_constructible_v && + std::is_move_assignable_v && + std::is_move_constructible_v && + std::is_move_assignable_v && + (std::is_nothrow_move_constructible_v || std::is_nothrow_move_constructible_v) + { + if (this->has_value() and rhs.has_value()) { + this->val = std::move(*rhs); + } else if (this->has_value()) { + detail::reinit_expected(this->unex, this->val, std::move(rhs.error())); + } else if (rhs.has_value()) { + detail::reinit_expected(this->val, this->unex, std::move(*rhs)); + } else { + this->unex = std::move(rhs.error()); + } + has_val = rhs.has_value(); + return *this; + } + + template + constexpr auto operator=(U&& rhs) -> expected& requires(!std::same_as>) && + (!detail::is_unexpected>) && + std::constructible_from&& std::is_assignable_v && + (std::is_nothrow_constructible_v || + std::is_nothrow_move_constructible_v || + std::is_nothrow_move_constructible_v) { + if (this->has_value()) { + this->val = std::forward(rhs); + return *this; + } + detail::reinit_expected(this->val, this->unex, std::forward(rhs)); + has_val = true; + return *this; + } + + template + requires std::constructible_from && + std::is_assignable_v && + (std::is_nothrow_constructible_v || + std::is_nothrow_move_constructible_v || + std::is_nothrow_move_constructible_v) + constexpr auto operator=(unexpected const& e) -> expected& { + using GF = G const&; + if (has_value()) { + detail::reinit_expected(this->unex, this->val, + std::forward(e.value())); + } else { + this->unex = std::forward(e.value()); + } + has_val = false; + return *this; + } + + template + requires std::constructible_from && + std::is_assignable_v && + (std::is_nothrow_constructible_v || + std::is_nothrow_move_constructible_v || + std::is_nothrow_move_constructible_v) + constexpr auto operator=(unexpected&& e) -> expected& { + using GF = G; + if (has_value()) { + detail::reinit_expected(this->unex, this->val, + std::forward(e.value())); + } else { + this->unex = std::forward(e.value()); + } + has_val = false; + return *this; + } + + // modifiers + template + requires std::is_nothrow_constructible_v + constexpr auto emplace(Args&&... args) noexcept -> T& { + if (has_value()) { + std::destroy_at(std::addressof(this->val)); + } else { + std::destroy_at(std::addressof(this->unex)); + has_val = true; + } + return *std::construct_at(std::addressof(this->val), + std::forward(args)...); + } + + template + requires std::is_nothrow_constructible_v&, Args...> + constexpr auto emplace(std::initializer_list il, + Args&&... args) noexcept -> T& { + if (has_value()) { + std::destroy_at(std::addressof(this->val)); + } else { + std::destroy_at(std::addressof(this->unex)); + has_val = true; + } + return *std::construct_at(std::addressof(this->val), il, + std::forward(args)...); + } + + // swap + constexpr void swap(expected& rhs) noexcept( + std::is_nothrow_constructible_v && + std::is_nothrow_swappable_v && + std::is_nothrow_move_constructible_v && + std::is_nothrow_swappable_v) + requires std::is_swappable_v && + std::is_swappable_v && + std::is_move_constructible_v && + std::is_move_constructible_v && + (std::is_nothrow_constructible_v || + std::is_nothrow_constructible_v) + { + if (rhs.has_value()) { + if (has_value()) { + using std::swap; + swap(this->val, rhs.val); + } else { + rhs.swap(*this); + } + } else { + if (has_value()) { + if constexpr (std::is_nothrow_move_constructible_v) { + E tmp(std::move(rhs.unex)); + std::destroy_at(std::addressof(rhs.unex)); + try { + std::construct_at(std::addressof(rhs.val), std::move(this->val)); + std::destroy_at(std::addressof(this->val)); + std::construct_at(std::addressof(this->unex), std::move(tmp)); + } catch (...) { + std::construct_at(std::addressof(rhs.unex), std::move(tmp)); + throw; + } + } else { + T tmp(std::move(this->val)); + std::destroy_at(std::addressof(this->val)); + try { + std::construct_at(std::addressof(this->unex), std::move(rhs.unex)); + std::destroy_at(std::addressof(rhs.unex)); + std::construct_at(std::addressof(rhs.val), std::move(tmp)); + } catch (...) { + std::construct_at(std::addressof(this->val), std::move(tmp)); + throw; + } + } + has_val = false; + rhs.has_val = true; + } else { + using std::swap; + swap(this->unex, rhs.unex); + } + } + } + + // observers + + // precondition: has_value() = true + constexpr auto operator->() const noexcept -> T const* { + return std::addressof(this->val); + } + + // precondition: has_value() = true + constexpr auto operator->() noexcept -> T* { + return std::addressof(this->val); + } + + // precondition: has_value() = true + constexpr auto operator*() const& noexcept -> T const& { return this->val; } + + // precondition: has_value() = true + constexpr auto operator*() & noexcept -> T& { return this->val; } + + // precondition: has_value() = true + constexpr auto operator*() const&& noexcept -> T const&& { + return std::move(this->val); + } + + // precondition: has_value() = true + constexpr auto operator*() && noexcept -> T&& { return std::move(this->val); } + + constexpr explicit operator bool() const noexcept { return has_val; } + + [[nodiscard]] constexpr auto has_value() const noexcept -> bool { + return has_val; + } + + constexpr auto value() const& -> T const& { + if (has_value()) { + return this->val; + } + throw bad_expected_access(error()); + } + + constexpr auto value() & -> T& { + if (has_value()) { + return this->val; + } + throw bad_expected_access(error()); + } + + constexpr auto value() const&& -> T const&& { + if (has_value()) { + return std::move(this->val); + } + throw bad_expected_access(std::move(error())); + } + + constexpr auto value() && -> T&& { + if (has_value()) { + return std::move(this->val); + } + throw bad_expected_access(std::move(error())); + } + + // precondition: has_value() = false + constexpr auto error() const& -> E const& { return this->unex; } + + // precondition: has_value() = false + constexpr auto error() & -> E& { return this->unex; } + + // precondition: has_value() = false + constexpr auto error() const&& -> E const&& { return std::move(this->unex); } + + // precondition: has_value() = false + constexpr auto error() && -> E&& { return std::move(this->unex); } + + template + requires std::is_copy_constructible_v && std::is_convertible_v + constexpr auto value_or(U&& v) const& -> T { + return has_value() ? **this : static_cast(std::forward(v)); + } + + template + requires std::is_move_constructible_v && std::is_convertible_v + constexpr auto value_or(U&& v) && -> T { + return has_value() ? std::move(**this) : static_cast(std::forward(v)); + } + + template>> + requires detail::is_expected && + std::is_same_v && + std::is_copy_constructible_v && std::is_copy_constructible_v + constexpr auto and_then(F&& f) & { + if (has_value()) { + return std::invoke(std::forward(f), **this); + } + return U(unexpect, error()); + } + + template>> + requires detail::is_expected && + std::is_same_v && + std::is_copy_constructible_v && + std::is_copy_constructible_v + constexpr auto and_then(F&& f) const& { + if (has_value()) { + return std::invoke(std::forward(f), **this); + } + return U(unexpect, error()); + } + + template>> + requires detail::is_expected && + std::is_same_v && + std::is_move_constructible_v && + std::is_move_constructible_v + constexpr auto and_then(F&& f) && { + if (has_value()) { + return std::invoke(std::forward(f), std::move(**this)); + } + return U(unexpect, std::move(error())); + } + + template>> + requires detail::is_expected && + std::is_same_v && + std::is_move_constructible_v && + std::is_move_constructible_v + constexpr auto and_then(F&& f) const&& { + if (has_value()) { + return std::invoke(std::forward(f), std::move(**this)); + } + return U(unexpect, std::move(error())); + } + + template>> + requires detail::is_expected && + std::is_same_v && + std::is_copy_constructible_v && + std::is_copy_constructible_v + constexpr auto or_else(F&& f) & { + if (has_value()) { + return G(**this); + } + return std::invoke(std::forward(f), error()); + } + + template>> + requires detail::is_expected && + std::is_same_v && + std::is_copy_constructible_v && + std::is_copy_constructible_v + constexpr auto or_else(F&& f) const& { + if (has_value()) { + return G(**this); + } + return std::invoke(std::forward(f), error()); + } + + template>> + requires detail::is_expected && + std::is_same_v && + std::is_move_constructible_v && + std::is_move_constructible_v + constexpr auto or_else(F&& f) && { + if (has_value()) { + return G(std::move(**this)); + } + return std::invoke(std::forward(f), std::move(error())); + } + + template>> + requires detail::is_expected && + std::is_same_v && + std::is_move_constructible_v && + std::is_move_constructible_v + constexpr auto or_else(F&& f) const&& { + if (has_value()) { + return G(std::move(**this)); + } + return std::invoke(std::forward(f), std::move(error())); + } + + template>> + requires std::is_void_v && + std::is_copy_constructible_v && + std::is_copy_constructible_v + constexpr auto or_else(F&& f) & { + if (has_value()) { + return expected(*this); + } + std::invoke(std::forward(f), error()); + return expected(*this); + } + + template>> + requires std::is_void_v && + std::is_copy_constructible_v && + std::is_copy_constructible_v + constexpr auto or_else(F&& f) const& { + if (has_value()) { + return expected(*this); + } + std::invoke(std::forward(f), error()); + return expected(*this); + } + + template>> + requires std::is_void_v && + std::is_move_constructible_v && + std::is_move_constructible_v && + std::is_copy_constructible_v + constexpr auto or_else(F&& f) && { + if (has_value()) { + return expected(std::move(*this)); + } + // TODO: is this copy necessary, as f can be just read argument function + std::invoke(std::forward(f), error()); + return expected(std::move(*this)); + } + + template>> + requires std::is_void_v && + std::is_move_constructible_v && + std::is_move_constructible_v && + std::is_copy_constructible_v + constexpr auto or_else(F&& f) const&& { + if (!has_value()) { + return expected(std::move(*this)); + } + // TODO: is this copy necessary, as f can be just read argument function + std::invoke(std::forward(f), error()); + return expected(std::move(*this)); + } + + template>> + requires std::is_copy_constructible_v && + std::is_copy_constructible_v + constexpr auto transform(F&& f) & { + if (has_value()) { + if constexpr (!std::same_as) { + return expected(std::invoke(std::forward(f), **this)); + } else { + std::invoke(std::forward(f), std::move(**this)); + return expected(); + } + } + return expected(unexpect, error()); + } + + template>> + requires std::is_copy_constructible_v && + std::is_copy_constructible_v + constexpr auto transform(F&& f) const& { + if (has_value()) { + if constexpr (!std::same_as) { + return expected(std::invoke(std::forward(f), **this)); + } else { + std::invoke(std::forward(f), std::move(**this)); + return expected(); + } + } + return expected(unexpect, error()); + } + + template>> + requires std::is_move_constructible_v && + std::is_move_constructible_v + constexpr auto transform(F&& f) && { + if (has_value()) { + if constexpr (!std::same_as) { + return expected( + std::invoke(std::forward(f), std::move(**this))); + } else { + std::invoke(std::forward(f), std::move(**this)); + return expected(); + } + } + return expected(unexpect, std::move(error())); + } + + template>> + requires std::is_move_constructible_v && + std::is_move_constructible_v + constexpr auto transform(F&& f) const&& { + if (has_value()) { + if constexpr (!std::same_as) { + return expected( + std::invoke(std::forward(f), std::move(**this))); + } else { + std::invoke(std::forward(f), std::move(**this)); + return expected(); + } + } + return expected(unexpect, std::move(error())); + } + + template>> + requires std::is_copy_constructible_v && + std::is_copy_constructible_v + constexpr auto transform_error(F&& f) & { + if (has_value()) { + return expected(**this); + } + return expected(unexpect, std::invoke(std::forward(f), error())); + } + + template>> + requires std::is_copy_constructible_v && + std::is_copy_constructible_v + constexpr auto transform_error(F&& f) const& { + if (has_value()) { + return expected(**this); + } + return expected(unexpect, std::invoke(std::forward(f), error())); + } + + template>> + requires std::is_move_constructible_v && + std::is_move_constructible_v + constexpr auto transform_error(F&& f) && { + if (has_value()) { + return expected(std::move(**this)); + } + return expected(unexpect, + std::invoke(std::forward(f), std::move(error()))); + } + + template>> + requires std::is_move_constructible_v && + std::is_move_constructible_v + constexpr auto transform_error(F&& f) const&& { + if (has_value()) { + return expected(std::move(**this)); + } + return expected(unexpect, + std::invoke(std::forward(f), std::move(error()))); + } + + // equality operators + template + requires(!std::is_void_v) && + requires(T const& t1, T2 const& t2, E const& e1, E2 const& e2) { + { t1 == t2 } -> std::convertible_to; + { e1 == e2 } -> std::convertible_to; + } + friend constexpr auto operator==(expected const& x, expected const& y) + -> bool { + if (x.has_value() != y.has_value()) { + return false; + } + return x.has_value() ? (*x == *y) : (x.error() == y.error()); + } + + template + requires(!detail::is_expected) && requires(T const& x, T2 const& v) { + { x == v } -> std::convertible_to; + } + friend constexpr auto operator==(expected const& x, T2 const& v) -> bool { + return x.has_value() && static_cast(*x == v); + } + + template + requires requires(E const& x, unexpected const& e) { + { x == e.value() } -> std::convertible_to; + } + friend constexpr auto operator==(expected const& x, unexpected const& e) + -> bool { + return !x.has_value() && static_cast(x.error() == e.value()); + } + + // specialized algorithms + friend constexpr void swap(expected& x, + expected& y) noexcept(noexcept(x.swap(y))) { + x.swap(y); + } + + private: + bool has_val{true}; + union { + T val; + E unex; + }; + }; + + template + class expected { + public: + using value_type = void; + using error_type = E; + using unexpected_type = unexpected; + + template + using rebind = expected; + + // constructors + + // postcondition: has_value() = true + constexpr expected() noexcept {}// NOLINT + + constexpr expected( + expected const& rhs) + requires std::is_copy_constructible_v && + std::is_trivially_copy_constructible_v + = default; + + constexpr expected( + expected const& rhs) + requires std::is_copy_constructible_v + : has_val(rhs.has_value()) { + if (!rhs.has_value()) { + std::construct_at(std::addressof(this->unex), rhs.error()); + } + } + + constexpr expected(expected&&) noexcept(std::is_nothrow_move_constructible_v) + requires std::is_move_constructible_v && + std::is_trivially_move_constructible_v + = default; + + constexpr expected(expected&& rhs) noexcept(std::is_nothrow_move_constructible_v) + requires std::is_move_constructible_v + : has_val(rhs.has_value()) { + if (!rhs.has_value()) { + std::construct_at(std::addressof(this->unex), std::move(rhs.error())); + } + } + + template + requires std::is_void_v && std::is_constructible_v && (!std::is_constructible_v, expected&>) && (!std::is_constructible_v, expected>) && (!std::is_constructible_v, expected const&>) && (!std::is_constructible_v, expected const&>) + constexpr explicit(!std::is_convertible_v) + expected(expected const& rhs)// NOLINT + : has_val(rhs.has_value()) { + if (!rhs.has_value()) { + std::construct_at(std::addressof(this->unex), + std::forward(rhs.error())); + } + } + + template + requires std::is_void_v && std::is_constructible_v && (!std::is_constructible_v, expected&>) && (!std::is_constructible_v, expected>) && (!std::is_constructible_v, expected const&>) && (!std::is_constructible_v, expected const&>) + constexpr explicit(!std::is_convertible_v) + expected(expected&& rhs)// NOLINT + : has_val(rhs.has_value()) { + if (!rhs.has_value()) { + std::construct_at(std::addressof(this->unex), + std::forward(rhs.error())); + } + } + + template + requires std::is_constructible_v + constexpr explicit(!std::is_convertible_v) + expected(unexpected const& e)// NOLINT + : has_val(false), unex(std::forward(e.value())) {} + + template + requires std::is_constructible_v + constexpr explicit(!std::is_convertible_v) + expected(unexpected&& e)// NOLINT + : has_val(false), unex(std::forward(e.value())) {} + + constexpr explicit expected(std::in_place_t /*unused*/) noexcept {} + + template + requires std::is_constructible_v + constexpr explicit expected(unexpect_t /*unused*/, Args&&... args) + : has_val(false), unex(std::forward(args)...) {} + + template + requires std::is_constructible_v&, Args...> + constexpr explicit expected(unexpect_t /*unused*/, std::initializer_list il, Args... args) + : has_val(false), + unex(il, std::forward(args)...) {} + + // destructor + constexpr ~expected() { + if constexpr (std::is_trivially_destructible_v) { + } else { + if (!has_value()) std::destroy_at(std::addressof(this->unex)); + } + } + + // assignment + constexpr auto operator=(expected const& rhs) -> expected&// NOLINT + requires std::is_copy_assignable_v && + std::is_copy_constructible_v + { + if (has_value() && rhs.has_value()) { + } else if (has_value()) { + std::construct_at(std::addressof(this->unex), rhs.unex); + has_val = false; + } else if (rhs.has_value()) { + std::destroy_at(std::addressof(this->unex)); + has_val = true; + } else { + this->unex = rhs.error(); + } + return *this; + } + + constexpr auto operator=(expected&& rhs) noexcept(std::is_nothrow_move_constructible_v && + std::is_nothrow_move_assignable_v) -> expected& + requires std::is_move_constructible_v && + std::is_move_assignable_v + { + if (has_value() && rhs.has_value()) { + } else if (has_value()) { + std::construct_at(std::addressof(this->unex), std::move(rhs.unex)); + has_val = false; + } else if (rhs.has_value()) { + std::destroy_at(std::addressof(this->unex)); + has_val = true; + } else { + this->unex = std::move(rhs.error()); + } + return *this; + } + + template + requires std::is_constructible_v and + std::is_assignable_v + constexpr auto operator=(unexpected const& e) -> expected& { + if (has_value()) { + std::construct_at(std::addressof(this->unex), + std::forward(e.value())); + has_val = false; + } else { + this->unex = std::forward(e.value()); + } + return *this; + } + + template + requires std::is_constructible_v && + std::is_assignable_v + constexpr auto operator=(unexpected&& e) -> expected& { + if (has_value()) { + std::construct_at(std::addressof(this->unex), std::forward(e.value())); + has_val = false; + } else { + this->unex = std::forward(e.value()); + } + return *this; + } + + // modifiers + constexpr void emplace() noexcept { + if (!has_value()) { + std::destroy_at(std::addressof(this->unex)); + has_val = true; + } + } + + // swap + constexpr void swap(expected& rhs) noexcept(std::is_nothrow_move_constructible_v && + std::is_nothrow_swappable_v) + requires std::is_swappable_v && + std::is_move_constructible_v + { + if (rhs.has_value()) { + if (has_value()) { + } else { + rhs.swap(*this); + } + } else { + if (has_value()) { + std::construct_at(std::addressof(this->unex), std::move(rhs.unex)); + std::destroy_at(std::addressof(rhs.unex)); + has_val = false; + rhs.has_val = true; + } else { + using std::swap; + swap(this->unex, rhs.unex); + } + } + } + + // observers + constexpr explicit operator bool() const noexcept { return has_val; } + + [[nodiscard]] constexpr auto has_value() const noexcept -> bool { + return has_val; + } + + // precondition: has_value() = true + constexpr void operator*() const noexcept {} + + constexpr void value() const& { + if (!has_value()) { + throw bad_expected_access(error()); + } + } + + constexpr void value() && { + if (!has_value()) { + throw bad_expected_access(std::move(error())); + } + } + + // precondition: has_value() = false + constexpr auto error() const& -> E const& { return this->unex; } + + // precondition: has_value() = false + constexpr auto error() & -> E& { return this->unex; } + + // precondition: has_value() = false + constexpr auto error() const&& -> E const&& { return std::move(this->unex); } + + // precondition: has_value() = false + constexpr auto error() && -> E&& { return std::move(this->unex); } + + // monadic + template>> + requires detail::is_expected && + std::is_same_v && + std::is_copy_constructible_v + constexpr auto and_then(F&& f) & { + if (has_value()) { + return std::invoke(std::forward(f)); + } + return U(unexpect, error()); + } + + template>> + requires detail::is_expected && + std::is_same_v && + std::is_copy_constructible_v + constexpr auto and_then(F&& f) const& { + if (has_value()) { + return std::invoke(std::forward(f)); + } + return U(unexpect, error()); + } + + template>> + requires detail::is_expected && + std::is_same_v && + std::is_move_constructible_v + constexpr auto and_then(F&& f) && { + if (has_value()) { + return std::invoke(std::forward(f)); + } + return U(unexpect, std::move(error())); + } + + template>> + requires detail::is_expected && + std::is_same_v && + std::is_move_constructible_v + constexpr auto and_then(F&& f) const&& { + if (has_value()) { + return std::invoke(std::forward(f)); + } + return U(unexpect, std::move(error())); + } + + template>> + requires detail::is_expected && + std::is_same_v && + std::is_copy_constructible_v + constexpr auto or_else(F&& f) & { + if (has_value()) { + return G{}; + } + return std::invoke(std::forward(f), error()); + } + + template>> + requires detail::is_expected && + std::is_same_v && + std::is_copy_constructible_v + constexpr auto or_else(F&& f) const& { + if (has_value()) { + return G{}; + } + return std::invoke(std::forward(f), error()); + } + + template>> + requires detail::is_expected && + std::is_same_v && + std::is_move_constructible_v + constexpr auto or_else(F&& f) && { + if (has_value()) { + return G{}; + } + return std::invoke(std::forward(f), std::move(error())); + } + + template>> + requires detail::is_expected && + std::is_same_v && + std::is_move_constructible_v + constexpr auto or_else(F&& f) const&& { + if (has_value()) { + return G{}; + } + return std::invoke(std::forward(f), std::move(error())); + } + + template>> + requires std::is_void_v && + std::is_copy_constructible_v + constexpr auto or_else(F&& f) & { + if (has_value()) { + return expected(*this); + } + std::invoke(std::forward(f), error()); + return expected(*this); + } + + template>> + requires std::is_void_v && + std::is_copy_constructible_v + constexpr auto or_else(F&& f) const& { + if (has_value()) { + return expected(*this); + } + std::invoke(std::forward(f), error()); + return expected(*this); + } + + template>> + requires std::is_void_v && + std::is_move_constructible_v && + std::is_copy_constructible_v + constexpr auto or_else(F&& f) && { + if (has_value()) { + return expected(std::move(*this)); + } + // TODO: is this copy necessary, as f can be just read argument function + std::invoke(std::forward(f), error()); + return expected(std::move(*this)); + } + + template>> + requires std::is_void_v && + std::is_move_constructible_v && + std::is_copy_constructible_v + constexpr auto or_else(F&& f) const&& { + if (!has_value()) { + return expected(std::move(*this)); + } + // TODO: is this copy necessary, as f can be just read argument function + std::invoke(std::forward(f), error()); + return expected(std::move(*this)); + } + + template>> + requires std::is_copy_constructible_v + constexpr auto transform(F&& f) & { + if (has_value()) { + if constexpr (!std::same_as) { + return expected(std::invoke(std::forward(f))); + } else { + std::invoke(std::forward(f)); + return expected(); + } + } + return expected(unexpect, error()); + } + + template>> + requires std::is_copy_constructible_v + constexpr auto transform(F&& f) const& { + if (has_value()) { + if constexpr (!std::same_as) { + return expected(std::invoke(std::forward(f))); + } else { + std::invoke(std::forward(f)); + return expected(); + } + } + return expected(unexpect, error()); + } + + template>> + requires std::is_move_constructible_v + constexpr auto transform(F&& f) && { + if (has_value()) { + if constexpr (!std::same_as) { + return expected(std::invoke(std::forward(f))); + } else { + std::invoke(std::forward(f)); + return expected(); + } + } + return expected(unexpect, std::move(error())); + } + + template>> + requires std::is_move_constructible_v + constexpr auto transform(F&& f) const&& { + if (has_value()) { + if constexpr (!std::same_as) { + return expected(std::invoke(std::forward(f))); + } else { + std::invoke(std::forward(f)); + return expected(); + } + } + return expected(unexpect, std::move(error())); + } + + template>> + requires std::is_copy_constructible_v + constexpr auto transform_error(F&& f) & { + if (has_value()) { + return expected{}; + } + return expected(unexpect, + std::invoke(std::forward(f), error())); + } + + template>> + requires std::is_copy_constructible_v + constexpr auto transform_error(F&& f) const& { + if (has_value()) { + return expected{}; + } + return expected(unexpect, + std::invoke(std::forward(f), error())); + } + + template>> + requires std::is_move_constructible_v + constexpr auto transform_error(F&& f) && { + if (has_value()) { + return expected{}; + } + return expected( + unexpect, std::invoke(std::forward(f), std::move(error()))); + } + + template>> + requires std::is_move_constructible_v + constexpr auto transform_error(F&& f) const&& { + if (has_value()) { + return expected{}; + } + return expected( + unexpect, std::invoke(std::forward(f), std::move(error()))); + } + + // expected equality operators + template + requires std::is_void_v && requires(E e, E2 e2) { + { e == e2 } -> std::convertible_to; + } + friend constexpr auto operator==(expected const& x, expected const& y) + -> bool { + if (x.has_value() != y.has_value()) return false; + return x.has_value() or static_cast(x.error() == y.error()); + } + + template + requires requires(expected const& x, unexpected const& e) { + { x.error() == e.value() } -> std::convertible_to; + } + friend constexpr auto operator==(expected const& x, unexpected const& e) + -> bool { + return !x.has_value() && static_cast(x.error() == e.value()); + } + + // specialized algorithms + friend constexpr void swap(expected& x, + expected& y) noexcept(noexcept(x.swap(y))) { + x.swap(y); + } + + private: + bool has_val{true}; + union { + E unex; + }; + }; + +}// namespace plg +#endif \ No newline at end of file diff --git a/test/cross_call_master/external/plugify/include/plg/flat_map.hpp b/test/cross_call_master/external/plugify/include/plg/flat_map.hpp new file mode 100644 index 0000000..9912431 --- /dev/null +++ b/test/cross_call_master/external/plugify/include/plg/flat_map.hpp @@ -0,0 +1,780 @@ +#pragma once + +#include "plg/macro.hpp" + +#ifdef __cpp_lib_flat_map +#include +namespace plg { + template> + using flat_map = std::flat_map; +} +#else +#include "plg/vector.hpp" + +namespace plg { + namespace detail { + template < typename T, typename U, typename = void > + struct is_transparent + : std::false_type {}; + + template < typename T, typename U > + struct is_transparent> + : std::true_type {}; + + template < typename T, typename U > + inline constexpr bool is_transparent_v = is_transparent::value; + + template + constexpr bool is_sorted(Iter first, Iter last, Compare comp) { + if (first != last) { + Iter next = first; + while (++next != last) { + if (comp(*next, *first)) { + return false; + } + ++first; + } + } + return true; + } + + template + constexpr bool is_sorted_unique(Iter first, Iter last, Compare comp) { + if (first != last) { + Iter next = first; + while (++next != last) { + if (!comp(*first, *next)) { + return false; + } + ++first; + } + } + return true; + } + + template + struct pair_compare : public Compare { + pair_compare() = default; + + explicit pair_compare(const Compare& compare) + : Compare(compare) {} + + bool operator()( + const typename Pair::first_type& l, + const typename Pair::first_type& r) const { + return Compare::operator()(l, r); + } + + bool operator()(const Pair& l, const Pair& r) const { + return Compare::operator()(l.first, r.first); + } + + bool operator()( + const typename Pair::first_type& l, + const Pair& r) const { + return Compare::operator()(l, r.first); + } + + bool operator()( + const Pair& l, + const typename Pair::first_type& r) const { + return Compare::operator()(l.first, r); + } + + template + requires (is_transparent_v) + bool operator()(const K& l, const Pair& r) const { + return Compare::operator()(l, r.first); + } + + template + requires (is_transparent_v) + bool operator()(const Pair& l, const K& r) const { + return Compare::operator()(l.first, r); + } + }; + + template + struct eq_compare : public Compare { + eq_compare() = default; + + explicit eq_compare(const Compare& compare) + : Compare(compare) {} + + template + bool operator()(const L& l, const R& r) const { + return !Compare::operator()(l, r) && !Compare::operator()(r, l); + } + }; + } // namespace detail + + struct sorted_range_t {}; + inline constexpr sorted_range_t sorted_range = sorted_range_t(); + + struct sorted_unique_range_t : public sorted_range_t {}; + inline constexpr sorted_unique_range_t sorted_unique_range = sorted_unique_range_t(); + + template, typename Container = std::vector>> + class flat_map { + public: + using key_type = Key; + using mapped_type = Value; + using value_type = typename Container::value_type; + + using size_type = typename Container::size_type; + using difference_type = typename Container::difference_type; + + using key_compare = Compare; + using container_type = Container; + + using reference = typename Container::reference; + using const_reference = typename Container::const_reference; + using pointer = typename Container::pointer; + using const_pointer = typename Container::const_pointer; + + using iterator = typename Container::iterator; + using const_iterator = typename Container::const_iterator; + using reverse_iterator = typename Container::reverse_iterator; + using const_reverse_iterator = typename Container::const_reverse_iterator; + + struct value_compare : private key_compare { + value_compare() = default; + + explicit value_compare(key_compare compare) + : key_compare(std::move(compare)) {} + + bool operator()(const value_type& l, const value_type& r) const { + return key_compare::operator()(l.first, r.first); + } + }; + + public: + flat_map() = default; + ~flat_map() = default; + + explicit flat_map(const Compare& c) + : _compare(c) {} + + template + explicit flat_map(const Allocator& a) + : _data(a) {} + + template + flat_map(const Compare& c, const Allocator& a) + : _compare(c), _data(a) {} + + template + flat_map(Iterator first, Iterator last) { + from_range(first, last); + } + + template + flat_map(sorted_range_t, Iterator first, Iterator last) { + from_range(sorted_range, first, last); + } + + template + flat_map(sorted_unique_range_t, Iterator first, Iterator last) { + from_range(sorted_unique_range, first, last); + } + + template + flat_map(Iterator first, Iterator last, const Compare& c) + : _compare(c) { + from_range(first, last); + } + + template + flat_map(sorted_range_t, Iterator first, Iterator last, const Compare& c) + : _compare(c) { + from_range(sorted_range, first, last); + } + + template + flat_map(sorted_unique_range_t, Iterator first, Iterator last, const Compare& c) + : _compare(c) { + from_range(sorted_unique_range, first, last); + } + + template + flat_map(Iterator first, Iterator last, const Allocator& a) + : _data(a) { + from_range(first, last); + } + + template + flat_map(sorted_range_t, Iterator first, Iterator last, const Allocator& a) + : _data(a) { + from_range(sorted_range, first, last); + } + + template + flat_map(sorted_unique_range_t, Iterator first, Iterator last, const Allocator& a) + : _data(a) { + from_range(sorted_unique_range, first, last); + } + + template + flat_map(Iterator first, Iterator last, const Compare& c, const Allocator& a) + : _compare(c), _data(a) { + from_range(first, last); + } + + template + flat_map(sorted_range_t, Iterator first, Iterator last, const Compare& c, const Allocator& a) + : _compare(c), _data(a) { + from_range(sorted_range, first, last); + } + + template + flat_map(sorted_unique_range_t, Iterator first, Iterator last, const Compare& c, const Allocator& a) + : _compare(c), _data(a) { + from_range(sorted_unique_range, first, last); + } + + flat_map(std::initializer_list list) { + from_range(list.begin(), list.end()); + } + + flat_map(sorted_range_t, std::initializer_list list) { + from_range(sorted_range, list.begin(), list.end()); + } + + flat_map(sorted_unique_range_t, std::initializer_list list) { + from_range(sorted_unique_range, list.begin(), list.end()); + } + + flat_map(std::initializer_list list, const Compare& c) + : _compare(c) { + from_range(list.begin(), list.end()); + } + + flat_map(sorted_range_t, std::initializer_list list, const Compare& c) + : _compare(c) { + from_range(sorted_range, list.begin(), list.end()); + } + + flat_map(sorted_unique_range_t, std::initializer_list list, const Compare& c) + : _compare(c) { + from_range(sorted_unique_range, list.begin(), list.end()); + } + + template + flat_map(std::initializer_list list, const Allocator& a) + : _data(a) { + from_range(list.begin(), list.end()); + } + + template + flat_map(sorted_range_t, std::initializer_list list, const Allocator& a) + : _data(a) { + from_range(sorted_range, list.begin(), list.end()); + } + + template + flat_map(sorted_unique_range_t, std::initializer_list list, const Allocator& a) + : _data(a) { + from_range(sorted_unique_range, list.begin(), list.end()); + } + + template + flat_map(std::initializer_list list, const Compare& c, const Allocator& a) + : _compare(c), _data(a) { + from_range(list.begin(), list.end()); + } + + template + flat_map(sorted_range_t, std::initializer_list list, const Compare& c, const Allocator& a) + : _compare(c), _data(a) { + from_range(sorted_range, list.begin(), list.end()); + } + + template + flat_map(sorted_unique_range_t, std::initializer_list list, const Compare& c, const Allocator& a) + : _compare(c), _data(a) { + from_range(sorted_unique_range, list.begin(), list.end()); + } + + template + flat_map(flat_map&& other, const Allocator& a) + : _compare(std::move(other._compare)), _data(std::move(other._data), a) {} + + template + flat_map(const flat_map& other, const Allocator& a) + : _compare(other._compare), _data(other._data, a) {} + + flat_map(flat_map&& other) noexcept = default; + flat_map(const flat_map& other) = default; + + flat_map& operator=(flat_map&& other) noexcept = default; + flat_map& operator=(const flat_map& other) = default; + + flat_map& operator=(std::initializer_list list) { + flat_map(list).swap(*this); + return *this; + } + + iterator begin() noexcept(noexcept(std::declval().begin())) { + return _data.begin(); + } + + const_iterator begin() const + noexcept(noexcept(std::declval().begin())) { + return _data.begin(); + } + + const_iterator cbegin() const + noexcept(noexcept(std::declval().cbegin())) { + return _data.cbegin(); + } + + iterator end() noexcept(noexcept(std::declval().end())) { + return _data.end(); + } + + const_iterator end() const + noexcept(noexcept(std::declval().end())) { + return _data.end(); + } + + const_iterator cend() const + noexcept(noexcept(std::declval().cend())) { + return _data.cend(); + } + + reverse_iterator rbegin() noexcept(noexcept(std::declval().rbegin())) { + return _data.rbegin(); + } + + const_reverse_iterator rbegin() const + noexcept(noexcept(std::declval().rbegin())) { + return _data.rbegin(); + } + + const_reverse_iterator crbegin() const + noexcept(noexcept(std::declval().crbegin())) { + return _data.crbegin(); + } + + reverse_iterator rend() noexcept(noexcept(std::declval().rend())) { + return _data.rend(); + } + + const_reverse_iterator rend() const + noexcept(noexcept(std::declval().rend())) { + return _data.rend(); + } + + const_reverse_iterator crend() const + noexcept(noexcept(std::declval().crend())) { + return _data.crend(); + } + + bool empty() const + noexcept(noexcept(std::declval().empty())) { + return _data.empty(); + } + + size_type size() const + noexcept(noexcept(std::declval().size())) { + return _data.size(); + } + + size_type max_size() const + noexcept(noexcept(std::declval().max_size())) { + return _data.max_size(); + } + + size_type capacity() const + noexcept(noexcept(std::declval().capacity())) { + return _data.capacity(); + } + + void reserve(size_type capacity) { + _data.reserve(capacity); + } + + void shrink_to_fit() { + _data.shrink_to_fit(); + } + + mapped_type& operator[](key_type&& key) { + const iterator iter = find(key); + return iter != end() + ? iter->second + : emplace(std::move(key), mapped_type()).first->second; + } + + mapped_type& operator[](const key_type& key) { + const iterator iter = find(key); + return iter != end() + ? iter->second + : emplace(key, mapped_type()).first->second; + } + + mapped_type& at(const key_type& key) { + const iterator iter = find(key); + PLUGIFY_ASSERT(iter != end(), "plg::flat_map::at(): key not found", std::out_of_range); + return iter->second; + } + + const mapped_type& at(const key_type& key) const { + const const_iterator iter = find(key); + PLUGIFY_ASSERT(iter != end(), "plg::flat_map::at(): key not found", std::out_of_range); + return iter->second; + } + + template + requires (detail::is_transparent_v) + mapped_type& at(const K& key) { + const iterator iter = find(key); + PLUGIFY_ASSERT(iter != end(), "plg::flat_map::at(): key not found", std::out_of_range); + return iter->second; + } + + template + requires (detail::is_transparent_v) + const mapped_type& at(const K& key) const { + const const_iterator iter = find(key); + PLUGIFY_ASSERT(iter != end(), "plg::flat_map::at(): key not found", std::out_of_range); + return iter->second; + } + + std::pair insert(value_type&& value) { + const iterator iter = lower_bound(value.first); + return iter == end() || _compare(value, *iter) + ? std::make_pair(_data.insert(iter, std::move(value)), true) + : std::make_pair(iter, false); + } + + std::pair insert(const value_type& value) { + const iterator iter = lower_bound(value.first); + return iter == end() || _compare(value, *iter) + ? std::make_pair(_data.insert(iter, value), true) + : std::make_pair(iter, false); + } + + iterator insert(const_iterator hint, value_type&& value) { + return (hint == begin() || _compare(*(hint - 1), value)) && (hint == end() || _compare(value, *hint)) + ? _data.insert(hint, std::move(value)) + : insert(std::move(value)).first; + } + + iterator insert(const_iterator hint, const value_type& value) { + return (hint == begin() || _compare(*(hint - 1), value)) && (hint == end() || _compare(value, *hint)) + ? _data.insert(hint, value) + : insert(value).first; + } + + template + std::pair insert_or_assign(key_type&& key, V&& value) { + iterator iter = lower_bound(key); + if (iter == end() || _compare(key, *iter)) { + iter = emplace_hint(iter, std::move(key), std::forward(value)); + return {iter, true}; + } + (*iter).second = std::forward(value); + return {iter, false}; + } + + template + std::pair insert_or_assign(const key_type& key, V&& value) { + iterator iter = lower_bound(key); + if (iter == end() || _compare(key, *iter)) { + iter = emplace_hint(iter, key, std::forward(value)); + return {iter, true}; + } + (*iter).second = std::forward(value); + return {iter, false}; + } + + template + void insert(Iterator first, Iterator last) { + insert_range(first, last); + } + + template + void insert(sorted_range_t, Iterator first, Iterator last) { + insert_range(sorted_range, first, last); + } + + void insert(std::initializer_list list) { + insert_range(list.begin(), list.end()); + } + + void insert(sorted_range_t, std::initializer_list list) { + insert_range(sorted_range, list.begin(), list.end()); + } + + template + std::pair emplace(Args&&... args) { + return insert(value_type(std::forward(args)...)); + } + + template + iterator emplace_hint(const_iterator hint, Args&&... args) { + return insert(hint, value_type(std::forward(args)...)); + } + + template + std::pair try_emplace(key_type&& key, Args&&... args) { + iterator iter = lower_bound(key); + if (iter == end() || _compare(key, *iter)) { + iter = emplace_hint(iter, std::move(key), std::forward(args)...); + return {iter, true}; + } + return {iter, false}; + } + + template + std::pair try_emplace(const key_type& key, Args&&... args) { + iterator iter = lower_bound(key); + if (iter == end() || _compare(key, *iter)) { + iter = emplace_hint(iter, key, std::forward(args)...); + return {iter, true}; + } + return {iter, false}; + } + + void clear() noexcept(noexcept(std::declval().clear())) { + _data.clear(); + } + + iterator erase(const_iterator iter) { + return _data.erase(iter); + } + + iterator erase(const_iterator first, const_iterator last) { + return _data.erase(first, last); + } + + size_type erase(const key_type& key) { + const const_iterator iter = find(key); + return iter != end() + ? (erase(iter), 1) + : 0; + } + + void swap(flat_map& other) noexcept(std::is_nothrow_swappable_v && std::is_nothrow_swappable_v) { + using std::swap; + swap(_compare, other._compare); + swap(_data, other._data); + } + + size_type count(const key_type& key) const { + const const_iterator iter = find(key); + return iter != end() ? 1 : 0; + } + + template + requires (detail::is_transparent_v) + size_type count(const K& key) const { + const const_iterator iter = find(key); + return iter != end() ? 1 : 0; + } + + iterator find(const key_type& key) { + const iterator iter = lower_bound(key); + return iter != end() && !_compare(key, *iter) + ? iter + : end(); + } + + const_iterator find(const key_type& key) const { + const const_iterator iter = lower_bound(key); + return iter != end() && !_compare(key, *iter) + ? iter + : end(); + } + + template + requires (detail::is_transparent_v) + iterator find(const K& key) { + const iterator iter = lower_bound(key); + return iter != end() && !_compare(key, *iter) + ? iter + : end(); + } + + template + requires (detail::is_transparent_v) + const_iterator find(const K& key) const { + const const_iterator iter = lower_bound(key); + return iter != end() && !_compare(key, *iter) + ? iter + : end(); + } + + bool contains(const key_type& key) const { + return find(key) != end(); + } + + template + requires (detail::is_transparent_v) + bool contains(const K& key) const { + return find(key) != end(); + } + + std::pair equal_range(const key_type& key) { + return std::equal_range(begin(), end(), key, _compare); + } + + std::pair equal_range(const key_type& key) const { + return std::equal_range(begin(), end(), key, _compare); + } + + template + requires (detail::is_transparent_v) + std::pair equal_range(const K& key) { + return std::equal_range(begin(), end(), key, _compare); + } + + template + requires (detail::is_transparent_v) + std::pair equal_range(const K& key) const { + return std::equal_range(begin(), end(), key, _compare); + } + + iterator lower_bound(const key_type& key) { + return std::lower_bound(begin(), end(), key, _compare); + } + + const_iterator lower_bound(const key_type& key) const { + return std::lower_bound(begin(), end(), key, _compare); + } + + template + requires (detail::is_transparent_v) + iterator lower_bound(const K& key) { + return std::lower_bound(begin(), end(), key, _compare); + } + + template + requires (detail::is_transparent_v) + const_iterator lower_bound(const K& key) const { + return std::lower_bound(begin(), end(), key, _compare); + } + + iterator upper_bound(const key_type& key) { + return std::upper_bound(begin(), end(), key, _compare); + } + + const_iterator upper_bound(const key_type& key) const { + return std::upper_bound(begin(), end(), key, _compare); + } + + template + requires (detail::is_transparent_v) + iterator upper_bound(const K& key) { + return std::upper_bound(begin(), end(), key, _compare); + } + + template + requires (detail::is_transparent_v) + const_iterator upper_bound(const K& key) const { + return std::upper_bound(begin(), end(), key, _compare); + } + + key_compare key_comp() const { + return _compare; + } + + value_compare value_comp() const { + return value_compare(key_comp()); + } + + private: + template + void from_range(Iter first, Iter last) { + assert(_data.empty()); + _data.insert(_data.end(), first, last); + std::sort(_data.begin(), _data.end(), value_comp()); + _data.erase( + std::unique(_data.begin(), _data.end(), + detail::eq_compare(value_comp())), + _data.end()); + } + + template + void from_range(sorted_range_t, Iter first, Iter last) { + assert(_data.empty()); + assert(detail::is_sorted(first, last, value_comp())); + _data.insert(_data.end(), first, last); + _data.erase( + std::unique(_data.begin(), _data.end(), + detail::eq_compare(value_comp())), + _data.end()); + } + + template + void from_range(sorted_unique_range_t, Iter first, Iter last) { + assert(_data.empty()); + assert(detail::is_sorted_unique(first, last, value_comp())); + _data.insert(_data.end(), first, last); + } + + private: + template + void insert_range(Iter first, Iter last) { + const auto mid_iter = _data.insert(_data.end(), first, last); + std::sort(mid_iter, _data.end(), value_comp()); + std::inplace_merge(_data.begin(), mid_iter, _data.end(), value_comp()); + _data.erase( + std::unique(_data.begin(), _data.end(), + detail::eq_compare(value_comp())), + _data.end()); + } + + template + void insert_range(sorted_range_t, Iter first, Iter last) { + assert(detail::is_sorted(first, last, value_comp())); + const auto mid_iter = _data.insert(_data.end(), first, last); + std::inplace_merge(_data.begin(), mid_iter, _data.end(), value_comp()); + _data.erase( + std::unique(_data.begin(), _data.end(), + detail::eq_compare(value_comp())), + _data.end()); + } + + private: + PLUGIFY_NO_UNIQUE_ADDRESS + detail::pair_compare _compare; + container_type _data; + }; + + template + void swap( + flat_map& l, + flat_map& r) noexcept(noexcept(l.swap(r))) { + l.swap(r); + } + + template + bool operator==( + const flat_map& l, + const flat_map& r) { + return l.size() == r.size() && std::equal(l.begin(), l.end(), r.begin()); + } + + template + auto operator<=>( + const flat_map& l, + const flat_map& r) { + if (l.size() < r.size()) { + return std::partial_ordering::less; + } else if (l.size() > r.size()) { + return std::partial_ordering::greater; + } else { + if (std::lexicographical_compare(l.cbegin(), l.cend(), r.cbegin(), r.cend())) { + return std::partial_ordering::less; + } else { + return std::partial_ordering::greater; + } + } + } + + template, typename Container = plg::vector>> + using map = flat_map; + +}// namespace plg +#endif \ No newline at end of file diff --git a/test/cross_call_master/external/plugify/include/plg/format.hpp b/test/cross_call_master/external/plugify/include/plg/format.hpp new file mode 100644 index 0000000..94e5bdb --- /dev/null +++ b/test/cross_call_master/external/plugify/include/plg/format.hpp @@ -0,0 +1,26 @@ +#pragma once + +#include "plg/macro.hpp" + +#ifdef __cpp_lib_format + +#include + +#else // __cpp_lib_format + +// Define FMT_FORMAT_H externally to force a difference location for {fmt} +#ifndef FMT_FORMAT_H +#define FMT_FORMAT_H +#endif + +#ifndef FMT_HEADER_ONLY +#define FMT_HEADER_ONLY +#endif +#include FMT_FORMAT_H + +namespace std { + using namespace fmt; + using namespace fmt::detail; +} + +#endif // __cpp_lib_format diff --git a/test/cross_call_master/plg/formatter.hpp b/test/cross_call_master/external/plugify/include/plg/formatter.hpp similarity index 100% rename from test/cross_call_master/plg/formatter.hpp rename to test/cross_call_master/external/plugify/include/plg/formatter.hpp diff --git a/test/example_plugin/plg/hash.hpp b/test/cross_call_master/external/plugify/include/plg/hash.hpp similarity index 91% rename from test/example_plugin/plg/hash.hpp rename to test/cross_call_master/external/plugify/include/plg/hash.hpp index de2e050..53f37bf 100644 --- a/test/example_plugin/plg/hash.hpp +++ b/test/cross_call_master/external/plugify/include/plg/hash.hpp @@ -76,4 +76,12 @@ namespace plg { (hash_combine(seed, args), ...); // fold expression return seed; } + + template + struct pair_hash { + size_t operator()(std::pair const& p) const { + return hash_combine_all(p.first, p.second); + } + }; + } diff --git a/test/example_plugin/plg/macro.hpp b/test/cross_call_master/external/plugify/include/plg/macro.hpp similarity index 99% rename from test/example_plugin/plg/macro.hpp rename to test/cross_call_master/external/plugify/include/plg/macro.hpp index 6008e31..a072fec 100644 --- a/test/example_plugin/plg/macro.hpp +++ b/test/cross_call_master/external/plugify/include/plg/macro.hpp @@ -257,7 +257,7 @@ # define PLUGIFY_NOINLINE [[gnu::noinline]] #elif PLUGIFY_COMPILER_MSVC # pragma warning(error: 4714) -# define PLUGIFY_FORCE_INLINE __forceinline +# define PLUGIFY_FORCE_INLINE [[msvc::forceinline]] # define PLUGIFY_NOINLINE __declspec(noinline) #else # define PLUGIFY_FORCE_INLINE inline diff --git a/test/cross_call_master/plg/numerics.hpp b/test/cross_call_master/external/plugify/include/plg/numerics.hpp similarity index 100% rename from test/cross_call_master/plg/numerics.hpp rename to test/cross_call_master/external/plugify/include/plg/numerics.hpp diff --git a/test/cross_call_master/external/plugify/include/plg/path.hpp b/test/cross_call_master/external/plugify/include/plg/path.hpp new file mode 100644 index 0000000..61343f3 --- /dev/null +++ b/test/cross_call_master/external/plugify/include/plg/path.hpp @@ -0,0 +1,52 @@ +#pragma once + +#include +#include +#include + +namespace plg { + using path_view = std::basic_string_view; + using path_string = std::filesystem::path::string_type; + using path_char = path_string::value_type; + using path_diff_t = path_string::difference_type; + +#if _WIN32 +#define PLUGIFY_PATH_LITERAL(x) L##x +#else +#define PLUGIFY_PATH_LITERAL(x) x +#endif + + template + bool insensitive_equals(const path_char lhs, const path_char rhs) { + if constexpr (std::is_same_v) { + return std::towlower(lhs) == rhs; // NOLINT + } else { + return std::tolower(lhs) == rhs; + } + } + + inline bool insensitive_equals(const path_view lhs, const path_view rhs) { + return lhs.size() == rhs.size() && std::equal(lhs.cbegin(), lhs.cend(), rhs.cbegin(), insensitive_equals); + } + + inline path_view extension_view(const std::filesystem::path& path) { + constexpr path_diff_t extension_size = 4; + if (!path.has_extension()) { return {}; } + const path_string& path_str = path.native(); + const auto offset = static_cast(path_str.size()) - extension_size; + if (offset <= 0) { return {}; } + return { path_str.cbegin() + offset, path_str.cend() }; + } + + inline bool has_extension(const std::filesystem::path& path, const path_view extension) { + return insensitive_equals(extension_view(path), extension); + } + + inline auto as_string(const std::filesystem::path& p) { +#if _WIN32 + return p.string(); // returns std::string by value +#else + return p.native(); // returns const std::string& +#endif + } +} diff --git a/test/cross_call_master/plg/plugin.hpp b/test/cross_call_master/external/plugify/include/plg/plugin.hpp similarity index 98% rename from test/cross_call_master/plg/plugin.hpp rename to test/cross_call_master/external/plugify/include/plg/plugin.hpp index 408f632..7498a8c 100644 --- a/test/cross_call_master/plg/plugin.hpp +++ b/test/cross_call_master/external/plugify/include/plg/plugin.hpp @@ -81,7 +81,7 @@ namespace plg { plg::vector GetDependencies() const { return plugin::GetDependencies(plugin::handle); } virtual void OnPluginStart() {}; - virtual void OnPluginUpdate(std::chrono::microseconds) {}; + virtual void OnPluginUpdate(std::chrono::milliseconds) {}; virtual void OnPluginEnd() {}; }; @@ -149,7 +149,7 @@ namespace plg { template \ struct has_overridden_OnPluginUpdate : std::false_type {}; \ template \ - struct has_overridden_OnPluginUpdate().OnPluginUpdate(std::chrono::microseconds{0}))>> \ + struct has_overridden_OnPluginUpdate().OnPluginUpdate(std::chrono::milliseconds{0}))>> \ : std::bool_constant> {}; \ template \ @@ -161,7 +161,7 @@ namespace plg { extern "C" plugin_api void Plugify_PluginStart() { \ GetPluginEntry()->OnPluginStart(); \ } \ - extern "C" plugin_api void Plugify_PluginUpdate(std::chrono::microseconds dt) { \ + extern "C" plugin_api void Plugify_PluginUpdate(std::chrono::milliseconds dt) { \ GetPluginEntry()->OnPluginUpdate(dt); \ } \ extern "C" plugin_api void Plugify_PluginEnd() { \ diff --git a/test/cross_call_master/plg/string.hpp b/test/cross_call_master/external/plugify/include/plg/string.hpp similarity index 92% rename from test/cross_call_master/plg/string.hpp rename to test/cross_call_master/external/plugify/include/plg/string.hpp index 17b4b29..175bed2 100644 --- a/test/cross_call_master/plg/string.hpp +++ b/test/cross_call_master/external/plugify/include/plg/string.hpp @@ -44,20 +44,63 @@ namespace plg { namespace detail { - template - struct is_allocator : std::false_type {}; - - template - struct is_allocator().allocate(std::size_t{}))>> : std::true_type {}; - - template - constexpr bool is_allocator_v = is_allocator::value; - - template - using is_traits = std::conjunction, std::is_integral>; - - template - constexpr bool is_traits_v = is_traits::value; + template + concept is_allocator = requires(Alloc& a, std::size_t n) { + typename std::allocator_traits::value_type; + typename std::allocator_traits::pointer; + typename std::allocator_traits::const_pointer; + typename std::allocator_traits::size_type; + typename std::allocator_traits::difference_type; + + { std::allocator_traits::allocate(a, n) } + -> std::convertible_to::pointer>; + + requires requires(typename std::allocator_traits::pointer p) { + std::allocator_traits::deallocate(a, p, n); + }; + }; + + template + concept is_char_traits = requires { + // Required type definitions + typename Traits::char_type; + typename Traits::int_type; + typename Traits::off_type; + typename Traits::pos_type; + typename Traits::state_type; + } && requires( + typename Traits::char_type c1, + typename Traits::char_type c2, + typename Traits::char_type& cr, + const typename Traits::char_type& ccr, + typename Traits::char_type* p, + const typename Traits::char_type* cp, + typename Traits::int_type i1, + typename Traits::int_type i2, + std::size_t n + ) { + // Character operations + { Traits::assign(cr, ccr) } -> std::same_as; + { Traits::eq(ccr, ccr) } -> std::convertible_to; + { Traits::lt(ccr, ccr) } -> std::convertible_to; + + // String operations + { Traits::compare(cp, cp, n) } -> std::convertible_to; + { Traits::length(cp) } -> std::convertible_to; + { Traits::find(cp, n, ccr) } -> std::convertible_to; + + // Memory operations + { Traits::move(p, cp, n) } -> std::same_as; + { Traits::copy(p, cp, n) } -> std::same_as; + { Traits::assign(p, n, c1) } -> std::same_as; + + // int_type operations + { Traits::not_eof(i1) } -> std::same_as; + { Traits::to_char_type(i1) } -> std::same_as; + { Traits::to_int_type(c1) } -> std::same_as; + { Traits::eq_int_type(i1, i2) } -> std::convertible_to; + { Traits::eof() } -> std::same_as; + }; struct uninitialized_size_tag {}; @@ -68,11 +111,12 @@ namespace plg { template concept string_compatible_range = std::ranges::input_range && std::convertible_to, Type>; #endif // PLUGIFY_STRING_CONTAINERS_RANGES - }// namespace detail + + } // namespace detail // basic_string // based on implementations from libc++, libstdc++ and Microsoft STL - template, typename Allocator = plg::allocator> requires (detail::is_traits_v && detail::is_allocator_v) + template, detail::is_allocator Allocator = plg::allocator> class basic_string { private: using allocator_traits = std::allocator_traits; @@ -1926,3 +1970,107 @@ namespace std { struct formatter, Allocator>> : plg::detail::string_formatter_base {}; }// namespace std #endif // PLUGIFY_STRING_NO_STD_FORMAT + +template +std::ostream& operator<<(std::ostream& os, const plg::basic_string& str) { + os << str.c_str(); + return os; +} + +#ifndef PLUGIFY_STRING_NO_STD_FORMAT +#include + +namespace plg { + namespace detail { + // Concept to match string-like types including char* and const char* + template + concept is_string_like = requires(T v) { + { std::string_view(v) }; + }; + } + + template + constexpr string join(const Range& range, std::string_view separator) { + string result; + + auto it = range.cbegin(); + auto end = range.cend(); + + if (it == end) return result; + + // First pass: compute total size + size_t total_size = 0; + size_t count = 0; + + for (auto tmp = it; tmp != end; ++tmp) { + using Elem = std::decay_t; + if constexpr (detail::is_string_like) { + total_size += std::string_view(*tmp).size(); + } else { + total_size += std::formatted_size("{}", *tmp); + } + ++count; + } + if (count > 1) { + total_size += (count - 1) * separator.size(); + } + result.reserve(total_size); + + auto in = std::back_inserter(result); + + // Second pass: actual formatting + /*if (it != end)*/ { + std::format_to(in, "{}", *it++); + } + while (it != end) { + std::format_to(in, "{}{}", separator, *it++); + } + + return result; + } + + template + constexpr string join(const Range& range, Proj&& proj, std::string_view separator) { + string result; + + auto it = range.cbegin(); + auto end = range.cend(); + + if (it == end) return result; + + // First pass: compute total size + size_t total_size = 0; + size_t count = 0; + + for (auto tmp = it; tmp != end; ++tmp) { + auto&& projected = std::invoke(std::forward(proj), *tmp); + using Elem = std::decay_t; + + if constexpr (detail::is_string_like) { + total_size += std::string_view(*projected).size(); + } else { + total_size += std::formatted_size("{}", projected); + } + ++count; + } + if (count > 1) { + total_size += (count - 1) * separator.size(); + } + result.reserve(total_size); + + auto out = std::back_inserter(result); + + // Second pass: actual formatting + { + auto&& projected = std::invoke(std::forward(proj), *it++); + std::format_to(out, "{}", projected); + } + while (it != end) { + auto&& projected = std::invoke(std::forward(proj), *it++); + std::format_to(out, "{}{}", separator, projected); + } + + return result; + } +} // namespace plugify +#endif // PLUGIFY_STRING_NO_STD_FORMAT \ No newline at end of file diff --git a/test/cross_call_master/plg/variant.hpp b/test/cross_call_master/external/plugify/include/plg/variant.hpp similarity index 100% rename from test/cross_call_master/plg/variant.hpp rename to test/cross_call_master/external/plugify/include/plg/variant.hpp diff --git a/test/example_plugin/plg/vector.hpp b/test/cross_call_master/external/plugify/include/plg/vector.hpp similarity index 96% rename from test/example_plugin/plg/vector.hpp rename to test/cross_call_master/external/plugify/include/plg/vector.hpp index b340a61..3ec8750 100644 --- a/test/example_plugin/plg/vector.hpp +++ b/test/cross_call_master/external/plugify/include/plg/vector.hpp @@ -27,7 +27,32 @@ #include "plg/allocator.hpp" namespace plg { - template + namespace detail { + template + concept is_alloc = requires(Alloc& a, std::size_t n) { + typename std::allocator_traits::value_type; + typename std::allocator_traits::pointer; + typename std::allocator_traits::const_pointer; + typename std::allocator_traits::size_type; + typename std::allocator_traits::difference_type; + + { std::allocator_traits::allocate(a, n) } + -> std::convertible_to::pointer>; + + requires requires(typename std::allocator_traits::pointer p) { + std::allocator_traits::deallocate(a, p, n); + }; + }; + + struct initialized_value_tag {}; + +#if PLUGIFY_VECTOR_CONTAINERS_RANGES + template + concept vector_compatible_range = std::ranges::input_range && std::convertible_to, Type>; +#endif + } // namespace detail + + template struct vector_iterator { using allocator_traits = std::allocator_traits; public: @@ -119,7 +144,7 @@ namespace plg { } #endif // __cpp_impl_three_way_comparison - template + template struct vector_const_iterator { using allocator_traits = std::allocator_traits; public: @@ -152,14 +177,14 @@ namespace plg { ++_current; return *this; } - constexpr vector_const_iterator operator++(int) const noexcept { + constexpr vector_const_iterator operator++(int) noexcept { return vector_const_iterator(_current++); } constexpr vector_const_iterator& operator--() noexcept { --_current; return *this; } - constexpr vector_const_iterator operator--(int) const noexcept { + constexpr vector_const_iterator operator--(int) noexcept { return vector_const_iterator(_current--); } constexpr vector_const_iterator& operator+=(const difference_type n) noexcept { @@ -232,18 +257,9 @@ namespace plg { return lhs.base() <=> rhs.base(); } - namespace detail { - struct initialized_value_tag {}; - -#if PLUGIFY_VECTOR_CONTAINERS_RANGES - template - concept vector_compatible_range = std::ranges::input_range && std::convertible_to, Type>; -#endif - } // namespace detail - // vector // based on implementations from libc++, libstdc++ and Microsoft STL - template> + template> class vector { using allocator_traits = std::allocator_traits; public: diff --git a/test/example_plugin/plg/version.hpp b/test/cross_call_master/external/plugify/include/plg/version.hpp similarity index 80% rename from test/example_plugin/plg/version.hpp rename to test/cross_call_master/external/plugify/include/plg/version.hpp index 355d33f..b12ac87 100644 --- a/test/example_plugin/plg/version.hpp +++ b/test/cross_call_master/external/plugify/include/plg/version.hpp @@ -6,10 +6,7 @@ #include #include #include -#include -#include #include -#include #if __has_include() #include #else @@ -22,11 +19,12 @@ #include "plg/hash.hpp" #include "plg/macro.hpp" +#include "plg/string.hpp" +#include "plg/vector.hpp" // from https://github.com/Neargye/semver namespace plg { namespace detail { - template struct resize_uninitialized { constexpr static auto resize(T& str, std::size_t size) -> std::void_t { @@ -67,7 +65,7 @@ namespace plg { struct prerelease_identifier { prerelease_identifier_type type; - std::string identifier; + string identifier; }; class version_parser; @@ -92,19 +90,19 @@ namespace plg { constexpr I2 minor() const noexcept { return minor_; } constexpr I3 patch() const noexcept { return patch_; } - constexpr const std::string& prerelease_tag() const { return prerelease_tag_; } - constexpr const std::string& build_metadata() const { return build_metadata_; } + constexpr const string& prerelease_tag() const { return prerelease_tag_; } + constexpr const string& build_metadata() const { return build_metadata_; } - constexpr std::string to_string() const; + constexpr string to_string() const; private: I1 major_ = 0; I2 minor_ = 1; I3 patch_ = 0; - std::string prerelease_tag_; - std::string build_metadata_; + string prerelease_tag_; + string build_metadata_; - std::vector prerelease_identifiers; + vector prerelease_identifiers; constexpr std::size_t length() const noexcept { return detail::length(major_) + detail::length(minor_) + detail::length(patch_) + 2 @@ -124,9 +122,9 @@ namespace plg { }; template - constexpr std::string version::to_string() const { - std::string result; - detail::resize_uninitialized{}.resize(result, length()); + constexpr string version::to_string() const { + string result; + detail::resize_uninitialized{}.resize(result, length()); auto it = result.end(); if (!build_metadata_.empty()) { @@ -169,7 +167,6 @@ namespace plg { }; namespace detail { - constexpr from_chars_result success(const char* ptr) noexcept { return from_chars_result{ ptr, std::errc{} }; } @@ -295,28 +292,28 @@ namespace plg { class token_stream { public: constexpr token_stream() = default; - constexpr explicit token_stream(std::vector tokens) noexcept : tokens(std::move(tokens)) {} + constexpr explicit token_stream(vector tokens) noexcept : tokens_(std::move(tokens)) {} constexpr void push(const token& token) noexcept { - tokens.push_back(token); + tokens_.push_back(token); } constexpr token advance() noexcept { - const token token = get(current); - ++current; + const token token = get(current_); + ++current_; return token; } constexpr token peek(std::size_t k = 0) const noexcept { - return get(current + k); + return get(current_ + k); } constexpr token previous() const noexcept { - return get(current - 1); + return get(current_ - 1); } constexpr bool advanceIfMatch(token& token, token_type type) noexcept { - if (get(current).type != type) { + if (get(current_).type != type) { return false; } @@ -338,11 +335,11 @@ namespace plg { } private: - std::size_t current = 0; - std::vector tokens; + std::size_t current_ = 0; + vector tokens_; constexpr token get(std::size_t i) const noexcept { - return tokens[i]; + return tokens_[i]; } }; @@ -481,7 +478,7 @@ namespace plg { class version_parser { public: - constexpr explicit version_parser(token_stream& stream) : stream{stream} { + constexpr explicit version_parser(token_stream& stream) : stream_{stream} { } template @@ -493,8 +490,8 @@ namespace plg { return result; } - if (!stream.consume(token_type::dot)) { - return failure(stream.previous().lexeme); + if (!stream_.consume(token_type::dot)) { + return failure(stream_.previous().lexeme); } result = parse_number(out.minor_); @@ -502,8 +499,8 @@ namespace plg { return result; } - if (!stream.consume(token_type::dot)) { - return failure(stream.previous().lexeme); + if (!stream_.consume(token_type::dot)) { + return failure(stream_.previous().lexeme); } result = parse_number(out.patch_); @@ -511,14 +508,14 @@ namespace plg { return result; } - if (stream.advanceIfMatch(token_type::hyphen)) { + if (stream_.advanceIfMatch(token_type::hyphen)) { result = parse_prerelease_tag(out.prerelease_tag_, out.prerelease_identifiers); if (!result) { return result; } } - if (stream.advanceIfMatch(token_type::plus)) { + if (stream_.advanceIfMatch(token_type::plus)) { result = parse_build_metadata(out.build_metadata_); if (!result) { return result; @@ -530,11 +527,11 @@ namespace plg { private: - token_stream& stream; + token_stream& stream_; template constexpr from_chars_result parse_number(Int& out) { - token token = stream.advance(); + token token = stream_.advance(); if (!is_digit(token)) { return failure(token.lexeme); @@ -545,30 +542,30 @@ namespace plg { if (first_digit == 0) { out = static_cast(result); - return success(stream.peek().lexeme); + return success(stream_.peek().lexeme); } - while (stream.advanceIfMatch(token, token_type::digit)) { + while (stream_.advanceIfMatch(token, token_type::digit)) { result = result * 10 + std::get(token.value); } if (detail::number_in_range(result)) { out = static_cast(result); - return success(stream.peek().lexeme); + return success(stream_.peek().lexeme); } return failure(token.lexeme, std::errc::result_out_of_range); } - constexpr from_chars_result parse_prerelease_tag(std::string& out, std::vector& out_identifiers) { - std::string result; + constexpr from_chars_result parse_prerelease_tag(string& out, vector& out_identifiers) { + string result; do { if (!result.empty()) { result.push_back('.'); } - std::string identifier; + string identifier; if (const auto res = parse_prerelease_identifier(identifier); !res) { return res; } @@ -576,35 +573,35 @@ namespace plg { result.append(identifier); out_identifiers.push_back(make_prerelease_identifier(identifier)); - } while (stream.advanceIfMatch(token_type::dot)); + } while (stream_.advanceIfMatch(token_type::dot)); out = result; - return success(stream.peek().lexeme); + return success(stream_.peek().lexeme); } - constexpr from_chars_result parse_build_metadata(std::string& out) { - std::string result; + constexpr from_chars_result parse_build_metadata(string& out) { + string result; do { if (!result.empty()) { result.push_back('.'); } - std::string identifier; + string identifier; if (const auto res = parse_build_identifier(identifier); !res) { return res; } result.append(identifier); - } while (stream.advanceIfMatch(token_type::dot)); + } while (stream_.advanceIfMatch(token_type::dot)); out = result; - return success(stream.peek().lexeme); + return success(stream_.peek().lexeme); } - constexpr from_chars_result parse_prerelease_identifier(std::string& out) { - std::string result; - token token = stream.advance(); + constexpr from_chars_result parse_prerelease_identifier(string& out) { + string result; + token token = stream_.advance(); do { switch (token.type) { @@ -635,13 +632,13 @@ namespace plg { default: return failure(token.lexeme); } - } while (stream.advanceIfMatch(token, token_type::hyphen) || stream.advanceIfMatch(token, token_type::letter) || stream.advanceIfMatch(token, token_type::digit)); + } while (stream_.advanceIfMatch(token, token_type::hyphen) || stream_.advanceIfMatch(token, token_type::letter) || stream_.advanceIfMatch(token, token_type::digit)); out = result; - return success(stream.peek().lexeme); + return success(stream_.peek().lexeme); } - constexpr detail::prerelease_identifier make_prerelease_identifier(const std::string& identifier) { + constexpr detail::prerelease_identifier make_prerelease_identifier(const string& identifier) { auto type = detail::prerelease_identifier_type::numeric; for (char c : identifier) { if (c == '-' || detail::is_letter(c)) { @@ -652,9 +649,9 @@ namespace plg { return detail::prerelease_identifier{ type, identifier }; } - constexpr from_chars_result parse_build_identifier(std::string& out) { - std::string result; - token token = stream.advance(); + constexpr from_chars_result parse_build_identifier(string& out) { + string result; + token token = stream_.advance(); do { switch (token.type) { @@ -673,10 +670,10 @@ namespace plg { default: return failure(token.lexeme); } - } while (stream.advanceIfMatch(token, token_type::hyphen) || stream.advanceIfMatch(token, token_type::letter) || stream.advanceIfMatch(token, token_type::digit)); + } while (stream_.advanceIfMatch(token, token_type::hyphen) || stream_.advanceIfMatch(token, token_type::letter) || stream_.advanceIfMatch(token, token_type::digit)); out = result; - return success(stream.peek().lexeme); + return success(stream_.peek().lexeme); } constexpr bool is_leading_zero(int digit) noexcept { @@ -684,12 +681,12 @@ namespace plg { return false; } - int k = 0; + size_t k = 0; int alpha_numerics = 0; int digits = 0; while (true) { - const token token = stream.peek(k); + const token token = stream_.peek(k); if (!is_alphanumeric(token)) { break; @@ -827,31 +824,44 @@ namespace plg { template class range_comparator { public: - constexpr range_comparator(const version& v, range_operator op) noexcept : v(v), op(op) {} + constexpr range_comparator(const version& v, range_operator op) noexcept : v_(v), op_(op) {} constexpr bool contains(const version& other) const noexcept { - switch (op) { + switch (op_) { case range_operator::less: - return detail::compare_parsed(other, v, version_compare_option::include_prerelease) < 0; + return detail::compare_parsed(other, v_, version_compare_option::include_prerelease) < 0; case range_operator::less_or_equal: - return detail::compare_parsed(other, v, version_compare_option::include_prerelease) <= 0; + return detail::compare_parsed(other, v_, version_compare_option::include_prerelease) <= 0; case range_operator::greater: - return detail::compare_parsed(other, v, version_compare_option::include_prerelease) > 0; + return detail::compare_parsed(other, v_, version_compare_option::include_prerelease) > 0; case range_operator::greater_or_equal: - return detail::compare_parsed(other, v, version_compare_option::include_prerelease) >= 0; + return detail::compare_parsed(other, v_, version_compare_option::include_prerelease) >= 0; case range_operator::equal: - return detail::compare_parsed(other, v, version_compare_option::include_prerelease) == 0; + return detail::compare_parsed(other, v_, version_compare_option::include_prerelease) == 0; } return false; } - constexpr const version& get_version() const noexcept { return v; } + constexpr const version& get_version() const noexcept { return v_; } + + constexpr range_operator get_operator() const noexcept { return op_; } - constexpr range_operator get_operator() const noexcept { return op; } + constexpr string to_string() const { + string result; + switch (op_) { + case range_operator::less: result += "<"; break; + case range_operator::less_or_equal: result += "<="; break; + case range_operator::greater: result += ">"; break; + case range_operator::greater_or_equal: result += ">="; break; + case range_operator::equal: result += "="; break; + } + result += v_.to_string(); + return result; + } private: - version v; - range_operator op; + version v_; + range_operator op_; }; class range_parser; @@ -868,36 +878,40 @@ namespace plg { } } - return std::all_of(ranges_comparators.begin(), ranges_comparators.end(), [&](const auto& ranges_comparator) { + return std::all_of(ranges_comparators_.begin(), ranges_comparators_.end(), [&](const auto& ranges_comparator) { return ranges_comparator.contains(v); }); } - constexpr const auto begin() const noexcept { - return ranges_comparators.begin(); + constexpr auto begin() const noexcept { + return ranges_comparators_.begin(); } - constexpr const auto end() const noexcept { - return ranges_comparators.end(); + constexpr auto end() const noexcept { + return ranges_comparators_.end(); } constexpr std::size_t size() const noexcept { - return ranges_comparators.size(); + return ranges_comparators_.size(); } constexpr bool empty() const noexcept { - return ranges_comparators.empty(); + return ranges_comparators_.empty(); + } + + constexpr string to_string() const { + return join(ranges_comparators_, " "); } private: - std::vector> ranges_comparators; + vector> ranges_comparators_; constexpr bool match_at_least_one_comparator_with_prerelease(const version& v) const noexcept { if (v.prerelease_tag().empty()) { return true; } - return std::any_of(ranges_comparators.begin(), ranges_comparators.end(), [&](const auto& ranges_comparator) { + return std::any_of(ranges_comparators_.begin(), ranges_comparators_.end(), [&](const auto& ranges_comparator) { const bool has_prerelease = !ranges_comparator.get_version().prerelease_tag().empty(); const bool equal_without_prerelease = detail::compare_parsed(v, ranges_comparator.get_version(), version_compare_option::exclude_prerelease) == 0; return has_prerelease && equal_without_prerelease; @@ -912,39 +926,43 @@ namespace plg { friend class detail::range_parser; constexpr bool contains(const version& v, version_compare_option option = version_compare_option::exclude_prerelease) const noexcept { - return std::any_of(ranges.begin(), ranges.end(), [&](const auto& range) { + return std::any_of(ranges_.begin(), ranges_.end(), [&](const auto& range) { return range.contains(v, option); }); } - constexpr const auto begin() const noexcept { - return ranges.begin(); + constexpr auto begin() const noexcept { + return ranges_.begin(); } - constexpr const auto end() const noexcept { - return ranges.end(); + constexpr auto end() const noexcept { + return ranges_.end(); } constexpr std::size_t size() const noexcept { - return ranges.size(); + return ranges_.size(); } constexpr bool empty() const noexcept { - return ranges.empty(); + return ranges_.empty(); + } + + constexpr string to_string() const { + return join(ranges_, " "); } private: - std::vector> ranges; + vector> ranges_; }; namespace detail { class range_parser { public: - constexpr explicit range_parser(token_stream stream) noexcept : stream(std::move(stream)) {} + constexpr explicit range_parser(token_stream stream) noexcept : stream_(std::move(stream)) {} template constexpr from_chars_result parse(range_set& out) noexcept { - std::vector> ranges; + vector> ranges; do { @@ -956,54 +974,54 @@ namespace plg { ranges.push_back(range); skip_whitespaces(); - } while (stream.advanceIfMatch(token_type::logical_or)); + } while (stream_.advanceIfMatch(token_type::logical_or)); - out.ranges = std::move(ranges); + out.ranges_ = std::move(ranges); - return success(stream.peek().lexeme); + return success(stream_.peek().lexeme); } private: - token_stream stream; + token_stream stream_; template constexpr from_chars_result parse_range(detail::range& out) noexcept { do { skip_whitespaces(); - if (const auto res = parse_range_comparator(out.ranges_comparators); !res) { + if (const auto res = parse_range_comparator(out.ranges_comparators_); !res) { return res; } skip_whitespaces(); - } while (stream.check(token_type::range_operator) || stream.check(token_type::digit)); + } while (stream_.check(token_type::range_operator) || stream_.check(token_type::digit)); - return success(stream.peek().lexeme); + return success(stream_.peek().lexeme); } template - constexpr from_chars_result parse_range_comparator(std::vector>& out) noexcept { + constexpr from_chars_result parse_range_comparator(vector>& out) noexcept { range_operator op = range_operator::equal; token token; - if (stream.advanceIfMatch(token, token_type::range_operator)) { + if (stream_.advanceIfMatch(token, token_type::range_operator)) { op = std::get(token.value); } skip_whitespaces(); version ver; - version_parser parser{ stream }; + version_parser parser{ stream_ }; if (const auto res = parser.parse(ver); !res) { return res; } out.emplace_back(ver, op); - return success(stream.peek().lexeme); + return success(stream_.peek().lexeme); } constexpr void skip_whitespaces() noexcept { - while (stream.advanceIfMatch(token_type::space)) { + while (stream_.advanceIfMatch(token_type::space)) { ; } } @@ -1060,5 +1078,38 @@ namespace std { return std::format_to(ctx.out(), "{}", ver.to_string()); } }; + template + struct formatter> { + constexpr auto parse(std::format_parse_context& ctx) { + return ctx.begin(); + } + + template + auto format(const plg::range_set& ver, FormatContext& ctx) const { + return std::format_to(ctx.out(), "{}", ver.to_string()); + } + }; + template + struct formatter> { + constexpr auto parse(std::format_parse_context& ctx) { + return ctx.begin(); + } + + template + auto format(const plg::detail::range& ver, FormatContext& ctx) const { + return std::format_to(ctx.out(), "{}", ver.to_string()); + } + }; + template + struct formatter> { + constexpr auto parse(std::format_parse_context& ctx) { + return ctx.begin(); + } + + template + auto format(const plg::detail::range_comparator& ver, FormatContext& ctx) const { + return std::format_to(ctx.out(), "{}", ver.to_string()); + } + }; }// namespace std #endif // PLUGIFY_VECTOR_NO_STD_FORMAT diff --git a/test/cross_call_master/pps/cross_call_worker.hpp b/test/cross_call_master/external/plugify/include/pps/cross_call_worker.hpp similarity index 100% rename from test/cross_call_master/pps/cross_call_worker.hpp rename to test/cross_call_master/external/plugify/include/pps/cross_call_worker.hpp diff --git a/test/cross_call_master/plg/format.hpp b/test/cross_call_master/plg/format.hpp deleted file mode 100644 index 1a013a7..0000000 --- a/test/cross_call_master/plg/format.hpp +++ /dev/null @@ -1,123 +0,0 @@ -#pragma once - -#include "plg/macro.hpp" - -#ifdef __cpp_lib_format - -#include - -#else // __cpp_lib_format - -// Define FMT_FORMAT_H externally to force a difference location for {fmt} -#ifndef FMT_FORMAT_H -#define FMT_FORMAT_H -#endif - -#ifndef FMT_HEADER_ONLY -#define FMT_HEADER_ONLY -#endif -#include FMT_FORMAT_H - -namespace std { - using namespace fmt; - using namespace fmt::detail; -} - -#endif // __cpp_lib_format - -#include -#include - -namespace plg { - namespace detail { - // Concept to match string-like types including char* and const char* - template - concept is_string_like = requires(T v) { - { std::string_view(v) }; - }; - } - - template - constexpr std::string join(const Range& range, std::string_view separator) { - std::string result; - - auto it = range.begin(); - auto end = range.end(); - - if (it == end) return result; - - // First pass: compute total size - size_t total_size = 0; - size_t count = 0; - - for (auto tmp = it; tmp != end; ++tmp) { - using Elem = std::decay_t; - if constexpr (detail::is_string_like) { - total_size += std::string_view(*tmp).size(); - } else { - total_size += std::formatted_size("{}", *tmp); - } - ++count; - } - if (count > 1) { - total_size += (count - 1) * separator.size(); - } - result.reserve(total_size); - - auto in = std::back_inserter(result); - - // Second pass: actual formatting - /*if (it != end)*/ { - std::format_to(in, "{}", *it++); - } - while (it != end) { - std::format_to(in, "{}{}", separator, *it++); - } - - return result; - } - - template - constexpr std::string join(const Range& range, Proj&& proj, std::string_view separator) { - std::string result; - - auto it = range.begin(); - auto end = range.end(); - - if (it == end) return result; - - // First pass: compute total size - size_t total_size = 0; - size_t count = 0; - - for (auto tmp = it; tmp != end; ++tmp) { - auto&& projected = std::invoke(std::forward(proj), *tmp); - using Elem = std::decay_t; - - if constexpr (detail::is_string_like) { - total_size += std::string_view(*projected).size(); - } else { - total_size += std::formatted_size("{}", projected); - } - ++count; - } - if (count > 1) { - total_size += (count - 1) * separator.size(); - } - result.reserve(total_size); - - auto out = std::back_inserter(result); - - // Second pass: actual formatting - { - auto&& projected = std::invoke(std::forward(proj), *it++); - std::format_to(out, "{}", projected); - } - while (it != end) { - auto&& projected = std::invoke(std::forward(proj), *it++); - std::format_to(out, "{}{}", separator, projected); - } - - return result; - } -} diff --git a/test/cross_call_worker/CMakeLists.txt b/test/cross_call_worker/CMakeLists.txt index aed87d3..efa0c92 100644 --- a/test/cross_call_worker/CMakeLists.txt +++ b/test/cross_call_worker/CMakeLists.txt @@ -33,7 +33,7 @@ include(CompatFormat) set(PLUGIN_SOURCES "plugin.cpp") add_library(${PROJECT_NAME} SHARED ${PLUGIN_SOURCES}) -target_include_directories(${PROJECT_NAME} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}) +target_include_directories(${PROJECT_NAME} PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/external/plugify/include") if(MSVC) target_compile_options(${PROJECT_NAME} PRIVATE /W4 /WX) diff --git a/test/cross_call_worker/plg/allocator.hpp b/test/cross_call_worker/external/plugify/include/plg/allocator.hpp similarity index 100% rename from test/cross_call_worker/plg/allocator.hpp rename to test/cross_call_worker/external/plugify/include/plg/allocator.hpp diff --git a/test/cross_call_worker/plg/any.hpp b/test/cross_call_worker/external/plugify/include/plg/any.hpp similarity index 100% rename from test/cross_call_worker/plg/any.hpp rename to test/cross_call_worker/external/plugify/include/plg/any.hpp diff --git a/test/cross_call_worker/plg/api.hpp b/test/cross_call_worker/external/plugify/include/plg/api.hpp similarity index 100% rename from test/cross_call_worker/plg/api.hpp rename to test/cross_call_worker/external/plugify/include/plg/api.hpp diff --git a/test/cross_call_worker/plg/debugging.hpp b/test/cross_call_worker/external/plugify/include/plg/debugging.hpp similarity index 100% rename from test/cross_call_worker/plg/debugging.hpp rename to test/cross_call_worker/external/plugify/include/plg/debugging.hpp diff --git a/test/example_plugin/plg/enum.hpp b/test/cross_call_worker/external/plugify/include/plg/enum.hpp similarity index 98% rename from test/example_plugin/plg/enum.hpp rename to test/cross_call_worker/external/plugify/include/plg/enum.hpp index 5a2b373..84f6d23 100644 --- a/test/example_plugin/plg/enum.hpp +++ b/test/cross_call_worker/external/plugify/include/plg/enum.hpp @@ -55,7 +55,7 @@ namespace plg { template constexpr auto value(std::size_t v) { - return static_cast(ENUM_MIN_VALUE + v); + return static_cast(ENUM_MIN_VALUE + static_cast(v)); } template diff --git a/test/cross_call_worker/external/plugify/include/plg/expected.hpp b/test/cross_call_worker/external/plugify/include/plg/expected.hpp new file mode 100644 index 0000000..d6bc137 --- /dev/null +++ b/test/cross_call_worker/external/plugify/include/plg/expected.hpp @@ -0,0 +1,1430 @@ +#pragma once + +#include "plg/macro.hpp" + +#ifdef __cpp_lib_expected +#include +namespace plg { + using std::expected; + using std::unexpected; +} +#else +#include +#include +#include +#include +#include +#include +#include +#include + +// from https://github.com/RishabhRD +namespace plg { + template + class unexpected { + public: + using value_type = E; + constexpr unexpected(unexpected const&) = default; + constexpr unexpected(unexpected&&) = default;// NOLINT + constexpr auto operator=(unexpected const&) -> unexpected& = default; + constexpr auto operator=(unexpected&&) -> unexpected& = default;// NOLINT + ~unexpected() = default; + + template + requires std::constructible_from + constexpr explicit unexpected(std::in_place_t /*unused*/, Args&&... args) + : val(std::forward(args)...) {} + + template + requires std::constructible_from&, Args...> + constexpr explicit unexpected(std::in_place_t /*unused*/, + std::initializer_list i_list, + Args&&... args) + : val(i_list, std::forward(args)...) {} + + template + requires(!std::same_as, unexpected>) && + (!std::same_as, std::in_place_t>) && + std::constructible_from + constexpr explicit unexpected(Err&& err)// NOLINT + : val(std::forward(err)) {} + + constexpr auto value() const& noexcept -> E const& { return val; } + constexpr auto value() & noexcept -> E& { return val; } + constexpr auto value() const&& noexcept -> E const&& { + return std::move(val); + } + constexpr auto value() && noexcept -> E&& { return std::move(val); } + + constexpr void swap(unexpected& other) noexcept( + std::is_nothrow_swappable_v) + requires(std::is_swappable_v) + { + using std::swap; + swap(val, other.val); + } + + template + requires(requires(E const& x, E2 const& y) { + { x == y } -> std::convertible_to; + }) + friend constexpr auto operator==(unexpected const& x, unexpected const& y) -> bool { + return x.value() == y.value(); + } + + friend constexpr void swap(unexpected& x, unexpected& y) noexcept(noexcept(x.swap(y))) + requires(std::is_swappable_v) + { + x.swap(y); + } + + private: + E val; + }; + + template + unexpected(E) -> unexpected; + + template + class bad_expected_access; + + template<> + class bad_expected_access : public std::exception { + protected: + bad_expected_access() noexcept = default; + bad_expected_access(bad_expected_access const&) = default; + bad_expected_access(bad_expected_access&&) = default; + auto operator=(bad_expected_access const&) -> bad_expected_access& = default; + auto operator=(bad_expected_access&&) -> bad_expected_access& = default; + ~bad_expected_access() override = default; + + public: + auto what() const noexcept -> char const* override {// NOLINT + return "bad expected access"; + } + }; + + template + class bad_expected_access : public bad_expected_access { + public: + explicit bad_expected_access(E e) : val(std::move(e)) {} + auto what() const noexcept -> char const* override {// NOLINT + return "bad expected access"; + } + + auto error() & noexcept -> E& { return val; } + auto error() const& noexcept -> E const& { return val; } + auto error() && noexcept -> E&& { return std::move(val); } + auto error() const&& noexcept -> const E&& { return std::move(val); } + + private: + E val; + }; + + struct unexpect_t {}; + inline constexpr unexpect_t unexpect{}; + + namespace detail { + template + concept non_void_destructible = std::same_as || std::destructible; + } + + template + class expected; + + namespace detail { + template + concept expected_constructible_from_other = + std::constructible_from && + std::constructible_from && + (!std::constructible_from&>) && + (!std::constructible_from>) && + (!std::constructible_from const&>) && + (!std::constructible_from const>) && + (!std::convertible_to&, T>) && + (!std::convertible_to&&, T>) && + (!std::convertible_to const&, T>) && + (!std::convertible_to const&&, T>) && + (!std::constructible_from, expected&>) && + (!std::constructible_from, expected>) && + (!std::constructible_from, expected const&>) && + (!std::constructible_from, expected const>); + + template + concept is_unexpected = + std::same_as, unexpected>; + + template + concept is_expected = + std::same_as, + expected>; + + // This function makes sure expected doesn't get into valueless_by_exception + // state due to any exception while assignment + template + constexpr void reinit_expected(T& newval, U& oldval, Args&&... args) { + if constexpr (std::is_nothrow_constructible_v) { + std::destroy_at(std::addressof(oldval)); + std::construct_at(std::addressof(newval), std::forward(args)...); + } else if constexpr (std::is_nothrow_move_constructible_v) { + T tmp(std::forward(args)...); + std::destroy_at(std::addressof(oldval)); + std::construct_at(std::addressof(newval), std::move(tmp)); + } else { + U tmp(std::move(oldval)); + std::destroy_at(std::addressof(oldval)); + try { + std::construct_at(std::addressof(newval), std::forward(args)...); + } catch (...) { + std::construct_at(std::addressof(oldval), std::move(tmp)); + throw; + } + } + } + + }// namespace detail + + template + class expected { + public: + using value_type = T; + using error_type = E; + using unexpected_type = unexpected; + + template + using rebind = expected; + + // constructors + // postcondition: has_value() = true + constexpr expected() + requires std::default_initializable + : val{} {}; + + // postcondition: has_value() = rhs.has_value() + constexpr expected(expected const& rhs) + requires std::copy_constructible && std::copy_constructible && + std::is_trivially_copy_constructible_v && + std::is_trivially_copy_constructible_v + = default; + + // postcondition: has_value() = rhs.has_value() + constexpr expected(expected const& rhs) + requires std::copy_constructible && std::copy_constructible + : has_val(rhs.has_val) { + if (rhs.has_value()) { + std::construct_at(std::addressof(this->val), *rhs); + } else { + std::construct_at(std::addressof(this->unex), rhs.error()); + } + } + + constexpr expected(expected&&) noexcept( + std::is_nothrow_move_constructible_v && + std::is_nothrow_move_constructible_v) + requires std::move_constructible && std::move_constructible && + std::is_trivially_move_constructible_v && + std::is_trivially_move_constructible_v + = default; + + constexpr expected(expected&& rhs) noexcept( + std::is_nothrow_move_constructible_v && + std::is_nothrow_move_constructible_v) + requires std::move_constructible && std::move_constructible + : has_val(rhs.has_value()) { + if (rhs.has_value()) { + std::construct_at(std::addressof(this->val), std::move(*rhs)); + } else { + std::construct_at(std::addressof(this->unex), std::move(rhs.error())); + } + } + + template + requires detail::expected_constructible_from_other + constexpr explicit(!std::convertible_to || + !std::convertible_to) + expected(expected const& rhs)// NOLINT + : has_val(rhs.has_value()) { + using UF = U const&; + using GF = G const&; + if (rhs.has_value()) { + std::construct_at(std::addressof(this->val), std::forward(*rhs)); + } else { + std::construct_at(std::addressof(this->unex), + std::forward(rhs.error())); + } + } + + template + requires detail::expected_constructible_from_other + constexpr explicit(!std::convertible_to || !std::convertible_to) + expected(expected&& rhs)// NOLINT + : has_val(rhs.has_value()) { + using UF = U const&; + using GF = G const&; + if (rhs.has_value()) { + std::construct_at(std::addressof(this->val), std::forward(*rhs)); + } else { + std::construct_at(std::addressof(this->unex), + std::forward(rhs.error())); + } + } + + template + requires(!std::same_as, std::in_place_t>) && + (!std::same_as, std::remove_cvref_t>) && + (!detail::is_unexpected) && + std::constructible_from + constexpr explicit(!std::convertible_to) expected(U&& v)// NOLINT + : val(std::forward(v)) {} + + template + requires std::constructible_from + constexpr explicit(!std::convertible_to) + expected(unexpected const& e)// NOLINT + : has_val{false}, unex(std::forward(e.value())) {} + + template + requires std::constructible_from + constexpr explicit(!std::convertible_to) + expected(unexpected&& e)// NOLINT + : has_val{false}, unex(std::forward(e.value())) {} + + template + requires std::constructible_from + constexpr explicit expected(std::in_place_t /*unused*/, Args&&... args) + : val(std::forward(args)...) {} + + template + requires std::constructible_from&, Args...> + constexpr explicit expected(std::in_place_t /*unused*/, + std::initializer_list il, + Args&&... args) + : val(il, std::forward(args)...) {} + + template + requires std::constructible_from + constexpr explicit expected(unexpect_t /*unused*/, Args&&... args) + : has_val{false}, unex(std::forward(args)...) {} + + template + requires std::constructible_from&, + Args...> + constexpr explicit expected(unexpect_t /*unused*/, + std::initializer_list il, + Args&&... args) + : has_val(false), + unex(il, std::forward(args)...) {} + + // destructor + constexpr ~expected() { + if constexpr (std::is_trivially_destructible_v and + std::is_trivially_destructible_v) { + } else if constexpr (std::is_trivially_destructible_v) { + if (!has_val) { + std::destroy_at(std::addressof(this->unex)); + } + } else if constexpr (std::is_trivially_destructible_v) { + if (has_val) { + std::destroy_at(std::addressof(this->val)); + } + } else { + if (has_val) { + std::destroy_at(std::addressof(this->val)); + } else { + std::destroy_at(std::addressof(this->unex)); + } + } + } + + // assignment + constexpr auto operator=(expected const& rhs)// NOLINT + -> expected& + requires std::is_copy_assignable_v && + std::is_copy_constructible_v && + std::is_copy_assignable_v && + std::is_copy_constructible_v && + (std::is_nothrow_move_constructible_v || + std::is_nothrow_move_constructible_v) + { + if (this->has_value() and rhs.has_value()) { + this->val = *rhs; + } else if (this->has_value()) { + detail::reinit_expected(this->unex, this->val, rhs.error()); + } else if (rhs.has_value()) { + detail::reinit_expected(this->val, this->unex, *rhs); + } else { + this->unex = rhs.error(); + } + has_val = rhs.has_value(); + return *this; + } + + constexpr auto operator=(expected&& rhs)// + noexcept(std::is_nothrow_move_assignable_v && + std::is_nothrow_move_constructible_v && + std::is_nothrow_move_assignable_v && + std::is_nothrow_move_constructible_v) + -> expected& + requires std::is_move_constructible_v && + std::is_move_assignable_v && + std::is_move_constructible_v && + std::is_move_assignable_v && + (std::is_nothrow_move_constructible_v || std::is_nothrow_move_constructible_v) + { + if (this->has_value() and rhs.has_value()) { + this->val = std::move(*rhs); + } else if (this->has_value()) { + detail::reinit_expected(this->unex, this->val, std::move(rhs.error())); + } else if (rhs.has_value()) { + detail::reinit_expected(this->val, this->unex, std::move(*rhs)); + } else { + this->unex = std::move(rhs.error()); + } + has_val = rhs.has_value(); + return *this; + } + + template + constexpr auto operator=(U&& rhs) -> expected& requires(!std::same_as>) && + (!detail::is_unexpected>) && + std::constructible_from&& std::is_assignable_v && + (std::is_nothrow_constructible_v || + std::is_nothrow_move_constructible_v || + std::is_nothrow_move_constructible_v) { + if (this->has_value()) { + this->val = std::forward(rhs); + return *this; + } + detail::reinit_expected(this->val, this->unex, std::forward(rhs)); + has_val = true; + return *this; + } + + template + requires std::constructible_from && + std::is_assignable_v && + (std::is_nothrow_constructible_v || + std::is_nothrow_move_constructible_v || + std::is_nothrow_move_constructible_v) + constexpr auto operator=(unexpected const& e) -> expected& { + using GF = G const&; + if (has_value()) { + detail::reinit_expected(this->unex, this->val, + std::forward(e.value())); + } else { + this->unex = std::forward(e.value()); + } + has_val = false; + return *this; + } + + template + requires std::constructible_from && + std::is_assignable_v && + (std::is_nothrow_constructible_v || + std::is_nothrow_move_constructible_v || + std::is_nothrow_move_constructible_v) + constexpr auto operator=(unexpected&& e) -> expected& { + using GF = G; + if (has_value()) { + detail::reinit_expected(this->unex, this->val, + std::forward(e.value())); + } else { + this->unex = std::forward(e.value()); + } + has_val = false; + return *this; + } + + // modifiers + template + requires std::is_nothrow_constructible_v + constexpr auto emplace(Args&&... args) noexcept -> T& { + if (has_value()) { + std::destroy_at(std::addressof(this->val)); + } else { + std::destroy_at(std::addressof(this->unex)); + has_val = true; + } + return *std::construct_at(std::addressof(this->val), + std::forward(args)...); + } + + template + requires std::is_nothrow_constructible_v&, Args...> + constexpr auto emplace(std::initializer_list il, + Args&&... args) noexcept -> T& { + if (has_value()) { + std::destroy_at(std::addressof(this->val)); + } else { + std::destroy_at(std::addressof(this->unex)); + has_val = true; + } + return *std::construct_at(std::addressof(this->val), il, + std::forward(args)...); + } + + // swap + constexpr void swap(expected& rhs) noexcept( + std::is_nothrow_constructible_v && + std::is_nothrow_swappable_v && + std::is_nothrow_move_constructible_v && + std::is_nothrow_swappable_v) + requires std::is_swappable_v && + std::is_swappable_v && + std::is_move_constructible_v && + std::is_move_constructible_v && + (std::is_nothrow_constructible_v || + std::is_nothrow_constructible_v) + { + if (rhs.has_value()) { + if (has_value()) { + using std::swap; + swap(this->val, rhs.val); + } else { + rhs.swap(*this); + } + } else { + if (has_value()) { + if constexpr (std::is_nothrow_move_constructible_v) { + E tmp(std::move(rhs.unex)); + std::destroy_at(std::addressof(rhs.unex)); + try { + std::construct_at(std::addressof(rhs.val), std::move(this->val)); + std::destroy_at(std::addressof(this->val)); + std::construct_at(std::addressof(this->unex), std::move(tmp)); + } catch (...) { + std::construct_at(std::addressof(rhs.unex), std::move(tmp)); + throw; + } + } else { + T tmp(std::move(this->val)); + std::destroy_at(std::addressof(this->val)); + try { + std::construct_at(std::addressof(this->unex), std::move(rhs.unex)); + std::destroy_at(std::addressof(rhs.unex)); + std::construct_at(std::addressof(rhs.val), std::move(tmp)); + } catch (...) { + std::construct_at(std::addressof(this->val), std::move(tmp)); + throw; + } + } + has_val = false; + rhs.has_val = true; + } else { + using std::swap; + swap(this->unex, rhs.unex); + } + } + } + + // observers + + // precondition: has_value() = true + constexpr auto operator->() const noexcept -> T const* { + return std::addressof(this->val); + } + + // precondition: has_value() = true + constexpr auto operator->() noexcept -> T* { + return std::addressof(this->val); + } + + // precondition: has_value() = true + constexpr auto operator*() const& noexcept -> T const& { return this->val; } + + // precondition: has_value() = true + constexpr auto operator*() & noexcept -> T& { return this->val; } + + // precondition: has_value() = true + constexpr auto operator*() const&& noexcept -> T const&& { + return std::move(this->val); + } + + // precondition: has_value() = true + constexpr auto operator*() && noexcept -> T&& { return std::move(this->val); } + + constexpr explicit operator bool() const noexcept { return has_val; } + + [[nodiscard]] constexpr auto has_value() const noexcept -> bool { + return has_val; + } + + constexpr auto value() const& -> T const& { + if (has_value()) { + return this->val; + } + throw bad_expected_access(error()); + } + + constexpr auto value() & -> T& { + if (has_value()) { + return this->val; + } + throw bad_expected_access(error()); + } + + constexpr auto value() const&& -> T const&& { + if (has_value()) { + return std::move(this->val); + } + throw bad_expected_access(std::move(error())); + } + + constexpr auto value() && -> T&& { + if (has_value()) { + return std::move(this->val); + } + throw bad_expected_access(std::move(error())); + } + + // precondition: has_value() = false + constexpr auto error() const& -> E const& { return this->unex; } + + // precondition: has_value() = false + constexpr auto error() & -> E& { return this->unex; } + + // precondition: has_value() = false + constexpr auto error() const&& -> E const&& { return std::move(this->unex); } + + // precondition: has_value() = false + constexpr auto error() && -> E&& { return std::move(this->unex); } + + template + requires std::is_copy_constructible_v && std::is_convertible_v + constexpr auto value_or(U&& v) const& -> T { + return has_value() ? **this : static_cast(std::forward(v)); + } + + template + requires std::is_move_constructible_v && std::is_convertible_v + constexpr auto value_or(U&& v) && -> T { + return has_value() ? std::move(**this) : static_cast(std::forward(v)); + } + + template>> + requires detail::is_expected && + std::is_same_v && + std::is_copy_constructible_v && std::is_copy_constructible_v + constexpr auto and_then(F&& f) & { + if (has_value()) { + return std::invoke(std::forward(f), **this); + } + return U(unexpect, error()); + } + + template>> + requires detail::is_expected && + std::is_same_v && + std::is_copy_constructible_v && + std::is_copy_constructible_v + constexpr auto and_then(F&& f) const& { + if (has_value()) { + return std::invoke(std::forward(f), **this); + } + return U(unexpect, error()); + } + + template>> + requires detail::is_expected && + std::is_same_v && + std::is_move_constructible_v && + std::is_move_constructible_v + constexpr auto and_then(F&& f) && { + if (has_value()) { + return std::invoke(std::forward(f), std::move(**this)); + } + return U(unexpect, std::move(error())); + } + + template>> + requires detail::is_expected && + std::is_same_v && + std::is_move_constructible_v && + std::is_move_constructible_v + constexpr auto and_then(F&& f) const&& { + if (has_value()) { + return std::invoke(std::forward(f), std::move(**this)); + } + return U(unexpect, std::move(error())); + } + + template>> + requires detail::is_expected && + std::is_same_v && + std::is_copy_constructible_v && + std::is_copy_constructible_v + constexpr auto or_else(F&& f) & { + if (has_value()) { + return G(**this); + } + return std::invoke(std::forward(f), error()); + } + + template>> + requires detail::is_expected && + std::is_same_v && + std::is_copy_constructible_v && + std::is_copy_constructible_v + constexpr auto or_else(F&& f) const& { + if (has_value()) { + return G(**this); + } + return std::invoke(std::forward(f), error()); + } + + template>> + requires detail::is_expected && + std::is_same_v && + std::is_move_constructible_v && + std::is_move_constructible_v + constexpr auto or_else(F&& f) && { + if (has_value()) { + return G(std::move(**this)); + } + return std::invoke(std::forward(f), std::move(error())); + } + + template>> + requires detail::is_expected && + std::is_same_v && + std::is_move_constructible_v && + std::is_move_constructible_v + constexpr auto or_else(F&& f) const&& { + if (has_value()) { + return G(std::move(**this)); + } + return std::invoke(std::forward(f), std::move(error())); + } + + template>> + requires std::is_void_v && + std::is_copy_constructible_v && + std::is_copy_constructible_v + constexpr auto or_else(F&& f) & { + if (has_value()) { + return expected(*this); + } + std::invoke(std::forward(f), error()); + return expected(*this); + } + + template>> + requires std::is_void_v && + std::is_copy_constructible_v && + std::is_copy_constructible_v + constexpr auto or_else(F&& f) const& { + if (has_value()) { + return expected(*this); + } + std::invoke(std::forward(f), error()); + return expected(*this); + } + + template>> + requires std::is_void_v && + std::is_move_constructible_v && + std::is_move_constructible_v && + std::is_copy_constructible_v + constexpr auto or_else(F&& f) && { + if (has_value()) { + return expected(std::move(*this)); + } + // TODO: is this copy necessary, as f can be just read argument function + std::invoke(std::forward(f), error()); + return expected(std::move(*this)); + } + + template>> + requires std::is_void_v && + std::is_move_constructible_v && + std::is_move_constructible_v && + std::is_copy_constructible_v + constexpr auto or_else(F&& f) const&& { + if (!has_value()) { + return expected(std::move(*this)); + } + // TODO: is this copy necessary, as f can be just read argument function + std::invoke(std::forward(f), error()); + return expected(std::move(*this)); + } + + template>> + requires std::is_copy_constructible_v && + std::is_copy_constructible_v + constexpr auto transform(F&& f) & { + if (has_value()) { + if constexpr (!std::same_as) { + return expected(std::invoke(std::forward(f), **this)); + } else { + std::invoke(std::forward(f), std::move(**this)); + return expected(); + } + } + return expected(unexpect, error()); + } + + template>> + requires std::is_copy_constructible_v && + std::is_copy_constructible_v + constexpr auto transform(F&& f) const& { + if (has_value()) { + if constexpr (!std::same_as) { + return expected(std::invoke(std::forward(f), **this)); + } else { + std::invoke(std::forward(f), std::move(**this)); + return expected(); + } + } + return expected(unexpect, error()); + } + + template>> + requires std::is_move_constructible_v && + std::is_move_constructible_v + constexpr auto transform(F&& f) && { + if (has_value()) { + if constexpr (!std::same_as) { + return expected( + std::invoke(std::forward(f), std::move(**this))); + } else { + std::invoke(std::forward(f), std::move(**this)); + return expected(); + } + } + return expected(unexpect, std::move(error())); + } + + template>> + requires std::is_move_constructible_v && + std::is_move_constructible_v + constexpr auto transform(F&& f) const&& { + if (has_value()) { + if constexpr (!std::same_as) { + return expected( + std::invoke(std::forward(f), std::move(**this))); + } else { + std::invoke(std::forward(f), std::move(**this)); + return expected(); + } + } + return expected(unexpect, std::move(error())); + } + + template>> + requires std::is_copy_constructible_v && + std::is_copy_constructible_v + constexpr auto transform_error(F&& f) & { + if (has_value()) { + return expected(**this); + } + return expected(unexpect, std::invoke(std::forward(f), error())); + } + + template>> + requires std::is_copy_constructible_v && + std::is_copy_constructible_v + constexpr auto transform_error(F&& f) const& { + if (has_value()) { + return expected(**this); + } + return expected(unexpect, std::invoke(std::forward(f), error())); + } + + template>> + requires std::is_move_constructible_v && + std::is_move_constructible_v + constexpr auto transform_error(F&& f) && { + if (has_value()) { + return expected(std::move(**this)); + } + return expected(unexpect, + std::invoke(std::forward(f), std::move(error()))); + } + + template>> + requires std::is_move_constructible_v && + std::is_move_constructible_v + constexpr auto transform_error(F&& f) const&& { + if (has_value()) { + return expected(std::move(**this)); + } + return expected(unexpect, + std::invoke(std::forward(f), std::move(error()))); + } + + // equality operators + template + requires(!std::is_void_v) && + requires(T const& t1, T2 const& t2, E const& e1, E2 const& e2) { + { t1 == t2 } -> std::convertible_to; + { e1 == e2 } -> std::convertible_to; + } + friend constexpr auto operator==(expected const& x, expected const& y) + -> bool { + if (x.has_value() != y.has_value()) { + return false; + } + return x.has_value() ? (*x == *y) : (x.error() == y.error()); + } + + template + requires(!detail::is_expected) && requires(T const& x, T2 const& v) { + { x == v } -> std::convertible_to; + } + friend constexpr auto operator==(expected const& x, T2 const& v) -> bool { + return x.has_value() && static_cast(*x == v); + } + + template + requires requires(E const& x, unexpected const& e) { + { x == e.value() } -> std::convertible_to; + } + friend constexpr auto operator==(expected const& x, unexpected const& e) + -> bool { + return !x.has_value() && static_cast(x.error() == e.value()); + } + + // specialized algorithms + friend constexpr void swap(expected& x, + expected& y) noexcept(noexcept(x.swap(y))) { + x.swap(y); + } + + private: + bool has_val{true}; + union { + T val; + E unex; + }; + }; + + template + class expected { + public: + using value_type = void; + using error_type = E; + using unexpected_type = unexpected; + + template + using rebind = expected; + + // constructors + + // postcondition: has_value() = true + constexpr expected() noexcept {}// NOLINT + + constexpr expected( + expected const& rhs) + requires std::is_copy_constructible_v && + std::is_trivially_copy_constructible_v + = default; + + constexpr expected( + expected const& rhs) + requires std::is_copy_constructible_v + : has_val(rhs.has_value()) { + if (!rhs.has_value()) { + std::construct_at(std::addressof(this->unex), rhs.error()); + } + } + + constexpr expected(expected&&) noexcept(std::is_nothrow_move_constructible_v) + requires std::is_move_constructible_v && + std::is_trivially_move_constructible_v + = default; + + constexpr expected(expected&& rhs) noexcept(std::is_nothrow_move_constructible_v) + requires std::is_move_constructible_v + : has_val(rhs.has_value()) { + if (!rhs.has_value()) { + std::construct_at(std::addressof(this->unex), std::move(rhs.error())); + } + } + + template + requires std::is_void_v && std::is_constructible_v && (!std::is_constructible_v, expected&>) && (!std::is_constructible_v, expected>) && (!std::is_constructible_v, expected const&>) && (!std::is_constructible_v, expected const&>) + constexpr explicit(!std::is_convertible_v) + expected(expected const& rhs)// NOLINT + : has_val(rhs.has_value()) { + if (!rhs.has_value()) { + std::construct_at(std::addressof(this->unex), + std::forward(rhs.error())); + } + } + + template + requires std::is_void_v && std::is_constructible_v && (!std::is_constructible_v, expected&>) && (!std::is_constructible_v, expected>) && (!std::is_constructible_v, expected const&>) && (!std::is_constructible_v, expected const&>) + constexpr explicit(!std::is_convertible_v) + expected(expected&& rhs)// NOLINT + : has_val(rhs.has_value()) { + if (!rhs.has_value()) { + std::construct_at(std::addressof(this->unex), + std::forward(rhs.error())); + } + } + + template + requires std::is_constructible_v + constexpr explicit(!std::is_convertible_v) + expected(unexpected const& e)// NOLINT + : has_val(false), unex(std::forward(e.value())) {} + + template + requires std::is_constructible_v + constexpr explicit(!std::is_convertible_v) + expected(unexpected&& e)// NOLINT + : has_val(false), unex(std::forward(e.value())) {} + + constexpr explicit expected(std::in_place_t /*unused*/) noexcept {} + + template + requires std::is_constructible_v + constexpr explicit expected(unexpect_t /*unused*/, Args&&... args) + : has_val(false), unex(std::forward(args)...) {} + + template + requires std::is_constructible_v&, Args...> + constexpr explicit expected(unexpect_t /*unused*/, std::initializer_list il, Args... args) + : has_val(false), + unex(il, std::forward(args)...) {} + + // destructor + constexpr ~expected() { + if constexpr (std::is_trivially_destructible_v) { + } else { + if (!has_value()) std::destroy_at(std::addressof(this->unex)); + } + } + + // assignment + constexpr auto operator=(expected const& rhs) -> expected&// NOLINT + requires std::is_copy_assignable_v && + std::is_copy_constructible_v + { + if (has_value() && rhs.has_value()) { + } else if (has_value()) { + std::construct_at(std::addressof(this->unex), rhs.unex); + has_val = false; + } else if (rhs.has_value()) { + std::destroy_at(std::addressof(this->unex)); + has_val = true; + } else { + this->unex = rhs.error(); + } + return *this; + } + + constexpr auto operator=(expected&& rhs) noexcept(std::is_nothrow_move_constructible_v && + std::is_nothrow_move_assignable_v) -> expected& + requires std::is_move_constructible_v && + std::is_move_assignable_v + { + if (has_value() && rhs.has_value()) { + } else if (has_value()) { + std::construct_at(std::addressof(this->unex), std::move(rhs.unex)); + has_val = false; + } else if (rhs.has_value()) { + std::destroy_at(std::addressof(this->unex)); + has_val = true; + } else { + this->unex = std::move(rhs.error()); + } + return *this; + } + + template + requires std::is_constructible_v and + std::is_assignable_v + constexpr auto operator=(unexpected const& e) -> expected& { + if (has_value()) { + std::construct_at(std::addressof(this->unex), + std::forward(e.value())); + has_val = false; + } else { + this->unex = std::forward(e.value()); + } + return *this; + } + + template + requires std::is_constructible_v && + std::is_assignable_v + constexpr auto operator=(unexpected&& e) -> expected& { + if (has_value()) { + std::construct_at(std::addressof(this->unex), std::forward(e.value())); + has_val = false; + } else { + this->unex = std::forward(e.value()); + } + return *this; + } + + // modifiers + constexpr void emplace() noexcept { + if (!has_value()) { + std::destroy_at(std::addressof(this->unex)); + has_val = true; + } + } + + // swap + constexpr void swap(expected& rhs) noexcept(std::is_nothrow_move_constructible_v && + std::is_nothrow_swappable_v) + requires std::is_swappable_v && + std::is_move_constructible_v + { + if (rhs.has_value()) { + if (has_value()) { + } else { + rhs.swap(*this); + } + } else { + if (has_value()) { + std::construct_at(std::addressof(this->unex), std::move(rhs.unex)); + std::destroy_at(std::addressof(rhs.unex)); + has_val = false; + rhs.has_val = true; + } else { + using std::swap; + swap(this->unex, rhs.unex); + } + } + } + + // observers + constexpr explicit operator bool() const noexcept { return has_val; } + + [[nodiscard]] constexpr auto has_value() const noexcept -> bool { + return has_val; + } + + // precondition: has_value() = true + constexpr void operator*() const noexcept {} + + constexpr void value() const& { + if (!has_value()) { + throw bad_expected_access(error()); + } + } + + constexpr void value() && { + if (!has_value()) { + throw bad_expected_access(std::move(error())); + } + } + + // precondition: has_value() = false + constexpr auto error() const& -> E const& { return this->unex; } + + // precondition: has_value() = false + constexpr auto error() & -> E& { return this->unex; } + + // precondition: has_value() = false + constexpr auto error() const&& -> E const&& { return std::move(this->unex); } + + // precondition: has_value() = false + constexpr auto error() && -> E&& { return std::move(this->unex); } + + // monadic + template>> + requires detail::is_expected && + std::is_same_v && + std::is_copy_constructible_v + constexpr auto and_then(F&& f) & { + if (has_value()) { + return std::invoke(std::forward(f)); + } + return U(unexpect, error()); + } + + template>> + requires detail::is_expected && + std::is_same_v && + std::is_copy_constructible_v + constexpr auto and_then(F&& f) const& { + if (has_value()) { + return std::invoke(std::forward(f)); + } + return U(unexpect, error()); + } + + template>> + requires detail::is_expected && + std::is_same_v && + std::is_move_constructible_v + constexpr auto and_then(F&& f) && { + if (has_value()) { + return std::invoke(std::forward(f)); + } + return U(unexpect, std::move(error())); + } + + template>> + requires detail::is_expected && + std::is_same_v && + std::is_move_constructible_v + constexpr auto and_then(F&& f) const&& { + if (has_value()) { + return std::invoke(std::forward(f)); + } + return U(unexpect, std::move(error())); + } + + template>> + requires detail::is_expected && + std::is_same_v && + std::is_copy_constructible_v + constexpr auto or_else(F&& f) & { + if (has_value()) { + return G{}; + } + return std::invoke(std::forward(f), error()); + } + + template>> + requires detail::is_expected && + std::is_same_v && + std::is_copy_constructible_v + constexpr auto or_else(F&& f) const& { + if (has_value()) { + return G{}; + } + return std::invoke(std::forward(f), error()); + } + + template>> + requires detail::is_expected && + std::is_same_v && + std::is_move_constructible_v + constexpr auto or_else(F&& f) && { + if (has_value()) { + return G{}; + } + return std::invoke(std::forward(f), std::move(error())); + } + + template>> + requires detail::is_expected && + std::is_same_v && + std::is_move_constructible_v + constexpr auto or_else(F&& f) const&& { + if (has_value()) { + return G{}; + } + return std::invoke(std::forward(f), std::move(error())); + } + + template>> + requires std::is_void_v && + std::is_copy_constructible_v + constexpr auto or_else(F&& f) & { + if (has_value()) { + return expected(*this); + } + std::invoke(std::forward(f), error()); + return expected(*this); + } + + template>> + requires std::is_void_v && + std::is_copy_constructible_v + constexpr auto or_else(F&& f) const& { + if (has_value()) { + return expected(*this); + } + std::invoke(std::forward(f), error()); + return expected(*this); + } + + template>> + requires std::is_void_v && + std::is_move_constructible_v && + std::is_copy_constructible_v + constexpr auto or_else(F&& f) && { + if (has_value()) { + return expected(std::move(*this)); + } + // TODO: is this copy necessary, as f can be just read argument function + std::invoke(std::forward(f), error()); + return expected(std::move(*this)); + } + + template>> + requires std::is_void_v && + std::is_move_constructible_v && + std::is_copy_constructible_v + constexpr auto or_else(F&& f) const&& { + if (!has_value()) { + return expected(std::move(*this)); + } + // TODO: is this copy necessary, as f can be just read argument function + std::invoke(std::forward(f), error()); + return expected(std::move(*this)); + } + + template>> + requires std::is_copy_constructible_v + constexpr auto transform(F&& f) & { + if (has_value()) { + if constexpr (!std::same_as) { + return expected(std::invoke(std::forward(f))); + } else { + std::invoke(std::forward(f)); + return expected(); + } + } + return expected(unexpect, error()); + } + + template>> + requires std::is_copy_constructible_v + constexpr auto transform(F&& f) const& { + if (has_value()) { + if constexpr (!std::same_as) { + return expected(std::invoke(std::forward(f))); + } else { + std::invoke(std::forward(f)); + return expected(); + } + } + return expected(unexpect, error()); + } + + template>> + requires std::is_move_constructible_v + constexpr auto transform(F&& f) && { + if (has_value()) { + if constexpr (!std::same_as) { + return expected(std::invoke(std::forward(f))); + } else { + std::invoke(std::forward(f)); + return expected(); + } + } + return expected(unexpect, std::move(error())); + } + + template>> + requires std::is_move_constructible_v + constexpr auto transform(F&& f) const&& { + if (has_value()) { + if constexpr (!std::same_as) { + return expected(std::invoke(std::forward(f))); + } else { + std::invoke(std::forward(f)); + return expected(); + } + } + return expected(unexpect, std::move(error())); + } + + template>> + requires std::is_copy_constructible_v + constexpr auto transform_error(F&& f) & { + if (has_value()) { + return expected{}; + } + return expected(unexpect, + std::invoke(std::forward(f), error())); + } + + template>> + requires std::is_copy_constructible_v + constexpr auto transform_error(F&& f) const& { + if (has_value()) { + return expected{}; + } + return expected(unexpect, + std::invoke(std::forward(f), error())); + } + + template>> + requires std::is_move_constructible_v + constexpr auto transform_error(F&& f) && { + if (has_value()) { + return expected{}; + } + return expected( + unexpect, std::invoke(std::forward(f), std::move(error()))); + } + + template>> + requires std::is_move_constructible_v + constexpr auto transform_error(F&& f) const&& { + if (has_value()) { + return expected{}; + } + return expected( + unexpect, std::invoke(std::forward(f), std::move(error()))); + } + + // expected equality operators + template + requires std::is_void_v && requires(E e, E2 e2) { + { e == e2 } -> std::convertible_to; + } + friend constexpr auto operator==(expected const& x, expected const& y) + -> bool { + if (x.has_value() != y.has_value()) return false; + return x.has_value() or static_cast(x.error() == y.error()); + } + + template + requires requires(expected const& x, unexpected const& e) { + { x.error() == e.value() } -> std::convertible_to; + } + friend constexpr auto operator==(expected const& x, unexpected const& e) + -> bool { + return !x.has_value() && static_cast(x.error() == e.value()); + } + + // specialized algorithms + friend constexpr void swap(expected& x, + expected& y) noexcept(noexcept(x.swap(y))) { + x.swap(y); + } + + private: + bool has_val{true}; + union { + E unex; + }; + }; + +}// namespace plg +#endif \ No newline at end of file diff --git a/test/cross_call_worker/external/plugify/include/plg/flat_map.hpp b/test/cross_call_worker/external/plugify/include/plg/flat_map.hpp new file mode 100644 index 0000000..9912431 --- /dev/null +++ b/test/cross_call_worker/external/plugify/include/plg/flat_map.hpp @@ -0,0 +1,780 @@ +#pragma once + +#include "plg/macro.hpp" + +#ifdef __cpp_lib_flat_map +#include +namespace plg { + template> + using flat_map = std::flat_map; +} +#else +#include "plg/vector.hpp" + +namespace plg { + namespace detail { + template < typename T, typename U, typename = void > + struct is_transparent + : std::false_type {}; + + template < typename T, typename U > + struct is_transparent> + : std::true_type {}; + + template < typename T, typename U > + inline constexpr bool is_transparent_v = is_transparent::value; + + template + constexpr bool is_sorted(Iter first, Iter last, Compare comp) { + if (first != last) { + Iter next = first; + while (++next != last) { + if (comp(*next, *first)) { + return false; + } + ++first; + } + } + return true; + } + + template + constexpr bool is_sorted_unique(Iter first, Iter last, Compare comp) { + if (first != last) { + Iter next = first; + while (++next != last) { + if (!comp(*first, *next)) { + return false; + } + ++first; + } + } + return true; + } + + template + struct pair_compare : public Compare { + pair_compare() = default; + + explicit pair_compare(const Compare& compare) + : Compare(compare) {} + + bool operator()( + const typename Pair::first_type& l, + const typename Pair::first_type& r) const { + return Compare::operator()(l, r); + } + + bool operator()(const Pair& l, const Pair& r) const { + return Compare::operator()(l.first, r.first); + } + + bool operator()( + const typename Pair::first_type& l, + const Pair& r) const { + return Compare::operator()(l, r.first); + } + + bool operator()( + const Pair& l, + const typename Pair::first_type& r) const { + return Compare::operator()(l.first, r); + } + + template + requires (is_transparent_v) + bool operator()(const K& l, const Pair& r) const { + return Compare::operator()(l, r.first); + } + + template + requires (is_transparent_v) + bool operator()(const Pair& l, const K& r) const { + return Compare::operator()(l.first, r); + } + }; + + template + struct eq_compare : public Compare { + eq_compare() = default; + + explicit eq_compare(const Compare& compare) + : Compare(compare) {} + + template + bool operator()(const L& l, const R& r) const { + return !Compare::operator()(l, r) && !Compare::operator()(r, l); + } + }; + } // namespace detail + + struct sorted_range_t {}; + inline constexpr sorted_range_t sorted_range = sorted_range_t(); + + struct sorted_unique_range_t : public sorted_range_t {}; + inline constexpr sorted_unique_range_t sorted_unique_range = sorted_unique_range_t(); + + template, typename Container = std::vector>> + class flat_map { + public: + using key_type = Key; + using mapped_type = Value; + using value_type = typename Container::value_type; + + using size_type = typename Container::size_type; + using difference_type = typename Container::difference_type; + + using key_compare = Compare; + using container_type = Container; + + using reference = typename Container::reference; + using const_reference = typename Container::const_reference; + using pointer = typename Container::pointer; + using const_pointer = typename Container::const_pointer; + + using iterator = typename Container::iterator; + using const_iterator = typename Container::const_iterator; + using reverse_iterator = typename Container::reverse_iterator; + using const_reverse_iterator = typename Container::const_reverse_iterator; + + struct value_compare : private key_compare { + value_compare() = default; + + explicit value_compare(key_compare compare) + : key_compare(std::move(compare)) {} + + bool operator()(const value_type& l, const value_type& r) const { + return key_compare::operator()(l.first, r.first); + } + }; + + public: + flat_map() = default; + ~flat_map() = default; + + explicit flat_map(const Compare& c) + : _compare(c) {} + + template + explicit flat_map(const Allocator& a) + : _data(a) {} + + template + flat_map(const Compare& c, const Allocator& a) + : _compare(c), _data(a) {} + + template + flat_map(Iterator first, Iterator last) { + from_range(first, last); + } + + template + flat_map(sorted_range_t, Iterator first, Iterator last) { + from_range(sorted_range, first, last); + } + + template + flat_map(sorted_unique_range_t, Iterator first, Iterator last) { + from_range(sorted_unique_range, first, last); + } + + template + flat_map(Iterator first, Iterator last, const Compare& c) + : _compare(c) { + from_range(first, last); + } + + template + flat_map(sorted_range_t, Iterator first, Iterator last, const Compare& c) + : _compare(c) { + from_range(sorted_range, first, last); + } + + template + flat_map(sorted_unique_range_t, Iterator first, Iterator last, const Compare& c) + : _compare(c) { + from_range(sorted_unique_range, first, last); + } + + template + flat_map(Iterator first, Iterator last, const Allocator& a) + : _data(a) { + from_range(first, last); + } + + template + flat_map(sorted_range_t, Iterator first, Iterator last, const Allocator& a) + : _data(a) { + from_range(sorted_range, first, last); + } + + template + flat_map(sorted_unique_range_t, Iterator first, Iterator last, const Allocator& a) + : _data(a) { + from_range(sorted_unique_range, first, last); + } + + template + flat_map(Iterator first, Iterator last, const Compare& c, const Allocator& a) + : _compare(c), _data(a) { + from_range(first, last); + } + + template + flat_map(sorted_range_t, Iterator first, Iterator last, const Compare& c, const Allocator& a) + : _compare(c), _data(a) { + from_range(sorted_range, first, last); + } + + template + flat_map(sorted_unique_range_t, Iterator first, Iterator last, const Compare& c, const Allocator& a) + : _compare(c), _data(a) { + from_range(sorted_unique_range, first, last); + } + + flat_map(std::initializer_list list) { + from_range(list.begin(), list.end()); + } + + flat_map(sorted_range_t, std::initializer_list list) { + from_range(sorted_range, list.begin(), list.end()); + } + + flat_map(sorted_unique_range_t, std::initializer_list list) { + from_range(sorted_unique_range, list.begin(), list.end()); + } + + flat_map(std::initializer_list list, const Compare& c) + : _compare(c) { + from_range(list.begin(), list.end()); + } + + flat_map(sorted_range_t, std::initializer_list list, const Compare& c) + : _compare(c) { + from_range(sorted_range, list.begin(), list.end()); + } + + flat_map(sorted_unique_range_t, std::initializer_list list, const Compare& c) + : _compare(c) { + from_range(sorted_unique_range, list.begin(), list.end()); + } + + template + flat_map(std::initializer_list list, const Allocator& a) + : _data(a) { + from_range(list.begin(), list.end()); + } + + template + flat_map(sorted_range_t, std::initializer_list list, const Allocator& a) + : _data(a) { + from_range(sorted_range, list.begin(), list.end()); + } + + template + flat_map(sorted_unique_range_t, std::initializer_list list, const Allocator& a) + : _data(a) { + from_range(sorted_unique_range, list.begin(), list.end()); + } + + template + flat_map(std::initializer_list list, const Compare& c, const Allocator& a) + : _compare(c), _data(a) { + from_range(list.begin(), list.end()); + } + + template + flat_map(sorted_range_t, std::initializer_list list, const Compare& c, const Allocator& a) + : _compare(c), _data(a) { + from_range(sorted_range, list.begin(), list.end()); + } + + template + flat_map(sorted_unique_range_t, std::initializer_list list, const Compare& c, const Allocator& a) + : _compare(c), _data(a) { + from_range(sorted_unique_range, list.begin(), list.end()); + } + + template + flat_map(flat_map&& other, const Allocator& a) + : _compare(std::move(other._compare)), _data(std::move(other._data), a) {} + + template + flat_map(const flat_map& other, const Allocator& a) + : _compare(other._compare), _data(other._data, a) {} + + flat_map(flat_map&& other) noexcept = default; + flat_map(const flat_map& other) = default; + + flat_map& operator=(flat_map&& other) noexcept = default; + flat_map& operator=(const flat_map& other) = default; + + flat_map& operator=(std::initializer_list list) { + flat_map(list).swap(*this); + return *this; + } + + iterator begin() noexcept(noexcept(std::declval().begin())) { + return _data.begin(); + } + + const_iterator begin() const + noexcept(noexcept(std::declval().begin())) { + return _data.begin(); + } + + const_iterator cbegin() const + noexcept(noexcept(std::declval().cbegin())) { + return _data.cbegin(); + } + + iterator end() noexcept(noexcept(std::declval().end())) { + return _data.end(); + } + + const_iterator end() const + noexcept(noexcept(std::declval().end())) { + return _data.end(); + } + + const_iterator cend() const + noexcept(noexcept(std::declval().cend())) { + return _data.cend(); + } + + reverse_iterator rbegin() noexcept(noexcept(std::declval().rbegin())) { + return _data.rbegin(); + } + + const_reverse_iterator rbegin() const + noexcept(noexcept(std::declval().rbegin())) { + return _data.rbegin(); + } + + const_reverse_iterator crbegin() const + noexcept(noexcept(std::declval().crbegin())) { + return _data.crbegin(); + } + + reverse_iterator rend() noexcept(noexcept(std::declval().rend())) { + return _data.rend(); + } + + const_reverse_iterator rend() const + noexcept(noexcept(std::declval().rend())) { + return _data.rend(); + } + + const_reverse_iterator crend() const + noexcept(noexcept(std::declval().crend())) { + return _data.crend(); + } + + bool empty() const + noexcept(noexcept(std::declval().empty())) { + return _data.empty(); + } + + size_type size() const + noexcept(noexcept(std::declval().size())) { + return _data.size(); + } + + size_type max_size() const + noexcept(noexcept(std::declval().max_size())) { + return _data.max_size(); + } + + size_type capacity() const + noexcept(noexcept(std::declval().capacity())) { + return _data.capacity(); + } + + void reserve(size_type capacity) { + _data.reserve(capacity); + } + + void shrink_to_fit() { + _data.shrink_to_fit(); + } + + mapped_type& operator[](key_type&& key) { + const iterator iter = find(key); + return iter != end() + ? iter->second + : emplace(std::move(key), mapped_type()).first->second; + } + + mapped_type& operator[](const key_type& key) { + const iterator iter = find(key); + return iter != end() + ? iter->second + : emplace(key, mapped_type()).first->second; + } + + mapped_type& at(const key_type& key) { + const iterator iter = find(key); + PLUGIFY_ASSERT(iter != end(), "plg::flat_map::at(): key not found", std::out_of_range); + return iter->second; + } + + const mapped_type& at(const key_type& key) const { + const const_iterator iter = find(key); + PLUGIFY_ASSERT(iter != end(), "plg::flat_map::at(): key not found", std::out_of_range); + return iter->second; + } + + template + requires (detail::is_transparent_v) + mapped_type& at(const K& key) { + const iterator iter = find(key); + PLUGIFY_ASSERT(iter != end(), "plg::flat_map::at(): key not found", std::out_of_range); + return iter->second; + } + + template + requires (detail::is_transparent_v) + const mapped_type& at(const K& key) const { + const const_iterator iter = find(key); + PLUGIFY_ASSERT(iter != end(), "plg::flat_map::at(): key not found", std::out_of_range); + return iter->second; + } + + std::pair insert(value_type&& value) { + const iterator iter = lower_bound(value.first); + return iter == end() || _compare(value, *iter) + ? std::make_pair(_data.insert(iter, std::move(value)), true) + : std::make_pair(iter, false); + } + + std::pair insert(const value_type& value) { + const iterator iter = lower_bound(value.first); + return iter == end() || _compare(value, *iter) + ? std::make_pair(_data.insert(iter, value), true) + : std::make_pair(iter, false); + } + + iterator insert(const_iterator hint, value_type&& value) { + return (hint == begin() || _compare(*(hint - 1), value)) && (hint == end() || _compare(value, *hint)) + ? _data.insert(hint, std::move(value)) + : insert(std::move(value)).first; + } + + iterator insert(const_iterator hint, const value_type& value) { + return (hint == begin() || _compare(*(hint - 1), value)) && (hint == end() || _compare(value, *hint)) + ? _data.insert(hint, value) + : insert(value).first; + } + + template + std::pair insert_or_assign(key_type&& key, V&& value) { + iterator iter = lower_bound(key); + if (iter == end() || _compare(key, *iter)) { + iter = emplace_hint(iter, std::move(key), std::forward(value)); + return {iter, true}; + } + (*iter).second = std::forward(value); + return {iter, false}; + } + + template + std::pair insert_or_assign(const key_type& key, V&& value) { + iterator iter = lower_bound(key); + if (iter == end() || _compare(key, *iter)) { + iter = emplace_hint(iter, key, std::forward(value)); + return {iter, true}; + } + (*iter).second = std::forward(value); + return {iter, false}; + } + + template + void insert(Iterator first, Iterator last) { + insert_range(first, last); + } + + template + void insert(sorted_range_t, Iterator first, Iterator last) { + insert_range(sorted_range, first, last); + } + + void insert(std::initializer_list list) { + insert_range(list.begin(), list.end()); + } + + void insert(sorted_range_t, std::initializer_list list) { + insert_range(sorted_range, list.begin(), list.end()); + } + + template + std::pair emplace(Args&&... args) { + return insert(value_type(std::forward(args)...)); + } + + template + iterator emplace_hint(const_iterator hint, Args&&... args) { + return insert(hint, value_type(std::forward(args)...)); + } + + template + std::pair try_emplace(key_type&& key, Args&&... args) { + iterator iter = lower_bound(key); + if (iter == end() || _compare(key, *iter)) { + iter = emplace_hint(iter, std::move(key), std::forward(args)...); + return {iter, true}; + } + return {iter, false}; + } + + template + std::pair try_emplace(const key_type& key, Args&&... args) { + iterator iter = lower_bound(key); + if (iter == end() || _compare(key, *iter)) { + iter = emplace_hint(iter, key, std::forward(args)...); + return {iter, true}; + } + return {iter, false}; + } + + void clear() noexcept(noexcept(std::declval().clear())) { + _data.clear(); + } + + iterator erase(const_iterator iter) { + return _data.erase(iter); + } + + iterator erase(const_iterator first, const_iterator last) { + return _data.erase(first, last); + } + + size_type erase(const key_type& key) { + const const_iterator iter = find(key); + return iter != end() + ? (erase(iter), 1) + : 0; + } + + void swap(flat_map& other) noexcept(std::is_nothrow_swappable_v && std::is_nothrow_swappable_v) { + using std::swap; + swap(_compare, other._compare); + swap(_data, other._data); + } + + size_type count(const key_type& key) const { + const const_iterator iter = find(key); + return iter != end() ? 1 : 0; + } + + template + requires (detail::is_transparent_v) + size_type count(const K& key) const { + const const_iterator iter = find(key); + return iter != end() ? 1 : 0; + } + + iterator find(const key_type& key) { + const iterator iter = lower_bound(key); + return iter != end() && !_compare(key, *iter) + ? iter + : end(); + } + + const_iterator find(const key_type& key) const { + const const_iterator iter = lower_bound(key); + return iter != end() && !_compare(key, *iter) + ? iter + : end(); + } + + template + requires (detail::is_transparent_v) + iterator find(const K& key) { + const iterator iter = lower_bound(key); + return iter != end() && !_compare(key, *iter) + ? iter + : end(); + } + + template + requires (detail::is_transparent_v) + const_iterator find(const K& key) const { + const const_iterator iter = lower_bound(key); + return iter != end() && !_compare(key, *iter) + ? iter + : end(); + } + + bool contains(const key_type& key) const { + return find(key) != end(); + } + + template + requires (detail::is_transparent_v) + bool contains(const K& key) const { + return find(key) != end(); + } + + std::pair equal_range(const key_type& key) { + return std::equal_range(begin(), end(), key, _compare); + } + + std::pair equal_range(const key_type& key) const { + return std::equal_range(begin(), end(), key, _compare); + } + + template + requires (detail::is_transparent_v) + std::pair equal_range(const K& key) { + return std::equal_range(begin(), end(), key, _compare); + } + + template + requires (detail::is_transparent_v) + std::pair equal_range(const K& key) const { + return std::equal_range(begin(), end(), key, _compare); + } + + iterator lower_bound(const key_type& key) { + return std::lower_bound(begin(), end(), key, _compare); + } + + const_iterator lower_bound(const key_type& key) const { + return std::lower_bound(begin(), end(), key, _compare); + } + + template + requires (detail::is_transparent_v) + iterator lower_bound(const K& key) { + return std::lower_bound(begin(), end(), key, _compare); + } + + template + requires (detail::is_transparent_v) + const_iterator lower_bound(const K& key) const { + return std::lower_bound(begin(), end(), key, _compare); + } + + iterator upper_bound(const key_type& key) { + return std::upper_bound(begin(), end(), key, _compare); + } + + const_iterator upper_bound(const key_type& key) const { + return std::upper_bound(begin(), end(), key, _compare); + } + + template + requires (detail::is_transparent_v) + iterator upper_bound(const K& key) { + return std::upper_bound(begin(), end(), key, _compare); + } + + template + requires (detail::is_transparent_v) + const_iterator upper_bound(const K& key) const { + return std::upper_bound(begin(), end(), key, _compare); + } + + key_compare key_comp() const { + return _compare; + } + + value_compare value_comp() const { + return value_compare(key_comp()); + } + + private: + template + void from_range(Iter first, Iter last) { + assert(_data.empty()); + _data.insert(_data.end(), first, last); + std::sort(_data.begin(), _data.end(), value_comp()); + _data.erase( + std::unique(_data.begin(), _data.end(), + detail::eq_compare(value_comp())), + _data.end()); + } + + template + void from_range(sorted_range_t, Iter first, Iter last) { + assert(_data.empty()); + assert(detail::is_sorted(first, last, value_comp())); + _data.insert(_data.end(), first, last); + _data.erase( + std::unique(_data.begin(), _data.end(), + detail::eq_compare(value_comp())), + _data.end()); + } + + template + void from_range(sorted_unique_range_t, Iter first, Iter last) { + assert(_data.empty()); + assert(detail::is_sorted_unique(first, last, value_comp())); + _data.insert(_data.end(), first, last); + } + + private: + template + void insert_range(Iter first, Iter last) { + const auto mid_iter = _data.insert(_data.end(), first, last); + std::sort(mid_iter, _data.end(), value_comp()); + std::inplace_merge(_data.begin(), mid_iter, _data.end(), value_comp()); + _data.erase( + std::unique(_data.begin(), _data.end(), + detail::eq_compare(value_comp())), + _data.end()); + } + + template + void insert_range(sorted_range_t, Iter first, Iter last) { + assert(detail::is_sorted(first, last, value_comp())); + const auto mid_iter = _data.insert(_data.end(), first, last); + std::inplace_merge(_data.begin(), mid_iter, _data.end(), value_comp()); + _data.erase( + std::unique(_data.begin(), _data.end(), + detail::eq_compare(value_comp())), + _data.end()); + } + + private: + PLUGIFY_NO_UNIQUE_ADDRESS + detail::pair_compare _compare; + container_type _data; + }; + + template + void swap( + flat_map& l, + flat_map& r) noexcept(noexcept(l.swap(r))) { + l.swap(r); + } + + template + bool operator==( + const flat_map& l, + const flat_map& r) { + return l.size() == r.size() && std::equal(l.begin(), l.end(), r.begin()); + } + + template + auto operator<=>( + const flat_map& l, + const flat_map& r) { + if (l.size() < r.size()) { + return std::partial_ordering::less; + } else if (l.size() > r.size()) { + return std::partial_ordering::greater; + } else { + if (std::lexicographical_compare(l.cbegin(), l.cend(), r.cbegin(), r.cend())) { + return std::partial_ordering::less; + } else { + return std::partial_ordering::greater; + } + } + } + + template, typename Container = plg::vector>> + using map = flat_map; + +}// namespace plg +#endif \ No newline at end of file diff --git a/test/cross_call_worker/external/plugify/include/plg/format.hpp b/test/cross_call_worker/external/plugify/include/plg/format.hpp new file mode 100644 index 0000000..94e5bdb --- /dev/null +++ b/test/cross_call_worker/external/plugify/include/plg/format.hpp @@ -0,0 +1,26 @@ +#pragma once + +#include "plg/macro.hpp" + +#ifdef __cpp_lib_format + +#include + +#else // __cpp_lib_format + +// Define FMT_FORMAT_H externally to force a difference location for {fmt} +#ifndef FMT_FORMAT_H +#define FMT_FORMAT_H +#endif + +#ifndef FMT_HEADER_ONLY +#define FMT_HEADER_ONLY +#endif +#include FMT_FORMAT_H + +namespace std { + using namespace fmt; + using namespace fmt::detail; +} + +#endif // __cpp_lib_format diff --git a/test/cross_call_worker/plg/formatter.hpp b/test/cross_call_worker/external/plugify/include/plg/formatter.hpp similarity index 100% rename from test/cross_call_worker/plg/formatter.hpp rename to test/cross_call_worker/external/plugify/include/plg/formatter.hpp diff --git a/test/cross_call_master/plg/hash.hpp b/test/cross_call_worker/external/plugify/include/plg/hash.hpp similarity index 91% rename from test/cross_call_master/plg/hash.hpp rename to test/cross_call_worker/external/plugify/include/plg/hash.hpp index de2e050..53f37bf 100644 --- a/test/cross_call_master/plg/hash.hpp +++ b/test/cross_call_worker/external/plugify/include/plg/hash.hpp @@ -76,4 +76,12 @@ namespace plg { (hash_combine(seed, args), ...); // fold expression return seed; } + + template + struct pair_hash { + size_t operator()(std::pair const& p) const { + return hash_combine_all(p.first, p.second); + } + }; + } diff --git a/test/cross_call_worker/plg/macro.hpp b/test/cross_call_worker/external/plugify/include/plg/macro.hpp similarity index 99% rename from test/cross_call_worker/plg/macro.hpp rename to test/cross_call_worker/external/plugify/include/plg/macro.hpp index 6008e31..a072fec 100644 --- a/test/cross_call_worker/plg/macro.hpp +++ b/test/cross_call_worker/external/plugify/include/plg/macro.hpp @@ -257,7 +257,7 @@ # define PLUGIFY_NOINLINE [[gnu::noinline]] #elif PLUGIFY_COMPILER_MSVC # pragma warning(error: 4714) -# define PLUGIFY_FORCE_INLINE __forceinline +# define PLUGIFY_FORCE_INLINE [[msvc::forceinline]] # define PLUGIFY_NOINLINE __declspec(noinline) #else # define PLUGIFY_FORCE_INLINE inline diff --git a/test/cross_call_worker/plg/numerics.hpp b/test/cross_call_worker/external/plugify/include/plg/numerics.hpp similarity index 100% rename from test/cross_call_worker/plg/numerics.hpp rename to test/cross_call_worker/external/plugify/include/plg/numerics.hpp diff --git a/test/cross_call_worker/external/plugify/include/plg/path.hpp b/test/cross_call_worker/external/plugify/include/plg/path.hpp new file mode 100644 index 0000000..61343f3 --- /dev/null +++ b/test/cross_call_worker/external/plugify/include/plg/path.hpp @@ -0,0 +1,52 @@ +#pragma once + +#include +#include +#include + +namespace plg { + using path_view = std::basic_string_view; + using path_string = std::filesystem::path::string_type; + using path_char = path_string::value_type; + using path_diff_t = path_string::difference_type; + +#if _WIN32 +#define PLUGIFY_PATH_LITERAL(x) L##x +#else +#define PLUGIFY_PATH_LITERAL(x) x +#endif + + template + bool insensitive_equals(const path_char lhs, const path_char rhs) { + if constexpr (std::is_same_v) { + return std::towlower(lhs) == rhs; // NOLINT + } else { + return std::tolower(lhs) == rhs; + } + } + + inline bool insensitive_equals(const path_view lhs, const path_view rhs) { + return lhs.size() == rhs.size() && std::equal(lhs.cbegin(), lhs.cend(), rhs.cbegin(), insensitive_equals); + } + + inline path_view extension_view(const std::filesystem::path& path) { + constexpr path_diff_t extension_size = 4; + if (!path.has_extension()) { return {}; } + const path_string& path_str = path.native(); + const auto offset = static_cast(path_str.size()) - extension_size; + if (offset <= 0) { return {}; } + return { path_str.cbegin() + offset, path_str.cend() }; + } + + inline bool has_extension(const std::filesystem::path& path, const path_view extension) { + return insensitive_equals(extension_view(path), extension); + } + + inline auto as_string(const std::filesystem::path& p) { +#if _WIN32 + return p.string(); // returns std::string by value +#else + return p.native(); // returns const std::string& +#endif + } +} diff --git a/test/example_plugin/plg/plugin.hpp b/test/cross_call_worker/external/plugify/include/plg/plugin.hpp similarity index 98% rename from test/example_plugin/plg/plugin.hpp rename to test/cross_call_worker/external/plugify/include/plg/plugin.hpp index 408f632..7498a8c 100644 --- a/test/example_plugin/plg/plugin.hpp +++ b/test/cross_call_worker/external/plugify/include/plg/plugin.hpp @@ -81,7 +81,7 @@ namespace plg { plg::vector GetDependencies() const { return plugin::GetDependencies(plugin::handle); } virtual void OnPluginStart() {}; - virtual void OnPluginUpdate(std::chrono::microseconds) {}; + virtual void OnPluginUpdate(std::chrono::milliseconds) {}; virtual void OnPluginEnd() {}; }; @@ -149,7 +149,7 @@ namespace plg { template \ struct has_overridden_OnPluginUpdate : std::false_type {}; \ template \ - struct has_overridden_OnPluginUpdate().OnPluginUpdate(std::chrono::microseconds{0}))>> \ + struct has_overridden_OnPluginUpdate().OnPluginUpdate(std::chrono::milliseconds{0}))>> \ : std::bool_constant> {}; \ template \ @@ -161,7 +161,7 @@ namespace plg { extern "C" plugin_api void Plugify_PluginStart() { \ GetPluginEntry()->OnPluginStart(); \ } \ - extern "C" plugin_api void Plugify_PluginUpdate(std::chrono::microseconds dt) { \ + extern "C" plugin_api void Plugify_PluginUpdate(std::chrono::milliseconds dt) { \ GetPluginEntry()->OnPluginUpdate(dt); \ } \ extern "C" plugin_api void Plugify_PluginEnd() { \ diff --git a/test/cross_call_worker/plg/string.hpp b/test/cross_call_worker/external/plugify/include/plg/string.hpp similarity index 92% rename from test/cross_call_worker/plg/string.hpp rename to test/cross_call_worker/external/plugify/include/plg/string.hpp index 17b4b29..175bed2 100644 --- a/test/cross_call_worker/plg/string.hpp +++ b/test/cross_call_worker/external/plugify/include/plg/string.hpp @@ -44,20 +44,63 @@ namespace plg { namespace detail { - template - struct is_allocator : std::false_type {}; - - template - struct is_allocator().allocate(std::size_t{}))>> : std::true_type {}; - - template - constexpr bool is_allocator_v = is_allocator::value; - - template - using is_traits = std::conjunction, std::is_integral>; - - template - constexpr bool is_traits_v = is_traits::value; + template + concept is_allocator = requires(Alloc& a, std::size_t n) { + typename std::allocator_traits::value_type; + typename std::allocator_traits::pointer; + typename std::allocator_traits::const_pointer; + typename std::allocator_traits::size_type; + typename std::allocator_traits::difference_type; + + { std::allocator_traits::allocate(a, n) } + -> std::convertible_to::pointer>; + + requires requires(typename std::allocator_traits::pointer p) { + std::allocator_traits::deallocate(a, p, n); + }; + }; + + template + concept is_char_traits = requires { + // Required type definitions + typename Traits::char_type; + typename Traits::int_type; + typename Traits::off_type; + typename Traits::pos_type; + typename Traits::state_type; + } && requires( + typename Traits::char_type c1, + typename Traits::char_type c2, + typename Traits::char_type& cr, + const typename Traits::char_type& ccr, + typename Traits::char_type* p, + const typename Traits::char_type* cp, + typename Traits::int_type i1, + typename Traits::int_type i2, + std::size_t n + ) { + // Character operations + { Traits::assign(cr, ccr) } -> std::same_as; + { Traits::eq(ccr, ccr) } -> std::convertible_to; + { Traits::lt(ccr, ccr) } -> std::convertible_to; + + // String operations + { Traits::compare(cp, cp, n) } -> std::convertible_to; + { Traits::length(cp) } -> std::convertible_to; + { Traits::find(cp, n, ccr) } -> std::convertible_to; + + // Memory operations + { Traits::move(p, cp, n) } -> std::same_as; + { Traits::copy(p, cp, n) } -> std::same_as; + { Traits::assign(p, n, c1) } -> std::same_as; + + // int_type operations + { Traits::not_eof(i1) } -> std::same_as; + { Traits::to_char_type(i1) } -> std::same_as; + { Traits::to_int_type(c1) } -> std::same_as; + { Traits::eq_int_type(i1, i2) } -> std::convertible_to; + { Traits::eof() } -> std::same_as; + }; struct uninitialized_size_tag {}; @@ -68,11 +111,12 @@ namespace plg { template concept string_compatible_range = std::ranges::input_range && std::convertible_to, Type>; #endif // PLUGIFY_STRING_CONTAINERS_RANGES - }// namespace detail + + } // namespace detail // basic_string // based on implementations from libc++, libstdc++ and Microsoft STL - template, typename Allocator = plg::allocator> requires (detail::is_traits_v && detail::is_allocator_v) + template, detail::is_allocator Allocator = plg::allocator> class basic_string { private: using allocator_traits = std::allocator_traits; @@ -1926,3 +1970,107 @@ namespace std { struct formatter, Allocator>> : plg::detail::string_formatter_base {}; }// namespace std #endif // PLUGIFY_STRING_NO_STD_FORMAT + +template +std::ostream& operator<<(std::ostream& os, const plg::basic_string& str) { + os << str.c_str(); + return os; +} + +#ifndef PLUGIFY_STRING_NO_STD_FORMAT +#include + +namespace plg { + namespace detail { + // Concept to match string-like types including char* and const char* + template + concept is_string_like = requires(T v) { + { std::string_view(v) }; + }; + } + + template + constexpr string join(const Range& range, std::string_view separator) { + string result; + + auto it = range.cbegin(); + auto end = range.cend(); + + if (it == end) return result; + + // First pass: compute total size + size_t total_size = 0; + size_t count = 0; + + for (auto tmp = it; tmp != end; ++tmp) { + using Elem = std::decay_t; + if constexpr (detail::is_string_like) { + total_size += std::string_view(*tmp).size(); + } else { + total_size += std::formatted_size("{}", *tmp); + } + ++count; + } + if (count > 1) { + total_size += (count - 1) * separator.size(); + } + result.reserve(total_size); + + auto in = std::back_inserter(result); + + // Second pass: actual formatting + /*if (it != end)*/ { + std::format_to(in, "{}", *it++); + } + while (it != end) { + std::format_to(in, "{}{}", separator, *it++); + } + + return result; + } + + template + constexpr string join(const Range& range, Proj&& proj, std::string_view separator) { + string result; + + auto it = range.cbegin(); + auto end = range.cend(); + + if (it == end) return result; + + // First pass: compute total size + size_t total_size = 0; + size_t count = 0; + + for (auto tmp = it; tmp != end; ++tmp) { + auto&& projected = std::invoke(std::forward(proj), *tmp); + using Elem = std::decay_t; + + if constexpr (detail::is_string_like) { + total_size += std::string_view(*projected).size(); + } else { + total_size += std::formatted_size("{}", projected); + } + ++count; + } + if (count > 1) { + total_size += (count - 1) * separator.size(); + } + result.reserve(total_size); + + auto out = std::back_inserter(result); + + // Second pass: actual formatting + { + auto&& projected = std::invoke(std::forward(proj), *it++); + std::format_to(out, "{}", projected); + } + while (it != end) { + auto&& projected = std::invoke(std::forward(proj), *it++); + std::format_to(out, "{}{}", separator, projected); + } + + return result; + } +} // namespace plugify +#endif // PLUGIFY_STRING_NO_STD_FORMAT \ No newline at end of file diff --git a/test/cross_call_worker/plg/variant.hpp b/test/cross_call_worker/external/plugify/include/plg/variant.hpp similarity index 100% rename from test/cross_call_worker/plg/variant.hpp rename to test/cross_call_worker/external/plugify/include/plg/variant.hpp diff --git a/test/cross_call_master/plg/vector.hpp b/test/cross_call_worker/external/plugify/include/plg/vector.hpp similarity index 96% rename from test/cross_call_master/plg/vector.hpp rename to test/cross_call_worker/external/plugify/include/plg/vector.hpp index b340a61..3ec8750 100644 --- a/test/cross_call_master/plg/vector.hpp +++ b/test/cross_call_worker/external/plugify/include/plg/vector.hpp @@ -27,7 +27,32 @@ #include "plg/allocator.hpp" namespace plg { - template + namespace detail { + template + concept is_alloc = requires(Alloc& a, std::size_t n) { + typename std::allocator_traits::value_type; + typename std::allocator_traits::pointer; + typename std::allocator_traits::const_pointer; + typename std::allocator_traits::size_type; + typename std::allocator_traits::difference_type; + + { std::allocator_traits::allocate(a, n) } + -> std::convertible_to::pointer>; + + requires requires(typename std::allocator_traits::pointer p) { + std::allocator_traits::deallocate(a, p, n); + }; + }; + + struct initialized_value_tag {}; + +#if PLUGIFY_VECTOR_CONTAINERS_RANGES + template + concept vector_compatible_range = std::ranges::input_range && std::convertible_to, Type>; +#endif + } // namespace detail + + template struct vector_iterator { using allocator_traits = std::allocator_traits; public: @@ -119,7 +144,7 @@ namespace plg { } #endif // __cpp_impl_three_way_comparison - template + template struct vector_const_iterator { using allocator_traits = std::allocator_traits; public: @@ -152,14 +177,14 @@ namespace plg { ++_current; return *this; } - constexpr vector_const_iterator operator++(int) const noexcept { + constexpr vector_const_iterator operator++(int) noexcept { return vector_const_iterator(_current++); } constexpr vector_const_iterator& operator--() noexcept { --_current; return *this; } - constexpr vector_const_iterator operator--(int) const noexcept { + constexpr vector_const_iterator operator--(int) noexcept { return vector_const_iterator(_current--); } constexpr vector_const_iterator& operator+=(const difference_type n) noexcept { @@ -232,18 +257,9 @@ namespace plg { return lhs.base() <=> rhs.base(); } - namespace detail { - struct initialized_value_tag {}; - -#if PLUGIFY_VECTOR_CONTAINERS_RANGES - template - concept vector_compatible_range = std::ranges::input_range && std::convertible_to, Type>; -#endif - } // namespace detail - // vector // based on implementations from libc++, libstdc++ and Microsoft STL - template> + template> class vector { using allocator_traits = std::allocator_traits; public: diff --git a/test/cross_call_worker/plg/version.hpp b/test/cross_call_worker/external/plugify/include/plg/version.hpp similarity index 80% rename from test/cross_call_worker/plg/version.hpp rename to test/cross_call_worker/external/plugify/include/plg/version.hpp index 355d33f..b12ac87 100644 --- a/test/cross_call_worker/plg/version.hpp +++ b/test/cross_call_worker/external/plugify/include/plg/version.hpp @@ -6,10 +6,7 @@ #include #include #include -#include -#include #include -#include #if __has_include() #include #else @@ -22,11 +19,12 @@ #include "plg/hash.hpp" #include "plg/macro.hpp" +#include "plg/string.hpp" +#include "plg/vector.hpp" // from https://github.com/Neargye/semver namespace plg { namespace detail { - template struct resize_uninitialized { constexpr static auto resize(T& str, std::size_t size) -> std::void_t { @@ -67,7 +65,7 @@ namespace plg { struct prerelease_identifier { prerelease_identifier_type type; - std::string identifier; + string identifier; }; class version_parser; @@ -92,19 +90,19 @@ namespace plg { constexpr I2 minor() const noexcept { return minor_; } constexpr I3 patch() const noexcept { return patch_; } - constexpr const std::string& prerelease_tag() const { return prerelease_tag_; } - constexpr const std::string& build_metadata() const { return build_metadata_; } + constexpr const string& prerelease_tag() const { return prerelease_tag_; } + constexpr const string& build_metadata() const { return build_metadata_; } - constexpr std::string to_string() const; + constexpr string to_string() const; private: I1 major_ = 0; I2 minor_ = 1; I3 patch_ = 0; - std::string prerelease_tag_; - std::string build_metadata_; + string prerelease_tag_; + string build_metadata_; - std::vector prerelease_identifiers; + vector prerelease_identifiers; constexpr std::size_t length() const noexcept { return detail::length(major_) + detail::length(minor_) + detail::length(patch_) + 2 @@ -124,9 +122,9 @@ namespace plg { }; template - constexpr std::string version::to_string() const { - std::string result; - detail::resize_uninitialized{}.resize(result, length()); + constexpr string version::to_string() const { + string result; + detail::resize_uninitialized{}.resize(result, length()); auto it = result.end(); if (!build_metadata_.empty()) { @@ -169,7 +167,6 @@ namespace plg { }; namespace detail { - constexpr from_chars_result success(const char* ptr) noexcept { return from_chars_result{ ptr, std::errc{} }; } @@ -295,28 +292,28 @@ namespace plg { class token_stream { public: constexpr token_stream() = default; - constexpr explicit token_stream(std::vector tokens) noexcept : tokens(std::move(tokens)) {} + constexpr explicit token_stream(vector tokens) noexcept : tokens_(std::move(tokens)) {} constexpr void push(const token& token) noexcept { - tokens.push_back(token); + tokens_.push_back(token); } constexpr token advance() noexcept { - const token token = get(current); - ++current; + const token token = get(current_); + ++current_; return token; } constexpr token peek(std::size_t k = 0) const noexcept { - return get(current + k); + return get(current_ + k); } constexpr token previous() const noexcept { - return get(current - 1); + return get(current_ - 1); } constexpr bool advanceIfMatch(token& token, token_type type) noexcept { - if (get(current).type != type) { + if (get(current_).type != type) { return false; } @@ -338,11 +335,11 @@ namespace plg { } private: - std::size_t current = 0; - std::vector tokens; + std::size_t current_ = 0; + vector tokens_; constexpr token get(std::size_t i) const noexcept { - return tokens[i]; + return tokens_[i]; } }; @@ -481,7 +478,7 @@ namespace plg { class version_parser { public: - constexpr explicit version_parser(token_stream& stream) : stream{stream} { + constexpr explicit version_parser(token_stream& stream) : stream_{stream} { } template @@ -493,8 +490,8 @@ namespace plg { return result; } - if (!stream.consume(token_type::dot)) { - return failure(stream.previous().lexeme); + if (!stream_.consume(token_type::dot)) { + return failure(stream_.previous().lexeme); } result = parse_number(out.minor_); @@ -502,8 +499,8 @@ namespace plg { return result; } - if (!stream.consume(token_type::dot)) { - return failure(stream.previous().lexeme); + if (!stream_.consume(token_type::dot)) { + return failure(stream_.previous().lexeme); } result = parse_number(out.patch_); @@ -511,14 +508,14 @@ namespace plg { return result; } - if (stream.advanceIfMatch(token_type::hyphen)) { + if (stream_.advanceIfMatch(token_type::hyphen)) { result = parse_prerelease_tag(out.prerelease_tag_, out.prerelease_identifiers); if (!result) { return result; } } - if (stream.advanceIfMatch(token_type::plus)) { + if (stream_.advanceIfMatch(token_type::plus)) { result = parse_build_metadata(out.build_metadata_); if (!result) { return result; @@ -530,11 +527,11 @@ namespace plg { private: - token_stream& stream; + token_stream& stream_; template constexpr from_chars_result parse_number(Int& out) { - token token = stream.advance(); + token token = stream_.advance(); if (!is_digit(token)) { return failure(token.lexeme); @@ -545,30 +542,30 @@ namespace plg { if (first_digit == 0) { out = static_cast(result); - return success(stream.peek().lexeme); + return success(stream_.peek().lexeme); } - while (stream.advanceIfMatch(token, token_type::digit)) { + while (stream_.advanceIfMatch(token, token_type::digit)) { result = result * 10 + std::get(token.value); } if (detail::number_in_range(result)) { out = static_cast(result); - return success(stream.peek().lexeme); + return success(stream_.peek().lexeme); } return failure(token.lexeme, std::errc::result_out_of_range); } - constexpr from_chars_result parse_prerelease_tag(std::string& out, std::vector& out_identifiers) { - std::string result; + constexpr from_chars_result parse_prerelease_tag(string& out, vector& out_identifiers) { + string result; do { if (!result.empty()) { result.push_back('.'); } - std::string identifier; + string identifier; if (const auto res = parse_prerelease_identifier(identifier); !res) { return res; } @@ -576,35 +573,35 @@ namespace plg { result.append(identifier); out_identifiers.push_back(make_prerelease_identifier(identifier)); - } while (stream.advanceIfMatch(token_type::dot)); + } while (stream_.advanceIfMatch(token_type::dot)); out = result; - return success(stream.peek().lexeme); + return success(stream_.peek().lexeme); } - constexpr from_chars_result parse_build_metadata(std::string& out) { - std::string result; + constexpr from_chars_result parse_build_metadata(string& out) { + string result; do { if (!result.empty()) { result.push_back('.'); } - std::string identifier; + string identifier; if (const auto res = parse_build_identifier(identifier); !res) { return res; } result.append(identifier); - } while (stream.advanceIfMatch(token_type::dot)); + } while (stream_.advanceIfMatch(token_type::dot)); out = result; - return success(stream.peek().lexeme); + return success(stream_.peek().lexeme); } - constexpr from_chars_result parse_prerelease_identifier(std::string& out) { - std::string result; - token token = stream.advance(); + constexpr from_chars_result parse_prerelease_identifier(string& out) { + string result; + token token = stream_.advance(); do { switch (token.type) { @@ -635,13 +632,13 @@ namespace plg { default: return failure(token.lexeme); } - } while (stream.advanceIfMatch(token, token_type::hyphen) || stream.advanceIfMatch(token, token_type::letter) || stream.advanceIfMatch(token, token_type::digit)); + } while (stream_.advanceIfMatch(token, token_type::hyphen) || stream_.advanceIfMatch(token, token_type::letter) || stream_.advanceIfMatch(token, token_type::digit)); out = result; - return success(stream.peek().lexeme); + return success(stream_.peek().lexeme); } - constexpr detail::prerelease_identifier make_prerelease_identifier(const std::string& identifier) { + constexpr detail::prerelease_identifier make_prerelease_identifier(const string& identifier) { auto type = detail::prerelease_identifier_type::numeric; for (char c : identifier) { if (c == '-' || detail::is_letter(c)) { @@ -652,9 +649,9 @@ namespace plg { return detail::prerelease_identifier{ type, identifier }; } - constexpr from_chars_result parse_build_identifier(std::string& out) { - std::string result; - token token = stream.advance(); + constexpr from_chars_result parse_build_identifier(string& out) { + string result; + token token = stream_.advance(); do { switch (token.type) { @@ -673,10 +670,10 @@ namespace plg { default: return failure(token.lexeme); } - } while (stream.advanceIfMatch(token, token_type::hyphen) || stream.advanceIfMatch(token, token_type::letter) || stream.advanceIfMatch(token, token_type::digit)); + } while (stream_.advanceIfMatch(token, token_type::hyphen) || stream_.advanceIfMatch(token, token_type::letter) || stream_.advanceIfMatch(token, token_type::digit)); out = result; - return success(stream.peek().lexeme); + return success(stream_.peek().lexeme); } constexpr bool is_leading_zero(int digit) noexcept { @@ -684,12 +681,12 @@ namespace plg { return false; } - int k = 0; + size_t k = 0; int alpha_numerics = 0; int digits = 0; while (true) { - const token token = stream.peek(k); + const token token = stream_.peek(k); if (!is_alphanumeric(token)) { break; @@ -827,31 +824,44 @@ namespace plg { template class range_comparator { public: - constexpr range_comparator(const version& v, range_operator op) noexcept : v(v), op(op) {} + constexpr range_comparator(const version& v, range_operator op) noexcept : v_(v), op_(op) {} constexpr bool contains(const version& other) const noexcept { - switch (op) { + switch (op_) { case range_operator::less: - return detail::compare_parsed(other, v, version_compare_option::include_prerelease) < 0; + return detail::compare_parsed(other, v_, version_compare_option::include_prerelease) < 0; case range_operator::less_or_equal: - return detail::compare_parsed(other, v, version_compare_option::include_prerelease) <= 0; + return detail::compare_parsed(other, v_, version_compare_option::include_prerelease) <= 0; case range_operator::greater: - return detail::compare_parsed(other, v, version_compare_option::include_prerelease) > 0; + return detail::compare_parsed(other, v_, version_compare_option::include_prerelease) > 0; case range_operator::greater_or_equal: - return detail::compare_parsed(other, v, version_compare_option::include_prerelease) >= 0; + return detail::compare_parsed(other, v_, version_compare_option::include_prerelease) >= 0; case range_operator::equal: - return detail::compare_parsed(other, v, version_compare_option::include_prerelease) == 0; + return detail::compare_parsed(other, v_, version_compare_option::include_prerelease) == 0; } return false; } - constexpr const version& get_version() const noexcept { return v; } + constexpr const version& get_version() const noexcept { return v_; } + + constexpr range_operator get_operator() const noexcept { return op_; } - constexpr range_operator get_operator() const noexcept { return op; } + constexpr string to_string() const { + string result; + switch (op_) { + case range_operator::less: result += "<"; break; + case range_operator::less_or_equal: result += "<="; break; + case range_operator::greater: result += ">"; break; + case range_operator::greater_or_equal: result += ">="; break; + case range_operator::equal: result += "="; break; + } + result += v_.to_string(); + return result; + } private: - version v; - range_operator op; + version v_; + range_operator op_; }; class range_parser; @@ -868,36 +878,40 @@ namespace plg { } } - return std::all_of(ranges_comparators.begin(), ranges_comparators.end(), [&](const auto& ranges_comparator) { + return std::all_of(ranges_comparators_.begin(), ranges_comparators_.end(), [&](const auto& ranges_comparator) { return ranges_comparator.contains(v); }); } - constexpr const auto begin() const noexcept { - return ranges_comparators.begin(); + constexpr auto begin() const noexcept { + return ranges_comparators_.begin(); } - constexpr const auto end() const noexcept { - return ranges_comparators.end(); + constexpr auto end() const noexcept { + return ranges_comparators_.end(); } constexpr std::size_t size() const noexcept { - return ranges_comparators.size(); + return ranges_comparators_.size(); } constexpr bool empty() const noexcept { - return ranges_comparators.empty(); + return ranges_comparators_.empty(); + } + + constexpr string to_string() const { + return join(ranges_comparators_, " "); } private: - std::vector> ranges_comparators; + vector> ranges_comparators_; constexpr bool match_at_least_one_comparator_with_prerelease(const version& v) const noexcept { if (v.prerelease_tag().empty()) { return true; } - return std::any_of(ranges_comparators.begin(), ranges_comparators.end(), [&](const auto& ranges_comparator) { + return std::any_of(ranges_comparators_.begin(), ranges_comparators_.end(), [&](const auto& ranges_comparator) { const bool has_prerelease = !ranges_comparator.get_version().prerelease_tag().empty(); const bool equal_without_prerelease = detail::compare_parsed(v, ranges_comparator.get_version(), version_compare_option::exclude_prerelease) == 0; return has_prerelease && equal_without_prerelease; @@ -912,39 +926,43 @@ namespace plg { friend class detail::range_parser; constexpr bool contains(const version& v, version_compare_option option = version_compare_option::exclude_prerelease) const noexcept { - return std::any_of(ranges.begin(), ranges.end(), [&](const auto& range) { + return std::any_of(ranges_.begin(), ranges_.end(), [&](const auto& range) { return range.contains(v, option); }); } - constexpr const auto begin() const noexcept { - return ranges.begin(); + constexpr auto begin() const noexcept { + return ranges_.begin(); } - constexpr const auto end() const noexcept { - return ranges.end(); + constexpr auto end() const noexcept { + return ranges_.end(); } constexpr std::size_t size() const noexcept { - return ranges.size(); + return ranges_.size(); } constexpr bool empty() const noexcept { - return ranges.empty(); + return ranges_.empty(); + } + + constexpr string to_string() const { + return join(ranges_, " "); } private: - std::vector> ranges; + vector> ranges_; }; namespace detail { class range_parser { public: - constexpr explicit range_parser(token_stream stream) noexcept : stream(std::move(stream)) {} + constexpr explicit range_parser(token_stream stream) noexcept : stream_(std::move(stream)) {} template constexpr from_chars_result parse(range_set& out) noexcept { - std::vector> ranges; + vector> ranges; do { @@ -956,54 +974,54 @@ namespace plg { ranges.push_back(range); skip_whitespaces(); - } while (stream.advanceIfMatch(token_type::logical_or)); + } while (stream_.advanceIfMatch(token_type::logical_or)); - out.ranges = std::move(ranges); + out.ranges_ = std::move(ranges); - return success(stream.peek().lexeme); + return success(stream_.peek().lexeme); } private: - token_stream stream; + token_stream stream_; template constexpr from_chars_result parse_range(detail::range& out) noexcept { do { skip_whitespaces(); - if (const auto res = parse_range_comparator(out.ranges_comparators); !res) { + if (const auto res = parse_range_comparator(out.ranges_comparators_); !res) { return res; } skip_whitespaces(); - } while (stream.check(token_type::range_operator) || stream.check(token_type::digit)); + } while (stream_.check(token_type::range_operator) || stream_.check(token_type::digit)); - return success(stream.peek().lexeme); + return success(stream_.peek().lexeme); } template - constexpr from_chars_result parse_range_comparator(std::vector>& out) noexcept { + constexpr from_chars_result parse_range_comparator(vector>& out) noexcept { range_operator op = range_operator::equal; token token; - if (stream.advanceIfMatch(token, token_type::range_operator)) { + if (stream_.advanceIfMatch(token, token_type::range_operator)) { op = std::get(token.value); } skip_whitespaces(); version ver; - version_parser parser{ stream }; + version_parser parser{ stream_ }; if (const auto res = parser.parse(ver); !res) { return res; } out.emplace_back(ver, op); - return success(stream.peek().lexeme); + return success(stream_.peek().lexeme); } constexpr void skip_whitespaces() noexcept { - while (stream.advanceIfMatch(token_type::space)) { + while (stream_.advanceIfMatch(token_type::space)) { ; } } @@ -1060,5 +1078,38 @@ namespace std { return std::format_to(ctx.out(), "{}", ver.to_string()); } }; + template + struct formatter> { + constexpr auto parse(std::format_parse_context& ctx) { + return ctx.begin(); + } + + template + auto format(const plg::range_set& ver, FormatContext& ctx) const { + return std::format_to(ctx.out(), "{}", ver.to_string()); + } + }; + template + struct formatter> { + constexpr auto parse(std::format_parse_context& ctx) { + return ctx.begin(); + } + + template + auto format(const plg::detail::range& ver, FormatContext& ctx) const { + return std::format_to(ctx.out(), "{}", ver.to_string()); + } + }; + template + struct formatter> { + constexpr auto parse(std::format_parse_context& ctx) { + return ctx.begin(); + } + + template + auto format(const plg::detail::range_comparator& ver, FormatContext& ctx) const { + return std::format_to(ctx.out(), "{}", ver.to_string()); + } + }; }// namespace std #endif // PLUGIFY_VECTOR_NO_STD_FORMAT diff --git a/test/cross_call_worker/pps/cross_call_master.hpp b/test/cross_call_worker/external/plugify/include/pps/cross_call_master.hpp similarity index 100% rename from test/cross_call_worker/pps/cross_call_master.hpp rename to test/cross_call_worker/external/plugify/include/pps/cross_call_master.hpp diff --git a/test/cross_call_worker/plg/format.hpp b/test/cross_call_worker/plg/format.hpp deleted file mode 100644 index 1a013a7..0000000 --- a/test/cross_call_worker/plg/format.hpp +++ /dev/null @@ -1,123 +0,0 @@ -#pragma once - -#include "plg/macro.hpp" - -#ifdef __cpp_lib_format - -#include - -#else // __cpp_lib_format - -// Define FMT_FORMAT_H externally to force a difference location for {fmt} -#ifndef FMT_FORMAT_H -#define FMT_FORMAT_H -#endif - -#ifndef FMT_HEADER_ONLY -#define FMT_HEADER_ONLY -#endif -#include FMT_FORMAT_H - -namespace std { - using namespace fmt; - using namespace fmt::detail; -} - -#endif // __cpp_lib_format - -#include -#include - -namespace plg { - namespace detail { - // Concept to match string-like types including char* and const char* - template - concept is_string_like = requires(T v) { - { std::string_view(v) }; - }; - } - - template - constexpr std::string join(const Range& range, std::string_view separator) { - std::string result; - - auto it = range.begin(); - auto end = range.end(); - - if (it == end) return result; - - // First pass: compute total size - size_t total_size = 0; - size_t count = 0; - - for (auto tmp = it; tmp != end; ++tmp) { - using Elem = std::decay_t; - if constexpr (detail::is_string_like) { - total_size += std::string_view(*tmp).size(); - } else { - total_size += std::formatted_size("{}", *tmp); - } - ++count; - } - if (count > 1) { - total_size += (count - 1) * separator.size(); - } - result.reserve(total_size); - - auto in = std::back_inserter(result); - - // Second pass: actual formatting - /*if (it != end)*/ { - std::format_to(in, "{}", *it++); - } - while (it != end) { - std::format_to(in, "{}{}", separator, *it++); - } - - return result; - } - - template - constexpr std::string join(const Range& range, Proj&& proj, std::string_view separator) { - std::string result; - - auto it = range.begin(); - auto end = range.end(); - - if (it == end) return result; - - // First pass: compute total size - size_t total_size = 0; - size_t count = 0; - - for (auto tmp = it; tmp != end; ++tmp) { - auto&& projected = std::invoke(std::forward(proj), *tmp); - using Elem = std::decay_t; - - if constexpr (detail::is_string_like) { - total_size += std::string_view(*projected).size(); - } else { - total_size += std::formatted_size("{}", projected); - } - ++count; - } - if (count > 1) { - total_size += (count - 1) * separator.size(); - } - result.reserve(total_size); - - auto out = std::back_inserter(result); - - // Second pass: actual formatting - { - auto&& projected = std::invoke(std::forward(proj), *it++); - std::format_to(out, "{}", projected); - } - while (it != end) { - auto&& projected = std::invoke(std::forward(proj), *it++); - std::format_to(out, "{}{}", separator, projected); - } - - return result; - } -} diff --git a/test/example_plugin/CMakeLists.txt b/test/example_plugin/CMakeLists.txt index 4faf412..f707562 100644 --- a/test/example_plugin/CMakeLists.txt +++ b/test/example_plugin/CMakeLists.txt @@ -24,7 +24,7 @@ include(CompatFormat) SET(PLUGIN_SOURCES "plugin.cpp") add_library(${PROJECT_NAME} SHARED ${PLUGIN_SOURCES}) -target_include_directories(${PROJECT_NAME} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}) +target_include_directories(${PROJECT_NAME} PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/external/plugify/include") if(MSVC) target_compile_options(${PROJECT_NAME} PRIVATE /W4 /WX) diff --git a/test/example_plugin/plg/allocator.hpp b/test/example_plugin/external/plugify/include/plg/allocator.hpp similarity index 100% rename from test/example_plugin/plg/allocator.hpp rename to test/example_plugin/external/plugify/include/plg/allocator.hpp diff --git a/test/example_plugin/plg/any.hpp b/test/example_plugin/external/plugify/include/plg/any.hpp similarity index 100% rename from test/example_plugin/plg/any.hpp rename to test/example_plugin/external/plugify/include/plg/any.hpp diff --git a/test/example_plugin/plg/api.hpp b/test/example_plugin/external/plugify/include/plg/api.hpp similarity index 53% rename from test/example_plugin/plg/api.hpp rename to test/example_plugin/external/plugify/include/plg/api.hpp index 48d7f7f..8eb02e2 100644 --- a/test/example_plugin/plg/api.hpp +++ b/test/example_plugin/external/plugify/include/plg/api.hpp @@ -1,7 +1,6 @@ #pragma once #include -#include namespace plg { constexpr int kApiVersion = 1; @@ -14,12 +13,4 @@ namespace plg { bool hasDebug{}; }; } -} - -namespace std::filesystem { -#if _WIN32 - using path_view = std::wstring_view; -#else - using path_view = std::string_view; -#endif -} // namespace std::filesystem +} \ No newline at end of file diff --git a/test/example_plugin/plg/debugging.hpp b/test/example_plugin/external/plugify/include/plg/debugging.hpp similarity index 100% rename from test/example_plugin/plg/debugging.hpp rename to test/example_plugin/external/plugify/include/plg/debugging.hpp diff --git a/test/cross_call_worker/plg/enum.hpp b/test/example_plugin/external/plugify/include/plg/enum.hpp similarity index 98% rename from test/cross_call_worker/plg/enum.hpp rename to test/example_plugin/external/plugify/include/plg/enum.hpp index 5a2b373..84f6d23 100644 --- a/test/cross_call_worker/plg/enum.hpp +++ b/test/example_plugin/external/plugify/include/plg/enum.hpp @@ -55,7 +55,7 @@ namespace plg { template constexpr auto value(std::size_t v) { - return static_cast(ENUM_MIN_VALUE + v); + return static_cast(ENUM_MIN_VALUE + static_cast(v)); } template diff --git a/test/example_plugin/external/plugify/include/plg/expected.hpp b/test/example_plugin/external/plugify/include/plg/expected.hpp new file mode 100644 index 0000000..d6bc137 --- /dev/null +++ b/test/example_plugin/external/plugify/include/plg/expected.hpp @@ -0,0 +1,1430 @@ +#pragma once + +#include "plg/macro.hpp" + +#ifdef __cpp_lib_expected +#include +namespace plg { + using std::expected; + using std::unexpected; +} +#else +#include +#include +#include +#include +#include +#include +#include +#include + +// from https://github.com/RishabhRD +namespace plg { + template + class unexpected { + public: + using value_type = E; + constexpr unexpected(unexpected const&) = default; + constexpr unexpected(unexpected&&) = default;// NOLINT + constexpr auto operator=(unexpected const&) -> unexpected& = default; + constexpr auto operator=(unexpected&&) -> unexpected& = default;// NOLINT + ~unexpected() = default; + + template + requires std::constructible_from + constexpr explicit unexpected(std::in_place_t /*unused*/, Args&&... args) + : val(std::forward(args)...) {} + + template + requires std::constructible_from&, Args...> + constexpr explicit unexpected(std::in_place_t /*unused*/, + std::initializer_list i_list, + Args&&... args) + : val(i_list, std::forward(args)...) {} + + template + requires(!std::same_as, unexpected>) && + (!std::same_as, std::in_place_t>) && + std::constructible_from + constexpr explicit unexpected(Err&& err)// NOLINT + : val(std::forward(err)) {} + + constexpr auto value() const& noexcept -> E const& { return val; } + constexpr auto value() & noexcept -> E& { return val; } + constexpr auto value() const&& noexcept -> E const&& { + return std::move(val); + } + constexpr auto value() && noexcept -> E&& { return std::move(val); } + + constexpr void swap(unexpected& other) noexcept( + std::is_nothrow_swappable_v) + requires(std::is_swappable_v) + { + using std::swap; + swap(val, other.val); + } + + template + requires(requires(E const& x, E2 const& y) { + { x == y } -> std::convertible_to; + }) + friend constexpr auto operator==(unexpected const& x, unexpected const& y) -> bool { + return x.value() == y.value(); + } + + friend constexpr void swap(unexpected& x, unexpected& y) noexcept(noexcept(x.swap(y))) + requires(std::is_swappable_v) + { + x.swap(y); + } + + private: + E val; + }; + + template + unexpected(E) -> unexpected; + + template + class bad_expected_access; + + template<> + class bad_expected_access : public std::exception { + protected: + bad_expected_access() noexcept = default; + bad_expected_access(bad_expected_access const&) = default; + bad_expected_access(bad_expected_access&&) = default; + auto operator=(bad_expected_access const&) -> bad_expected_access& = default; + auto operator=(bad_expected_access&&) -> bad_expected_access& = default; + ~bad_expected_access() override = default; + + public: + auto what() const noexcept -> char const* override {// NOLINT + return "bad expected access"; + } + }; + + template + class bad_expected_access : public bad_expected_access { + public: + explicit bad_expected_access(E e) : val(std::move(e)) {} + auto what() const noexcept -> char const* override {// NOLINT + return "bad expected access"; + } + + auto error() & noexcept -> E& { return val; } + auto error() const& noexcept -> E const& { return val; } + auto error() && noexcept -> E&& { return std::move(val); } + auto error() const&& noexcept -> const E&& { return std::move(val); } + + private: + E val; + }; + + struct unexpect_t {}; + inline constexpr unexpect_t unexpect{}; + + namespace detail { + template + concept non_void_destructible = std::same_as || std::destructible; + } + + template + class expected; + + namespace detail { + template + concept expected_constructible_from_other = + std::constructible_from && + std::constructible_from && + (!std::constructible_from&>) && + (!std::constructible_from>) && + (!std::constructible_from const&>) && + (!std::constructible_from const>) && + (!std::convertible_to&, T>) && + (!std::convertible_to&&, T>) && + (!std::convertible_to const&, T>) && + (!std::convertible_to const&&, T>) && + (!std::constructible_from, expected&>) && + (!std::constructible_from, expected>) && + (!std::constructible_from, expected const&>) && + (!std::constructible_from, expected const>); + + template + concept is_unexpected = + std::same_as, unexpected>; + + template + concept is_expected = + std::same_as, + expected>; + + // This function makes sure expected doesn't get into valueless_by_exception + // state due to any exception while assignment + template + constexpr void reinit_expected(T& newval, U& oldval, Args&&... args) { + if constexpr (std::is_nothrow_constructible_v) { + std::destroy_at(std::addressof(oldval)); + std::construct_at(std::addressof(newval), std::forward(args)...); + } else if constexpr (std::is_nothrow_move_constructible_v) { + T tmp(std::forward(args)...); + std::destroy_at(std::addressof(oldval)); + std::construct_at(std::addressof(newval), std::move(tmp)); + } else { + U tmp(std::move(oldval)); + std::destroy_at(std::addressof(oldval)); + try { + std::construct_at(std::addressof(newval), std::forward(args)...); + } catch (...) { + std::construct_at(std::addressof(oldval), std::move(tmp)); + throw; + } + } + } + + }// namespace detail + + template + class expected { + public: + using value_type = T; + using error_type = E; + using unexpected_type = unexpected; + + template + using rebind = expected; + + // constructors + // postcondition: has_value() = true + constexpr expected() + requires std::default_initializable + : val{} {}; + + // postcondition: has_value() = rhs.has_value() + constexpr expected(expected const& rhs) + requires std::copy_constructible && std::copy_constructible && + std::is_trivially_copy_constructible_v && + std::is_trivially_copy_constructible_v + = default; + + // postcondition: has_value() = rhs.has_value() + constexpr expected(expected const& rhs) + requires std::copy_constructible && std::copy_constructible + : has_val(rhs.has_val) { + if (rhs.has_value()) { + std::construct_at(std::addressof(this->val), *rhs); + } else { + std::construct_at(std::addressof(this->unex), rhs.error()); + } + } + + constexpr expected(expected&&) noexcept( + std::is_nothrow_move_constructible_v && + std::is_nothrow_move_constructible_v) + requires std::move_constructible && std::move_constructible && + std::is_trivially_move_constructible_v && + std::is_trivially_move_constructible_v + = default; + + constexpr expected(expected&& rhs) noexcept( + std::is_nothrow_move_constructible_v && + std::is_nothrow_move_constructible_v) + requires std::move_constructible && std::move_constructible + : has_val(rhs.has_value()) { + if (rhs.has_value()) { + std::construct_at(std::addressof(this->val), std::move(*rhs)); + } else { + std::construct_at(std::addressof(this->unex), std::move(rhs.error())); + } + } + + template + requires detail::expected_constructible_from_other + constexpr explicit(!std::convertible_to || + !std::convertible_to) + expected(expected const& rhs)// NOLINT + : has_val(rhs.has_value()) { + using UF = U const&; + using GF = G const&; + if (rhs.has_value()) { + std::construct_at(std::addressof(this->val), std::forward(*rhs)); + } else { + std::construct_at(std::addressof(this->unex), + std::forward(rhs.error())); + } + } + + template + requires detail::expected_constructible_from_other + constexpr explicit(!std::convertible_to || !std::convertible_to) + expected(expected&& rhs)// NOLINT + : has_val(rhs.has_value()) { + using UF = U const&; + using GF = G const&; + if (rhs.has_value()) { + std::construct_at(std::addressof(this->val), std::forward(*rhs)); + } else { + std::construct_at(std::addressof(this->unex), + std::forward(rhs.error())); + } + } + + template + requires(!std::same_as, std::in_place_t>) && + (!std::same_as, std::remove_cvref_t>) && + (!detail::is_unexpected) && + std::constructible_from + constexpr explicit(!std::convertible_to) expected(U&& v)// NOLINT + : val(std::forward(v)) {} + + template + requires std::constructible_from + constexpr explicit(!std::convertible_to) + expected(unexpected const& e)// NOLINT + : has_val{false}, unex(std::forward(e.value())) {} + + template + requires std::constructible_from + constexpr explicit(!std::convertible_to) + expected(unexpected&& e)// NOLINT + : has_val{false}, unex(std::forward(e.value())) {} + + template + requires std::constructible_from + constexpr explicit expected(std::in_place_t /*unused*/, Args&&... args) + : val(std::forward(args)...) {} + + template + requires std::constructible_from&, Args...> + constexpr explicit expected(std::in_place_t /*unused*/, + std::initializer_list il, + Args&&... args) + : val(il, std::forward(args)...) {} + + template + requires std::constructible_from + constexpr explicit expected(unexpect_t /*unused*/, Args&&... args) + : has_val{false}, unex(std::forward(args)...) {} + + template + requires std::constructible_from&, + Args...> + constexpr explicit expected(unexpect_t /*unused*/, + std::initializer_list il, + Args&&... args) + : has_val(false), + unex(il, std::forward(args)...) {} + + // destructor + constexpr ~expected() { + if constexpr (std::is_trivially_destructible_v and + std::is_trivially_destructible_v) { + } else if constexpr (std::is_trivially_destructible_v) { + if (!has_val) { + std::destroy_at(std::addressof(this->unex)); + } + } else if constexpr (std::is_trivially_destructible_v) { + if (has_val) { + std::destroy_at(std::addressof(this->val)); + } + } else { + if (has_val) { + std::destroy_at(std::addressof(this->val)); + } else { + std::destroy_at(std::addressof(this->unex)); + } + } + } + + // assignment + constexpr auto operator=(expected const& rhs)// NOLINT + -> expected& + requires std::is_copy_assignable_v && + std::is_copy_constructible_v && + std::is_copy_assignable_v && + std::is_copy_constructible_v && + (std::is_nothrow_move_constructible_v || + std::is_nothrow_move_constructible_v) + { + if (this->has_value() and rhs.has_value()) { + this->val = *rhs; + } else if (this->has_value()) { + detail::reinit_expected(this->unex, this->val, rhs.error()); + } else if (rhs.has_value()) { + detail::reinit_expected(this->val, this->unex, *rhs); + } else { + this->unex = rhs.error(); + } + has_val = rhs.has_value(); + return *this; + } + + constexpr auto operator=(expected&& rhs)// + noexcept(std::is_nothrow_move_assignable_v && + std::is_nothrow_move_constructible_v && + std::is_nothrow_move_assignable_v && + std::is_nothrow_move_constructible_v) + -> expected& + requires std::is_move_constructible_v && + std::is_move_assignable_v && + std::is_move_constructible_v && + std::is_move_assignable_v && + (std::is_nothrow_move_constructible_v || std::is_nothrow_move_constructible_v) + { + if (this->has_value() and rhs.has_value()) { + this->val = std::move(*rhs); + } else if (this->has_value()) { + detail::reinit_expected(this->unex, this->val, std::move(rhs.error())); + } else if (rhs.has_value()) { + detail::reinit_expected(this->val, this->unex, std::move(*rhs)); + } else { + this->unex = std::move(rhs.error()); + } + has_val = rhs.has_value(); + return *this; + } + + template + constexpr auto operator=(U&& rhs) -> expected& requires(!std::same_as>) && + (!detail::is_unexpected>) && + std::constructible_from&& std::is_assignable_v && + (std::is_nothrow_constructible_v || + std::is_nothrow_move_constructible_v || + std::is_nothrow_move_constructible_v) { + if (this->has_value()) { + this->val = std::forward(rhs); + return *this; + } + detail::reinit_expected(this->val, this->unex, std::forward(rhs)); + has_val = true; + return *this; + } + + template + requires std::constructible_from && + std::is_assignable_v && + (std::is_nothrow_constructible_v || + std::is_nothrow_move_constructible_v || + std::is_nothrow_move_constructible_v) + constexpr auto operator=(unexpected const& e) -> expected& { + using GF = G const&; + if (has_value()) { + detail::reinit_expected(this->unex, this->val, + std::forward(e.value())); + } else { + this->unex = std::forward(e.value()); + } + has_val = false; + return *this; + } + + template + requires std::constructible_from && + std::is_assignable_v && + (std::is_nothrow_constructible_v || + std::is_nothrow_move_constructible_v || + std::is_nothrow_move_constructible_v) + constexpr auto operator=(unexpected&& e) -> expected& { + using GF = G; + if (has_value()) { + detail::reinit_expected(this->unex, this->val, + std::forward(e.value())); + } else { + this->unex = std::forward(e.value()); + } + has_val = false; + return *this; + } + + // modifiers + template + requires std::is_nothrow_constructible_v + constexpr auto emplace(Args&&... args) noexcept -> T& { + if (has_value()) { + std::destroy_at(std::addressof(this->val)); + } else { + std::destroy_at(std::addressof(this->unex)); + has_val = true; + } + return *std::construct_at(std::addressof(this->val), + std::forward(args)...); + } + + template + requires std::is_nothrow_constructible_v&, Args...> + constexpr auto emplace(std::initializer_list il, + Args&&... args) noexcept -> T& { + if (has_value()) { + std::destroy_at(std::addressof(this->val)); + } else { + std::destroy_at(std::addressof(this->unex)); + has_val = true; + } + return *std::construct_at(std::addressof(this->val), il, + std::forward(args)...); + } + + // swap + constexpr void swap(expected& rhs) noexcept( + std::is_nothrow_constructible_v && + std::is_nothrow_swappable_v && + std::is_nothrow_move_constructible_v && + std::is_nothrow_swappable_v) + requires std::is_swappable_v && + std::is_swappable_v && + std::is_move_constructible_v && + std::is_move_constructible_v && + (std::is_nothrow_constructible_v || + std::is_nothrow_constructible_v) + { + if (rhs.has_value()) { + if (has_value()) { + using std::swap; + swap(this->val, rhs.val); + } else { + rhs.swap(*this); + } + } else { + if (has_value()) { + if constexpr (std::is_nothrow_move_constructible_v) { + E tmp(std::move(rhs.unex)); + std::destroy_at(std::addressof(rhs.unex)); + try { + std::construct_at(std::addressof(rhs.val), std::move(this->val)); + std::destroy_at(std::addressof(this->val)); + std::construct_at(std::addressof(this->unex), std::move(tmp)); + } catch (...) { + std::construct_at(std::addressof(rhs.unex), std::move(tmp)); + throw; + } + } else { + T tmp(std::move(this->val)); + std::destroy_at(std::addressof(this->val)); + try { + std::construct_at(std::addressof(this->unex), std::move(rhs.unex)); + std::destroy_at(std::addressof(rhs.unex)); + std::construct_at(std::addressof(rhs.val), std::move(tmp)); + } catch (...) { + std::construct_at(std::addressof(this->val), std::move(tmp)); + throw; + } + } + has_val = false; + rhs.has_val = true; + } else { + using std::swap; + swap(this->unex, rhs.unex); + } + } + } + + // observers + + // precondition: has_value() = true + constexpr auto operator->() const noexcept -> T const* { + return std::addressof(this->val); + } + + // precondition: has_value() = true + constexpr auto operator->() noexcept -> T* { + return std::addressof(this->val); + } + + // precondition: has_value() = true + constexpr auto operator*() const& noexcept -> T const& { return this->val; } + + // precondition: has_value() = true + constexpr auto operator*() & noexcept -> T& { return this->val; } + + // precondition: has_value() = true + constexpr auto operator*() const&& noexcept -> T const&& { + return std::move(this->val); + } + + // precondition: has_value() = true + constexpr auto operator*() && noexcept -> T&& { return std::move(this->val); } + + constexpr explicit operator bool() const noexcept { return has_val; } + + [[nodiscard]] constexpr auto has_value() const noexcept -> bool { + return has_val; + } + + constexpr auto value() const& -> T const& { + if (has_value()) { + return this->val; + } + throw bad_expected_access(error()); + } + + constexpr auto value() & -> T& { + if (has_value()) { + return this->val; + } + throw bad_expected_access(error()); + } + + constexpr auto value() const&& -> T const&& { + if (has_value()) { + return std::move(this->val); + } + throw bad_expected_access(std::move(error())); + } + + constexpr auto value() && -> T&& { + if (has_value()) { + return std::move(this->val); + } + throw bad_expected_access(std::move(error())); + } + + // precondition: has_value() = false + constexpr auto error() const& -> E const& { return this->unex; } + + // precondition: has_value() = false + constexpr auto error() & -> E& { return this->unex; } + + // precondition: has_value() = false + constexpr auto error() const&& -> E const&& { return std::move(this->unex); } + + // precondition: has_value() = false + constexpr auto error() && -> E&& { return std::move(this->unex); } + + template + requires std::is_copy_constructible_v && std::is_convertible_v + constexpr auto value_or(U&& v) const& -> T { + return has_value() ? **this : static_cast(std::forward(v)); + } + + template + requires std::is_move_constructible_v && std::is_convertible_v + constexpr auto value_or(U&& v) && -> T { + return has_value() ? std::move(**this) : static_cast(std::forward(v)); + } + + template>> + requires detail::is_expected && + std::is_same_v && + std::is_copy_constructible_v && std::is_copy_constructible_v + constexpr auto and_then(F&& f) & { + if (has_value()) { + return std::invoke(std::forward(f), **this); + } + return U(unexpect, error()); + } + + template>> + requires detail::is_expected && + std::is_same_v && + std::is_copy_constructible_v && + std::is_copy_constructible_v + constexpr auto and_then(F&& f) const& { + if (has_value()) { + return std::invoke(std::forward(f), **this); + } + return U(unexpect, error()); + } + + template>> + requires detail::is_expected && + std::is_same_v && + std::is_move_constructible_v && + std::is_move_constructible_v + constexpr auto and_then(F&& f) && { + if (has_value()) { + return std::invoke(std::forward(f), std::move(**this)); + } + return U(unexpect, std::move(error())); + } + + template>> + requires detail::is_expected && + std::is_same_v && + std::is_move_constructible_v && + std::is_move_constructible_v + constexpr auto and_then(F&& f) const&& { + if (has_value()) { + return std::invoke(std::forward(f), std::move(**this)); + } + return U(unexpect, std::move(error())); + } + + template>> + requires detail::is_expected && + std::is_same_v && + std::is_copy_constructible_v && + std::is_copy_constructible_v + constexpr auto or_else(F&& f) & { + if (has_value()) { + return G(**this); + } + return std::invoke(std::forward(f), error()); + } + + template>> + requires detail::is_expected && + std::is_same_v && + std::is_copy_constructible_v && + std::is_copy_constructible_v + constexpr auto or_else(F&& f) const& { + if (has_value()) { + return G(**this); + } + return std::invoke(std::forward(f), error()); + } + + template>> + requires detail::is_expected && + std::is_same_v && + std::is_move_constructible_v && + std::is_move_constructible_v + constexpr auto or_else(F&& f) && { + if (has_value()) { + return G(std::move(**this)); + } + return std::invoke(std::forward(f), std::move(error())); + } + + template>> + requires detail::is_expected && + std::is_same_v && + std::is_move_constructible_v && + std::is_move_constructible_v + constexpr auto or_else(F&& f) const&& { + if (has_value()) { + return G(std::move(**this)); + } + return std::invoke(std::forward(f), std::move(error())); + } + + template>> + requires std::is_void_v && + std::is_copy_constructible_v && + std::is_copy_constructible_v + constexpr auto or_else(F&& f) & { + if (has_value()) { + return expected(*this); + } + std::invoke(std::forward(f), error()); + return expected(*this); + } + + template>> + requires std::is_void_v && + std::is_copy_constructible_v && + std::is_copy_constructible_v + constexpr auto or_else(F&& f) const& { + if (has_value()) { + return expected(*this); + } + std::invoke(std::forward(f), error()); + return expected(*this); + } + + template>> + requires std::is_void_v && + std::is_move_constructible_v && + std::is_move_constructible_v && + std::is_copy_constructible_v + constexpr auto or_else(F&& f) && { + if (has_value()) { + return expected(std::move(*this)); + } + // TODO: is this copy necessary, as f can be just read argument function + std::invoke(std::forward(f), error()); + return expected(std::move(*this)); + } + + template>> + requires std::is_void_v && + std::is_move_constructible_v && + std::is_move_constructible_v && + std::is_copy_constructible_v + constexpr auto or_else(F&& f) const&& { + if (!has_value()) { + return expected(std::move(*this)); + } + // TODO: is this copy necessary, as f can be just read argument function + std::invoke(std::forward(f), error()); + return expected(std::move(*this)); + } + + template>> + requires std::is_copy_constructible_v && + std::is_copy_constructible_v + constexpr auto transform(F&& f) & { + if (has_value()) { + if constexpr (!std::same_as) { + return expected(std::invoke(std::forward(f), **this)); + } else { + std::invoke(std::forward(f), std::move(**this)); + return expected(); + } + } + return expected(unexpect, error()); + } + + template>> + requires std::is_copy_constructible_v && + std::is_copy_constructible_v + constexpr auto transform(F&& f) const& { + if (has_value()) { + if constexpr (!std::same_as) { + return expected(std::invoke(std::forward(f), **this)); + } else { + std::invoke(std::forward(f), std::move(**this)); + return expected(); + } + } + return expected(unexpect, error()); + } + + template>> + requires std::is_move_constructible_v && + std::is_move_constructible_v + constexpr auto transform(F&& f) && { + if (has_value()) { + if constexpr (!std::same_as) { + return expected( + std::invoke(std::forward(f), std::move(**this))); + } else { + std::invoke(std::forward(f), std::move(**this)); + return expected(); + } + } + return expected(unexpect, std::move(error())); + } + + template>> + requires std::is_move_constructible_v && + std::is_move_constructible_v + constexpr auto transform(F&& f) const&& { + if (has_value()) { + if constexpr (!std::same_as) { + return expected( + std::invoke(std::forward(f), std::move(**this))); + } else { + std::invoke(std::forward(f), std::move(**this)); + return expected(); + } + } + return expected(unexpect, std::move(error())); + } + + template>> + requires std::is_copy_constructible_v && + std::is_copy_constructible_v + constexpr auto transform_error(F&& f) & { + if (has_value()) { + return expected(**this); + } + return expected(unexpect, std::invoke(std::forward(f), error())); + } + + template>> + requires std::is_copy_constructible_v && + std::is_copy_constructible_v + constexpr auto transform_error(F&& f) const& { + if (has_value()) { + return expected(**this); + } + return expected(unexpect, std::invoke(std::forward(f), error())); + } + + template>> + requires std::is_move_constructible_v && + std::is_move_constructible_v + constexpr auto transform_error(F&& f) && { + if (has_value()) { + return expected(std::move(**this)); + } + return expected(unexpect, + std::invoke(std::forward(f), std::move(error()))); + } + + template>> + requires std::is_move_constructible_v && + std::is_move_constructible_v + constexpr auto transform_error(F&& f) const&& { + if (has_value()) { + return expected(std::move(**this)); + } + return expected(unexpect, + std::invoke(std::forward(f), std::move(error()))); + } + + // equality operators + template + requires(!std::is_void_v) && + requires(T const& t1, T2 const& t2, E const& e1, E2 const& e2) { + { t1 == t2 } -> std::convertible_to; + { e1 == e2 } -> std::convertible_to; + } + friend constexpr auto operator==(expected const& x, expected const& y) + -> bool { + if (x.has_value() != y.has_value()) { + return false; + } + return x.has_value() ? (*x == *y) : (x.error() == y.error()); + } + + template + requires(!detail::is_expected) && requires(T const& x, T2 const& v) { + { x == v } -> std::convertible_to; + } + friend constexpr auto operator==(expected const& x, T2 const& v) -> bool { + return x.has_value() && static_cast(*x == v); + } + + template + requires requires(E const& x, unexpected const& e) { + { x == e.value() } -> std::convertible_to; + } + friend constexpr auto operator==(expected const& x, unexpected const& e) + -> bool { + return !x.has_value() && static_cast(x.error() == e.value()); + } + + // specialized algorithms + friend constexpr void swap(expected& x, + expected& y) noexcept(noexcept(x.swap(y))) { + x.swap(y); + } + + private: + bool has_val{true}; + union { + T val; + E unex; + }; + }; + + template + class expected { + public: + using value_type = void; + using error_type = E; + using unexpected_type = unexpected; + + template + using rebind = expected; + + // constructors + + // postcondition: has_value() = true + constexpr expected() noexcept {}// NOLINT + + constexpr expected( + expected const& rhs) + requires std::is_copy_constructible_v && + std::is_trivially_copy_constructible_v + = default; + + constexpr expected( + expected const& rhs) + requires std::is_copy_constructible_v + : has_val(rhs.has_value()) { + if (!rhs.has_value()) { + std::construct_at(std::addressof(this->unex), rhs.error()); + } + } + + constexpr expected(expected&&) noexcept(std::is_nothrow_move_constructible_v) + requires std::is_move_constructible_v && + std::is_trivially_move_constructible_v + = default; + + constexpr expected(expected&& rhs) noexcept(std::is_nothrow_move_constructible_v) + requires std::is_move_constructible_v + : has_val(rhs.has_value()) { + if (!rhs.has_value()) { + std::construct_at(std::addressof(this->unex), std::move(rhs.error())); + } + } + + template + requires std::is_void_v && std::is_constructible_v && (!std::is_constructible_v, expected&>) && (!std::is_constructible_v, expected>) && (!std::is_constructible_v, expected const&>) && (!std::is_constructible_v, expected const&>) + constexpr explicit(!std::is_convertible_v) + expected(expected const& rhs)// NOLINT + : has_val(rhs.has_value()) { + if (!rhs.has_value()) { + std::construct_at(std::addressof(this->unex), + std::forward(rhs.error())); + } + } + + template + requires std::is_void_v && std::is_constructible_v && (!std::is_constructible_v, expected&>) && (!std::is_constructible_v, expected>) && (!std::is_constructible_v, expected const&>) && (!std::is_constructible_v, expected const&>) + constexpr explicit(!std::is_convertible_v) + expected(expected&& rhs)// NOLINT + : has_val(rhs.has_value()) { + if (!rhs.has_value()) { + std::construct_at(std::addressof(this->unex), + std::forward(rhs.error())); + } + } + + template + requires std::is_constructible_v + constexpr explicit(!std::is_convertible_v) + expected(unexpected const& e)// NOLINT + : has_val(false), unex(std::forward(e.value())) {} + + template + requires std::is_constructible_v + constexpr explicit(!std::is_convertible_v) + expected(unexpected&& e)// NOLINT + : has_val(false), unex(std::forward(e.value())) {} + + constexpr explicit expected(std::in_place_t /*unused*/) noexcept {} + + template + requires std::is_constructible_v + constexpr explicit expected(unexpect_t /*unused*/, Args&&... args) + : has_val(false), unex(std::forward(args)...) {} + + template + requires std::is_constructible_v&, Args...> + constexpr explicit expected(unexpect_t /*unused*/, std::initializer_list il, Args... args) + : has_val(false), + unex(il, std::forward(args)...) {} + + // destructor + constexpr ~expected() { + if constexpr (std::is_trivially_destructible_v) { + } else { + if (!has_value()) std::destroy_at(std::addressof(this->unex)); + } + } + + // assignment + constexpr auto operator=(expected const& rhs) -> expected&// NOLINT + requires std::is_copy_assignable_v && + std::is_copy_constructible_v + { + if (has_value() && rhs.has_value()) { + } else if (has_value()) { + std::construct_at(std::addressof(this->unex), rhs.unex); + has_val = false; + } else if (rhs.has_value()) { + std::destroy_at(std::addressof(this->unex)); + has_val = true; + } else { + this->unex = rhs.error(); + } + return *this; + } + + constexpr auto operator=(expected&& rhs) noexcept(std::is_nothrow_move_constructible_v && + std::is_nothrow_move_assignable_v) -> expected& + requires std::is_move_constructible_v && + std::is_move_assignable_v + { + if (has_value() && rhs.has_value()) { + } else if (has_value()) { + std::construct_at(std::addressof(this->unex), std::move(rhs.unex)); + has_val = false; + } else if (rhs.has_value()) { + std::destroy_at(std::addressof(this->unex)); + has_val = true; + } else { + this->unex = std::move(rhs.error()); + } + return *this; + } + + template + requires std::is_constructible_v and + std::is_assignable_v + constexpr auto operator=(unexpected const& e) -> expected& { + if (has_value()) { + std::construct_at(std::addressof(this->unex), + std::forward(e.value())); + has_val = false; + } else { + this->unex = std::forward(e.value()); + } + return *this; + } + + template + requires std::is_constructible_v && + std::is_assignable_v + constexpr auto operator=(unexpected&& e) -> expected& { + if (has_value()) { + std::construct_at(std::addressof(this->unex), std::forward(e.value())); + has_val = false; + } else { + this->unex = std::forward(e.value()); + } + return *this; + } + + // modifiers + constexpr void emplace() noexcept { + if (!has_value()) { + std::destroy_at(std::addressof(this->unex)); + has_val = true; + } + } + + // swap + constexpr void swap(expected& rhs) noexcept(std::is_nothrow_move_constructible_v && + std::is_nothrow_swappable_v) + requires std::is_swappable_v && + std::is_move_constructible_v + { + if (rhs.has_value()) { + if (has_value()) { + } else { + rhs.swap(*this); + } + } else { + if (has_value()) { + std::construct_at(std::addressof(this->unex), std::move(rhs.unex)); + std::destroy_at(std::addressof(rhs.unex)); + has_val = false; + rhs.has_val = true; + } else { + using std::swap; + swap(this->unex, rhs.unex); + } + } + } + + // observers + constexpr explicit operator bool() const noexcept { return has_val; } + + [[nodiscard]] constexpr auto has_value() const noexcept -> bool { + return has_val; + } + + // precondition: has_value() = true + constexpr void operator*() const noexcept {} + + constexpr void value() const& { + if (!has_value()) { + throw bad_expected_access(error()); + } + } + + constexpr void value() && { + if (!has_value()) { + throw bad_expected_access(std::move(error())); + } + } + + // precondition: has_value() = false + constexpr auto error() const& -> E const& { return this->unex; } + + // precondition: has_value() = false + constexpr auto error() & -> E& { return this->unex; } + + // precondition: has_value() = false + constexpr auto error() const&& -> E const&& { return std::move(this->unex); } + + // precondition: has_value() = false + constexpr auto error() && -> E&& { return std::move(this->unex); } + + // monadic + template>> + requires detail::is_expected && + std::is_same_v && + std::is_copy_constructible_v + constexpr auto and_then(F&& f) & { + if (has_value()) { + return std::invoke(std::forward(f)); + } + return U(unexpect, error()); + } + + template>> + requires detail::is_expected && + std::is_same_v && + std::is_copy_constructible_v + constexpr auto and_then(F&& f) const& { + if (has_value()) { + return std::invoke(std::forward(f)); + } + return U(unexpect, error()); + } + + template>> + requires detail::is_expected && + std::is_same_v && + std::is_move_constructible_v + constexpr auto and_then(F&& f) && { + if (has_value()) { + return std::invoke(std::forward(f)); + } + return U(unexpect, std::move(error())); + } + + template>> + requires detail::is_expected && + std::is_same_v && + std::is_move_constructible_v + constexpr auto and_then(F&& f) const&& { + if (has_value()) { + return std::invoke(std::forward(f)); + } + return U(unexpect, std::move(error())); + } + + template>> + requires detail::is_expected && + std::is_same_v && + std::is_copy_constructible_v + constexpr auto or_else(F&& f) & { + if (has_value()) { + return G{}; + } + return std::invoke(std::forward(f), error()); + } + + template>> + requires detail::is_expected && + std::is_same_v && + std::is_copy_constructible_v + constexpr auto or_else(F&& f) const& { + if (has_value()) { + return G{}; + } + return std::invoke(std::forward(f), error()); + } + + template>> + requires detail::is_expected && + std::is_same_v && + std::is_move_constructible_v + constexpr auto or_else(F&& f) && { + if (has_value()) { + return G{}; + } + return std::invoke(std::forward(f), std::move(error())); + } + + template>> + requires detail::is_expected && + std::is_same_v && + std::is_move_constructible_v + constexpr auto or_else(F&& f) const&& { + if (has_value()) { + return G{}; + } + return std::invoke(std::forward(f), std::move(error())); + } + + template>> + requires std::is_void_v && + std::is_copy_constructible_v + constexpr auto or_else(F&& f) & { + if (has_value()) { + return expected(*this); + } + std::invoke(std::forward(f), error()); + return expected(*this); + } + + template>> + requires std::is_void_v && + std::is_copy_constructible_v + constexpr auto or_else(F&& f) const& { + if (has_value()) { + return expected(*this); + } + std::invoke(std::forward(f), error()); + return expected(*this); + } + + template>> + requires std::is_void_v && + std::is_move_constructible_v && + std::is_copy_constructible_v + constexpr auto or_else(F&& f) && { + if (has_value()) { + return expected(std::move(*this)); + } + // TODO: is this copy necessary, as f can be just read argument function + std::invoke(std::forward(f), error()); + return expected(std::move(*this)); + } + + template>> + requires std::is_void_v && + std::is_move_constructible_v && + std::is_copy_constructible_v + constexpr auto or_else(F&& f) const&& { + if (!has_value()) { + return expected(std::move(*this)); + } + // TODO: is this copy necessary, as f can be just read argument function + std::invoke(std::forward(f), error()); + return expected(std::move(*this)); + } + + template>> + requires std::is_copy_constructible_v + constexpr auto transform(F&& f) & { + if (has_value()) { + if constexpr (!std::same_as) { + return expected(std::invoke(std::forward(f))); + } else { + std::invoke(std::forward(f)); + return expected(); + } + } + return expected(unexpect, error()); + } + + template>> + requires std::is_copy_constructible_v + constexpr auto transform(F&& f) const& { + if (has_value()) { + if constexpr (!std::same_as) { + return expected(std::invoke(std::forward(f))); + } else { + std::invoke(std::forward(f)); + return expected(); + } + } + return expected(unexpect, error()); + } + + template>> + requires std::is_move_constructible_v + constexpr auto transform(F&& f) && { + if (has_value()) { + if constexpr (!std::same_as) { + return expected(std::invoke(std::forward(f))); + } else { + std::invoke(std::forward(f)); + return expected(); + } + } + return expected(unexpect, std::move(error())); + } + + template>> + requires std::is_move_constructible_v + constexpr auto transform(F&& f) const&& { + if (has_value()) { + if constexpr (!std::same_as) { + return expected(std::invoke(std::forward(f))); + } else { + std::invoke(std::forward(f)); + return expected(); + } + } + return expected(unexpect, std::move(error())); + } + + template>> + requires std::is_copy_constructible_v + constexpr auto transform_error(F&& f) & { + if (has_value()) { + return expected{}; + } + return expected(unexpect, + std::invoke(std::forward(f), error())); + } + + template>> + requires std::is_copy_constructible_v + constexpr auto transform_error(F&& f) const& { + if (has_value()) { + return expected{}; + } + return expected(unexpect, + std::invoke(std::forward(f), error())); + } + + template>> + requires std::is_move_constructible_v + constexpr auto transform_error(F&& f) && { + if (has_value()) { + return expected{}; + } + return expected( + unexpect, std::invoke(std::forward(f), std::move(error()))); + } + + template>> + requires std::is_move_constructible_v + constexpr auto transform_error(F&& f) const&& { + if (has_value()) { + return expected{}; + } + return expected( + unexpect, std::invoke(std::forward(f), std::move(error()))); + } + + // expected equality operators + template + requires std::is_void_v && requires(E e, E2 e2) { + { e == e2 } -> std::convertible_to; + } + friend constexpr auto operator==(expected const& x, expected const& y) + -> bool { + if (x.has_value() != y.has_value()) return false; + return x.has_value() or static_cast(x.error() == y.error()); + } + + template + requires requires(expected const& x, unexpected const& e) { + { x.error() == e.value() } -> std::convertible_to; + } + friend constexpr auto operator==(expected const& x, unexpected const& e) + -> bool { + return !x.has_value() && static_cast(x.error() == e.value()); + } + + // specialized algorithms + friend constexpr void swap(expected& x, + expected& y) noexcept(noexcept(x.swap(y))) { + x.swap(y); + } + + private: + bool has_val{true}; + union { + E unex; + }; + }; + +}// namespace plg +#endif \ No newline at end of file diff --git a/test/example_plugin/external/plugify/include/plg/flat_map.hpp b/test/example_plugin/external/plugify/include/plg/flat_map.hpp new file mode 100644 index 0000000..9912431 --- /dev/null +++ b/test/example_plugin/external/plugify/include/plg/flat_map.hpp @@ -0,0 +1,780 @@ +#pragma once + +#include "plg/macro.hpp" + +#ifdef __cpp_lib_flat_map +#include +namespace plg { + template> + using flat_map = std::flat_map; +} +#else +#include "plg/vector.hpp" + +namespace plg { + namespace detail { + template < typename T, typename U, typename = void > + struct is_transparent + : std::false_type {}; + + template < typename T, typename U > + struct is_transparent> + : std::true_type {}; + + template < typename T, typename U > + inline constexpr bool is_transparent_v = is_transparent::value; + + template + constexpr bool is_sorted(Iter first, Iter last, Compare comp) { + if (first != last) { + Iter next = first; + while (++next != last) { + if (comp(*next, *first)) { + return false; + } + ++first; + } + } + return true; + } + + template + constexpr bool is_sorted_unique(Iter first, Iter last, Compare comp) { + if (first != last) { + Iter next = first; + while (++next != last) { + if (!comp(*first, *next)) { + return false; + } + ++first; + } + } + return true; + } + + template + struct pair_compare : public Compare { + pair_compare() = default; + + explicit pair_compare(const Compare& compare) + : Compare(compare) {} + + bool operator()( + const typename Pair::first_type& l, + const typename Pair::first_type& r) const { + return Compare::operator()(l, r); + } + + bool operator()(const Pair& l, const Pair& r) const { + return Compare::operator()(l.first, r.first); + } + + bool operator()( + const typename Pair::first_type& l, + const Pair& r) const { + return Compare::operator()(l, r.first); + } + + bool operator()( + const Pair& l, + const typename Pair::first_type& r) const { + return Compare::operator()(l.first, r); + } + + template + requires (is_transparent_v) + bool operator()(const K& l, const Pair& r) const { + return Compare::operator()(l, r.first); + } + + template + requires (is_transparent_v) + bool operator()(const Pair& l, const K& r) const { + return Compare::operator()(l.first, r); + } + }; + + template + struct eq_compare : public Compare { + eq_compare() = default; + + explicit eq_compare(const Compare& compare) + : Compare(compare) {} + + template + bool operator()(const L& l, const R& r) const { + return !Compare::operator()(l, r) && !Compare::operator()(r, l); + } + }; + } // namespace detail + + struct sorted_range_t {}; + inline constexpr sorted_range_t sorted_range = sorted_range_t(); + + struct sorted_unique_range_t : public sorted_range_t {}; + inline constexpr sorted_unique_range_t sorted_unique_range = sorted_unique_range_t(); + + template, typename Container = std::vector>> + class flat_map { + public: + using key_type = Key; + using mapped_type = Value; + using value_type = typename Container::value_type; + + using size_type = typename Container::size_type; + using difference_type = typename Container::difference_type; + + using key_compare = Compare; + using container_type = Container; + + using reference = typename Container::reference; + using const_reference = typename Container::const_reference; + using pointer = typename Container::pointer; + using const_pointer = typename Container::const_pointer; + + using iterator = typename Container::iterator; + using const_iterator = typename Container::const_iterator; + using reverse_iterator = typename Container::reverse_iterator; + using const_reverse_iterator = typename Container::const_reverse_iterator; + + struct value_compare : private key_compare { + value_compare() = default; + + explicit value_compare(key_compare compare) + : key_compare(std::move(compare)) {} + + bool operator()(const value_type& l, const value_type& r) const { + return key_compare::operator()(l.first, r.first); + } + }; + + public: + flat_map() = default; + ~flat_map() = default; + + explicit flat_map(const Compare& c) + : _compare(c) {} + + template + explicit flat_map(const Allocator& a) + : _data(a) {} + + template + flat_map(const Compare& c, const Allocator& a) + : _compare(c), _data(a) {} + + template + flat_map(Iterator first, Iterator last) { + from_range(first, last); + } + + template + flat_map(sorted_range_t, Iterator first, Iterator last) { + from_range(sorted_range, first, last); + } + + template + flat_map(sorted_unique_range_t, Iterator first, Iterator last) { + from_range(sorted_unique_range, first, last); + } + + template + flat_map(Iterator first, Iterator last, const Compare& c) + : _compare(c) { + from_range(first, last); + } + + template + flat_map(sorted_range_t, Iterator first, Iterator last, const Compare& c) + : _compare(c) { + from_range(sorted_range, first, last); + } + + template + flat_map(sorted_unique_range_t, Iterator first, Iterator last, const Compare& c) + : _compare(c) { + from_range(sorted_unique_range, first, last); + } + + template + flat_map(Iterator first, Iterator last, const Allocator& a) + : _data(a) { + from_range(first, last); + } + + template + flat_map(sorted_range_t, Iterator first, Iterator last, const Allocator& a) + : _data(a) { + from_range(sorted_range, first, last); + } + + template + flat_map(sorted_unique_range_t, Iterator first, Iterator last, const Allocator& a) + : _data(a) { + from_range(sorted_unique_range, first, last); + } + + template + flat_map(Iterator first, Iterator last, const Compare& c, const Allocator& a) + : _compare(c), _data(a) { + from_range(first, last); + } + + template + flat_map(sorted_range_t, Iterator first, Iterator last, const Compare& c, const Allocator& a) + : _compare(c), _data(a) { + from_range(sorted_range, first, last); + } + + template + flat_map(sorted_unique_range_t, Iterator first, Iterator last, const Compare& c, const Allocator& a) + : _compare(c), _data(a) { + from_range(sorted_unique_range, first, last); + } + + flat_map(std::initializer_list list) { + from_range(list.begin(), list.end()); + } + + flat_map(sorted_range_t, std::initializer_list list) { + from_range(sorted_range, list.begin(), list.end()); + } + + flat_map(sorted_unique_range_t, std::initializer_list list) { + from_range(sorted_unique_range, list.begin(), list.end()); + } + + flat_map(std::initializer_list list, const Compare& c) + : _compare(c) { + from_range(list.begin(), list.end()); + } + + flat_map(sorted_range_t, std::initializer_list list, const Compare& c) + : _compare(c) { + from_range(sorted_range, list.begin(), list.end()); + } + + flat_map(sorted_unique_range_t, std::initializer_list list, const Compare& c) + : _compare(c) { + from_range(sorted_unique_range, list.begin(), list.end()); + } + + template + flat_map(std::initializer_list list, const Allocator& a) + : _data(a) { + from_range(list.begin(), list.end()); + } + + template + flat_map(sorted_range_t, std::initializer_list list, const Allocator& a) + : _data(a) { + from_range(sorted_range, list.begin(), list.end()); + } + + template + flat_map(sorted_unique_range_t, std::initializer_list list, const Allocator& a) + : _data(a) { + from_range(sorted_unique_range, list.begin(), list.end()); + } + + template + flat_map(std::initializer_list list, const Compare& c, const Allocator& a) + : _compare(c), _data(a) { + from_range(list.begin(), list.end()); + } + + template + flat_map(sorted_range_t, std::initializer_list list, const Compare& c, const Allocator& a) + : _compare(c), _data(a) { + from_range(sorted_range, list.begin(), list.end()); + } + + template + flat_map(sorted_unique_range_t, std::initializer_list list, const Compare& c, const Allocator& a) + : _compare(c), _data(a) { + from_range(sorted_unique_range, list.begin(), list.end()); + } + + template + flat_map(flat_map&& other, const Allocator& a) + : _compare(std::move(other._compare)), _data(std::move(other._data), a) {} + + template + flat_map(const flat_map& other, const Allocator& a) + : _compare(other._compare), _data(other._data, a) {} + + flat_map(flat_map&& other) noexcept = default; + flat_map(const flat_map& other) = default; + + flat_map& operator=(flat_map&& other) noexcept = default; + flat_map& operator=(const flat_map& other) = default; + + flat_map& operator=(std::initializer_list list) { + flat_map(list).swap(*this); + return *this; + } + + iterator begin() noexcept(noexcept(std::declval().begin())) { + return _data.begin(); + } + + const_iterator begin() const + noexcept(noexcept(std::declval().begin())) { + return _data.begin(); + } + + const_iterator cbegin() const + noexcept(noexcept(std::declval().cbegin())) { + return _data.cbegin(); + } + + iterator end() noexcept(noexcept(std::declval().end())) { + return _data.end(); + } + + const_iterator end() const + noexcept(noexcept(std::declval().end())) { + return _data.end(); + } + + const_iterator cend() const + noexcept(noexcept(std::declval().cend())) { + return _data.cend(); + } + + reverse_iterator rbegin() noexcept(noexcept(std::declval().rbegin())) { + return _data.rbegin(); + } + + const_reverse_iterator rbegin() const + noexcept(noexcept(std::declval().rbegin())) { + return _data.rbegin(); + } + + const_reverse_iterator crbegin() const + noexcept(noexcept(std::declval().crbegin())) { + return _data.crbegin(); + } + + reverse_iterator rend() noexcept(noexcept(std::declval().rend())) { + return _data.rend(); + } + + const_reverse_iterator rend() const + noexcept(noexcept(std::declval().rend())) { + return _data.rend(); + } + + const_reverse_iterator crend() const + noexcept(noexcept(std::declval().crend())) { + return _data.crend(); + } + + bool empty() const + noexcept(noexcept(std::declval().empty())) { + return _data.empty(); + } + + size_type size() const + noexcept(noexcept(std::declval().size())) { + return _data.size(); + } + + size_type max_size() const + noexcept(noexcept(std::declval().max_size())) { + return _data.max_size(); + } + + size_type capacity() const + noexcept(noexcept(std::declval().capacity())) { + return _data.capacity(); + } + + void reserve(size_type capacity) { + _data.reserve(capacity); + } + + void shrink_to_fit() { + _data.shrink_to_fit(); + } + + mapped_type& operator[](key_type&& key) { + const iterator iter = find(key); + return iter != end() + ? iter->second + : emplace(std::move(key), mapped_type()).first->second; + } + + mapped_type& operator[](const key_type& key) { + const iterator iter = find(key); + return iter != end() + ? iter->second + : emplace(key, mapped_type()).first->second; + } + + mapped_type& at(const key_type& key) { + const iterator iter = find(key); + PLUGIFY_ASSERT(iter != end(), "plg::flat_map::at(): key not found", std::out_of_range); + return iter->second; + } + + const mapped_type& at(const key_type& key) const { + const const_iterator iter = find(key); + PLUGIFY_ASSERT(iter != end(), "plg::flat_map::at(): key not found", std::out_of_range); + return iter->second; + } + + template + requires (detail::is_transparent_v) + mapped_type& at(const K& key) { + const iterator iter = find(key); + PLUGIFY_ASSERT(iter != end(), "plg::flat_map::at(): key not found", std::out_of_range); + return iter->second; + } + + template + requires (detail::is_transparent_v) + const mapped_type& at(const K& key) const { + const const_iterator iter = find(key); + PLUGIFY_ASSERT(iter != end(), "plg::flat_map::at(): key not found", std::out_of_range); + return iter->second; + } + + std::pair insert(value_type&& value) { + const iterator iter = lower_bound(value.first); + return iter == end() || _compare(value, *iter) + ? std::make_pair(_data.insert(iter, std::move(value)), true) + : std::make_pair(iter, false); + } + + std::pair insert(const value_type& value) { + const iterator iter = lower_bound(value.first); + return iter == end() || _compare(value, *iter) + ? std::make_pair(_data.insert(iter, value), true) + : std::make_pair(iter, false); + } + + iterator insert(const_iterator hint, value_type&& value) { + return (hint == begin() || _compare(*(hint - 1), value)) && (hint == end() || _compare(value, *hint)) + ? _data.insert(hint, std::move(value)) + : insert(std::move(value)).first; + } + + iterator insert(const_iterator hint, const value_type& value) { + return (hint == begin() || _compare(*(hint - 1), value)) && (hint == end() || _compare(value, *hint)) + ? _data.insert(hint, value) + : insert(value).first; + } + + template + std::pair insert_or_assign(key_type&& key, V&& value) { + iterator iter = lower_bound(key); + if (iter == end() || _compare(key, *iter)) { + iter = emplace_hint(iter, std::move(key), std::forward(value)); + return {iter, true}; + } + (*iter).second = std::forward(value); + return {iter, false}; + } + + template + std::pair insert_or_assign(const key_type& key, V&& value) { + iterator iter = lower_bound(key); + if (iter == end() || _compare(key, *iter)) { + iter = emplace_hint(iter, key, std::forward(value)); + return {iter, true}; + } + (*iter).second = std::forward(value); + return {iter, false}; + } + + template + void insert(Iterator first, Iterator last) { + insert_range(first, last); + } + + template + void insert(sorted_range_t, Iterator first, Iterator last) { + insert_range(sorted_range, first, last); + } + + void insert(std::initializer_list list) { + insert_range(list.begin(), list.end()); + } + + void insert(sorted_range_t, std::initializer_list list) { + insert_range(sorted_range, list.begin(), list.end()); + } + + template + std::pair emplace(Args&&... args) { + return insert(value_type(std::forward(args)...)); + } + + template + iterator emplace_hint(const_iterator hint, Args&&... args) { + return insert(hint, value_type(std::forward(args)...)); + } + + template + std::pair try_emplace(key_type&& key, Args&&... args) { + iterator iter = lower_bound(key); + if (iter == end() || _compare(key, *iter)) { + iter = emplace_hint(iter, std::move(key), std::forward(args)...); + return {iter, true}; + } + return {iter, false}; + } + + template + std::pair try_emplace(const key_type& key, Args&&... args) { + iterator iter = lower_bound(key); + if (iter == end() || _compare(key, *iter)) { + iter = emplace_hint(iter, key, std::forward(args)...); + return {iter, true}; + } + return {iter, false}; + } + + void clear() noexcept(noexcept(std::declval().clear())) { + _data.clear(); + } + + iterator erase(const_iterator iter) { + return _data.erase(iter); + } + + iterator erase(const_iterator first, const_iterator last) { + return _data.erase(first, last); + } + + size_type erase(const key_type& key) { + const const_iterator iter = find(key); + return iter != end() + ? (erase(iter), 1) + : 0; + } + + void swap(flat_map& other) noexcept(std::is_nothrow_swappable_v && std::is_nothrow_swappable_v) { + using std::swap; + swap(_compare, other._compare); + swap(_data, other._data); + } + + size_type count(const key_type& key) const { + const const_iterator iter = find(key); + return iter != end() ? 1 : 0; + } + + template + requires (detail::is_transparent_v) + size_type count(const K& key) const { + const const_iterator iter = find(key); + return iter != end() ? 1 : 0; + } + + iterator find(const key_type& key) { + const iterator iter = lower_bound(key); + return iter != end() && !_compare(key, *iter) + ? iter + : end(); + } + + const_iterator find(const key_type& key) const { + const const_iterator iter = lower_bound(key); + return iter != end() && !_compare(key, *iter) + ? iter + : end(); + } + + template + requires (detail::is_transparent_v) + iterator find(const K& key) { + const iterator iter = lower_bound(key); + return iter != end() && !_compare(key, *iter) + ? iter + : end(); + } + + template + requires (detail::is_transparent_v) + const_iterator find(const K& key) const { + const const_iterator iter = lower_bound(key); + return iter != end() && !_compare(key, *iter) + ? iter + : end(); + } + + bool contains(const key_type& key) const { + return find(key) != end(); + } + + template + requires (detail::is_transparent_v) + bool contains(const K& key) const { + return find(key) != end(); + } + + std::pair equal_range(const key_type& key) { + return std::equal_range(begin(), end(), key, _compare); + } + + std::pair equal_range(const key_type& key) const { + return std::equal_range(begin(), end(), key, _compare); + } + + template + requires (detail::is_transparent_v) + std::pair equal_range(const K& key) { + return std::equal_range(begin(), end(), key, _compare); + } + + template + requires (detail::is_transparent_v) + std::pair equal_range(const K& key) const { + return std::equal_range(begin(), end(), key, _compare); + } + + iterator lower_bound(const key_type& key) { + return std::lower_bound(begin(), end(), key, _compare); + } + + const_iterator lower_bound(const key_type& key) const { + return std::lower_bound(begin(), end(), key, _compare); + } + + template + requires (detail::is_transparent_v) + iterator lower_bound(const K& key) { + return std::lower_bound(begin(), end(), key, _compare); + } + + template + requires (detail::is_transparent_v) + const_iterator lower_bound(const K& key) const { + return std::lower_bound(begin(), end(), key, _compare); + } + + iterator upper_bound(const key_type& key) { + return std::upper_bound(begin(), end(), key, _compare); + } + + const_iterator upper_bound(const key_type& key) const { + return std::upper_bound(begin(), end(), key, _compare); + } + + template + requires (detail::is_transparent_v) + iterator upper_bound(const K& key) { + return std::upper_bound(begin(), end(), key, _compare); + } + + template + requires (detail::is_transparent_v) + const_iterator upper_bound(const K& key) const { + return std::upper_bound(begin(), end(), key, _compare); + } + + key_compare key_comp() const { + return _compare; + } + + value_compare value_comp() const { + return value_compare(key_comp()); + } + + private: + template + void from_range(Iter first, Iter last) { + assert(_data.empty()); + _data.insert(_data.end(), first, last); + std::sort(_data.begin(), _data.end(), value_comp()); + _data.erase( + std::unique(_data.begin(), _data.end(), + detail::eq_compare(value_comp())), + _data.end()); + } + + template + void from_range(sorted_range_t, Iter first, Iter last) { + assert(_data.empty()); + assert(detail::is_sorted(first, last, value_comp())); + _data.insert(_data.end(), first, last); + _data.erase( + std::unique(_data.begin(), _data.end(), + detail::eq_compare(value_comp())), + _data.end()); + } + + template + void from_range(sorted_unique_range_t, Iter first, Iter last) { + assert(_data.empty()); + assert(detail::is_sorted_unique(first, last, value_comp())); + _data.insert(_data.end(), first, last); + } + + private: + template + void insert_range(Iter first, Iter last) { + const auto mid_iter = _data.insert(_data.end(), first, last); + std::sort(mid_iter, _data.end(), value_comp()); + std::inplace_merge(_data.begin(), mid_iter, _data.end(), value_comp()); + _data.erase( + std::unique(_data.begin(), _data.end(), + detail::eq_compare(value_comp())), + _data.end()); + } + + template + void insert_range(sorted_range_t, Iter first, Iter last) { + assert(detail::is_sorted(first, last, value_comp())); + const auto mid_iter = _data.insert(_data.end(), first, last); + std::inplace_merge(_data.begin(), mid_iter, _data.end(), value_comp()); + _data.erase( + std::unique(_data.begin(), _data.end(), + detail::eq_compare(value_comp())), + _data.end()); + } + + private: + PLUGIFY_NO_UNIQUE_ADDRESS + detail::pair_compare _compare; + container_type _data; + }; + + template + void swap( + flat_map& l, + flat_map& r) noexcept(noexcept(l.swap(r))) { + l.swap(r); + } + + template + bool operator==( + const flat_map& l, + const flat_map& r) { + return l.size() == r.size() && std::equal(l.begin(), l.end(), r.begin()); + } + + template + auto operator<=>( + const flat_map& l, + const flat_map& r) { + if (l.size() < r.size()) { + return std::partial_ordering::less; + } else if (l.size() > r.size()) { + return std::partial_ordering::greater; + } else { + if (std::lexicographical_compare(l.cbegin(), l.cend(), r.cbegin(), r.cend())) { + return std::partial_ordering::less; + } else { + return std::partial_ordering::greater; + } + } + } + + template, typename Container = plg::vector>> + using map = flat_map; + +}// namespace plg +#endif \ No newline at end of file diff --git a/test/example_plugin/external/plugify/include/plg/format.hpp b/test/example_plugin/external/plugify/include/plg/format.hpp new file mode 100644 index 0000000..94e5bdb --- /dev/null +++ b/test/example_plugin/external/plugify/include/plg/format.hpp @@ -0,0 +1,26 @@ +#pragma once + +#include "plg/macro.hpp" + +#ifdef __cpp_lib_format + +#include + +#else // __cpp_lib_format + +// Define FMT_FORMAT_H externally to force a difference location for {fmt} +#ifndef FMT_FORMAT_H +#define FMT_FORMAT_H +#endif + +#ifndef FMT_HEADER_ONLY +#define FMT_HEADER_ONLY +#endif +#include FMT_FORMAT_H + +namespace std { + using namespace fmt; + using namespace fmt::detail; +} + +#endif // __cpp_lib_format diff --git a/test/example_plugin/plg/formatter.hpp b/test/example_plugin/external/plugify/include/plg/formatter.hpp similarity index 100% rename from test/example_plugin/plg/formatter.hpp rename to test/example_plugin/external/plugify/include/plg/formatter.hpp diff --git a/test/cross_call_worker/plg/hash.hpp b/test/example_plugin/external/plugify/include/plg/hash.hpp similarity index 91% rename from test/cross_call_worker/plg/hash.hpp rename to test/example_plugin/external/plugify/include/plg/hash.hpp index de2e050..53f37bf 100644 --- a/test/cross_call_worker/plg/hash.hpp +++ b/test/example_plugin/external/plugify/include/plg/hash.hpp @@ -76,4 +76,12 @@ namespace plg { (hash_combine(seed, args), ...); // fold expression return seed; } + + template + struct pair_hash { + size_t operator()(std::pair const& p) const { + return hash_combine_all(p.first, p.second); + } + }; + } diff --git a/test/cross_call_master/plg/macro.hpp b/test/example_plugin/external/plugify/include/plg/macro.hpp similarity index 99% rename from test/cross_call_master/plg/macro.hpp rename to test/example_plugin/external/plugify/include/plg/macro.hpp index 6008e31..a072fec 100644 --- a/test/cross_call_master/plg/macro.hpp +++ b/test/example_plugin/external/plugify/include/plg/macro.hpp @@ -257,7 +257,7 @@ # define PLUGIFY_NOINLINE [[gnu::noinline]] #elif PLUGIFY_COMPILER_MSVC # pragma warning(error: 4714) -# define PLUGIFY_FORCE_INLINE __forceinline +# define PLUGIFY_FORCE_INLINE [[msvc::forceinline]] # define PLUGIFY_NOINLINE __declspec(noinline) #else # define PLUGIFY_FORCE_INLINE inline diff --git a/test/example_plugin/plg/numerics.hpp b/test/example_plugin/external/plugify/include/plg/numerics.hpp similarity index 100% rename from test/example_plugin/plg/numerics.hpp rename to test/example_plugin/external/plugify/include/plg/numerics.hpp diff --git a/test/example_plugin/external/plugify/include/plg/path.hpp b/test/example_plugin/external/plugify/include/plg/path.hpp new file mode 100644 index 0000000..61343f3 --- /dev/null +++ b/test/example_plugin/external/plugify/include/plg/path.hpp @@ -0,0 +1,52 @@ +#pragma once + +#include +#include +#include + +namespace plg { + using path_view = std::basic_string_view; + using path_string = std::filesystem::path::string_type; + using path_char = path_string::value_type; + using path_diff_t = path_string::difference_type; + +#if _WIN32 +#define PLUGIFY_PATH_LITERAL(x) L##x +#else +#define PLUGIFY_PATH_LITERAL(x) x +#endif + + template + bool insensitive_equals(const path_char lhs, const path_char rhs) { + if constexpr (std::is_same_v) { + return std::towlower(lhs) == rhs; // NOLINT + } else { + return std::tolower(lhs) == rhs; + } + } + + inline bool insensitive_equals(const path_view lhs, const path_view rhs) { + return lhs.size() == rhs.size() && std::equal(lhs.cbegin(), lhs.cend(), rhs.cbegin(), insensitive_equals); + } + + inline path_view extension_view(const std::filesystem::path& path) { + constexpr path_diff_t extension_size = 4; + if (!path.has_extension()) { return {}; } + const path_string& path_str = path.native(); + const auto offset = static_cast(path_str.size()) - extension_size; + if (offset <= 0) { return {}; } + return { path_str.cbegin() + offset, path_str.cend() }; + } + + inline bool has_extension(const std::filesystem::path& path, const path_view extension) { + return insensitive_equals(extension_view(path), extension); + } + + inline auto as_string(const std::filesystem::path& p) { +#if _WIN32 + return p.string(); // returns std::string by value +#else + return p.native(); // returns const std::string& +#endif + } +} diff --git a/test/cross_call_worker/plg/plugin.hpp b/test/example_plugin/external/plugify/include/plg/plugin.hpp similarity index 98% rename from test/cross_call_worker/plg/plugin.hpp rename to test/example_plugin/external/plugify/include/plg/plugin.hpp index 408f632..7498a8c 100644 --- a/test/cross_call_worker/plg/plugin.hpp +++ b/test/example_plugin/external/plugify/include/plg/plugin.hpp @@ -81,7 +81,7 @@ namespace plg { plg::vector GetDependencies() const { return plugin::GetDependencies(plugin::handle); } virtual void OnPluginStart() {}; - virtual void OnPluginUpdate(std::chrono::microseconds) {}; + virtual void OnPluginUpdate(std::chrono::milliseconds) {}; virtual void OnPluginEnd() {}; }; @@ -149,7 +149,7 @@ namespace plg { template \ struct has_overridden_OnPluginUpdate : std::false_type {}; \ template \ - struct has_overridden_OnPluginUpdate().OnPluginUpdate(std::chrono::microseconds{0}))>> \ + struct has_overridden_OnPluginUpdate().OnPluginUpdate(std::chrono::milliseconds{0}))>> \ : std::bool_constant> {}; \ template \ @@ -161,7 +161,7 @@ namespace plg { extern "C" plugin_api void Plugify_PluginStart() { \ GetPluginEntry()->OnPluginStart(); \ } \ - extern "C" plugin_api void Plugify_PluginUpdate(std::chrono::microseconds dt) { \ + extern "C" plugin_api void Plugify_PluginUpdate(std::chrono::milliseconds dt) { \ GetPluginEntry()->OnPluginUpdate(dt); \ } \ extern "C" plugin_api void Plugify_PluginEnd() { \ diff --git a/test/example_plugin/plg/string.hpp b/test/example_plugin/external/plugify/include/plg/string.hpp similarity index 92% rename from test/example_plugin/plg/string.hpp rename to test/example_plugin/external/plugify/include/plg/string.hpp index 17b4b29..175bed2 100644 --- a/test/example_plugin/plg/string.hpp +++ b/test/example_plugin/external/plugify/include/plg/string.hpp @@ -44,20 +44,63 @@ namespace plg { namespace detail { - template - struct is_allocator : std::false_type {}; - - template - struct is_allocator().allocate(std::size_t{}))>> : std::true_type {}; - - template - constexpr bool is_allocator_v = is_allocator::value; - - template - using is_traits = std::conjunction, std::is_integral>; - - template - constexpr bool is_traits_v = is_traits::value; + template + concept is_allocator = requires(Alloc& a, std::size_t n) { + typename std::allocator_traits::value_type; + typename std::allocator_traits::pointer; + typename std::allocator_traits::const_pointer; + typename std::allocator_traits::size_type; + typename std::allocator_traits::difference_type; + + { std::allocator_traits::allocate(a, n) } + -> std::convertible_to::pointer>; + + requires requires(typename std::allocator_traits::pointer p) { + std::allocator_traits::deallocate(a, p, n); + }; + }; + + template + concept is_char_traits = requires { + // Required type definitions + typename Traits::char_type; + typename Traits::int_type; + typename Traits::off_type; + typename Traits::pos_type; + typename Traits::state_type; + } && requires( + typename Traits::char_type c1, + typename Traits::char_type c2, + typename Traits::char_type& cr, + const typename Traits::char_type& ccr, + typename Traits::char_type* p, + const typename Traits::char_type* cp, + typename Traits::int_type i1, + typename Traits::int_type i2, + std::size_t n + ) { + // Character operations + { Traits::assign(cr, ccr) } -> std::same_as; + { Traits::eq(ccr, ccr) } -> std::convertible_to; + { Traits::lt(ccr, ccr) } -> std::convertible_to; + + // String operations + { Traits::compare(cp, cp, n) } -> std::convertible_to; + { Traits::length(cp) } -> std::convertible_to; + { Traits::find(cp, n, ccr) } -> std::convertible_to; + + // Memory operations + { Traits::move(p, cp, n) } -> std::same_as; + { Traits::copy(p, cp, n) } -> std::same_as; + { Traits::assign(p, n, c1) } -> std::same_as; + + // int_type operations + { Traits::not_eof(i1) } -> std::same_as; + { Traits::to_char_type(i1) } -> std::same_as; + { Traits::to_int_type(c1) } -> std::same_as; + { Traits::eq_int_type(i1, i2) } -> std::convertible_to; + { Traits::eof() } -> std::same_as; + }; struct uninitialized_size_tag {}; @@ -68,11 +111,12 @@ namespace plg { template concept string_compatible_range = std::ranges::input_range && std::convertible_to, Type>; #endif // PLUGIFY_STRING_CONTAINERS_RANGES - }// namespace detail + + } // namespace detail // basic_string // based on implementations from libc++, libstdc++ and Microsoft STL - template, typename Allocator = plg::allocator> requires (detail::is_traits_v && detail::is_allocator_v) + template, detail::is_allocator Allocator = plg::allocator> class basic_string { private: using allocator_traits = std::allocator_traits; @@ -1926,3 +1970,107 @@ namespace std { struct formatter, Allocator>> : plg::detail::string_formatter_base {}; }// namespace std #endif // PLUGIFY_STRING_NO_STD_FORMAT + +template +std::ostream& operator<<(std::ostream& os, const plg::basic_string& str) { + os << str.c_str(); + return os; +} + +#ifndef PLUGIFY_STRING_NO_STD_FORMAT +#include + +namespace plg { + namespace detail { + // Concept to match string-like types including char* and const char* + template + concept is_string_like = requires(T v) { + { std::string_view(v) }; + }; + } + + template + constexpr string join(const Range& range, std::string_view separator) { + string result; + + auto it = range.cbegin(); + auto end = range.cend(); + + if (it == end) return result; + + // First pass: compute total size + size_t total_size = 0; + size_t count = 0; + + for (auto tmp = it; tmp != end; ++tmp) { + using Elem = std::decay_t; + if constexpr (detail::is_string_like) { + total_size += std::string_view(*tmp).size(); + } else { + total_size += std::formatted_size("{}", *tmp); + } + ++count; + } + if (count > 1) { + total_size += (count - 1) * separator.size(); + } + result.reserve(total_size); + + auto in = std::back_inserter(result); + + // Second pass: actual formatting + /*if (it != end)*/ { + std::format_to(in, "{}", *it++); + } + while (it != end) { + std::format_to(in, "{}{}", separator, *it++); + } + + return result; + } + + template + constexpr string join(const Range& range, Proj&& proj, std::string_view separator) { + string result; + + auto it = range.cbegin(); + auto end = range.cend(); + + if (it == end) return result; + + // First pass: compute total size + size_t total_size = 0; + size_t count = 0; + + for (auto tmp = it; tmp != end; ++tmp) { + auto&& projected = std::invoke(std::forward(proj), *tmp); + using Elem = std::decay_t; + + if constexpr (detail::is_string_like) { + total_size += std::string_view(*projected).size(); + } else { + total_size += std::formatted_size("{}", projected); + } + ++count; + } + if (count > 1) { + total_size += (count - 1) * separator.size(); + } + result.reserve(total_size); + + auto out = std::back_inserter(result); + + // Second pass: actual formatting + { + auto&& projected = std::invoke(std::forward(proj), *it++); + std::format_to(out, "{}", projected); + } + while (it != end) { + auto&& projected = std::invoke(std::forward(proj), *it++); + std::format_to(out, "{}{}", separator, projected); + } + + return result; + } +} // namespace plugify +#endif // PLUGIFY_STRING_NO_STD_FORMAT \ No newline at end of file diff --git a/test/example_plugin/plg/variant.hpp b/test/example_plugin/external/plugify/include/plg/variant.hpp similarity index 100% rename from test/example_plugin/plg/variant.hpp rename to test/example_plugin/external/plugify/include/plg/variant.hpp diff --git a/test/cross_call_worker/plg/vector.hpp b/test/example_plugin/external/plugify/include/plg/vector.hpp similarity index 96% rename from test/cross_call_worker/plg/vector.hpp rename to test/example_plugin/external/plugify/include/plg/vector.hpp index b340a61..3ec8750 100644 --- a/test/cross_call_worker/plg/vector.hpp +++ b/test/example_plugin/external/plugify/include/plg/vector.hpp @@ -27,7 +27,32 @@ #include "plg/allocator.hpp" namespace plg { - template + namespace detail { + template + concept is_alloc = requires(Alloc& a, std::size_t n) { + typename std::allocator_traits::value_type; + typename std::allocator_traits::pointer; + typename std::allocator_traits::const_pointer; + typename std::allocator_traits::size_type; + typename std::allocator_traits::difference_type; + + { std::allocator_traits::allocate(a, n) } + -> std::convertible_to::pointer>; + + requires requires(typename std::allocator_traits::pointer p) { + std::allocator_traits::deallocate(a, p, n); + }; + }; + + struct initialized_value_tag {}; + +#if PLUGIFY_VECTOR_CONTAINERS_RANGES + template + concept vector_compatible_range = std::ranges::input_range && std::convertible_to, Type>; +#endif + } // namespace detail + + template struct vector_iterator { using allocator_traits = std::allocator_traits; public: @@ -119,7 +144,7 @@ namespace plg { } #endif // __cpp_impl_three_way_comparison - template + template struct vector_const_iterator { using allocator_traits = std::allocator_traits; public: @@ -152,14 +177,14 @@ namespace plg { ++_current; return *this; } - constexpr vector_const_iterator operator++(int) const noexcept { + constexpr vector_const_iterator operator++(int) noexcept { return vector_const_iterator(_current++); } constexpr vector_const_iterator& operator--() noexcept { --_current; return *this; } - constexpr vector_const_iterator operator--(int) const noexcept { + constexpr vector_const_iterator operator--(int) noexcept { return vector_const_iterator(_current--); } constexpr vector_const_iterator& operator+=(const difference_type n) noexcept { @@ -232,18 +257,9 @@ namespace plg { return lhs.base() <=> rhs.base(); } - namespace detail { - struct initialized_value_tag {}; - -#if PLUGIFY_VECTOR_CONTAINERS_RANGES - template - concept vector_compatible_range = std::ranges::input_range && std::convertible_to, Type>; -#endif - } // namespace detail - // vector // based on implementations from libc++, libstdc++ and Microsoft STL - template> + template> class vector { using allocator_traits = std::allocator_traits; public: diff --git a/test/cross_call_master/plg/version.hpp b/test/example_plugin/external/plugify/include/plg/version.hpp similarity index 80% rename from test/cross_call_master/plg/version.hpp rename to test/example_plugin/external/plugify/include/plg/version.hpp index 355d33f..b12ac87 100644 --- a/test/cross_call_master/plg/version.hpp +++ b/test/example_plugin/external/plugify/include/plg/version.hpp @@ -6,10 +6,7 @@ #include #include #include -#include -#include #include -#include #if __has_include() #include #else @@ -22,11 +19,12 @@ #include "plg/hash.hpp" #include "plg/macro.hpp" +#include "plg/string.hpp" +#include "plg/vector.hpp" // from https://github.com/Neargye/semver namespace plg { namespace detail { - template struct resize_uninitialized { constexpr static auto resize(T& str, std::size_t size) -> std::void_t { @@ -67,7 +65,7 @@ namespace plg { struct prerelease_identifier { prerelease_identifier_type type; - std::string identifier; + string identifier; }; class version_parser; @@ -92,19 +90,19 @@ namespace plg { constexpr I2 minor() const noexcept { return minor_; } constexpr I3 patch() const noexcept { return patch_; } - constexpr const std::string& prerelease_tag() const { return prerelease_tag_; } - constexpr const std::string& build_metadata() const { return build_metadata_; } + constexpr const string& prerelease_tag() const { return prerelease_tag_; } + constexpr const string& build_metadata() const { return build_metadata_; } - constexpr std::string to_string() const; + constexpr string to_string() const; private: I1 major_ = 0; I2 minor_ = 1; I3 patch_ = 0; - std::string prerelease_tag_; - std::string build_metadata_; + string prerelease_tag_; + string build_metadata_; - std::vector prerelease_identifiers; + vector prerelease_identifiers; constexpr std::size_t length() const noexcept { return detail::length(major_) + detail::length(minor_) + detail::length(patch_) + 2 @@ -124,9 +122,9 @@ namespace plg { }; template - constexpr std::string version::to_string() const { - std::string result; - detail::resize_uninitialized{}.resize(result, length()); + constexpr string version::to_string() const { + string result; + detail::resize_uninitialized{}.resize(result, length()); auto it = result.end(); if (!build_metadata_.empty()) { @@ -169,7 +167,6 @@ namespace plg { }; namespace detail { - constexpr from_chars_result success(const char* ptr) noexcept { return from_chars_result{ ptr, std::errc{} }; } @@ -295,28 +292,28 @@ namespace plg { class token_stream { public: constexpr token_stream() = default; - constexpr explicit token_stream(std::vector tokens) noexcept : tokens(std::move(tokens)) {} + constexpr explicit token_stream(vector tokens) noexcept : tokens_(std::move(tokens)) {} constexpr void push(const token& token) noexcept { - tokens.push_back(token); + tokens_.push_back(token); } constexpr token advance() noexcept { - const token token = get(current); - ++current; + const token token = get(current_); + ++current_; return token; } constexpr token peek(std::size_t k = 0) const noexcept { - return get(current + k); + return get(current_ + k); } constexpr token previous() const noexcept { - return get(current - 1); + return get(current_ - 1); } constexpr bool advanceIfMatch(token& token, token_type type) noexcept { - if (get(current).type != type) { + if (get(current_).type != type) { return false; } @@ -338,11 +335,11 @@ namespace plg { } private: - std::size_t current = 0; - std::vector tokens; + std::size_t current_ = 0; + vector tokens_; constexpr token get(std::size_t i) const noexcept { - return tokens[i]; + return tokens_[i]; } }; @@ -481,7 +478,7 @@ namespace plg { class version_parser { public: - constexpr explicit version_parser(token_stream& stream) : stream{stream} { + constexpr explicit version_parser(token_stream& stream) : stream_{stream} { } template @@ -493,8 +490,8 @@ namespace plg { return result; } - if (!stream.consume(token_type::dot)) { - return failure(stream.previous().lexeme); + if (!stream_.consume(token_type::dot)) { + return failure(stream_.previous().lexeme); } result = parse_number(out.minor_); @@ -502,8 +499,8 @@ namespace plg { return result; } - if (!stream.consume(token_type::dot)) { - return failure(stream.previous().lexeme); + if (!stream_.consume(token_type::dot)) { + return failure(stream_.previous().lexeme); } result = parse_number(out.patch_); @@ -511,14 +508,14 @@ namespace plg { return result; } - if (stream.advanceIfMatch(token_type::hyphen)) { + if (stream_.advanceIfMatch(token_type::hyphen)) { result = parse_prerelease_tag(out.prerelease_tag_, out.prerelease_identifiers); if (!result) { return result; } } - if (stream.advanceIfMatch(token_type::plus)) { + if (stream_.advanceIfMatch(token_type::plus)) { result = parse_build_metadata(out.build_metadata_); if (!result) { return result; @@ -530,11 +527,11 @@ namespace plg { private: - token_stream& stream; + token_stream& stream_; template constexpr from_chars_result parse_number(Int& out) { - token token = stream.advance(); + token token = stream_.advance(); if (!is_digit(token)) { return failure(token.lexeme); @@ -545,30 +542,30 @@ namespace plg { if (first_digit == 0) { out = static_cast(result); - return success(stream.peek().lexeme); + return success(stream_.peek().lexeme); } - while (stream.advanceIfMatch(token, token_type::digit)) { + while (stream_.advanceIfMatch(token, token_type::digit)) { result = result * 10 + std::get(token.value); } if (detail::number_in_range(result)) { out = static_cast(result); - return success(stream.peek().lexeme); + return success(stream_.peek().lexeme); } return failure(token.lexeme, std::errc::result_out_of_range); } - constexpr from_chars_result parse_prerelease_tag(std::string& out, std::vector& out_identifiers) { - std::string result; + constexpr from_chars_result parse_prerelease_tag(string& out, vector& out_identifiers) { + string result; do { if (!result.empty()) { result.push_back('.'); } - std::string identifier; + string identifier; if (const auto res = parse_prerelease_identifier(identifier); !res) { return res; } @@ -576,35 +573,35 @@ namespace plg { result.append(identifier); out_identifiers.push_back(make_prerelease_identifier(identifier)); - } while (stream.advanceIfMatch(token_type::dot)); + } while (stream_.advanceIfMatch(token_type::dot)); out = result; - return success(stream.peek().lexeme); + return success(stream_.peek().lexeme); } - constexpr from_chars_result parse_build_metadata(std::string& out) { - std::string result; + constexpr from_chars_result parse_build_metadata(string& out) { + string result; do { if (!result.empty()) { result.push_back('.'); } - std::string identifier; + string identifier; if (const auto res = parse_build_identifier(identifier); !res) { return res; } result.append(identifier); - } while (stream.advanceIfMatch(token_type::dot)); + } while (stream_.advanceIfMatch(token_type::dot)); out = result; - return success(stream.peek().lexeme); + return success(stream_.peek().lexeme); } - constexpr from_chars_result parse_prerelease_identifier(std::string& out) { - std::string result; - token token = stream.advance(); + constexpr from_chars_result parse_prerelease_identifier(string& out) { + string result; + token token = stream_.advance(); do { switch (token.type) { @@ -635,13 +632,13 @@ namespace plg { default: return failure(token.lexeme); } - } while (stream.advanceIfMatch(token, token_type::hyphen) || stream.advanceIfMatch(token, token_type::letter) || stream.advanceIfMatch(token, token_type::digit)); + } while (stream_.advanceIfMatch(token, token_type::hyphen) || stream_.advanceIfMatch(token, token_type::letter) || stream_.advanceIfMatch(token, token_type::digit)); out = result; - return success(stream.peek().lexeme); + return success(stream_.peek().lexeme); } - constexpr detail::prerelease_identifier make_prerelease_identifier(const std::string& identifier) { + constexpr detail::prerelease_identifier make_prerelease_identifier(const string& identifier) { auto type = detail::prerelease_identifier_type::numeric; for (char c : identifier) { if (c == '-' || detail::is_letter(c)) { @@ -652,9 +649,9 @@ namespace plg { return detail::prerelease_identifier{ type, identifier }; } - constexpr from_chars_result parse_build_identifier(std::string& out) { - std::string result; - token token = stream.advance(); + constexpr from_chars_result parse_build_identifier(string& out) { + string result; + token token = stream_.advance(); do { switch (token.type) { @@ -673,10 +670,10 @@ namespace plg { default: return failure(token.lexeme); } - } while (stream.advanceIfMatch(token, token_type::hyphen) || stream.advanceIfMatch(token, token_type::letter) || stream.advanceIfMatch(token, token_type::digit)); + } while (stream_.advanceIfMatch(token, token_type::hyphen) || stream_.advanceIfMatch(token, token_type::letter) || stream_.advanceIfMatch(token, token_type::digit)); out = result; - return success(stream.peek().lexeme); + return success(stream_.peek().lexeme); } constexpr bool is_leading_zero(int digit) noexcept { @@ -684,12 +681,12 @@ namespace plg { return false; } - int k = 0; + size_t k = 0; int alpha_numerics = 0; int digits = 0; while (true) { - const token token = stream.peek(k); + const token token = stream_.peek(k); if (!is_alphanumeric(token)) { break; @@ -827,31 +824,44 @@ namespace plg { template class range_comparator { public: - constexpr range_comparator(const version& v, range_operator op) noexcept : v(v), op(op) {} + constexpr range_comparator(const version& v, range_operator op) noexcept : v_(v), op_(op) {} constexpr bool contains(const version& other) const noexcept { - switch (op) { + switch (op_) { case range_operator::less: - return detail::compare_parsed(other, v, version_compare_option::include_prerelease) < 0; + return detail::compare_parsed(other, v_, version_compare_option::include_prerelease) < 0; case range_operator::less_or_equal: - return detail::compare_parsed(other, v, version_compare_option::include_prerelease) <= 0; + return detail::compare_parsed(other, v_, version_compare_option::include_prerelease) <= 0; case range_operator::greater: - return detail::compare_parsed(other, v, version_compare_option::include_prerelease) > 0; + return detail::compare_parsed(other, v_, version_compare_option::include_prerelease) > 0; case range_operator::greater_or_equal: - return detail::compare_parsed(other, v, version_compare_option::include_prerelease) >= 0; + return detail::compare_parsed(other, v_, version_compare_option::include_prerelease) >= 0; case range_operator::equal: - return detail::compare_parsed(other, v, version_compare_option::include_prerelease) == 0; + return detail::compare_parsed(other, v_, version_compare_option::include_prerelease) == 0; } return false; } - constexpr const version& get_version() const noexcept { return v; } + constexpr const version& get_version() const noexcept { return v_; } + + constexpr range_operator get_operator() const noexcept { return op_; } - constexpr range_operator get_operator() const noexcept { return op; } + constexpr string to_string() const { + string result; + switch (op_) { + case range_operator::less: result += "<"; break; + case range_operator::less_or_equal: result += "<="; break; + case range_operator::greater: result += ">"; break; + case range_operator::greater_or_equal: result += ">="; break; + case range_operator::equal: result += "="; break; + } + result += v_.to_string(); + return result; + } private: - version v; - range_operator op; + version v_; + range_operator op_; }; class range_parser; @@ -868,36 +878,40 @@ namespace plg { } } - return std::all_of(ranges_comparators.begin(), ranges_comparators.end(), [&](const auto& ranges_comparator) { + return std::all_of(ranges_comparators_.begin(), ranges_comparators_.end(), [&](const auto& ranges_comparator) { return ranges_comparator.contains(v); }); } - constexpr const auto begin() const noexcept { - return ranges_comparators.begin(); + constexpr auto begin() const noexcept { + return ranges_comparators_.begin(); } - constexpr const auto end() const noexcept { - return ranges_comparators.end(); + constexpr auto end() const noexcept { + return ranges_comparators_.end(); } constexpr std::size_t size() const noexcept { - return ranges_comparators.size(); + return ranges_comparators_.size(); } constexpr bool empty() const noexcept { - return ranges_comparators.empty(); + return ranges_comparators_.empty(); + } + + constexpr string to_string() const { + return join(ranges_comparators_, " "); } private: - std::vector> ranges_comparators; + vector> ranges_comparators_; constexpr bool match_at_least_one_comparator_with_prerelease(const version& v) const noexcept { if (v.prerelease_tag().empty()) { return true; } - return std::any_of(ranges_comparators.begin(), ranges_comparators.end(), [&](const auto& ranges_comparator) { + return std::any_of(ranges_comparators_.begin(), ranges_comparators_.end(), [&](const auto& ranges_comparator) { const bool has_prerelease = !ranges_comparator.get_version().prerelease_tag().empty(); const bool equal_without_prerelease = detail::compare_parsed(v, ranges_comparator.get_version(), version_compare_option::exclude_prerelease) == 0; return has_prerelease && equal_without_prerelease; @@ -912,39 +926,43 @@ namespace plg { friend class detail::range_parser; constexpr bool contains(const version& v, version_compare_option option = version_compare_option::exclude_prerelease) const noexcept { - return std::any_of(ranges.begin(), ranges.end(), [&](const auto& range) { + return std::any_of(ranges_.begin(), ranges_.end(), [&](const auto& range) { return range.contains(v, option); }); } - constexpr const auto begin() const noexcept { - return ranges.begin(); + constexpr auto begin() const noexcept { + return ranges_.begin(); } - constexpr const auto end() const noexcept { - return ranges.end(); + constexpr auto end() const noexcept { + return ranges_.end(); } constexpr std::size_t size() const noexcept { - return ranges.size(); + return ranges_.size(); } constexpr bool empty() const noexcept { - return ranges.empty(); + return ranges_.empty(); + } + + constexpr string to_string() const { + return join(ranges_, " "); } private: - std::vector> ranges; + vector> ranges_; }; namespace detail { class range_parser { public: - constexpr explicit range_parser(token_stream stream) noexcept : stream(std::move(stream)) {} + constexpr explicit range_parser(token_stream stream) noexcept : stream_(std::move(stream)) {} template constexpr from_chars_result parse(range_set& out) noexcept { - std::vector> ranges; + vector> ranges; do { @@ -956,54 +974,54 @@ namespace plg { ranges.push_back(range); skip_whitespaces(); - } while (stream.advanceIfMatch(token_type::logical_or)); + } while (stream_.advanceIfMatch(token_type::logical_or)); - out.ranges = std::move(ranges); + out.ranges_ = std::move(ranges); - return success(stream.peek().lexeme); + return success(stream_.peek().lexeme); } private: - token_stream stream; + token_stream stream_; template constexpr from_chars_result parse_range(detail::range& out) noexcept { do { skip_whitespaces(); - if (const auto res = parse_range_comparator(out.ranges_comparators); !res) { + if (const auto res = parse_range_comparator(out.ranges_comparators_); !res) { return res; } skip_whitespaces(); - } while (stream.check(token_type::range_operator) || stream.check(token_type::digit)); + } while (stream_.check(token_type::range_operator) || stream_.check(token_type::digit)); - return success(stream.peek().lexeme); + return success(stream_.peek().lexeme); } template - constexpr from_chars_result parse_range_comparator(std::vector>& out) noexcept { + constexpr from_chars_result parse_range_comparator(vector>& out) noexcept { range_operator op = range_operator::equal; token token; - if (stream.advanceIfMatch(token, token_type::range_operator)) { + if (stream_.advanceIfMatch(token, token_type::range_operator)) { op = std::get(token.value); } skip_whitespaces(); version ver; - version_parser parser{ stream }; + version_parser parser{ stream_ }; if (const auto res = parser.parse(ver); !res) { return res; } out.emplace_back(ver, op); - return success(stream.peek().lexeme); + return success(stream_.peek().lexeme); } constexpr void skip_whitespaces() noexcept { - while (stream.advanceIfMatch(token_type::space)) { + while (stream_.advanceIfMatch(token_type::space)) { ; } } @@ -1060,5 +1078,38 @@ namespace std { return std::format_to(ctx.out(), "{}", ver.to_string()); } }; + template + struct formatter> { + constexpr auto parse(std::format_parse_context& ctx) { + return ctx.begin(); + } + + template + auto format(const plg::range_set& ver, FormatContext& ctx) const { + return std::format_to(ctx.out(), "{}", ver.to_string()); + } + }; + template + struct formatter> { + constexpr auto parse(std::format_parse_context& ctx) { + return ctx.begin(); + } + + template + auto format(const plg::detail::range& ver, FormatContext& ctx) const { + return std::format_to(ctx.out(), "{}", ver.to_string()); + } + }; + template + struct formatter> { + constexpr auto parse(std::format_parse_context& ctx) { + return ctx.begin(); + } + + template + auto format(const plg::detail::range_comparator& ver, FormatContext& ctx) const { + return std::format_to(ctx.out(), "{}", ver.to_string()); + } + }; }// namespace std #endif // PLUGIFY_VECTOR_NO_STD_FORMAT diff --git a/test/example_plugin/plg/format.hpp b/test/example_plugin/plg/format.hpp deleted file mode 100644 index 1a013a7..0000000 --- a/test/example_plugin/plg/format.hpp +++ /dev/null @@ -1,123 +0,0 @@ -#pragma once - -#include "plg/macro.hpp" - -#ifdef __cpp_lib_format - -#include - -#else // __cpp_lib_format - -// Define FMT_FORMAT_H externally to force a difference location for {fmt} -#ifndef FMT_FORMAT_H -#define FMT_FORMAT_H -#endif - -#ifndef FMT_HEADER_ONLY -#define FMT_HEADER_ONLY -#endif -#include FMT_FORMAT_H - -namespace std { - using namespace fmt; - using namespace fmt::detail; -} - -#endif // __cpp_lib_format - -#include -#include - -namespace plg { - namespace detail { - // Concept to match string-like types including char* and const char* - template - concept is_string_like = requires(T v) { - { std::string_view(v) }; - }; - } - - template - constexpr std::string join(const Range& range, std::string_view separator) { - std::string result; - - auto it = range.begin(); - auto end = range.end(); - - if (it == end) return result; - - // First pass: compute total size - size_t total_size = 0; - size_t count = 0; - - for (auto tmp = it; tmp != end; ++tmp) { - using Elem = std::decay_t; - if constexpr (detail::is_string_like) { - total_size += std::string_view(*tmp).size(); - } else { - total_size += std::formatted_size("{}", *tmp); - } - ++count; - } - if (count > 1) { - total_size += (count - 1) * separator.size(); - } - result.reserve(total_size); - - auto in = std::back_inserter(result); - - // Second pass: actual formatting - /*if (it != end)*/ { - std::format_to(in, "{}", *it++); - } - while (it != end) { - std::format_to(in, "{}{}", separator, *it++); - } - - return result; - } - - template - constexpr std::string join(const Range& range, Proj&& proj, std::string_view separator) { - std::string result; - - auto it = range.begin(); - auto end = range.end(); - - if (it == end) return result; - - // First pass: compute total size - size_t total_size = 0; - size_t count = 0; - - for (auto tmp = it; tmp != end; ++tmp) { - auto&& projected = std::invoke(std::forward(proj), *tmp); - using Elem = std::decay_t; - - if constexpr (detail::is_string_like) { - total_size += std::string_view(*projected).size(); - } else { - total_size += std::formatted_size("{}", projected); - } - ++count; - } - if (count > 1) { - total_size += (count - 1) * separator.size(); - } - result.reserve(total_size); - - auto out = std::back_inserter(result); - - // Second pass: actual formatting - { - auto&& projected = std::invoke(std::forward(proj), *it++); - std::format_to(out, "{}", projected); - } - while (it != end) { - auto&& projected = std::invoke(std::forward(proj), *it++); - std::format_to(out, "{}{}", separator, projected); - } - - return result; - } -} diff --git a/version.txt b/version.txt index b8061b5..a14da29 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -2.0.15 +2.0.16