From f0b72c0f14449693fcc5c9359df1d437b23848e0 Mon Sep 17 00:00:00 2001 From: Vitaly Fanaskov Date: Fri, 10 Jan 2025 22:25:20 +0100 Subject: [PATCH] Small experimental matcher for variants --- src/include/fl/expected/expected.hpp | 10 ++++ src/include/fl/utils/callable_traits.hpp | 44 +++++++++++++++ src/include/fl/utils/match.hpp | 71 ++++++++++++++++++++++++ test/CMakeLists.txt | 2 + test/test_match.cpp | 29 ++++++++++ 5 files changed, 156 insertions(+) create mode 100644 src/include/fl/utils/callable_traits.hpp create mode 100644 src/include/fl/utils/match.hpp create mode 100644 test/test_match.cpp diff --git a/src/include/fl/expected/expected.hpp b/src/include/fl/expected/expected.hpp index 06cd38c..1da5dcb 100644 --- a/src/include/fl/expected/expected.hpp +++ b/src/include/fl/expected/expected.hpp @@ -1,3 +1,13 @@ +// +// MIT License +// +// Copyright (c) 2024-present Vitaly Fanaskov +// +// fl -- Functional tools for C++ +// Project home: https://github.com/vt4a2h/fl +// +// See LICENSE file for the further details. +// #include namespace fl { diff --git a/src/include/fl/utils/callable_traits.hpp b/src/include/fl/utils/callable_traits.hpp new file mode 100644 index 0000000..33b35ce --- /dev/null +++ b/src/include/fl/utils/callable_traits.hpp @@ -0,0 +1,44 @@ +// +// MIT License +// +// Copyright (c) 2025-present Vitaly Fanaskov +// +// fl -- Functional tools for C++ +// Project home: https://github.com/vt4a2h/fl +// +// See LICENSE file for the further details. +// +#pragma once + +#include + +namespace fl { + +template +struct callable_traits; + +template +struct callable_traits : public callable_traits {}; + +template +struct callable_traits +{ + static constexpr std::size_t args_count = sizeof...(Args); + + using result_type = R; + + template + requires (index < args_count) + struct arg + { + using type = typename std::tuple_element>::type; + }; +}; + +template +struct callable_traits : public callable_traits +{ + using class_type = C; +}; + +} // namespace fl \ No newline at end of file diff --git a/src/include/fl/utils/match.hpp b/src/include/fl/utils/match.hpp new file mode 100644 index 0000000..80c7506 --- /dev/null +++ b/src/include/fl/utils/match.hpp @@ -0,0 +1,71 @@ +// +// MIT License +// +// Copyright (c) 2025-present Vitaly Fanaskov +// +// fl -- Functional tools for C++ +// Project home: https://github.com/vt4a2h/fl +// +// See LICENSE file for the further details. +// +#pragma once + +#include +#include +#include + +namespace fl { + +namespace details { + +template +constexpr bool is_variant = false; + +template +constexpr bool is_variant> = true; + +template +concept IsVariant = is_variant>; + +template +constexpr bool invocable_with() +{ + return [](std::index_sequence){ + return (... || std::is_invocable_v>); + }(std::make_index_sequence>{}); +} + +template +concept InvocableWithAnyOf = invocable_with, std::remove_cvref_t>(); + +template +bool invokeIfCan(Value &valuePtr, Callable f) +{ + if constexpr (std::is_invocable_v>) { + std::invoke(f, valuePtr); + return true; + } else { + return false; + } +} + +template ...Callable> +bool invokeIfHoldsCorrectAlternative(const Variant &variant, const Callable &...callable) +{ + auto *v = std::get_if>(&variant); + return v && ((... || invokeIfCan(*v, callable))); +} + +} // namespace details + +template ...Callable> +void match(const Variant &variant, Callable ...callable) +{ + using Variant_t = std::remove_cvref_t; + + [&variant, &callable...](std::index_sequence){ + (... || details::invokeIfHoldsCorrectAlternative(variant, callable...)); + }(std::make_index_sequence>{}); +} + +} // namespace fl \ No newline at end of file diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index fb4e4f2..0e7d15e 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -29,6 +29,7 @@ target_sources(tests PRIVATE test_writer_move_copy.cpp # test_utils_ap_optional.cpp # test_applicative_optional.cpp + test_match.cpp expected/test_expected_create.cpp expected/test_expected_unexpected_create.cpp expected/test_expected_unexpected_access_error.cpp @@ -57,6 +58,7 @@ if(MSVC) target_compile_options(tests PRIVATE "/wd4996;" # external warning in fmt + "/wd4244;" # convert int/double in tests ) endif() diff --git a/test/test_match.cpp b/test/test_match.cpp new file mode 100644 index 0000000..fd33699 --- /dev/null +++ b/test/test_match.cpp @@ -0,0 +1,29 @@ +// +// MIT License +// +// Copyright (c) 2025-present Vitaly Fanaskov +// +// fl -- Functional tools for C++ +// Project home: https://github.com/vt4a2h/fl +// +// See LICENSE file for the further details. +// +#include "catch.hpp" + +#include + +#include + +template +struct overloaded : Ts... { using Ts::operator()...; }; + +TEST_CASE("Invoked for all alternatives") +{ + auto intHandler = [](int v){ fmt::println("int: {}", v); }; + auto stringHandler = [](const std::string &v){ fmt::println("string: {}", v); }; + auto doubleHandler = [](double v){ fmt::println("double: {}", v); }; + + std::variant v = 42; + + fl::match(v, stringHandler, intHandler, doubleHandler); +} \ No newline at end of file