diff options
author | Ansariel <ansariel.hiller@phoenixviewer.com> | 2024-05-22 19:04:52 +0200 |
---|---|---|
committer | Ansariel <ansariel.hiller@phoenixviewer.com> | 2024-05-22 19:04:52 +0200 |
commit | 1b67dd855c41f5a0cda7ec2a68d98071986ca703 (patch) | |
tree | ab243607f74f78200787bba5b9b88f07ef1b966f /indra/llcommon/llsdutil.h | |
parent | 6d6eabca44d08d5b97bfe3e941d2b9687c2246ea (diff) | |
parent | e1623bb276f83a43ce7a197e388720c05bdefe61 (diff) |
Merge remote-tracking branch 'origin/main' into DRTVWR-600-maint-A
# Conflicts:
# autobuild.xml
# indra/cmake/CMakeLists.txt
# indra/cmake/GoogleMock.cmake
# indra/llaudio/llaudioengine_fmodstudio.cpp
# indra/llaudio/llaudioengine_fmodstudio.h
# indra/llaudio/lllistener_fmodstudio.cpp
# indra/llaudio/lllistener_fmodstudio.h
# indra/llaudio/llstreamingaudio_fmodstudio.cpp
# indra/llaudio/llstreamingaudio_fmodstudio.h
# indra/llcharacter/llmultigesture.cpp
# indra/llcharacter/llmultigesture.h
# indra/llimage/llimage.cpp
# indra/llimage/llimagepng.cpp
# indra/llimage/llimageworker.cpp
# indra/llimage/tests/llimageworker_test.cpp
# indra/llmessage/tests/llmockhttpclient.h
# indra/llprimitive/llgltfmaterial.h
# indra/llrender/llfontfreetype.cpp
# indra/llui/llcombobox.cpp
# indra/llui/llfolderview.cpp
# indra/llui/llfolderviewmodel.h
# indra/llui/lllineeditor.cpp
# indra/llui/lllineeditor.h
# indra/llui/lltextbase.cpp
# indra/llui/lltextbase.h
# indra/llui/lltexteditor.cpp
# indra/llui/lltextvalidate.cpp
# indra/llui/lltextvalidate.h
# indra/llui/lluictrl.h
# indra/llui/llview.cpp
# indra/llwindow/llwindowmacosx.cpp
# indra/newview/app_settings/settings.xml
# indra/newview/llappearancemgr.cpp
# indra/newview/llappearancemgr.h
# indra/newview/llavatarpropertiesprocessor.cpp
# indra/newview/llavatarpropertiesprocessor.h
# indra/newview/llbreadcrumbview.cpp
# indra/newview/llbreadcrumbview.h
# indra/newview/llbreastmotion.cpp
# indra/newview/llbreastmotion.h
# indra/newview/llconversationmodel.h
# indra/newview/lldensityctrl.cpp
# indra/newview/lldensityctrl.h
# indra/newview/llface.inl
# indra/newview/llfloatereditsky.cpp
# indra/newview/llfloatereditwater.cpp
# indra/newview/llfloateremojipicker.h
# indra/newview/llfloaterimsessiontab.cpp
# indra/newview/llfloaterprofiletexture.cpp
# indra/newview/llfloaterprofiletexture.h
# indra/newview/llgesturemgr.cpp
# indra/newview/llgesturemgr.h
# indra/newview/llimpanel.cpp
# indra/newview/llimpanel.h
# indra/newview/llinventorybridge.cpp
# indra/newview/llinventorybridge.h
# indra/newview/llinventoryclipboard.cpp
# indra/newview/llinventoryclipboard.h
# indra/newview/llinventoryfunctions.cpp
# indra/newview/llinventoryfunctions.h
# indra/newview/llinventorygallery.cpp
# indra/newview/lllistbrowser.cpp
# indra/newview/lllistbrowser.h
# indra/newview/llpanelobjectinventory.cpp
# indra/newview/llpanelprofile.cpp
# indra/newview/llpanelprofile.h
# indra/newview/llpreviewgesture.cpp
# indra/newview/llsavedsettingsglue.cpp
# indra/newview/llsavedsettingsglue.h
# indra/newview/lltooldraganddrop.cpp
# indra/newview/llurllineeditorctrl.cpp
# indra/newview/llvectorperfoptions.cpp
# indra/newview/llvectorperfoptions.h
# indra/newview/llviewerparceloverlay.cpp
# indra/newview/llviewertexlayer.cpp
# indra/newview/llviewertexturelist.cpp
# indra/newview/macmain.h
# indra/test/test.cpp
Diffstat (limited to 'indra/llcommon/llsdutil.h')
-rw-r--r-- | indra/llcommon/llsdutil.h | 1348 |
1 files changed, 674 insertions, 674 deletions
diff --git a/indra/llcommon/llsdutil.h b/indra/llcommon/llsdutil.h index b7afa59d79..aa234e2f62 100644 --- a/indra/llcommon/llsdutil.h +++ b/indra/llcommon/llsdutil.h @@ -1,674 +1,674 @@ -/** - * @file llsdutil.h - * @author Phoenix - * @date 2006-05-24 - * @brief Utility classes, functions, etc, for using structured data. - * - * $LicenseInfo:firstyear=2006&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 of the License only. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA - * $/LicenseInfo$ - */ - -#ifndef LL_LLSDUTIL_H -#define LL_LLSDUTIL_H - -#include "apply.h" // LL::invoke() -#include "function_types.h" // LL::function_arity -#include "llsd.h" -#include <boost/functional/hash.hpp> -#include <cassert> -#include <memory> // std::shared_ptr -#include <type_traits> -#include <vector> - -// U32 -LL_COMMON_API LLSD ll_sd_from_U32(const U32); -LL_COMMON_API U32 ll_U32_from_sd(const LLSD& sd); - -// U64 -LL_COMMON_API LLSD ll_sd_from_U64(const U64); -LL_COMMON_API U64 ll_U64_from_sd(const LLSD& sd); - -// IP Address -LL_COMMON_API LLSD ll_sd_from_ipaddr(const U32); -LL_COMMON_API U32 ll_ipaddr_from_sd(const LLSD& sd); - -// Binary to string -LL_COMMON_API LLSD ll_string_from_binary(const LLSD& sd); - -//String to binary -LL_COMMON_API LLSD ll_binary_from_string(const LLSD& sd); - -// Serializes sd to static buffer and returns pointer, useful for gdb debugging. -LL_COMMON_API char* ll_print_sd(const LLSD& sd); - -// Serializes sd to static buffer and returns pointer, using "pretty printing" mode. -LL_COMMON_API char* ll_pretty_print_sd_ptr(const LLSD* sd); -LL_COMMON_API char* ll_pretty_print_sd(const LLSD& sd); - -LL_COMMON_API std::string ll_stream_notation_sd(const LLSD& sd); - -//compares the structure of an LLSD to a template LLSD and stores the -//"valid" values in a 3rd LLSD. Default values -//are pulled from the template. Extra keys/values in the test -//are ignored in the resultant LLSD. Ordering of arrays matters -//Returns false if the test is of same type but values differ in type -//Otherwise, returns true - -LL_COMMON_API bool compare_llsd_with_template( - const LLSD& llsd_to_test, - const LLSD& template_llsd, - LLSD& resultant_llsd); - -// filter_llsd_with_template() is a direct clone (copy-n-paste) of -// compare_llsd_with_template with the following differences: -// (1) bool vs BOOL return types -// (2) A map with the key value "*" is a special value and maps any key in the -// test llsd that doesn't have an explicitly matching key in the template. -// (3) The element of an array with exactly one element is taken as a template -// for *all* the elements of the test array. If the template array is of -// different size, compare_llsd_with_template() semantics apply. -bool filter_llsd_with_template( - const LLSD & llsd_to_test, - const LLSD & template_llsd, - LLSD & resultant_llsd); - -/** - * Recursively determine whether a given LLSD data block "matches" another - * LLSD prototype. The returned string is empty() on success, non-empty() on - * mismatch. - * - * This function tests structure (types) rather than data values. It is - * intended for when a consumer expects an LLSD block with a particular - * structure, and must succinctly detect whether the arriving block is - * well-formed. For instance, a test of the form: - * @code - * if (! (data.has("request") && data.has("target") && data.has("modifier") ...)) - * @endcode - * could instead be expressed by initializing a prototype LLSD map with the - * required keys and writing: - * @code - * if (! llsd_matches(prototype, data).empty()) - * @endcode - * - * A non-empty return value is an error-message fragment intended to indicate - * to (English-speaking) developers where in the prototype structure the - * mismatch occurred. - * - * * If a slot in the prototype isUndefined(), then anything is valid at that - * place in the real object. (Passing prototype == LLSD() matches anything - * at all.) - * * An array in the prototype must match a data array at least that large. - * (Additional entries in the data array are ignored.) Every isDefined() - * entry in the prototype array must match the corresponding entry in the - * data array. - * * A map in the prototype must match a map in the data. Every key in the - * prototype map must match a corresponding key in the data map. (Additional - * keys in the data map are ignored.) Every isDefined() value in the - * prototype map must match the corresponding key's value in the data map. - * * Scalar values in the prototype are tested for @em type rather than value. - * For instance, a String in the prototype matches any String at all. In - * effect, storing an Integer at a particular place in the prototype asserts - * that the caller intends to apply asInteger() to the corresponding slot in - * the data. - * * A String in the prototype matches String, Boolean, Integer, Real, UUID, - * Date and URI, because asString() applied to any of these produces a - * meaningful result. - * * Similarly, a Boolean, Integer or Real in the prototype can match any of - * Boolean, Integer or Real in the data -- or even String. - * * UUID matches UUID or String. - * * Date matches Date or String. - * * URI matches URI or String. - * * Binary in the prototype matches only Binary in the data. - * - * @TODO: when a Boolean, Integer or Real in the prototype matches a String in - * the data, we should examine the String @em value to ensure it can be - * meaningfully converted to the requested type. The same goes for UUID, Date - * and URI. - */ -LL_COMMON_API std::string llsd_matches(const LLSD& prototype, const LLSD& data, const std::string& pfx=""); - -/// Deep equality. If you want to compare LLSD::Real values for approximate -/// equality rather than bitwise equality, pass @a bits as for -/// is_approx_equal_fraction(). -LL_COMMON_API bool llsd_equals(const LLSD& lhs, const LLSD& rhs, int bits=-1); -/// If you don't care about LLSD::Real equality -inline bool operator==(const LLSD& lhs, const LLSD& rhs) -{ - return llsd_equals(lhs, rhs); -} -inline bool operator!=(const LLSD& lhs, const LLSD& rhs) -{ - // operator!=() should always be the negation of operator==() - return ! (lhs == rhs); -} - -// Simple function to copy data out of input & output iterators if -// there is no need for casting. -template<typename Input> LLSD llsd_copy_array(Input iter, Input end) -{ - LLSD dest; - for (; iter != end; ++iter) - { - dest.append(*iter); - } - return dest; -} - -namespace llsd -{ - -/** - * Drill down to locate an element in 'blob' according to 'path', where 'path' - * is one of the following: - * - * - LLSD::String: 'blob' is an LLSD::Map. Find the entry with key 'path'. - * - LLSD::Integer: 'blob' is an LLSD::Array. Find the entry with index 'path'. - * - Any other 'path' type will be interpreted as LLSD::Array, and 'blob' is a - * nested structure. For each element of 'path': - * - If it's an LLSD::Integer, select the entry with that index from an - * LLSD::Array at that level. - * - If it's an LLSD::String, select the entry with that key from an - * LLSD::Map at that level. - * - Anything else is an error. - * - * By implication, if path.isUndefined() or otherwise equivalent to an empty - * LLSD::Array, drill[_ref]() returns 'blob' as is. - */ -LLSD drill(const LLSD& blob, const LLSD& path); -LLSD& drill_ref( LLSD& blob, const LLSD& path); - -} - -namespace llsd -{ - -/** - * Construct an LLSD::Array inline, using modern C++ variadic arguments. - */ - -// recursion tail -inline -void array_(LLSD&) {} - -// recursive call -template <typename T0, typename... Ts> -void array_(LLSD& data, T0&& v0, Ts&&... vs) -{ - data.append(std::forward<T0>(v0)); - array_(data, std::forward<Ts>(vs)...); -} - -// public interface -template <typename... Ts> -LLSD array(Ts&&... vs) -{ - LLSD data; - array_(data, std::forward<Ts>(vs)...); - return data; -} - -} // namespace llsd - -/***************************************************************************** -* LLSDMap -*****************************************************************************/ -/** - * Construct an LLSD::Map inline, with implicit conversion to LLSD. Usage: - * - * @code - * void somefunc(const LLSD&); - * ... - * somefunc(LLSDMap("alpha", "abc")("number", 17)("pi", 3.14)); - * @endcode - * - * For completeness, LLSDMap() with no args constructs an empty map, so - * <tt>LLSDMap()("alpha", "abc")("number", 17)("pi", 3.14)</tt> produces a map - * equivalent to the above. But for most purposes, LLSD() is already - * equivalent to an empty map, and if you explicitly want an empty isMap(), - * there's LLSD::emptyMap(). However, supporting a no-args LLSDMap() - * constructor follows the principle of least astonishment. - */ -class LLSDMap -{ -public: - LLSDMap(): - _data(LLSD::emptyMap()) - {} - LLSDMap(const LLSD::String& key, const LLSD& value): - _data(LLSD::emptyMap()) - { - _data[key] = value; - } - - LLSDMap& operator()(const LLSD::String& key, const LLSD& value) - { - _data[key] = value; - return *this; - } - - operator LLSD() const { return _data; } - LLSD get() const { return _data; } - -private: - LLSD _data; -}; - -namespace llsd -{ - -/** - * Construct an LLSD::Map inline, using modern C++ variadic arguments. - */ - -// recursion tail -inline -void map_(LLSD&) {} - -// recursive call -template <typename T0, typename... Ts> -void map_(LLSD& data, const LLSD::String& k0, T0&& v0, Ts&&... vs) -{ - data[k0] = v0; - map_(data, std::forward<Ts>(vs)...); -} - -// public interface -template <typename... Ts> -LLSD map(Ts&&... vs) -{ - LLSD data; - map_(data, std::forward<Ts>(vs)...); - return data; -} - -} // namespace llsd - -/***************************************************************************** -* LLSDParam -*****************************************************************************/ -struct LLSDParamBase -{ - virtual ~LLSDParamBase() {} -}; - -/** - * LLSDParam is a customization point for passing LLSD values to function - * parameters of more or less arbitrary type. LLSD provides a small set of - * native conversions; but if a generic algorithm explicitly constructs an - * LLSDParam object in the function's argument list, a consumer can provide - * LLSDParam specializations to support more different parameter types than - * LLSD's native conversions. - * - * Usage: - * - * @code - * void somefunc(const paramtype&); - * ... - * somefunc(..., LLSDParam<paramtype>(someLLSD), ...); - * @endcode - */ -template <typename T> -class LLSDParam: public LLSDParamBase -{ -public: - /** - * Default implementation converts to T on construction, saves converted - * value for later retrieval - */ - LLSDParam(const LLSD& value): - value_(value) - {} - - operator T() const { return value_; } - -private: - T value_; -}; - -/** - * 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>: public LLSDParamBase -{ -private: - LLSD value_; - // LLSDParam<LLSD>::operator T() works by instantiating an LLSDParam<T> on - // demand. Returning that engages LLSDParam<T>::operator T(), producing - // the desired result. But LLSDParam<const char*> owns a std::string whose - // c_str() is returned by its operator const char*(). If we return a temp - // LLSDParam<const char*>, the compiler can destroy it right away, as soon - // as we've called operator const char*(). That's a problem! That - // invalidates the const char* we've just passed to the subject function. - // This LLSDParam<LLSD> is presumably guaranteed to survive until the - // subject function has returned, so we must ensure that any constructed - // LLSDParam<T> lives just as long as this LLSDParam<LLSD> does. Putting - // each LLSDParam<T> on the heap and capturing a smart pointer in a vector - // works. We would have liked to use std::unique_ptr, but vector entries - // must be copyable. - // (Alternatively we could assume that every instance of LLSDParam<LLSD> - // will be asked for at most ONE conversion. We could store a scalar - // std::unique_ptr and, when constructing an new LLSDParam<T>, assert that - // the unique_ptr is empty. But some future change in usage patterns, and - // consequent failure of that assertion, would be very mysterious. Instead - // of explaining how to fix it, just fix it now.) - mutable std::vector<std::shared_ptr<LLSDParamBase>> converters_; - -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 - { - // capture 'ptr' with the specific subclass type because converters_ - // only stores LLSDParamBase pointers - auto ptr{ std::make_shared<LLSDParam<std::decay_t<T>>>(value_) }; - // keep the new converter alive until we ourselves are destroyed - converters_.push_back(ptr); - return *ptr; - } -}; - -/** - * 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 - * operator to choose, even though to us it seems obvious. But that's okay, we - * can specialize LLSDParam for such target types, explicitly specifying the - * desired conversion -- that's part of what LLSDParam is all about. Turns out - * we have to do that enough to make it worthwhile generalizing. Use a macro - * because I need to specify one of the asReal, etc., explicit conversion - * methods as well as a type. If I'm overlooking a clever way to implement - * that using a template instead, feel free to reimplement. - */ -#define LLSDParam_for(T, AS) \ -template <> \ -class LLSDParam<T>: public LLSDParamBase \ -{ \ -public: \ - LLSDParam(const LLSD& value): \ - value_((T)value.AS()) \ - {} \ - \ - operator T() const { return value_; } \ - \ -private: \ - T value_; \ -} - -LLSDParam_for(float, asReal); -LLSDParam_for(LLUUID, asUUID); -LLSDParam_for(LLDate, asDate); -LLSDParam_for(LLURI, asURI); -LLSDParam_for(LLSD::Binary, asBinary); - -/** - * LLSDParam<const char*> is an example of the kind of conversion you can - * support with LLSDParam beyond native LLSD conversions. Normally you can't - * pass an LLSD object to a function accepting const char* -- but you can - * safely pass an LLSDParam<const char*>(yourLLSD). - */ -template <> -class LLSDParam<const char*>: public LLSDParamBase -{ -private: - // The difference here is that we store a std::string rather than a const - // char*. It's important that the LLSDParam object own the std::string. - std::string value_; - // We don't bother storing the incoming LLSD object, but we do have to - // distinguish whether value_ is an empty string because the LLSD object - // contains an empty string or because it's isUndefined(). - bool undefined_; - -public: - LLSDParam(const LLSD& value): - value_(value), - undefined_(value.isUndefined()) - {} - - // The const char* we retrieve is for storage owned by our value_ member. - // That's how we guarantee that the const char* is valid for the lifetime - // of this LLSDParam object. Constructing your LLSDParam in the argument - // list should ensure that the LLSDParam object will persist for the - // duration of the function call. - operator const char*() const - { - if (undefined_) - { - // By default, an isUndefined() LLSD object's asString() method - // will produce an empty string. But for a function accepting - // const char*, it's often important to be able to pass NULL, and - // isUndefined() seems like the best way. If you want to pass an - // empty string, you can still pass LLSD(""). Without this special - // case, though, no LLSD value could pass NULL. - return NULL; - } - return value_.c_str(); - } -}; - -namespace llsd -{ - -/***************************************************************************** -* range-based for-loop helpers for LLSD -*****************************************************************************/ -/// Usage: for (LLSD item : inArray(someLLSDarray)) { ... } -class inArray -{ -public: - inArray(const LLSD& array): - _array(array) - {} - - typedef LLSD::array_const_iterator const_iterator; - typedef LLSD::array_iterator iterator; - - iterator begin() { return _array.beginArray(); } - iterator end() { return _array.endArray(); } - const_iterator begin() const { return _array.beginArray(); } - const_iterator end() const { return _array.endArray(); } - -private: - LLSD _array; -}; - -/// MapEntry is what you get from dereferencing an LLSD::map_[const_]iterator. -typedef std::map<LLSD::String, LLSD>::value_type MapEntry; - -/// Usage: for([const] MapEntry& e : inMap(someLLSDmap)) { ... } -class inMap -{ -public: - inMap(const LLSD& map): - _map(map) - {} - - typedef LLSD::map_const_iterator const_iterator; - typedef LLSD::map_iterator iterator; - - iterator begin() { return _map.beginMap(); } - iterator end() { return _map.endMap(); } - const_iterator begin() const { return _map.beginMap(); } - const_iterator end() const { return _map.endMap(); } - -private: - LLSD _map; -}; - -} // namespace llsd - - -// Creates a deep clone of an LLSD object. Maps, Arrays and binary objects -// are duplicated, atomic primitives (Boolean, Integer, Real, etc) simply -// use a shared reference. -// Optionally a filter may be specified to control what is duplicated. The -// map takes the form "keyname/boolean". -// If the value is true the value will be duplicated otherwise it will be skipped -// when encountered in a map. A key name of "*" can be specified as a wild card -// and will specify the default behavior. If no wild card is given and the clone -// encounters a name not in the filter, that value will be skipped. -LLSD llsd_clone(LLSD value, LLSD filter = LLSD()); - -// Creates a shallow copy of a map or array. If passed any other type of LLSD -// object it simply returns that value. See llsd_clone for a description of -// the filter parameter. -LLSD llsd_shallow(LLSD value, LLSD filter = LLSD()); - -namespace llsd -{ - -// llsd namespace aliases -inline -LLSD clone (LLSD value, LLSD filter=LLSD()) { return llsd_clone (value, filter); } -inline -LLSD shallow(LLSD value, LLSD filter=LLSD()) { return llsd_shallow(value, filter); } - -} // namespace llsd - -// Specialization for generating a hash value from an LLSD block. -namespace boost -{ -template <> -struct hash<LLSD> -{ - typedef LLSD argument_type; - typedef std::size_t result_type; - result_type operator()(argument_type const& s) const - { - result_type seed(0); - - LLSD::Type stype = s.type(); - boost::hash_combine(seed, (S32)stype); - - switch (stype) - { - case LLSD::TypeBoolean: - boost::hash_combine(seed, s.asBoolean()); - break; - case LLSD::TypeInteger: - boost::hash_combine(seed, s.asInteger()); - break; - case LLSD::TypeReal: - boost::hash_combine(seed, s.asReal()); - break; - case LLSD::TypeURI: - case LLSD::TypeString: - boost::hash_combine(seed, s.asString()); - break; - case LLSD::TypeUUID: - boost::hash_combine(seed, s.asUUID()); - break; - case LLSD::TypeDate: - boost::hash_combine(seed, s.asDate().secondsSinceEpoch()); - break; - case LLSD::TypeBinary: - { - const LLSD::Binary &b(s.asBinary()); - boost::hash_range(seed, b.begin(), b.end()); - break; - } - case LLSD::TypeMap: - { - for (LLSD::map_const_iterator itm = s.beginMap(); itm != s.endMap(); ++itm) - { - boost::hash_combine(seed, (*itm).first); - boost::hash_combine(seed, (*itm).second); - } - break; - } - case LLSD::TypeArray: - for (LLSD::array_const_iterator ita = s.beginArray(); ita != s.endArray(); ++ita) - { - boost::hash_combine(seed, (*ita)); - } - break; - case LLSD::TypeUndefined: - default: - break; - } - - return seed; - } -}; -} - -namespace LL -{ - -/***************************************************************************** -* apply(function, LLSD array) -*****************************************************************************/ -// validate incoming LLSD blob, and return an LLSD array suitable to pass to -// the function of interest -LLSD apply_llsd_fix(size_t arity, const LLSD& args); - -// 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])...); -} - -// use apply_n<ARITY>(function, LLSD) to call a specific arity of a variadic -// function with (that many) items from the passed LLSD array -template <size_t ARITY, typename CALLABLE> -auto apply_n(CALLABLE&& func, const LLSD& args) -{ - return apply_impl(std::forward<CALLABLE>(func), - apply_llsd_fix(ARITY, args), - std::make_index_sequence<ARITY>()); -} - -/** - * apply(function, LLSD) goes beyond C++17 std::apply(). For this case - * @a function @emph cannot be variadic: the compiler must know at compile - * time how many arguments to pass. This isn't Python. (But see apply_n() to - * pass a specific number of args to a variadic function.) - */ -template <typename CALLABLE> -auto apply(CALLABLE&& func, const LLSD& args) -{ - // infer arity from the definition of func - constexpr auto arity = function_arity< - typename std::remove_reference<CALLABLE>::type>::value; - // now that we have a compile-time arity, apply_n() works - return apply_n<arity>(std::forward<CALLABLE>(func), args); -} - -} // namespace LL - -#endif // LL_LLSDUTIL_H +/**
+ * @file llsdutil.h
+ * @author Phoenix
+ * @date 2006-05-24
+ * @brief Utility classes, functions, etc, for using structured data.
+ *
+ * $LicenseInfo:firstyear=2006&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_LLSDUTIL_H
+#define LL_LLSDUTIL_H
+
+#include "apply.h" // LL::invoke()
+#include "function_types.h" // LL::function_arity
+#include "llsd.h"
+#include <boost/functional/hash.hpp>
+#include <cassert>
+#include <memory> // std::shared_ptr
+#include <type_traits>
+#include <vector>
+
+// U32
+LL_COMMON_API LLSD ll_sd_from_U32(const U32);
+LL_COMMON_API U32 ll_U32_from_sd(const LLSD& sd);
+
+// U64
+LL_COMMON_API LLSD ll_sd_from_U64(const U64);
+LL_COMMON_API U64 ll_U64_from_sd(const LLSD& sd);
+
+// IP Address
+LL_COMMON_API LLSD ll_sd_from_ipaddr(const U32);
+LL_COMMON_API U32 ll_ipaddr_from_sd(const LLSD& sd);
+
+// Binary to string
+LL_COMMON_API LLSD ll_string_from_binary(const LLSD& sd);
+
+//String to binary
+LL_COMMON_API LLSD ll_binary_from_string(const LLSD& sd);
+
+// Serializes sd to static buffer and returns pointer, useful for gdb debugging.
+LL_COMMON_API char* ll_print_sd(const LLSD& sd);
+
+// Serializes sd to static buffer and returns pointer, using "pretty printing" mode.
+LL_COMMON_API char* ll_pretty_print_sd_ptr(const LLSD* sd);
+LL_COMMON_API char* ll_pretty_print_sd(const LLSD& sd);
+
+LL_COMMON_API std::string ll_stream_notation_sd(const LLSD& sd);
+
+//compares the structure of an LLSD to a template LLSD and stores the
+//"valid" values in a 3rd LLSD. Default values
+//are pulled from the template. Extra keys/values in the test
+//are ignored in the resultant LLSD. Ordering of arrays matters
+//Returns false if the test is of same type but values differ in type
+//Otherwise, returns true
+
+LL_COMMON_API bool compare_llsd_with_template(
+ const LLSD& llsd_to_test,
+ const LLSD& template_llsd,
+ LLSD& resultant_llsd);
+
+// filter_llsd_with_template() is a direct clone (copy-n-paste) of
+// compare_llsd_with_template with the following differences:
+// (1) bool vs BOOL return types
+// (2) A map with the key value "*" is a special value and maps any key in the
+// test llsd that doesn't have an explicitly matching key in the template.
+// (3) The element of an array with exactly one element is taken as a template
+// for *all* the elements of the test array. If the template array is of
+// different size, compare_llsd_with_template() semantics apply.
+bool filter_llsd_with_template(
+ const LLSD & llsd_to_test,
+ const LLSD & template_llsd,
+ LLSD & resultant_llsd);
+
+/**
+ * Recursively determine whether a given LLSD data block "matches" another
+ * LLSD prototype. The returned string is empty() on success, non-empty() on
+ * mismatch.
+ *
+ * This function tests structure (types) rather than data values. It is
+ * intended for when a consumer expects an LLSD block with a particular
+ * structure, and must succinctly detect whether the arriving block is
+ * well-formed. For instance, a test of the form:
+ * @code
+ * if (! (data.has("request") && data.has("target") && data.has("modifier") ...))
+ * @endcode
+ * could instead be expressed by initializing a prototype LLSD map with the
+ * required keys and writing:
+ * @code
+ * if (! llsd_matches(prototype, data).empty())
+ * @endcode
+ *
+ * A non-empty return value is an error-message fragment intended to indicate
+ * to (English-speaking) developers where in the prototype structure the
+ * mismatch occurred.
+ *
+ * * If a slot in the prototype isUndefined(), then anything is valid at that
+ * place in the real object. (Passing prototype == LLSD() matches anything
+ * at all.)
+ * * An array in the prototype must match a data array at least that large.
+ * (Additional entries in the data array are ignored.) Every isDefined()
+ * entry in the prototype array must match the corresponding entry in the
+ * data array.
+ * * A map in the prototype must match a map in the data. Every key in the
+ * prototype map must match a corresponding key in the data map. (Additional
+ * keys in the data map are ignored.) Every isDefined() value in the
+ * prototype map must match the corresponding key's value in the data map.
+ * * Scalar values in the prototype are tested for @em type rather than value.
+ * For instance, a String in the prototype matches any String at all. In
+ * effect, storing an Integer at a particular place in the prototype asserts
+ * that the caller intends to apply asInteger() to the corresponding slot in
+ * the data.
+ * * A String in the prototype matches String, Boolean, Integer, Real, UUID,
+ * Date and URI, because asString() applied to any of these produces a
+ * meaningful result.
+ * * Similarly, a Boolean, Integer or Real in the prototype can match any of
+ * Boolean, Integer or Real in the data -- or even String.
+ * * UUID matches UUID or String.
+ * * Date matches Date or String.
+ * * URI matches URI or String.
+ * * Binary in the prototype matches only Binary in the data.
+ *
+ * @TODO: when a Boolean, Integer or Real in the prototype matches a String in
+ * the data, we should examine the String @em value to ensure it can be
+ * meaningfully converted to the requested type. The same goes for UUID, Date
+ * and URI.
+ */
+LL_COMMON_API std::string llsd_matches(const LLSD& prototype, const LLSD& data, const std::string& pfx="");
+
+/// Deep equality. If you want to compare LLSD::Real values for approximate
+/// equality rather than bitwise equality, pass @a bits as for
+/// is_approx_equal_fraction().
+LL_COMMON_API bool llsd_equals(const LLSD& lhs, const LLSD& rhs, int bits=-1);
+/// If you don't care about LLSD::Real equality
+inline bool operator==(const LLSD& lhs, const LLSD& rhs)
+{
+ return llsd_equals(lhs, rhs);
+}
+inline bool operator!=(const LLSD& lhs, const LLSD& rhs)
+{
+ // operator!=() should always be the negation of operator==()
+ return ! (lhs == rhs);
+}
+
+// Simple function to copy data out of input & output iterators if
+// there is no need for casting.
+template<typename Input> LLSD llsd_copy_array(Input iter, Input end)
+{
+ LLSD dest;
+ for (; iter != end; ++iter)
+ {
+ dest.append(*iter);
+ }
+ return dest;
+}
+
+namespace llsd
+{
+
+/**
+ * Drill down to locate an element in 'blob' according to 'path', where 'path'
+ * is one of the following:
+ *
+ * - LLSD::String: 'blob' is an LLSD::Map. Find the entry with key 'path'.
+ * - LLSD::Integer: 'blob' is an LLSD::Array. Find the entry with index 'path'.
+ * - Any other 'path' type will be interpreted as LLSD::Array, and 'blob' is a
+ * nested structure. For each element of 'path':
+ * - If it's an LLSD::Integer, select the entry with that index from an
+ * LLSD::Array at that level.
+ * - If it's an LLSD::String, select the entry with that key from an
+ * LLSD::Map at that level.
+ * - Anything else is an error.
+ *
+ * By implication, if path.isUndefined() or otherwise equivalent to an empty
+ * LLSD::Array, drill[_ref]() returns 'blob' as is.
+ */
+LLSD drill(const LLSD& blob, const LLSD& path);
+LLSD& drill_ref( LLSD& blob, const LLSD& path);
+
+}
+
+namespace llsd
+{
+
+/**
+ * Construct an LLSD::Array inline, using modern C++ variadic arguments.
+ */
+
+// recursion tail
+inline
+void array_(LLSD&) {}
+
+// recursive call
+template <typename T0, typename... Ts>
+void array_(LLSD& data, T0&& v0, Ts&&... vs)
+{
+ data.append(std::forward<T0>(v0));
+ array_(data, std::forward<Ts>(vs)...);
+}
+
+// public interface
+template <typename... Ts>
+LLSD array(Ts&&... vs)
+{
+ LLSD data;
+ array_(data, std::forward<Ts>(vs)...);
+ return data;
+}
+
+} // namespace llsd
+
+/*****************************************************************************
+* LLSDMap
+*****************************************************************************/
+/**
+ * Construct an LLSD::Map inline, with implicit conversion to LLSD. Usage:
+ *
+ * @code
+ * void somefunc(const LLSD&);
+ * ...
+ * somefunc(LLSDMap("alpha", "abc")("number", 17)("pi", 3.14));
+ * @endcode
+ *
+ * For completeness, LLSDMap() with no args constructs an empty map, so
+ * <tt>LLSDMap()("alpha", "abc")("number", 17)("pi", 3.14)</tt> produces a map
+ * equivalent to the above. But for most purposes, LLSD() is already
+ * equivalent to an empty map, and if you explicitly want an empty isMap(),
+ * there's LLSD::emptyMap(). However, supporting a no-args LLSDMap()
+ * constructor follows the principle of least astonishment.
+ */
+class LLSDMap
+{
+public:
+ LLSDMap():
+ _data(LLSD::emptyMap())
+ {}
+ LLSDMap(const LLSD::String& key, const LLSD& value):
+ _data(LLSD::emptyMap())
+ {
+ _data[key] = value;
+ }
+
+ LLSDMap& operator()(const LLSD::String& key, const LLSD& value)
+ {
+ _data[key] = value;
+ return *this;
+ }
+
+ operator LLSD() const { return _data; }
+ LLSD get() const { return _data; }
+
+private:
+ LLSD _data;
+};
+
+namespace llsd
+{
+
+/**
+ * Construct an LLSD::Map inline, using modern C++ variadic arguments.
+ */
+
+// recursion tail
+inline
+void map_(LLSD&) {}
+
+// recursive call
+template <typename T0, typename... Ts>
+void map_(LLSD& data, const LLSD::String& k0, T0&& v0, Ts&&... vs)
+{
+ data[k0] = v0;
+ map_(data, std::forward<Ts>(vs)...);
+}
+
+// public interface
+template <typename... Ts>
+LLSD map(Ts&&... vs)
+{
+ LLSD data;
+ map_(data, std::forward<Ts>(vs)...);
+ return data;
+}
+
+} // namespace llsd
+
+/*****************************************************************************
+* LLSDParam
+*****************************************************************************/
+struct LLSDParamBase
+{
+ virtual ~LLSDParamBase() {}
+};
+
+/**
+ * LLSDParam is a customization point for passing LLSD values to function
+ * parameters of more or less arbitrary type. LLSD provides a small set of
+ * native conversions; but if a generic algorithm explicitly constructs an
+ * LLSDParam object in the function's argument list, a consumer can provide
+ * LLSDParam specializations to support more different parameter types than
+ * LLSD's native conversions.
+ *
+ * Usage:
+ *
+ * @code
+ * void somefunc(const paramtype&);
+ * ...
+ * somefunc(..., LLSDParam<paramtype>(someLLSD), ...);
+ * @endcode
+ */
+template <typename T>
+class LLSDParam: public LLSDParamBase
+{
+public:
+ /**
+ * Default implementation converts to T on construction, saves converted
+ * value for later retrieval
+ */
+ LLSDParam(const LLSD& value):
+ value_(value)
+ {}
+
+ operator T() const { return value_; }
+
+private:
+ T value_;
+};
+
+/**
+ * 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>: public LLSDParamBase
+{
+private:
+ LLSD value_;
+ // LLSDParam<LLSD>::operator T() works by instantiating an LLSDParam<T> on
+ // demand. Returning that engages LLSDParam<T>::operator T(), producing
+ // the desired result. But LLSDParam<const char*> owns a std::string whose
+ // c_str() is returned by its operator const char*(). If we return a temp
+ // LLSDParam<const char*>, the compiler can destroy it right away, as soon
+ // as we've called operator const char*(). That's a problem! That
+ // invalidates the const char* we've just passed to the subject function.
+ // This LLSDParam<LLSD> is presumably guaranteed to survive until the
+ // subject function has returned, so we must ensure that any constructed
+ // LLSDParam<T> lives just as long as this LLSDParam<LLSD> does. Putting
+ // each LLSDParam<T> on the heap and capturing a smart pointer in a vector
+ // works. We would have liked to use std::unique_ptr, but vector entries
+ // must be copyable.
+ // (Alternatively we could assume that every instance of LLSDParam<LLSD>
+ // will be asked for at most ONE conversion. We could store a scalar
+ // std::unique_ptr and, when constructing an new LLSDParam<T>, assert that
+ // the unique_ptr is empty. But some future change in usage patterns, and
+ // consequent failure of that assertion, would be very mysterious. Instead
+ // of explaining how to fix it, just fix it now.)
+ mutable std::vector<std::shared_ptr<LLSDParamBase>> converters_;
+
+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
+ {
+ // capture 'ptr' with the specific subclass type because converters_
+ // only stores LLSDParamBase pointers
+ auto ptr{ std::make_shared<LLSDParam<std::decay_t<T>>>(value_) };
+ // keep the new converter alive until we ourselves are destroyed
+ converters_.push_back(ptr);
+ return *ptr;
+ }
+};
+
+/**
+ * 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
+ * operator to choose, even though to us it seems obvious. But that's okay, we
+ * can specialize LLSDParam for such target types, explicitly specifying the
+ * desired conversion -- that's part of what LLSDParam is all about. Turns out
+ * we have to do that enough to make it worthwhile generalizing. Use a macro
+ * because I need to specify one of the asReal, etc., explicit conversion
+ * methods as well as a type. If I'm overlooking a clever way to implement
+ * that using a template instead, feel free to reimplement.
+ */
+#define LLSDParam_for(T, AS) \
+template <> \
+class LLSDParam<T>: public LLSDParamBase \
+{ \
+public: \
+ LLSDParam(const LLSD& value): \
+ value_((T)value.AS()) \
+ {} \
+ \
+ operator T() const { return value_; } \
+ \
+private: \
+ T value_; \
+}
+
+LLSDParam_for(float, asReal);
+LLSDParam_for(LLUUID, asUUID);
+LLSDParam_for(LLDate, asDate);
+LLSDParam_for(LLURI, asURI);
+LLSDParam_for(LLSD::Binary, asBinary);
+
+/**
+ * LLSDParam<const char*> is an example of the kind of conversion you can
+ * support with LLSDParam beyond native LLSD conversions. Normally you can't
+ * pass an LLSD object to a function accepting const char* -- but you can
+ * safely pass an LLSDParam<const char*>(yourLLSD).
+ */
+template <>
+class LLSDParam<const char*>: public LLSDParamBase
+{
+private:
+ // The difference here is that we store a std::string rather than a const
+ // char*. It's important that the LLSDParam object own the std::string.
+ std::string value_;
+ // We don't bother storing the incoming LLSD object, but we do have to
+ // distinguish whether value_ is an empty string because the LLSD object
+ // contains an empty string or because it's isUndefined().
+ bool undefined_;
+
+public:
+ LLSDParam(const LLSD& value):
+ value_(value),
+ undefined_(value.isUndefined())
+ {}
+
+ // The const char* we retrieve is for storage owned by our value_ member.
+ // That's how we guarantee that the const char* is valid for the lifetime
+ // of this LLSDParam object. Constructing your LLSDParam in the argument
+ // list should ensure that the LLSDParam object will persist for the
+ // duration of the function call.
+ operator const char*() const
+ {
+ if (undefined_)
+ {
+ // By default, an isUndefined() LLSD object's asString() method
+ // will produce an empty string. But for a function accepting
+ // const char*, it's often important to be able to pass NULL, and
+ // isUndefined() seems like the best way. If you want to pass an
+ // empty string, you can still pass LLSD(""). Without this special
+ // case, though, no LLSD value could pass NULL.
+ return NULL;
+ }
+ return value_.c_str();
+ }
+};
+
+namespace llsd
+{
+
+/*****************************************************************************
+* range-based for-loop helpers for LLSD
+*****************************************************************************/
+/// Usage: for (LLSD item : inArray(someLLSDarray)) { ... }
+class inArray
+{
+public:
+ inArray(const LLSD& array):
+ _array(array)
+ {}
+
+ typedef LLSD::array_const_iterator const_iterator;
+ typedef LLSD::array_iterator iterator;
+
+ iterator begin() { return _array.beginArray(); }
+ iterator end() { return _array.endArray(); }
+ const_iterator begin() const { return _array.beginArray(); }
+ const_iterator end() const { return _array.endArray(); }
+
+private:
+ LLSD _array;
+};
+
+/// MapEntry is what you get from dereferencing an LLSD::map_[const_]iterator.
+typedef std::map<LLSD::String, LLSD>::value_type MapEntry;
+
+/// Usage: for([const] MapEntry& e : inMap(someLLSDmap)) { ... }
+class inMap
+{
+public:
+ inMap(const LLSD& map):
+ _map(map)
+ {}
+
+ typedef LLSD::map_const_iterator const_iterator;
+ typedef LLSD::map_iterator iterator;
+
+ iterator begin() { return _map.beginMap(); }
+ iterator end() { return _map.endMap(); }
+ const_iterator begin() const { return _map.beginMap(); }
+ const_iterator end() const { return _map.endMap(); }
+
+private:
+ LLSD _map;
+};
+
+} // namespace llsd
+
+
+// Creates a deep clone of an LLSD object. Maps, Arrays and binary objects
+// are duplicated, atomic primitives (Boolean, Integer, Real, etc) simply
+// use a shared reference.
+// Optionally a filter may be specified to control what is duplicated. The
+// map takes the form "keyname/boolean".
+// If the value is true the value will be duplicated otherwise it will be skipped
+// when encountered in a map. A key name of "*" can be specified as a wild card
+// and will specify the default behavior. If no wild card is given and the clone
+// encounters a name not in the filter, that value will be skipped.
+LLSD llsd_clone(LLSD value, LLSD filter = LLSD());
+
+// Creates a shallow copy of a map or array. If passed any other type of LLSD
+// object it simply returns that value. See llsd_clone for a description of
+// the filter parameter.
+LLSD llsd_shallow(LLSD value, LLSD filter = LLSD());
+
+namespace llsd
+{
+
+// llsd namespace aliases
+inline
+LLSD clone (LLSD value, LLSD filter=LLSD()) { return llsd_clone (value, filter); }
+inline
+LLSD shallow(LLSD value, LLSD filter=LLSD()) { return llsd_shallow(value, filter); }
+
+} // namespace llsd
+
+// Specialization for generating a hash value from an LLSD block.
+namespace boost
+{
+template <>
+struct hash<LLSD>
+{
+ typedef LLSD argument_type;
+ typedef std::size_t result_type;
+ result_type operator()(argument_type const& s) const
+ {
+ result_type seed(0);
+
+ LLSD::Type stype = s.type();
+ boost::hash_combine(seed, (S32)stype);
+
+ switch (stype)
+ {
+ case LLSD::TypeBoolean:
+ boost::hash_combine(seed, s.asBoolean());
+ break;
+ case LLSD::TypeInteger:
+ boost::hash_combine(seed, s.asInteger());
+ break;
+ case LLSD::TypeReal:
+ boost::hash_combine(seed, s.asReal());
+ break;
+ case LLSD::TypeURI:
+ case LLSD::TypeString:
+ boost::hash_combine(seed, s.asString());
+ break;
+ case LLSD::TypeUUID:
+ boost::hash_combine(seed, s.asUUID());
+ break;
+ case LLSD::TypeDate:
+ boost::hash_combine(seed, s.asDate().secondsSinceEpoch());
+ break;
+ case LLSD::TypeBinary:
+ {
+ const LLSD::Binary &b(s.asBinary());
+ boost::hash_range(seed, b.begin(), b.end());
+ break;
+ }
+ case LLSD::TypeMap:
+ {
+ for (LLSD::map_const_iterator itm = s.beginMap(); itm != s.endMap(); ++itm)
+ {
+ boost::hash_combine(seed, (*itm).first);
+ boost::hash_combine(seed, (*itm).second);
+ }
+ break;
+ }
+ case LLSD::TypeArray:
+ for (LLSD::array_const_iterator ita = s.beginArray(); ita != s.endArray(); ++ita)
+ {
+ boost::hash_combine(seed, (*ita));
+ }
+ break;
+ case LLSD::TypeUndefined:
+ default:
+ break;
+ }
+
+ return seed;
+ }
+};
+}
+
+namespace LL
+{
+
+/*****************************************************************************
+* apply(function, LLSD array)
+*****************************************************************************/
+// validate incoming LLSD blob, and return an LLSD array suitable to pass to
+// the function of interest
+LLSD apply_llsd_fix(size_t arity, const LLSD& args);
+
+// 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])...);
+}
+
+// use apply_n<ARITY>(function, LLSD) to call a specific arity of a variadic
+// function with (that many) items from the passed LLSD array
+template <size_t ARITY, typename CALLABLE>
+auto apply_n(CALLABLE&& func, const LLSD& args)
+{
+ return apply_impl(std::forward<CALLABLE>(func),
+ apply_llsd_fix(ARITY, args),
+ std::make_index_sequence<ARITY>());
+}
+
+/**
+ * apply(function, LLSD) goes beyond C++17 std::apply(). For this case
+ * @a function @emph cannot be variadic: the compiler must know at compile
+ * time how many arguments to pass. This isn't Python. (But see apply_n() to
+ * pass a specific number of args to a variadic function.)
+ */
+template <typename CALLABLE>
+auto apply(CALLABLE&& func, const LLSD& args)
+{
+ // infer arity from the definition of func
+ constexpr auto arity = function_arity<
+ typename std::remove_reference<CALLABLE>::type>::value;
+ // now that we have a compile-time arity, apply_n() works
+ return apply_n<arity>(std::forward<CALLABLE>(func), args);
+}
+
+} // namespace LL
+
+#endif // LL_LLSDUTIL_H
|