Skip to content

Commit

Permalink
cetl::pf17::overloadedcetl::overloaded (#131)
Browse files Browse the repository at this point in the history
See issue #130
  • Loading branch information
serges147 authored Jul 3, 2024
1 parent eff484c commit 6c279ce
Show file tree
Hide file tree
Showing 3 changed files with 68 additions and 51 deletions.
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

0 comments on commit 6c279ce

Please sign in to comment.