diff options
author | Nat Goodspeed <nat@lindenlab.com> | 2022-12-19 16:29:06 -0500 |
---|---|---|
committer | Nat Goodspeed <nat@lindenlab.com> | 2023-07-13 12:34:12 -0400 |
commit | c682603417e1ef8290aacf274ff49821bd204c0b (patch) | |
tree | 54d245703df0cd6ef1f59b8607b70b2bcd3ebeea /indra/llcommon/llsdutil.h | |
parent | 324f0d9b8abad3a74a7c19a6e28f8c77c76b3b83 (diff) |
DRTVWR-558: Extend LL::apply() to LLSD array arguments.
Make apply(function, std::array) and apply(function, std::vector) available
even when we borrow the C++17 implementation of apply(function, std::tuple).
Add apply(function, LLSD) with interpretations:
* isUndefined() is treated as an empty array, for calling a nullary function
* scalar LLSD is treated as a single-entry array, for calling a unary function
* isArray() converts function parameters using LLSDParam
* isMap() is an error.
Add unit tests for all flavors of LL::apply().
(cherry picked from commit 3006c24251c6259d00df9e0f4f66b8a617e6026d)
Diffstat (limited to 'indra/llcommon/llsdutil.h')
-rw-r--r-- | indra/llcommon/llsdutil.h | 81 |
1 files changed, 80 insertions, 1 deletions
diff --git a/indra/llcommon/llsdutil.h b/indra/llcommon/llsdutil.h index 372278c51a..eaf8825791 100644 --- a/indra/llcommon/llsdutil.h +++ b/indra/llcommon/llsdutil.h @@ -31,6 +31,7 @@ #include "llsd.h" #include <boost/functional/hash.hpp> +#include <cassert> // U32 LL_COMMON_API LLSD ll_sd_from_U32(const U32); @@ -333,6 +334,31 @@ private: }; /** + * LLSDParam<LLSD> is for when you don't already have the target parameter + * type in hand. Instantiate LLSDParam<LLSD>(your LLSD object), and the + * templated conversion operator will try to select a more specific LLSDParam + * specialization. + */ +template <> +class LLSDParam<LLSD> +{ +private: + LLSD value_; + +public: + LLSDParam(const LLSD& value): value_(value) {} + + /// if we're literally being asked for an LLSD parameter, avoid infinite + /// recursion + operator LLSD() const { return value_; } + + /// otherwise, instantiate a more specific LLSDParam<T> to convert; that + /// preserves the existing customization mechanism + template <typename T> + operator T() const { return LLSDParam<T>(value_); } +}; + +/** * Turns out that several target types could accept an LLSD param using any of * a few different conversions, e.g. LLUUID's constructor can accept LLUUID or * std::string. Therefore, the compiler can't decide which LLSD conversion @@ -350,7 +376,7 @@ class LLSDParam<T> \ { \ public: \ LLSDParam(const LLSD& value): \ - _value((T)value.AS()) \ + _value((T)value.AS()) \ {} \ \ operator T() const { return _value; } \ @@ -555,4 +581,57 @@ struct hash<LLSD> } }; } + +namespace LL +{ + +/***************************************************************************** +* apply(function, LLSD array) +*****************************************************************************/ +// Derived from https://stackoverflow.com/a/20441189 +// and https://en.cppreference.com/w/cpp/utility/apply . +// We can't simply make a tuple from the LLSD array and then apply() that +// tuple to the function -- how would make_tuple() deduce the correct +// parameter type for each entry? We must go directly to the target function. +template <typename CALLABLE, std::size_t... I> +auto apply_impl(CALLABLE&& func, const LLSD& array, std::index_sequence<I...>) +{ + // call func(unpacked args), using generic LLSDParam<LLSD> to convert each + // entry in 'array' to the target parameter type + return std::forward<CALLABLE>(func)(LLSDParam<LLSD>(array[I])...); +} + +template <typename CALLABLE> +auto apply(CALLABLE&& func, const LLSD& args) +{ + LLSD array; + constexpr auto arity = boost::function_traits<CALLABLE>::arity; + // LLSD supports a number of types, two of which are aggregates: Map and + // Array. We don't try to support Map: supporting Map would seem to + // promise that we could somehow match the string key to 'func's parameter + // names. Uh sorry, maybe in some future version of C++ with reflection. + assert(! args.isMap()); + // We expect an LLSD array, but what the heck, treat isUndefined() as a + // zero-length array for calling a nullary 'func'. + if (args.isUndefined() || args.isArray()) + { + // this works because LLSD().size() == 0 + assert(args.size() == arity); + array = args; + } + else // args is one of the scalar types + { + // scalar_LLSD.size() == 0, so don't test that here. + // You can pass a scalar LLSD only to a unary 'func'. + assert(arity == 1); + // make an array of it + array = llsd::array(args); + } + return apply_impl(std::forward<CALLABLE>(func), + array, + std::make_index_sequence<arity>()); +} + +} // namespace LL + #endif // LL_LLSDUTIL_H |