/** * @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 #include 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(args)...); * }, * args); * @ENDCODE * * Presumably this is because there's only one instance of the generic lambda * @em type, with a variadic operator()(). * * 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(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 auto apply_impl(CALLABLE&& func, TUPLE&& args, std::index_sequence) { // call func(unpacked args) return std::forward(func)(std::move(std::get(args))...); } template auto apply(CALLABLE&& func, const std::tuple& 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(func), args, std::index_sequence_for{}); } // per https://stackoverflow.com/a/57510428/5533635 template auto apply(CALLABLE&& func, const std::array& args) { return apply(std::forward(func), std::tuple_cat(args)); } // per https://stackoverflow.com/a/28411055/5533635 template auto apply_impl(CALLABLE&& func, const std::vector& args, std::index_sequence) { return apply_impl(std::forward(func), std::make_tuple(std::forward(args[I])...), I...); } // this goes beyond C++17 std::apply() template auto apply(CALLABLE&& func, const std::vector& args) { constexpr auto arity = boost::function_traits::arity; assert(args.size() == arity); return apply_impl(std::forward(func), args, std::make_index_sequence()); } #endif // C++14 } // namespace LL #endif /* ! defined(LL_APPLY_H) */