Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Variant match expression and object #44

Merged
merged 1 commit into from
Mar 14, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
162 changes: 80 additions & 82 deletions include/utils/match.hxx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
///
/// License: MIT
///
/// Copyright: Copyright (c) 2022
/// Copyright: Copyright (c) 2022-23
/// \file match.hxx

#ifndef CORTEX_MATCH
Expand All @@ -26,111 +26,109 @@
# include <concepts>
#endif /// __cpp_concepts >= 201907L

namespace cxl
namespace cxl::utils
{
namespace utils

/// \brief match structure
///
/// \details Inherits the properties
/// of the variadic template of
/// invocables and interfaces the
/// invocables `()` operators.
///
/// \tparam ...Fs
template<typename... Fs>
struct match : Fs...
{
/// \brief match structure
///
/// \details Inherits the properties
/// of the variadic template of
/// invocables and interfaces the
/// invocables `()` operators.
///
/// \tparam ...Fs concept: std::invocable
template<typename... Fs>
struct match : Fs...
{

/// \brief Forward Constructor
///
/// \details Consumes all function types
/// and forwards them to the variadic constructor
/// of the invocables.
///
/// \tparam ...Ts
///
/// \param ...ts
template<class... Ts>
explicit match(Ts&&... ts)
: Fs { std::forward<Ts>(ts) }... { }

/// \brief Invocation declaration
///
/// \details Declares `match` to be using
/// the invocation operators `()`.
using Fs::operator()...;
};

/// \brief Template Deduction Guide
template<typename... Fs>
match(Fs&&...) -> match<std::remove_reference_t<Fs>...>;

/// \brief Match Expression Notation

/// \brief Forward Constructor
///
/// \details Overloads `>>` to reverse the argument
/// order of a match expression for a cleaner syntax
/// with existing match types.
/// \details Consumes all function types
/// and forwards them to the variadic constructor
/// of the invocables.
///
/// \tparam ...Ts
/// \tparam ...Fs concept: std::invocable
/// \param var type: std::variant<Ts...>
/// \param m type match<Fs...>&&
/// \return decltype(auto)
template<typename... Ts, typename... Fs>
constexpr auto
operator| (const std::variant<Ts...>& var,
const match<Fs...>& m) -> void {
std::visit(m, var);
}

/// \brief Match Expression Assignment Overload
///
/// \details Overloads the `>>=` operator to
/// reverse the argument order of a match
/// expression for a cleaner syntax with existing
/// match types and to denote an explicit syntax
/// for match expressions that return a value.
/// \param ...ts
template<class... Ts>
explicit match(Ts&&... ts)
: Fs { std::forward<Ts>(ts) }... { }

/// \brief Invocation declaration
///
/// \tparam R
/// \tparam ...Ts
/// \tparam ...Fs concept: std::invocable
/// \param var type: const std::variant<Ts...>&
/// \param m type: match<Fs...>&&
/// \return R
template<typename... Ts, typename... Fs>
constexpr auto
operator|= (const std::variant<Ts...>& var,
const match<Fs...>& m) -> decltype(auto) {
return std::visit(m, var);
}
/// \details Declares `match` to be using
/// the invocation operators `()`.
using Fs::operator()...;
};

/// \brief Template Deduction Guide
template<typename... Fs>
match(Fs&&...) -> match<std::remove_reference_t<Fs>...>;

} // namespace utils
/// \brief Match Expression Notation
///
/// \details Overloads `>>` to reverse the argument
/// order of a match expression for a cleaner syntax
/// with existing match types.
///
/// \tparam ...Ts
/// \tparam ...Fs
/// \param var type: const std::variant<Ts...>&
/// \param m type const match<Fs...>&
/// \return auto
template<typename... Ts, typename... Fs>
constexpr auto
operator>> (
const std::variant<Ts...>& var,
const match<Fs...>& m
) -> void
{ std::visit(m, var); }

/// \brief Match Expression Assignment Overload
///
/// \details Overloads the `>>=` operator to
/// reverse the argument order of a match
/// expression for a cleaner syntax with existing
/// match types and to denote an explicit syntax
/// for match expressions that return a value.
///
/// \tparam ...Ts
/// \tparam ...Fs
/// \param var type: const std::variant<Ts...>&
/// \param m type: const match<Fs...>&
/// \return decltype(auto)
template<typename... Ts, typename... Fs>
constexpr auto
operator<< (
const std::variant<Ts...>& var,
const match<Fs...>& m
) -> decltype(auto)
{ return std::visit(m, var); }

