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

cetl::pf17::overloadedcetl::overloaded #131

Merged
merged 1 commit into from
Jul 3, 2024
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
7 changes: 4 additions & 3 deletions cetlvast/suites/unittest/test_pf17_variant_other.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
// CSpell: words chronomorphize

#include <cetl/pf17/variant.hpp> // The tested header always goes first.
#include <cetl/visit_helpers.hpp>
#include "test_pf17_variant.hpp"
#include <cetlvast/helpers.hpp>

Expand Down Expand Up @@ -675,7 +676,7 @@ TEST(test_variant, basic_operations)
using cetl::pf17::holds_alternative;
using cetl::pf17::get;
using cetl::pf17::get_if;
using cetl::pf17::make_overloaded;
using cetl::make_overloaded;
using cetl::pf17::in_place_index;

variant<int, char, monostate> var;
Expand Down Expand Up @@ -818,7 +819,7 @@ TEST(test_variant, visit)
using cetl::pf17::get;
using cetl::pf17::visit;
using cetl::pf17::monostate;
using cetl::pf17::make_overloaded;
using cetl::make_overloaded;
#if __cpp_exceptions
using cetl::pf17::bad_variant_access;
#endif
Expand Down Expand Up @@ -898,7 +899,7 @@ TEST(test_variant, visit)

// Constexpr visitation is not possible in C++14.
#if __cplusplus >= 201703L
using cetl::pf17::make_overloaded;
using cetl::make_overloaded;
using cetl::pf17::in_place_type;
static_assert(1110 == visit(make_overloaded([](const std::int8_t a,
const float b) { return a + static_cast<std::int64_t>(b); },
Expand Down
48 changes: 0 additions & 48 deletions include/cetl/pf17/utility.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -55,54 +55,6 @@ constexpr in_place_index_t<I> in_place_index{};

// --------------------------------------------------------------------------------------------

/// This is a helper for use with \ref visit that uses standard overload resolution to pick the best overload
/// among a set of lambdas given by the user. Unfortunately, in C++14 we have to use it with a factory function;
/// please see \ref make_overloaded for details. In C++17 this can be used without the factory in a much simpler way.
///
/// This function is not found in the C++ standard library, but is a common extension in the wild.
template <typename...>
struct overloaded;
template <typename T>
struct overloaded<T> : public T
{
using T::operator();
// SFINAE is needed to ensure this constructor does not hide the copy/move constructors.
template <typename A, std::enable_if_t<!std::is_same<overloaded<T>, std::decay_t<A>>::value, int> = 0>
constexpr explicit overloaded(A&& arg)
: T(std::forward<A>(arg))
{
}
};
template <typename T, typename... Ts>
struct overloaded<T, Ts...> : public T, public overloaded<Ts...>
{
using T::operator();
using overloaded<Ts...>::operator();
// If B were empty, the ctor would need sfinae to avoid hiding the copy/move ctors; ensure this is not so.
template <typename A, typename... B, std::enable_if_t<(sizeof...(B) > 0), int> = 0>
constexpr explicit overloaded(A&& a, B&&... b)
: T(std::forward<A>(a))
, overloaded<B...>(std::forward<B>(b)...)
{
}
};

/// Returns an instance of \ref overloaded that can be used with \ref visit. The usage is as follows:
/// @code
/// visit(make_overloaded(
/// [](const auto&) { return "fallback"; },
/// [](double) { return "double"; },
/// [](const std::string&) { return "string"; }
/// ), variant);
/// @endcode
template <typename... Ts>
constexpr overloaded<Ts...> make_overloaded(Ts&&... ts)
{
return overloaded<Ts...>(std::forward<Ts>(ts)...);
}

// --------------------------------------------------------------------------------------------

// Non-standard extensions for internal use.
// We keep the definitions here to make it clear they are designed to detect the above-defined types, not std:: ones.
namespace detail
Expand Down
64 changes: 64 additions & 0 deletions include/cetl/visit_helpers.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
/// @file
/// Contains helpers for building visitors for the `cetl::visit` invocation.
///
/// @copyright
/// Copyright (C) OpenCyphal Development Team <opencyphal.org>
/// Copyright Amazon.com Inc. or its affiliates.
/// SPDX-License-Identifier: MIT

#ifndef CETL_VISIT_HELPERS_HPP_INCLUDED
#define CETL_VISIT_HELPERS_HPP_INCLUDED

#include <type_traits>

namespace cetl
{
/// This is a helper for use with \ref visit that uses standard overload resolution to pick the best overload
/// among a set of lambdas given by the user. Unfortunately, in C++14 we have to use it with a factory function;
/// please see \ref make_overloaded for details. In C++17 this can be used without the factory in a much simpler way.
///
/// This function is not found in the C++ standard library, but is a common extension in the wild.
template <typename...>
struct overloaded;
template <typename T>
struct overloaded<T> : public T
{
using T::operator();
// SFINAE is needed to ensure this constructor does not hide the copy/move constructors.
template <typename A, std::enable_if_t<!std::is_same<overloaded<T>, std::decay_t<A>>::value, int> = 0>
constexpr explicit overloaded(A&& arg)
: T(std::forward<A>(arg))
{
}
};
template <typename T, typename... Ts>
struct overloaded<T, Ts...> : public T, public overloaded<Ts...>
{
using T::operator();
using overloaded<Ts...>::operator();
// If B were empty, the ctor would need sfinae to avoid hiding the copy/move ctors; ensure this is not so.
template <typename A, typename... B, std::enable_if_t<(sizeof...(B) > 0), int> = 0>
constexpr explicit overloaded(A&& a, B&&... b)
: T(std::forward<A>(a))
, overloaded<B...>(std::forward<B>(b)...)
{
}
};

/// Returns an instance of \ref overloaded that can be used with \ref visit. The usage is as follows:
/// @code
/// visit(make_overloaded(
/// [](const auto&) { return "fallback"; },
/// [](double) { return "double"; },
/// [](const std::string&) { return "string"; }
/// ), variant);
/// @endcode
template <typename... Ts>
constexpr overloaded<Ts...> make_overloaded(Ts&&... ts)
{
return overloaded<Ts...>(std::forward<Ts>(ts)...);
}

} // namespace cetl

#endif // CETL_VISIT_HELPERS_HPP_INCLUDED
Loading