summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNat Goodspeed <nat@lindenlab.com>2023-01-20 22:34:31 -0500
committerNat Goodspeed <nat@lindenlab.com>2023-07-13 12:49:09 -0400
commit2c894ecb25de044f5cb9c408c5264e5234b73983 (patch)
tree89c6d956f0ff42c6fefa8d0763296cea06c87d53
parentb36deb79c2e99bfa600b5895c277ffb78c61957f (diff)
DRTVWR-558: Extend LLEventDispatcher::add() overloads.
Add LL::always_return<T>(), which takes a callable and variadic arguments. It calls the callable with those arguments and, if the returned type is convertible to T, converts it and returns it. Otherwise it returns T(). always_return() is generalized from, and supersedes, LLEventDispatcher::ReturnLLSD. Add LL::function_arity<CALLABLE>, which extends boost::function_types::function_arity by reporting results for both std::function<CALLABLE> and boost::function<CALLABLE>. Use for LL::apply(function, LLSD array) as well as for LLEventDispatcher. Make LLEventDispatcher::add() overloads uniformly distinguish between a callable (whether non-static member function or otherwise) that accepts a single LLSD parameter, versus any other signature. Accepting exactly one LLSD parameter signals that the callable will accept the composite arguments LLSD blob, instead of asking LLEventDispatcher to unpack the arguments blob into individual arguments. Support add(subclass method) overloads for arbitrary-parameters methods as well as for (const LLSD&) methods. Update tests accordingly: we need no longer pass the boilerplate lambda instance getter that binds and returns 'this'. Extract to the two LLEventDispatcher::make_invoker() overloads the LL::apply() logic formerly found in ReturnLLSD. Change lleventdispatcher_test.cpp tests from boost::bind(), which accepts variadic arguments (even though it only passes a fixed set to the target callable), to fixed-signature lambdas. This is because the revamped add() overloads care about signature. Add a test for a non-static method that accepts (const LLSD&), in other words the composite arguments LLSD blob, and likewise returns LLSD. (cherry picked from commit 95b787f7d7226ee9de79dfc9816f33c8bf199aad)
-rw-r--r--indra/llcommon/CMakeLists.txt2
-rw-r--r--indra/llcommon/always_return.h124
-rw-r--r--indra/llcommon/function_types.h49
-rw-r--r--indra/llcommon/lleventdispatcher.cpp7
-rw-r--r--indra/llcommon/lleventdispatcher.h326
-rw-r--r--indra/llcommon/llsdutil.h4
-rw-r--r--indra/llcommon/tests/lleventdispatcher_test.cpp62
7 files changed, 460 insertions, 114 deletions
diff --git a/indra/llcommon/CMakeLists.txt b/indra/llcommon/CMakeLists.txt
index 33e8301e12..64751926d0 100644
--- a/indra/llcommon/CMakeLists.txt
+++ b/indra/llcommon/CMakeLists.txt
@@ -117,11 +117,13 @@ set(llcommon_SOURCE_FILES
set(llcommon_HEADER_FILES
CMakeLists.txt
+ always_return.h
apply.h
chrono.h
classic_callback.h
ctype_workaround.h
fix_macros.h
+ function_types.h
indra_constants.h
lazyeventapi.h
linden_common.h
diff --git a/indra/llcommon/always_return.h b/indra/llcommon/always_return.h
new file mode 100644
index 0000000000..6b9f1fdeaf
--- /dev/null
+++ b/indra/llcommon/always_return.h
@@ -0,0 +1,124 @@
+/**
+ * @file always_return.h
+ * @author Nat Goodspeed
+ * @date 2023-01-20
+ * @brief Call specified callable with arbitrary arguments, but always return
+ * specified type.
+ *
+ * $LicenseInfo:firstyear=2023&license=viewerlgpl$
+ * Copyright (c) 2023, Linden Research, Inc.
+ * $/LicenseInfo$
+ */
+
+#if ! defined(LL_ALWAYS_RETURN_H)
+#define LL_ALWAYS_RETURN_H
+
+#include <type_traits> // std::enable_if, std::is_convertible
+
+namespace LL
+{
+
+#if __cpp_lib_is_invocable >= 201703L // C++17
+ template <typename CALLABLE, typename... ARGS>
+ using invoke_result = std::invoke_result<CALLABLE, ARGS...>;
+#else // C++14
+ template <typename CALLABLE, typename... ARGS>
+ using invoke_result = std::result_of<CALLABLE(ARGS...)>;
+#endif // C++14
+
+ /**
+ * AlwaysReturn<T>()(some_function, some_args...) calls
+ * some_function(some_args...). It is guaranteed to return a value of type
+ * T, regardless of the return type of some_function(). If some_function()
+ * returns a type convertible to T, it will convert and return that value.
+ * Otherwise (notably if some_function() is void), AlwaysReturn returns
+ * T().
+ *
+ * When some_function() returns a type not convertible to T, if
+ * you want AlwaysReturn to return some T value other than
+ * default-constructed T(), pass that value to AlwaysReturn's constructor.
+ */
+ template <typename DESIRED>
+ class AlwaysReturn
+ {
+ public:
+ /// pass explicit default value if other than default-constructed type
+ AlwaysReturn(const DESIRED& dft=DESIRED()): mDefault(dft) {}
+
+ // callable returns a type not convertible to DESIRED, return default
+ template <typename CALLABLE, typename... ARGS,
+ typename std::enable_if<
+ ! std::is_convertible<
+ typename invoke_result<CALLABLE, ARGS...>::type,
+ DESIRED
+ >::value,
+ bool
+ >::type=true>
+ DESIRED operator()(CALLABLE&& callable, ARGS&&... args)
+ {
+ // discard whatever callable(args) returns
+ std::forward<CALLABLE>(callable)(std::forward<ARGS>(args)...);
+ return mDefault;
+ }
+
+ // callable returns a type convertible to DESIRED
+ template <typename CALLABLE, typename... ARGS,
+ typename std::enable_if<
+ std::is_convertible<
+ typename invoke_result<CALLABLE, ARGS...>::type,
+ DESIRED
+ >::value,
+ bool
+ >::type=true>
+ DESIRED operator()(CALLABLE&& callable, ARGS&&... args)
+ {
+ return { std::forward<CALLABLE>(callable)(std::forward<ARGS>(args)...) };
+ }
+
+ private:
+ DESIRED mDefault;
+ };
+
+ /**
+ * always_return<T>(some_function, some_args...) calls
+ * some_function(some_args...). It is guaranteed to return a value of type
+ * T, regardless of the return type of some_function(). If some_function()
+ * returns a type convertible to T, it will convert and return that value.
+ * Otherwise (notably if some_function() is void), always_return() returns
+ * T().
+ */
+ template <typename DESIRED, typename CALLABLE, typename... ARGS>
+ DESIRED always_return(CALLABLE&& callable, ARGS&&... args)
+ {
+ return AlwaysReturn<DESIRED>()(std::forward<CALLABLE>(callable),
+ std::forward<ARGS>(args)...);
+ }
+
+ /**
+ * make_always_return<T>(some_function) returns a callable which, when
+ * called with appropriate some_function() arguments, always returns a
+ * value of type T, regardless of the return type of some_function(). If
+ * some_function() returns a type convertible to T, the returned callable
+ * will convert and return that value. Otherwise (notably if
+ * some_function() is void), the returned callable returns T().
+ *
+ * When some_function() returns a type not convertible to T, if
+ * you want the returned callable to return some T value other than
+ * default-constructed T(), pass that value to make_always_return() as its
+ * optional second argument.
+ */
+ template <typename DESIRED, typename CALLABLE>
+ auto make_always_return(CALLABLE&& callable, const DESIRED& dft=DESIRED())
+ {
+ return
+ [dft, callable = std::forward<CALLABLE>(callable)]
+ (auto&&... args)
+ {
+ return AlwaysReturn<DESIRED>(dft)(callable,
+ std::forward<decltype(args)>(args)...);
+ };
+ }
+
+} // namespace LL
+
+#endif /* ! defined(LL_ALWAYS_RETURN_H) */
diff --git a/indra/llcommon/function_types.h b/indra/llcommon/function_types.h
new file mode 100644
index 0000000000..3f42f6d640
--- /dev/null
+++ b/indra/llcommon/function_types.h
@@ -0,0 +1,49 @@
+/**
+ * @file function_types.h
+ * @author Nat Goodspeed
+ * @date 2023-01-20
+ * @brief Extend boost::function_types to examine boost::function and
+ * std::function
+ *
+ * $LicenseInfo:firstyear=2023&license=viewerlgpl$
+ * Copyright (c) 2023, Linden Research, Inc.
+ * $/LicenseInfo$
+ */
+
+#if ! defined(LL_FUNCTION_TYPES_H)
+#define LL_FUNCTION_TYPES_H
+
+#include <boost/function.hpp>
+#include <boost/function_types/function_arity.hpp>
+#include <functional>
+
+namespace LL
+{
+
+ template <typename F>
+ struct function_arity_impl
+ {
+ static constexpr auto value = boost::function_types::function_arity<F>::value;
+ };
+
+ template <typename F>
+ struct function_arity_impl<std::function<F>>
+ {
+ static constexpr auto value = function_arity_impl<F>::value;
+ };
+
+ template <typename F>
+ struct function_arity_impl<boost::function<F>>
+ {
+ static constexpr auto value = function_arity_impl<F>::value;
+ };
+
+ template <typename F>
+ struct function_arity
+ {
+ static constexpr auto value = function_arity_impl<typename std::decay<F>::type>::value;
+ };
+
+} // namespace LL
+
+#endif /* ! defined(LL_FUNCTION_TYPES_H) */
diff --git a/indra/llcommon/lleventdispatcher.cpp b/indra/llcommon/lleventdispatcher.cpp
index d10cf16b88..caff854753 100644
--- a/indra/llcommon/lleventdispatcher.cpp
+++ b/indra/llcommon/lleventdispatcher.cpp
@@ -384,8 +384,7 @@ struct LLEventDispatcher::LLSDDispatchEntry: public LLEventDispatcher::DispatchE
(desc, ": bad request: ", mismatch);
}
// Event syntax looks good, go for it!
- mFunc(event);
- return {};
+ return mFunc(event);
}
LLSD addMetadata(LLSD meta) const override
@@ -603,8 +602,8 @@ void LLEventDispatcher::addMapParamsDispatchEntry(const std::string& name,
}
/// Register a callable by name
-void LLEventDispatcher::add(const std::string& name, const std::string& desc,
- const Callable& callable, const LLSD& required)
+void LLEventDispatcher::addLLSD(const std::string& name, const std::string& desc,
+ const Callable& callable, const LLSD& required)
{
mDispatch.emplace(name, new LLSDDispatchEntry(desc, callable, required));
}
diff --git a/indra/llcommon/lleventdispatcher.h b/indra/llcommon/lleventdispatcher.h
index 5ab860b6dd..789a59459c 100644
--- a/indra/llcommon/lleventdispatcher.h
+++ b/indra/llcommon/lleventdispatcher.h
@@ -32,17 +32,18 @@
#if ! defined(LL_LLEVENTDISPATCHER_H)
#define LL_LLEVENTDISPATCHER_H
-#include <boost/iterator/transform_iterator.hpp>
#include <boost/function_types/is_member_function_pointer.hpp>
#include <boost/function_types/is_nonmember_callable_builtin.hpp>
-#include <boost/function_types/function_arity.hpp>
-#include <boost/function_types/result_type.hpp>
+#include <boost/hof/is_invocable.hpp> // until C++17, when we get std::is_invocable
+#include <boost/iterator/transform_iterator.hpp>
#include <functional> // std::function
#include <memory> // std::unique_ptr
#include <string>
#include <typeinfo>
#include <type_traits>
#include <utility> // std::pair
+#include "always_return.h"
+#include "function_types.h" // LL::function_arity
#include "llevents.h"
#include "llptrto.h"
#include "llsdutil.h"
@@ -78,7 +79,7 @@ public:
//@{
/// Accept any C++ callable with the right signature
- typedef std::function<void(const LLSD&)> Callable;
+ typedef std::function<LLSD(const LLSD&)> Callable;
/**
* Register a @a callable by @a name. The passed @a callable accepts a
@@ -90,19 +91,25 @@ public:
void add(const std::string& name,
const std::string& desc,
const Callable& callable,
- const LLSD& required=LLSD());
+ const LLSD& required=LLSD())
+ {
+ addLLSD(name, desc, callable, required);
+ }
- /**
- * The case of a free function (or static method) accepting(const LLSD&)
- * could also be intercepted by the arbitrary-args overload below. Ensure
- * that it's directed to the Callable overload above instead.
- */
+ template <typename CALLABLE,
+ typename=typename std::enable_if<
+ boost::hof::is_invocable<CALLABLE, LLSD>::value
+ >::type>
void add(const std::string& name,
const std::string& desc,
- void (*f)(const LLSD&),
+ CALLABLE&& callable,
const LLSD& required=LLSD())
{
- add(name, desc, Callable(f), required);
+ addLLSD(
+ name,
+ desc,
+ Callable(LL::make_always_return<LLSD>(std::forward<CALLABLE>(callable))),
+ required);
}
/**
@@ -111,6 +118,27 @@ public:
* specifying a <tt>std::bind()</tt> expression. The passed @a method
* accepts a single LLSD value, presumably containing other parameters.
*/
+ template <typename R, class CLASS>
+ void add(const std::string& name,
+ const std::string& desc,
+ R (CLASS::*method)(const LLSD&),
+ const LLSD& required=LLSD())
+ {
+ addMethod<CLASS>(name, desc, method, required);
+ }
+
+ /// Overload for both const and non-const methods. The passed @a method
+ /// accepts a single LLSD value, presumably containing other parameters.
+ template <typename R, class CLASS>
+ void add(const std::string& name,
+ const std::string& desc,
+ R (CLASS::*method)(const LLSD&) const,
+ const LLSD& required=LLSD())
+ {
+ addMethod<CLASS>(name, desc, method, required);
+ }
+
+ // because the compiler can't match a method returning void to the above
template <class CLASS>
void add(const std::string& name,
const std::string& desc,
@@ -131,6 +159,128 @@ public:
addMethod<CLASS>(name, desc, method, required);
}
+ // non-const nullary method
+ template <typename R, class CLASS>
+ void add(const std::string& name,
+ const std::string& desc,
+ R (CLASS::*method)())
+ {
+ addVMethod<CLASS>(name, desc, method);
+ }
+
+ // const nullary method
+ template <typename R, class CLASS>
+ void add(const std::string& name,
+ const std::string& desc,
+ R (CLASS::*method)() const)
+ {
+ addVMethod<CLASS>(name, desc, method);
+ }
+
+ // non-const nullary method returning void
+ template <class CLASS>
+ void add(const std::string& name,
+ const std::string& desc,
+ void (CLASS::*method)())
+ {
+ addVMethod<CLASS>(name, desc, method);
+ }
+
+ // const nullary method returning void
+ template <class CLASS>
+ void add(const std::string& name,
+ const std::string& desc,
+ void (CLASS::*method)() const)
+ {
+ addVMethod<CLASS>(name, desc, method);
+ }
+
+ // non-const unary method (but method accepting LLSD should use the other add())
+ // enable_if usage per https://stackoverflow.com/a/39913395/5533635
+ template <typename R, class CLASS, typename ARG,
+ typename = typename std::enable_if<
+ ! std::is_same<typename std::decay<ARG>::type, LLSD>::value
+ >::type>
+ void add(const std::string& name,
+ const std::string& desc,
+ R (CLASS::*method)(ARG))
+ {
+ addVMethod<CLASS>(name, desc, method);
+ }
+
+ // const unary method (but method accepting LLSD should use the other add())
+ template <typename R, class CLASS, typename ARG,
+ typename = typename std::enable_if<
+ ! std::is_same<typename std::decay<ARG>::type, LLSD>::value
+ >::type>
+ void add(const std::string& name,
+ const std::string& desc,
+ R (CLASS::*method)(ARG) const)
+ {
+ addVMethod<CLASS>(name, desc, method);
+ }
+
+ // non-const unary method returning void
+ // enable_if usage per https://stackoverflow.com/a/39913395/5533635
+ template <class CLASS, typename ARG,
+ typename = typename std::enable_if<
+ ! std::is_same<typename std::decay<ARG>::type, LLSD>::value
+ >::type>
+ void add(const std::string& name,
+ const std::string& desc,
+ void (CLASS::*method)(ARG))
+ {
+ addVMethod<CLASS>(name, desc, method);
+ }
+
+ // const unary method returning void
+ template <class CLASS, typename ARG,
+ typename = typename std::enable_if<
+ ! std::is_same<typename std::decay<ARG>::type, LLSD>::value
+ >::type>
+ void add(const std::string& name,
+ const std::string& desc,
+ void (CLASS::*method)(ARG) const)
+ {
+ addVMethod<CLASS>(name, desc, method);
+ }
+
+ // non-const binary (or more) method
+ template <typename R, class CLASS, typename ARG0, typename ARG1, typename... ARGS>
+ void add(const std::string& name,
+ const std::string& desc,
+ R (CLASS::*method)(ARG0, ARG1, ARGS...))
+ {
+ addVMethod<CLASS>(name, desc, method);
+ }
+
+ // const binary (or more) method
+ template <typename R, class CLASS, typename ARG0, typename ARG1, typename... ARGS>
+ void add(const std::string& name,
+ const std::string& desc,
+ R (CLASS::*method)(ARG0, ARG1, ARGS...) const)
+ {
+ addVMethod<CLASS>(name, desc, method);
+ }
+
+ // non-const binary (or more) method returning void
+ template <class CLASS, typename ARG0, typename ARG1, typename... ARGS>
+ void add(const std::string& name,
+ const std::string& desc,
+ void (CLASS::*method)(ARG0, ARG1, ARGS...))
+ {
+ addVMethod<CLASS>(name, desc, method);
+ }
+
+ // const binary (or more) method returning void
+ template <class CLASS, typename ARG0, typename ARG1, typename... ARGS>
+ void add(const std::string& name,
+ const std::string& desc,
+ void (CLASS::*method)(ARG0, ARG1, ARGS...) const)
+ {
+ addVMethod<CLASS>(name, desc, method);
+ }
+
//@}
/// @name Register functions with arbitrary param lists
@@ -143,12 +293,16 @@ public:
* When calling this name, pass an LLSD::Array. Each entry in turn will be
* converted to the corresponding parameter type using LLSDParam.
*/
- // enable_if usage per https://stackoverflow.com/a/39913395/5533635
- template<typename Function,
- typename = typename std::enable_if<
- boost::function_types::is_nonmember_callable_builtin<Function>::value
+ template <typename CALLABLE,
+ typename=typename std::enable_if<
+ ! boost::hof::is_invocable<CALLABLE, LLSD>()
>::type>
- void add(const std::string& name, const std::string& desc, Function f);
+ void add(const std::string& name,
+ const std::string& desc,
+ CALLABLE&& f)
+ {
+ addV(name, desc, f);
+ }
/**
* Register a nonstatic class method with arbitrary parameters.
@@ -156,11 +310,7 @@ public:
* To cover cases such as a method on an LLSingleton we don't yet want to
* instantiate, instead of directly storing an instance pointer, accept a
* nullary callable returning a pointer/reference to the desired class
- * instance. If you already have an instance in hand,
- * boost::lambda::var(instance) or boost::lambda::constant(instance_ptr)
- * produce suitable callables.
- *
- * TODO: variant accepting a method of the containing class, no getter.
+ * instance.
*
* When calling this name, pass an LLSD::Array. Each entry in turn will be
* converted to the corresponding parameter type using LLSDParam.
@@ -186,7 +336,8 @@ public:
*/
template<typename Function,
typename = typename std::enable_if<
- boost::function_types::is_nonmember_callable_builtin<Function>::value
+ boost::function_types::is_nonmember_callable_builtin<Function>::value &&
+ ! boost::hof::is_invocable<Function, LLSD>::value
>::type>
void add(const std::string& name, const std::string& desc, Function f,
const LLSD& params, const LLSD& defaults=LLSD());
@@ -348,6 +499,11 @@ public:
friend std::ostream& operator<<(std::ostream&, const LLEventDispatcher&);
private:
+ void addLLSD(const std::string& name,
+ const std::string& desc,
+ const Callable& callable,
+ const LLSD& required);
+
template <class CLASS, typename METHOD>
void addMethod(const std::string& name, const std::string& desc,
const METHOD& method, const LLSD& required)
@@ -359,9 +515,40 @@ private:
}
else
{
- add(name, desc, std::bind(method, downcast, std::placeholders::_1), required);
+ add(name,
+ desc,
+ Callable(LL::make_always_return<LLSD>(
+ [downcast, method]
+ (const LLSD& args)
+ {
+ return (downcast->*method)(args);
+ })),
+ required);
}
}
+
+ template <class CLASS, typename METHOD>
+ void addVMethod(const std::string& name, const std::string& desc,
+ const METHOD& method)
+ {
+ CLASS* downcast = dynamic_cast<CLASS*>(this);
+ if (! downcast)
+ {
+ addFail(name, typeid(CLASS).name());
+ }
+ else
+ {
+ // add() arbitrary method plus InstanceGetter, where the
+ // InstanceGetter in this case returns 'this'. We don't need to
+ // worry about binding 'this' because, once this LLEventDispatcher
+ // is destroyed, the DispatchEntry goes away too.
+ add(name, desc, method, [downcast](){ return downcast; });
+ }
+ }
+
+ template <typename Function>
+ void addV(const std::string& name, const std::string& desc, Function f);
+
void addFail(const std::string& name, const std::string& classname) const;
LLSD try_call(const std::string& key, const std::string& name,
const LLSD& event) const;
@@ -405,21 +592,19 @@ private:
const invoker_function& invoker,
const LLSD& params,
const LLSD& defaults);
- template <typename RETURNTYPE>
- struct ReturnLLSD;
};
/*****************************************************************************
* LLEventDispatcher template implementation details
*****************************************************************************/
-template<typename Function, typename>
-void LLEventDispatcher::add(const std::string& name, const std::string& desc, Function f)
+template <typename Function>
+void LLEventDispatcher::addV(const std::string& name, const std::string& desc, Function f)
{
// Construct an invoker_function, a callable accepting const LLSD&.
// Add to DispatchMap an ArrayParamsDispatchEntry that will handle the
// caller's LLSD::Array.
addArrayParamsDispatchEntry(name, desc, make_invoker(f),
- boost::function_types::function_arity<Function>::value);
+ LL::function_arity<Function>::value);
}
template<typename Method, typename InstanceGetter, typename>
@@ -429,7 +614,7 @@ void LLEventDispatcher::add(const std::string& name, const std::string& desc, Me
// Subtract 1 from the compile-time arity because the getter takes care of
// the first parameter. We only need (arity - 1) additional arguments.
addArrayParamsDispatchEntry(name, desc, make_invoker(f, getter),
- boost::function_types::function_arity<Method>::value - 1);
+ LL::function_arity<Method>::value - 1);
}
template<typename Function, typename>
@@ -448,64 +633,23 @@ void LLEventDispatcher::add(const std::string& name, const std::string& desc, Me
addMapParamsDispatchEntry(name, desc, make_invoker(f, getter), params, defaults);
}
-// general case, when f() has a non-void return type
-template <typename RETURNTYPE>
-struct LLEventDispatcher::ReturnLLSD
-{
- template <typename Function>
- LLSD operator()(Function f, const LLSD& args)
- {
- return { LL::apply(f, args) };
- }
-
- template <typename Method, typename InstanceGetter>
- LLSD operator()(Method f, const InstanceGetter& getter, const LLSD& args)
- {
- constexpr auto arity = boost::function_types::function_arity<
- typename std::remove_reference<Method>::type>::value - 1;
-
- // Use bind_front() to bind the method to (a pointer to) the object
- // returned by getter(). It's okay to capture and bind a pointer
- // because this bind_front() object will last only as long as this
- // operator() call.
- return { LL::apply_n<arity>(LL::bind_front(f, LL::get_ptr(getter())), args) };
- }
-};
-
-// specialize for void return type
-template <>
-struct LLEventDispatcher::ReturnLLSD<void>
-{
- template <typename Function>
- LLSD operator()(Function f, const LLSD& args)
- {
- LL::apply(f, args);
- return {};
- }
-
- template <typename Method, typename InstanceGetter>
- LLSD operator()(Method f, const InstanceGetter& getter, const LLSD& args)
- {
- constexpr auto arity = boost::function_types::function_arity<
- typename std::remove_reference<Method>::type>::value - 1;
-
- // Use bind_front() to bind the method to (a pointer to) the object
- // returned by getter(). It's okay to capture and bind a pointer
- // because this bind_front() object will last only as long as this
- // operator() call.
- LL::apply_n<arity>(LL::bind_front(f, LL::get_ptr(getter())), args);
- return {};
- }
-};
-
template <typename Function>
LLEventDispatcher::invoker_function
LLEventDispatcher::make_invoker(Function f)
{
+ // Return an invoker_function that accepts (const LLSD& args).
return [f](const LLSD& args)
{
- return ReturnLLSD<typename boost::function_types::result_type<Function>::type>()
- (f, args);
+ // When called, call always_return<LLSD>, directing it to call
+ // f(expanded args). always_return<LLSD> guarantees we'll get an LLSD
+ // value back, even if it's undefined because 'f' doesn't return a
+ // type convertible to LLSD.
+ return LL::always_return<LLSD>(
+ [f, args]
+ ()
+ {
+ return LL::apply(f, args);
+ });
};
}
@@ -513,10 +657,24 @@ template <typename Method, typename InstanceGetter>
LLEventDispatcher::invoker_function
LLEventDispatcher::make_invoker(Method f, const InstanceGetter& getter)
{
+ // function_arity<member function> includes its implicit 'this' pointer
+ constexpr auto arity = LL::function_arity<
+ typename std::remove_reference<Method>::type>::value - 1;
+
return [f, getter](const LLSD& args)
{
- return ReturnLLSD<typename boost::function_types::result_type<Method>::type>()
- (f, getter, args);
+ // always_return<LLSD>() immediately calls the lambda we pass, and
+ // returns LLSD whether our passed lambda returns void or non-void.
+ return LL::always_return<LLSD>(
+ [f, getter, args]
+ ()
+ {
+ // Use bind_front() to bind the method to (a pointer to) the object
+ // returned by getter(). It's okay to capture and bind a pointer
+ // because this bind_front() object will last only as long as this
+ // lambda call.
+ return LL::apply_n<arity>(LL::bind_front(f, LL::get_ptr(getter())), args);
+ });
};
}
diff --git a/indra/llcommon/llsdutil.h b/indra/llcommon/llsdutil.h
index baf4400768..a6fd2fdac2 100644
--- a/indra/llcommon/llsdutil.h
+++ b/indra/llcommon/llsdutil.h
@@ -30,9 +30,9 @@
#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 <boost/function_types/function_arity.hpp>
#include <cassert>
#include <type_traits>
@@ -628,7 +628,7 @@ template <typename CALLABLE>
auto apply(CALLABLE&& func, const LLSD& args)
{
// infer arity from the definition of func
- constexpr auto arity = boost::function_types::function_arity<
+ 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);
diff --git a/indra/llcommon/tests/lleventdispatcher_test.cpp b/indra/llcommon/tests/lleventdispatcher_test.cpp
index 8e84a9e038..2a3644e2e1 100644
--- a/indra/llcommon/tests/lleventdispatcher_test.cpp
+++ b/indra/llcommon/tests/lleventdispatcher_test.cpp
@@ -423,9 +423,9 @@ namespace tut
work.add(name, desc, &Dispatcher::cmethod1, required);
// Non-subclass method with/out required params
addf("method1", "method1", &v);
- work.add(name, desc, boost::bind(&Vars::method1, boost::ref(v), _1));
+ work.add(name, desc, [this](const LLSD& args){ return v.method1(args); });
addf("method1_req", "method1", &v);
- work.add(name, desc, boost::bind(&Vars::method1, boost::ref(v), _1), required);
+ work.add(name, desc, [this](const LLSD& args){ return v.method1(args); }, required);
/*--------------- Arbitrary params, array style ----------------*/
@@ -609,6 +609,7 @@ namespace tut
void addf(const std::string& n, const std::string& d, Vars* v)
{
+ debug("addf('", n, "', '", d, "')");
// This method is to capture in our own DescMap the name and
// description of every registered function, for metadata query
// testing.
@@ -1339,22 +1340,25 @@ namespace tut
DispatchResult(): LLDispatchListener("results", "op")
{
- // As of 2022-12-22, LLEventDispatcher's shorthand add() methods
- // for pointer-to-method of same instance only support methods
- // with signature void(const LLSD&). The generic add(pointer-to-
- // method) requires an instance getter.
- add("strfunc", "return string", &DR::strfunc, [this](){ return this; });
- add("voidfunc", "void function", &DR::voidfunc, [this](){ return this; });
- add("emptyfunc", "return empty LLSD", &DR::emptyfunc, [this](){ return this; });
- add("intfunc", "return Integer LLSD", &DR::intfunc, [this](){ return this; });
- add("mapfunc", "return map LLSD", &DR::mapfunc, [this](){ return this; });
- add("arrayfunc", "return array LLSD", &DR::arrayfunc, [this](){ return this; });
+ add("strfunc", "return string", &DR::strfunc);
+ add("voidfunc", "void function", &DR::voidfunc);
+ add("emptyfunc", "return empty LLSD", &DR::emptyfunc);
+ add("intfunc", "return Integer LLSD", &DR::intfunc);
+ add("llsdfunc", "return passed LLSD", &DR::llsdfunc);
+ add("mapfunc", "return map LLSD", &DR::mapfunc);
+ add("arrayfunc", "return array LLSD", &DR::arrayfunc);
}
std::string strfunc(const std::string& str) const { return "got " + str; }
void voidfunc() const {}
LLSD emptyfunc() const { return {}; }
int intfunc(int i) const { return -i; }
+ LLSD llsdfunc(const LLSD& event) const
+ {
+ LLSD result{ event };
+ result["with"] = "string";
+ return result;
+ }
LLSD mapfunc(int i, const std::string& str) const
{
return llsd::map("i", intfunc(i), "str", strfunc(str));
@@ -1395,6 +1399,16 @@ namespace tut
template<> template<>
void object::test<26>()
{
+ set_test_name("LLSD echo");
+ DispatchResult service;
+ LLSD result{ service("llsdfunc", llsd::map("op", "llsdfunc", "reqid", 17)) };
+ ensure_equals("llsdfunc() mismatch", result,
+ llsd::map("op", "llsdfunc", "reqid", 17, "with", "string"));
+ }
+
+ template<> template<>
+ void object::test<27>()
+ {
set_test_name("map LLSD result");
DispatchResult service;
LLSD result{ service("mapfunc", llsd::array(-12, "value")) };
@@ -1402,7 +1416,7 @@ namespace tut
}
template<> template<>
- void object::test<27>()
+ void object::test<28>()
{
set_test_name("array LLSD result");
DispatchResult service;
@@ -1411,7 +1425,7 @@ namespace tut
}
template<> template<>
- void object::test<28>()
+ void object::test<29>()
{
set_test_name("listener error, no reply");
DispatchResult service;
@@ -1422,7 +1436,7 @@ namespace tut
}
template<> template<>
- void object::test<29>()
+ void object::test<30>()
{
set_test_name("listener error with reply");
DispatchResult service;
@@ -1435,7 +1449,7 @@ namespace tut
}
template<> template<>
- void object::test<30>()
+ void object::test<31>()
{
set_test_name("listener call to void function");
DispatchResult service;
@@ -1452,7 +1466,7 @@ namespace tut
}
template<> template<>
- void object::test<31>()
+ void object::test<32>()
{
set_test_name("listener call to string function");
DispatchResult service;
@@ -1468,7 +1482,7 @@ namespace tut
}
template<> template<>
- void object::test<32>()
+ void object::test<33>()
{
set_test_name("listener call to map function");
DispatchResult service;
@@ -1485,7 +1499,7 @@ namespace tut
}
template<> template<>
- void object::test<33>()
+ void object::test<34>()
{
set_test_name("batched map success");
DispatchResult service;
@@ -1512,7 +1526,7 @@ namespace tut
}
template<> template<>
- void object::test<34>()
+ void object::test<35>()
{
set_test_name("batched map error");
DispatchResult service;
@@ -1545,7 +1559,7 @@ namespace tut
}
template<> template<>
- void object::test<35>()
+ void object::test<36>()
{
set_test_name("batched map exception");
DispatchResult service;
@@ -1568,7 +1582,7 @@ namespace tut
}
template<> template<>
- void object::test<36>()
+ void object::test<37>()
{
set_test_name("batched array success");
DispatchResult service;
@@ -1602,7 +1616,7 @@ namespace tut
}
template<> template<>
- void object::test<37>()
+ void object::test<38>()
{
set_test_name("batched array error");
DispatchResult service;
@@ -1633,7 +1647,7 @@ namespace tut
}
template<> template<>
- void object::test<38>()
+ void object::test<39>()
{
set_test_name("batched array exception");
DispatchResult service;