116 lines
3.6 KiB
C++
116 lines
3.6 KiB
C++
/**
|
|
* @file apply.h
|
|
* @author Nat Goodspeed
|
|
* @date 2022-06-18
|
|
* @brief C++14 version of std::apply()
|
|
*
|
|
* $LicenseInfo:firstyear=2022&license=viewerlgpl$
|
|
* Copyright (c) 2022, Linden Research, Inc.
|
|
* $/LicenseInfo$
|
|
*/
|
|
|
|
#if ! defined(LL_APPLY_H)
|
|
#define LL_APPLY_H
|
|
|
|
#include <boost/type_traits/function_traits.hpp>
|
|
#include <tuple>
|
|
|
|
namespace LL
|
|
{
|
|
|
|
/**
|
|
* USAGE NOTE:
|
|
* https://stackoverflow.com/a/40523474/5533635
|
|
*
|
|
* If you're trying to pass apply() a variadic function, the compiler
|
|
* complains that it can't deduce the callable type, presumably because it
|
|
* doesn't know which arity to reify to pass.
|
|
*
|
|
* But it works to wrap the variadic function in a generic lambda, e.g.:
|
|
*
|
|
* @CODE
|
|
* LL::apply(
|
|
* [](auto&&... args)
|
|
* {
|
|
* return variadic(std::forward<decltype(args)>(args)...);
|
|
* },
|
|
* args);
|
|
* @ENDCODE
|
|
*
|
|
* Presumably this is because there's only one instance of the generic lambda
|
|
* @em type, with a variadic <tt>operator()()</tt>.
|
|
*
|
|
* It's pointless to provide a wrapper @em function that implicitly supplies
|
|
* the generic lambda. You couldn't pass your variadic function to our wrapper
|
|
* function, for the same original reason!
|
|
*
|
|
* Instead we provide a wrapper @em macro. Sorry, Dr. Stroustrup.
|
|
*/
|
|
#define VAPPLY(FUNC, ARGS) \
|
|
LL::apply( \
|
|
[](auto&&... args) \
|
|
{ \
|
|
return (FUNC)(std::forward<decltype(args)>(args)...); \
|
|
}, \
|
|
(ARGS))
|
|
|
|
#if __cplusplus >= 201703L
|
|
|
|
// C++17 implementation
|
|
using std::apply;
|
|
|
|
#else // C++14
|
|
|
|
// Derived from https://stackoverflow.com/a/20441189
|
|
// and https://en.cppreference.com/w/cpp/utility/apply
|
|
template <typename CALLABLE, typename TUPLE, std::size_t... I>
|
|
auto apply_impl(CALLABLE&& func, TUPLE&& args, std::index_sequence<I...>)
|
|
{
|
|
// call func(unpacked args)
|
|
return std::forward<CALLABLE>(func)(std::move(std::get<I>(args))...);
|
|
}
|
|
|
|
template <typename CALLABLE, typename... ARGS>
|
|
auto apply(CALLABLE&& func, const std::tuple<ARGS...>& args)
|
|
{
|
|
// std::index_sequence_for is the magic sauce here, generating an argument
|
|
// pack of indexes for each entry in args. apply_impl() can then pass
|
|
// those to std::get() to unpack args into individual arguments.
|
|
return apply_impl(std::forward<CALLABLE>(func),
|
|
args,
|
|
std::index_sequence_for<ARGS...>{});
|
|
}
|
|
|
|
// per https://stackoverflow.com/a/57510428/5533635
|
|
template <typename CALLABLE, typename T, size_t SIZE>
|
|
auto apply(CALLABLE&& func, const std::array<T, SIZE>& args)
|
|
{
|
|
return apply(std::forward<CALLABLE>(func), std::tuple_cat(args));
|
|
}
|
|
|
|
// per https://stackoverflow.com/a/28411055/5533635
|
|
template <typename CALLABLE, typename T, std::size_t... I>
|
|
auto apply_impl(CALLABLE&& func, const std::vector<T>& args, std::index_sequence<I...>)
|
|
{
|
|
return apply_impl(std::forward<CALLABLE>(func),
|
|
std::make_tuple(std::forward<T>(args[I])...),
|
|
I...);
|
|
}
|
|
|
|
// this goes beyond C++17 std::apply()
|
|
template <typename CALLABLE, typename T>
|
|
auto apply(CALLABLE&& func, const std::vector<T>& args)
|
|
{
|
|
constexpr auto arity = boost::function_traits<CALLABLE>::arity;
|
|
assert(args.size() == arity);
|
|
return apply_impl(std::forward<CALLABLE>(func),
|
|
args,
|
|
std::make_index_sequence<arity>());
|
|
}
|
|
|
|
#endif // C++14
|
|
|
|
} // namespace LL
|
|
|
|
#endif /* ! defined(LL_APPLY_H) */
|