/// \brief Wildcard Empty Placeholder
///
/// \details Names a placeholder type
/// that can match to anything in a match
/// expression. The value is un-retrievable
/// from `_`, use `auto&& <name>` to have a
/// from `bottom`, use `auto&& <var-name>` to have a
/// retrievable value.
struct _
struct match_any
{
template<typename T>
explicit constexpr _([[maybe_unused]] T&& t) noexcept { }
explicit constexpr match_any([[maybe_unused]] T&& t) noexcept { }

explicit constexpr _(const _&) noexcept = delete;
explicit constexpr _(_&&) noexcept = delete;
explicit constexpr match_any(const match_any&) noexcept = delete;
explicit constexpr match_any(match_any&&) noexcept = delete;

constexpr auto
operator= (const _&) noexcept -> _& = delete;
operator= (const match_any&) noexcept -> match_any& = delete;

constexpr auto
operator= (_&&) noexcept -> _& = delete;
operator= (match_any&&) noexcept -> match_any& = delete;

constexpr ~_() noexcept = delete;
constexpr ~match_any() noexcept = delete;
};

} // namespace cxl
} // namespace cxl::utils

#endif /// CORTEX_MATCH
68 changes: 37 additions & 31 deletions src/utils/match.test.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -8,43 +8,48 @@
#include <utils/match.hxx>
#include <variant>

TEST_CASE("match test") {
TEST_CASE("match test")
{
using namespace std::literals;
using var_t = std::variant<int, float, std::string>;

auto var7 { var_t { 7 } };
auto varstr { var_t { "Hello"s } };
auto varf { var_t { 6.78f } };

SECTION("match with std::visit") {
std::visit(cxl::utils::match {
[](std::string& s) {
fmt::print("s: std::string = {}\n", s);
},
[](int& i) { fmt::print("i: int = {}\n", i); },
[](float& f) { fmt::print("s: float = {}\n", f); },
[](cxl::_) { fmt::print("Other\n"); } },
var7);
SECTION("match with std::visit")
{
std::visit(
cxl::utils::match {
[](std::string& s) { fmt::print("s: std::string = {}\n", s); },
[](int& i) { fmt::print("i: int = {}\n", i); },
[](float& f) { fmt::print("s: float = {}\n", f); },
[](cxl::utils::match_any) { fmt::print("Other\n"); }
},
var7
);

auto r = std::visit(
cxl::utils::match {
[]([[maybe_unused]] std::string& s) { return "String"s; },
[]([[maybe_unused]] int& i) { return "Int"s; },
[]([[maybe_unused]] float& f) { return "Float"s; },
[](cxl::_) { return "Other"s; } },
varf);
[](cxl::utils::match_any) { return "Other"s; } },
varf
);

REQUIRE(r == "Float"s);

fmt::print("r = {}\n", r);
}

SECTION("match expression syntax sugar") {
SECTION("match expression syntax sugar")
{
auto print_match = cxl::utils::match {
[](std::string s) { fmt::print("s: std::string = {}\n", s); },
[](int i) { fmt::print("i: int = {}\n", i); },
[](float f) { fmt::print("s: float = {}\n", f); },
[](cxl::_) { fmt::print("Other\n"); }
[](cxl::utils::match_any) { fmt::print("Other\n"); }
};

using namespace cxl;
Expand All @@ -53,37 +58,38 @@ TEST_CASE("match test") {
[]([[maybe_unused]] std::string s) { return "String"s; },
[]([[maybe_unused]] int i) { return "Int"s; },
[]([[maybe_unused]] float f) { return "Float"s; },
[](_) { return "Other"s; }
[](utils::match_any) { return "Other"s; }
};

SECTION("Regular match expression") {
varstr |
cxl::utils::match {
[](std::string s) {
fmt::print("s: std::string = {}\n", s);
},
[](int i) { fmt::print("i: int = {}\n", i); },
[](float f) { fmt::print("s: float = {}\n", f); },
[](cxl::_) { fmt::print("Other\n"); }
};
SECTION("Regular match expression")
{
varstr >> cxl::utils::match {
[](std::string s) { fmt::print("s: std::string = {}\n", s); },
[](int i) { fmt::print("i: int = {}\n", i); },
[](float f) { fmt::print("s: float = {}\n", f); },
[](cxl::utils::match_any) { fmt::print("Other\n"); }
};

varf | print_match;
varf >> print_match;

REQUIRE(std::is_void_v<decltype(var7 | name_match)> == true);
REQUIRE(std::is_void_v<decltype(var7 >> name_match)> == true);
}

SECTION("Returning match expression") {
auto r = varstr |= cxl::utils::match {
SECTION("Returning match expression")
{
auto r = varstr << cxl::utils::match {
[]([[maybe_unused]] std::string s) { return "String"s; },
[]([[maybe_unused]] int i) { return "Int"s; },
[]([[maybe_unused]] float f) { return "Float"s; },
[](cxl::_) { return "Other"s; }
[](cxl::utils::match_any) { return "Other"s; }
};

fmt::print("r = {}\n", r);
REQUIRE(r == "String");

auto name = var7 |= name_match;
auto name = var7 << name_match;
fmt::print("name = {}\n", name);
REQUIRE(name == "Int");
}
}
}