/** * @file lleventdispatcher.h * @author Nat Goodspeed * @date 2009-06-18 * @brief Central mechanism for dispatching events by string name. This is * useful when you have a single LLEventPump listener on which you can * request different operations, vs. instantiating a different * LLEventPump for each such operation. * * $LicenseInfo:firstyear=2009&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$ * * The invoker machinery that constructs a boost::fusion argument list for use * with boost::fusion::invoke() is derived from * http://www.boost.org/doc/libs/1_45_0/libs/function_types/example/interpreter.hpp * whose license information is copied below: * * "(C) Copyright Tobias Schwinger * * Use modification and distribution are subject to the boost Software License, * Version 1.0. (See http://www.boost.org/LICENSE_1_0.txt)." */ #if ! defined(LL_LLEVENTDISPATCHER_H) #define LL_LLEVENTDISPATCHER_H // nil is too generic a term to be allowed to be a global macro. In // particular, boost::fusion defines a 'class nil' (properly encapsulated in a // namespace) that a global 'nil' macro breaks badly. #if defined(nil) // Capture the value of the macro 'nil', hoping int is an appropriate type. static const int nil_(nil); // Now forget the macro. #undef nil // Finally, reintroduce 'nil' as a properly-scoped alias for the previously- // defined const 'nil_'. Make it static since otherwise it produces duplicate- // symbol link errors later. static const int& nil(nil_); #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "llevents.h" #include "llsdutil.h" class LLSD; /** * Given an LLSD map, examine a string-valued key and call a corresponding * callable. This class is designed to be contained by an LLEventPump * listener class that will register some of its own methods, though any * callable can be used. */ class LL_COMMON_API LLEventDispatcher { public: LLEventDispatcher(const std::string& desc, const std::string& key); virtual ~LLEventDispatcher(); /// @name Register functions accepting(const LLSD&) //@{ /// Accept any C++ callable with the right signature, typically a /// boost::bind() expression typedef boost::function Callable; /** * Register a @a callable by @a name. The passed @a callable accepts a * single LLSD value and uses it in any way desired, e.g. extract * parameters and call some other function. The optional @a required * parameter is used to validate the structure of each incoming event (see * llsd_matches()). */ void add(const std::string& name, const std::string& desc, const Callable& callable, const LLSD& required=LLSD()); /** * 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. */ void add(const std::string& name, const std::string& desc, void (*f)(const LLSD&), const LLSD& required=LLSD()) { add(name, desc, Callable(f), required); } /** * Special case: a subclass of this class can pass an unbound member * function pointer (of an LLEventDispatcher subclass) without explicitly * specifying the boost::bind() expression. The passed @a method * accepts a single LLSD value, presumably containing other parameters. */ template void add(const std::string& name, const std::string& desc, void (CLASS::*method)(const LLSD&), const LLSD& required=LLSD()) { addMethod(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 void add(const std::string& name, const std::string& desc, void (CLASS::*method)(const LLSD&) const, const LLSD& required=LLSD()) { addMethod(name, desc, method, required); } //@} /// @name Register functions with arbitrary param lists //@{ /** * Register a free function with arbitrary parameters. (This also works * for static class methods.) * * @note This supports functions with up to about 6 parameters -- after * that you start getting dismaying compile errors in which * boost::fusion::joint_view is mentioned a surprising number of times. * * When calling this name, pass an LLSD::Array. Each entry in turn will be * converted to the corresponding parameter type using LLSDParam. */ template typename boost::enable_if< boost::function_types::is_nonmember_callable_builtin >::type add(const std::string& name, const std::string& desc, Function f); /** * Register a nonstatic class method with arbitrary parameters. * * @note This supports functions with up to about 6 parameters -- after * that you start getting dismaying compile errors in which * boost::fusion::joint_view is mentioned a surprising number of times. * * 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. * * When calling this name, pass an LLSD::Array. Each entry in turn will be * converted to the corresponding parameter type using LLSDParam. */ template typename boost::enable_if< boost::function_types::is_member_function_pointer >::type add(const std::string& name, const std::string& desc, Method f, const InstanceGetter& getter); /** * Register a free function with arbitrary parameters. (This also works * for static class methods.) * * @note This supports functions with up to about 6 parameters -- after * that you start getting dismaying compile errors in which * boost::fusion::joint_view is mentioned a surprising number of times. * * Pass an LLSD::Array of parameter names, and optionally another * LLSD::Array of default parameter values, a la LLSDArgsMapper. * * When calling this name, pass an LLSD::Map. We will internally generate * an LLSD::Array using LLSDArgsMapper and then convert each entry in turn * to the corresponding parameter type using LLSDParam. */ template typename boost::enable_if< boost::function_types::is_nonmember_callable_builtin >::type add(const std::string& name, const std::string& desc, Function f, const LLSD& params, const LLSD& defaults=LLSD()); /** * Register a nonstatic class method with arbitrary parameters. * * @note This supports functions with up to about 6 parameters -- after * that you start getting dismaying compile errors in which * boost::fusion::joint_view is mentioned a surprising number of times. * * 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. * * Pass an LLSD::Array of parameter names, and optionally another * LLSD::Array of default parameter values, a la LLSDArgsMapper. * * When calling this name, pass an LLSD::Map. We will internally generate * an LLSD::Array using LLSDArgsMapper and then convert each entry in turn * to the corresponding parameter type using LLSDParam. */ template typename boost::enable_if< boost::function_types::is_member_function_pointer >::type add(const std::string& name, const std::string& desc, Method f, const InstanceGetter& getter, const LLSD& params, const LLSD& defaults=LLSD()); //@} /// Unregister a callable bool remove(const std::string& name); /// Call a registered callable with an explicitly-specified name. If no /// such callable exists, die with LL_ERRS. If the @a event fails to match /// the @a required prototype specified at add() time, die with LL_ERRS. void operator()(const std::string& name, const LLSD& event) const; /// Call a registered callable with an explicitly-specified name and /// return true. If no such callable exists, return /// false. If the @a event fails to match the @a required /// prototype specified at add() time, die with LL_ERRS. bool try_call(const std::string& name, const LLSD& event) const; /// Extract the @a key value from the incoming @a event, and call the /// callable whose name is specified by that map @a key. If no such /// callable exists, die with LL_ERRS. If the @a event fails to match the /// @a required prototype specified at add() time, die with LL_ERRS. void operator()(const LLSD& event) const; /// Extract the @a key value from the incoming @a event, call the callable /// whose name is specified by that map @a key and return true. /// If no such callable exists, return false. If the @a event /// fails to match the @a required prototype specified at add() time, die /// with LL_ERRS. bool try_call(const LLSD& event) const; /// @name Iterate over defined names //@{ typedef std::pair NameDesc; private: struct DispatchEntry { DispatchEntry(const std::string& desc); virtual ~DispatchEntry() {} // suppress MSVC warning, sigh std::string mDesc; virtual void call(const std::string& desc, const LLSD& event) const = 0; virtual LLSD addMetadata(LLSD) const = 0; }; // Tried using boost::ptr_map, but ptr_map<> // wants its value type to be "clonable," even just to dereference an // iterator. I don't want to clone entries -- if I have to copy an entry // around, I want it to continue pointing to the same DispatchEntry // subclass object. However, I definitely want DispatchMap to destroy // DispatchEntry if no references are outstanding at the time an entry is // removed. This looks like a job for boost::shared_ptr. typedef std::map > DispatchMap; public: /// We want the flexibility to redefine what data we store per name, /// therefore our public interface doesn't expose DispatchMap iterators, /// or DispatchMap itself, or DispatchEntry. Instead we explicitly /// transform each DispatchMap item to NameDesc on dereferencing. typedef boost::transform_iterator const_iterator; const_iterator begin() const { return boost::make_transform_iterator(mDispatch.begin(), makeNameDesc); } const_iterator end() const { return boost::make_transform_iterator(mDispatch.end(), makeNameDesc); } //@} /// Get information about a specific Callable LLSD getMetadata(const std::string& name) const; /// Retrieve the LLSD key we use for one-arg operator() method std::string getDispatchKey() const { return mKey; } private: template void addMethod(const std::string& name, const std::string& desc, const METHOD& method, const LLSD& required) { CLASS* downcast = dynamic_cast(this); if (! downcast) { addFail(name, typeid(CLASS).name()); } else { add(name, desc, boost::bind(method, downcast, _1), required); } } void addFail(const std::string& name, const std::string& classname) const; std::string mDesc, mKey; DispatchMap mDispatch; static NameDesc makeNameDesc(const DispatchMap::value_type& item) { return NameDesc(item.first, item.second->mDesc); } struct LLSDDispatchEntry; struct ParamsDispatchEntry; struct ArrayParamsDispatchEntry; struct MapParamsDispatchEntry; // Step 2 of parameter analysis. Instantiating invoker // implicitly sets its From and To parameters to the (compile time) begin // and end iterators over that function's parameter types. template< typename Function , class From = typename boost::mpl::begin< boost::function_types::parameter_types >::type , class To = typename boost::mpl::end< boost::function_types::parameter_types >::type > struct invoker; // deliver LLSD arguments one at a time typedef boost::function args_source; // obtain args from an args_source to build param list and call target // function typedef boost::function invoker_function; template invoker_function make_invoker(Function f); template invoker_function make_invoker(Method f, const InstanceGetter& getter); void addArrayParamsDispatchEntry(const std::string& name, const std::string& desc, const invoker_function& invoker, LLSD::Integer arity); void addMapParamsDispatchEntry(const std::string& name, const std::string& desc, const invoker_function& invoker, const LLSD& params, const LLSD& defaults); }; /***************************************************************************** * LLEventDispatcher template implementation details *****************************************************************************/ // Step 3 of parameter analysis, the recursive case. template struct LLEventDispatcher::invoker { template struct remove_cv_ref : boost::remove_cv< typename boost::remove_reference::type > { }; // apply() accepts an arbitrary boost::fusion sequence as args. It // examines the next parameter type in the parameter-types sequence // bounded by From and To, obtains the next LLSD object from the passed // args_source and constructs an LLSDParam of appropriate type to try // to convert the value. It then recurs with the next parameter-types // iterator, passing the args sequence thus far. template static inline void apply(Function func, const args_source& argsrc, Args const & args) { typedef typename boost::mpl::deref::type arg_type; typedef typename boost::mpl::next::type next_iter_type; typedef typename remove_cv_ref::type plain_arg_type; invoker::apply ( func, argsrc, boost::fusion::push_back(args, LLSDParam(argsrc()))); } // Special treatment for instance (first) parameter of a non-static member // function. Accept the instance-getter callable, calling that to produce // the first args value. Since we know we're at the top of the recursion // chain, we need not also require a partial args sequence from our caller. template static inline void method_apply(Function func, const args_source& argsrc, const InstanceGetter& getter) { typedef typename boost::mpl::next::type next_iter_type; // Instead of grabbing the first item from argsrc and making an // LLSDParam of it, call getter() and pass that as the instance param. invoker::apply ( func, argsrc, boost::fusion::push_back(boost::fusion::nil(), boost::ref(getter()))); } }; // Step 4 of parameter analysis, the leaf case. When the general // invoker logic has advanced From until it matches To, // the compiler will pick this template specialization. template struct LLEventDispatcher::invoker { // the argument list is complete, now call the function template static inline void apply(Function func, const args_source&, Args const & args) { boost::fusion::invoke(func, args); } }; template typename boost::enable_if< boost::function_types::is_nonmember_callable_builtin >::type LLEventDispatcher::add(const std::string& name, const std::string& desc, Function f) { // Construct an invoker_function, a callable accepting const args_source&. // Add to DispatchMap an ArrayParamsDispatchEntry that will handle the // caller's LLSD::Array. addArrayParamsDispatchEntry(name, desc, make_invoker(f), boost::function_types::function_arity::value); } template typename boost::enable_if< boost::function_types::is_member_function_pointer >::type LLEventDispatcher::add(const std::string& name, const std::string& desc, Method f, const InstanceGetter& getter) { // 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::value - 1); } template typename boost::enable_if< boost::function_types::is_nonmember_callable_builtin >::type LLEventDispatcher::add(const std::string& name, const std::string& desc, Function f, const LLSD& params, const LLSD& defaults) { // See comments for previous is_nonmember_callable_builtin add(). addMapParamsDispatchEntry(name, desc, make_invoker(f), params, defaults); } template typename boost::enable_if< boost::function_types::is_member_function_pointer >::type LLEventDispatcher::add(const std::string& name, const std::string& desc, Method f, const InstanceGetter& getter, const LLSD& params, const LLSD& defaults) { addMapParamsDispatchEntry(name, desc, make_invoker(f, getter), params, defaults); } template LLEventDispatcher::invoker_function LLEventDispatcher::make_invoker(Function f) { // Step 1 of parameter analysis, the top of the recursion. Passing a // suitable f (see add()'s enable_if condition) to this method causes it // to infer the function type; specifying that function type to invoker<> // causes it to fill in the begin/end MPL iterators over the function's // list of parameter types. // While normally invoker::apply() could infer its template type from the // boost::fusion::nil parameter value, here we must be explicit since // we're boost::bind()ing it rather than calling it directly. return boost::bind(&invoker::template apply, f, _1, boost::fusion::nil()); } template LLEventDispatcher::invoker_function LLEventDispatcher::make_invoker(Method f, const InstanceGetter& getter) { // Use invoker::method_apply() to treat the instance (first) arg specially. return boost::bind(&invoker::template method_apply, f, _1, getter); } /***************************************************************************** * LLDispatchListener *****************************************************************************/ /** * Bundle an LLEventPump and a listener with an LLEventDispatcher. A class * that contains (or derives from) LLDispatchListener need only specify the * LLEventPump name and dispatch key, and add() its methods. Incoming events * will automatically be dispatched. */ class LL_COMMON_API LLDispatchListener: public LLEventDispatcher { public: LLDispatchListener(const std::string& pumpname, const std::string& key); std::string getPumpName() const { return mPump.getName(); } private: bool process(const LLSD& event); LLEventStream mPump; LLTempBoundListener mBoundListener; }; #endif /* ! defined(LL_LLEVENTDISPATCHER_H) */