PageRenderTime 39ms CodeModel.GetById 1ms app.highlight 32ms RepoModel.GetById 1ms app.codeStats 0ms

/indra/llcommon/lleventdispatcher.h

https://bitbucket.org/lindenlab/viewer-beta/
C++ Header | 541 lines | 258 code | 54 blank | 229 comment | 1 complexity | 672eb9173ffdbae8ca494c34c51241b7 MD5 | raw file
  1/**
  2 * @file   lleventdispatcher.h
  3 * @author Nat Goodspeed
  4 * @date   2009-06-18
  5 * @brief  Central mechanism for dispatching events by string name. This is
  6 *         useful when you have a single LLEventPump listener on which you can
  7 *         request different operations, vs. instantiating a different
  8 *         LLEventPump for each such operation.
  9 * 
 10 * $LicenseInfo:firstyear=2009&license=viewerlgpl$
 11 * Second Life Viewer Source Code
 12 * Copyright (C) 2010, Linden Research, Inc.
 13 * 
 14 * This library is free software; you can redistribute it and/or
 15 * modify it under the terms of the GNU Lesser General Public
 16 * License as published by the Free Software Foundation;
 17 * version 2.1 of the License only.
 18 * 
 19 * This library is distributed in the hope that it will be useful,
 20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 22 * Lesser General Public License for more details.
 23 * 
 24 * You should have received a copy of the GNU Lesser General Public
 25 * License along with this library; if not, write to the Free Software
 26 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 27 * 
 28 * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
 29 * $/LicenseInfo$
 30 *
 31 * The invoker machinery that constructs a boost::fusion argument list for use
 32 * with boost::fusion::invoke() is derived from
 33 * http://www.boost.org/doc/libs/1_45_0/libs/function_types/example/interpreter.hpp
 34 * whose license information is copied below:
 35 *
 36 * "(C) Copyright Tobias Schwinger
 37 *
 38 * Use modification and distribution are subject to the boost Software License,
 39 * Version 1.0. (See http://www.boost.org/LICENSE_1_0.txt)."
 40 */
 41
 42#if ! defined(LL_LLEVENTDISPATCHER_H)
 43#define LL_LLEVENTDISPATCHER_H
 44
 45// nil is too generic a term to be allowed to be a global macro. In
 46// particular, boost::fusion defines a 'class nil' (properly encapsulated in a
 47// namespace) that a global 'nil' macro breaks badly.
 48#if defined(nil)
 49// Capture the value of the macro 'nil', hoping int is an appropriate type.
 50static const int nil_(nil);
 51// Now forget the macro.
 52#undef nil
 53// Finally, reintroduce 'nil' as a properly-scoped alias for the previously-
 54// defined const 'nil_'. Make it static since otherwise it produces duplicate-
 55// symbol link errors later.
 56static const int& nil(nil_);
 57#endif
 58
 59#include <string>
 60#include <boost/shared_ptr.hpp>
 61#include <boost/function.hpp>
 62#include <boost/bind.hpp>
 63#include <boost/iterator/transform_iterator.hpp>
 64#include <boost/utility/enable_if.hpp>
 65#include <boost/function_types/is_nonmember_callable_builtin.hpp>
 66#include <boost/function_types/parameter_types.hpp>
 67#include <boost/function_types/function_arity.hpp>
 68#include <boost/type_traits/remove_cv.hpp>
 69#include <boost/type_traits/remove_reference.hpp>
 70#include <boost/fusion/include/push_back.hpp>
 71#include <boost/fusion/include/cons.hpp>
 72#include <boost/fusion/include/invoke.hpp>
 73#include <boost/mpl/begin.hpp>
 74#include <boost/mpl/end.hpp>
 75#include <boost/mpl/next.hpp>
 76#include <boost/mpl/deref.hpp>
 77#include <typeinfo>
 78#include "llevents.h"
 79#include "llsdutil.h"
 80
 81class LLSD;
 82
 83/**
 84 * Given an LLSD map, examine a string-valued key and call a corresponding
 85 * callable. This class is designed to be contained by an LLEventPump
 86 * listener class that will register some of its own methods, though any
 87 * callable can be used.
 88 */
 89class LL_COMMON_API LLEventDispatcher
 90{
 91public:
 92    LLEventDispatcher(const std::string& desc, const std::string& key);
 93    virtual ~LLEventDispatcher();
 94
 95    /// @name Register functions accepting(const LLSD&)
 96    //@{
 97
 98    /// Accept any C++ callable with the right signature, typically a
 99    /// boost::bind() expression
100    typedef boost::function<void(const LLSD&)> Callable;
101
102    /**
103     * Register a @a callable by @a name. The passed @a callable accepts a
104     * single LLSD value and uses it in any way desired, e.g. extract
105     * parameters and call some other function. The optional @a required
106     * parameter is used to validate the structure of each incoming event (see
107     * llsd_matches()).
108     */
109    void add(const std::string& name,
110             const std::string& desc,
111             const Callable& callable,
112             const LLSD& required=LLSD());
113
114    /**
115     * The case of a free function (or static method) accepting(const LLSD&)
116     * could also be intercepted by the arbitrary-args overload below. Ensure
117     * that it's directed to the Callable overload above instead.
118     */
119    void add(const std::string& name,
120             const std::string& desc,
121             void (*f)(const LLSD&),
122             const LLSD& required=LLSD())
123    {
124        add(name, desc, Callable(f), required);
125    }
126
127    /**
128     * Special case: a subclass of this class can pass an unbound member
129     * function pointer (of an LLEventDispatcher subclass) without explicitly
130     * specifying the <tt>boost::bind()</tt> expression. The passed @a method
131     * accepts a single LLSD value, presumably containing other parameters.
132     */
133    template <class CLASS>
134    void add(const std::string& name,
135             const std::string& desc,
136             void (CLASS::*method)(const LLSD&),
137             const LLSD& required=LLSD())
138    {
139        addMethod<CLASS>(name, desc, method, required);
140    }
141
142    /// Overload for both const and non-const methods. The passed @a method
143    /// accepts a single LLSD value, presumably containing other parameters.
144    template <class CLASS>
145    void add(const std::string& name,
146             const std::string& desc,
147             void (CLASS::*method)(const LLSD&) const,
148             const LLSD& required=LLSD())
149    {
150        addMethod<CLASS>(name, desc, method, required);
151    }
152
153    //@}
154
155    /// @name Register functions with arbitrary param lists
156    //@{
157
158    /**
159     * Register a free function with arbitrary parameters. (This also works
160     * for static class methods.)
161     *
162     * @note This supports functions with up to about 6 parameters -- after
163     * that you start getting dismaying compile errors in which
164     * boost::fusion::joint_view is mentioned a surprising number of times.
165     *
166     * When calling this name, pass an LLSD::Array. Each entry in turn will be
167     * converted to the corresponding parameter type using LLSDParam.
168     */
169    template<typename Function>
170    typename boost::enable_if< boost::function_types::is_nonmember_callable_builtin<Function>
171                               >::type add(const std::string& name,
172                                           const std::string& desc,
173                                           Function f);
174
175    /**
176     * Register a nonstatic class method with arbitrary parameters.
177     *
178     * @note This supports functions with up to about 6 parameters -- after
179     * that you start getting dismaying compile errors in which
180     * boost::fusion::joint_view is mentioned a surprising number of times.
181     *
182     * To cover cases such as a method on an LLSingleton we don't yet want to
183     * instantiate, instead of directly storing an instance pointer, accept a
184     * nullary callable returning a pointer/reference to the desired class
185     * instance. If you already have an instance in hand,
186     * boost::lambda::var(instance) or boost::lambda::constant(instance_ptr)
187     * produce suitable callables.
188     *
189     * When calling this name, pass an LLSD::Array. Each entry in turn will be
190     * converted to the corresponding parameter type using LLSDParam.
191     */
192    template<typename Method, typename InstanceGetter>
193    typename boost::enable_if< boost::function_types::is_member_function_pointer<Method>
194                               >::type add(const std::string& name,
195                                           const std::string& desc,
196                                           Method f,
197                                           const InstanceGetter& getter);
198
199    /**
200     * Register a free function with arbitrary parameters. (This also works
201     * for static class methods.)
202     *
203     * @note This supports functions with up to about 6 parameters -- after
204     * that you start getting dismaying compile errors in which
205     * boost::fusion::joint_view is mentioned a surprising number of times.
206     *
207     * Pass an LLSD::Array of parameter names, and optionally another
208     * LLSD::Array of default parameter values, a la LLSDArgsMapper.
209     *
210     * When calling this name, pass an LLSD::Map. We will internally generate
211     * an LLSD::Array using LLSDArgsMapper and then convert each entry in turn
212     * to the corresponding parameter type using LLSDParam.
213     */
214    template<typename Function>
215    typename boost::enable_if< boost::function_types::is_nonmember_callable_builtin<Function>
216                               >::type add(const std::string& name,
217                                           const std::string& desc,
218                                           Function f,
219                                           const LLSD& params,
220                                           const LLSD& defaults=LLSD());
221
222    /**
223     * Register a nonstatic class method with arbitrary parameters.
224     *
225     * @note This supports functions with up to about 6 parameters -- after
226     * that you start getting dismaying compile errors in which
227     * boost::fusion::joint_view is mentioned a surprising number of times.
228     *
229     * To cover cases such as a method on an LLSingleton we don't yet want to
230     * instantiate, instead of directly storing an instance pointer, accept a
231     * nullary callable returning a pointer/reference to the desired class
232     * instance. If you already have an instance in hand,
233     * boost::lambda::var(instance) or boost::lambda::constant(instance_ptr)
234     * produce suitable callables.
235     *
236     * Pass an LLSD::Array of parameter names, and optionally another
237     * LLSD::Array of default parameter values, a la LLSDArgsMapper.
238     *
239     * When calling this name, pass an LLSD::Map. We will internally generate
240     * an LLSD::Array using LLSDArgsMapper and then convert each entry in turn
241     * to the corresponding parameter type using LLSDParam.
242     */
243    template<typename Method, typename InstanceGetter>
244    typename boost::enable_if< boost::function_types::is_member_function_pointer<Method>
245                               >::type add(const std::string& name,
246                                           const std::string& desc,
247                                           Method f,
248                                           const InstanceGetter& getter,
249                                           const LLSD& params,
250                                           const LLSD& defaults=LLSD());
251
252    //@}    
253
254    /// Unregister a callable
255    bool remove(const std::string& name);
256
257    /// Call a registered callable with an explicitly-specified name. If no
258    /// such callable exists, die with LL_ERRS. If the @a event fails to match
259    /// the @a required prototype specified at add() time, die with LL_ERRS.
260    void operator()(const std::string& name, const LLSD& event) const;
261
262    /// Call a registered callable with an explicitly-specified name and
263    /// return <tt>true</tt>. If no such callable exists, return
264    /// <tt>false</tt>. If the @a event fails to match the @a required
265    /// prototype specified at add() time, die with LL_ERRS.
266    bool try_call(const std::string& name, const LLSD& event) const;
267
268    /// Extract the @a key value from the incoming @a event, and call the
269    /// callable whose name is specified by that map @a key. If no such
270    /// callable exists, die with LL_ERRS. If the @a event fails to match the
271    /// @a required prototype specified at add() time, die with LL_ERRS.
272    void operator()(const LLSD& event) const;
273
274    /// Extract the @a key value from the incoming @a event, call the callable
275    /// whose name is specified by that map @a key and return <tt>true</tt>.
276    /// If no such callable exists, return <tt>false</tt>. If the @a event
277    /// fails to match the @a required prototype specified at add() time, die
278    /// with LL_ERRS.
279    bool try_call(const LLSD& event) const;
280
281    /// @name Iterate over defined names
282    //@{
283    typedef std::pair<std::string, std::string> NameDesc;
284
285private:
286    struct DispatchEntry
287    {
288        DispatchEntry(const std::string& desc);
289        virtual ~DispatchEntry() {} // suppress MSVC warning, sigh
290
291        std::string mDesc;
292
293        virtual void call(const std::string& desc, const LLSD& event) const = 0;
294        virtual LLSD addMetadata(LLSD) const = 0;
295    };
296    // Tried using boost::ptr_map<std::string, DispatchEntry>, but ptr_map<>
297    // wants its value type to be "clonable," even just to dereference an
298    // iterator. I don't want to clone entries -- if I have to copy an entry
299    // around, I want it to continue pointing to the same DispatchEntry
300    // subclass object. However, I definitely want DispatchMap to destroy
301    // DispatchEntry if no references are outstanding at the time an entry is
302    // removed. This looks like a job for boost::shared_ptr.
303    typedef std::map<std::string, boost::shared_ptr<DispatchEntry> > DispatchMap;
304
305public:
306    /// We want the flexibility to redefine what data we store per name,
307    /// therefore our public interface doesn't expose DispatchMap iterators,
308    /// or DispatchMap itself, or DispatchEntry. Instead we explicitly
309    /// transform each DispatchMap item to NameDesc on dereferencing.
310    typedef boost::transform_iterator<NameDesc(*)(const DispatchMap::value_type&), DispatchMap::const_iterator> const_iterator;
311    const_iterator begin() const
312    {
313        return boost::make_transform_iterator(mDispatch.begin(), makeNameDesc);
314    }
315    const_iterator end() const
316    {
317        return boost::make_transform_iterator(mDispatch.end(), makeNameDesc);
318    }
319    //@}
320
321    /// Get information about a specific Callable
322    LLSD getMetadata(const std::string& name) const;
323
324    /// Retrieve the LLSD key we use for one-arg <tt>operator()</tt> method
325    std::string getDispatchKey() const { return mKey; }
326
327private:
328    template <class CLASS, typename METHOD>
329    void addMethod(const std::string& name, const std::string& desc,
330                   const METHOD& method, const LLSD& required)
331    {
332        CLASS* downcast = dynamic_cast<CLASS*>(this);
333        if (! downcast)
334        {
335            addFail(name, typeid(CLASS).name());
336        }
337        else
338        {
339            add(name, desc, boost::bind(method, downcast, _1), required);
340        }
341    }
342    void addFail(const std::string& name, const std::string& classname) const;
343
344    std::string mDesc, mKey;
345    DispatchMap mDispatch;
346
347    static NameDesc makeNameDesc(const DispatchMap::value_type& item)
348    {
349        return NameDesc(item.first, item.second->mDesc);
350    }
351
352    struct LLSDDispatchEntry;
353    struct ParamsDispatchEntry;
354    struct ArrayParamsDispatchEntry;
355    struct MapParamsDispatchEntry;
356
357    // Step 2 of parameter analysis. Instantiating invoker<some_function_type>
358    // implicitly sets its From and To parameters to the (compile time) begin
359    // and end iterators over that function's parameter types.
360    template< typename Function
361              , class From = typename boost::mpl::begin< boost::function_types::parameter_types<Function> >::type
362              , class To   = typename boost::mpl::end< boost::function_types::parameter_types<Function> >::type
363              >
364    struct invoker;
365
366    // deliver LLSD arguments one at a time
367    typedef boost::function<LLSD()> args_source;
368    // obtain args from an args_source to build param list and call target
369    // function
370    typedef boost::function<void(const args_source&)> invoker_function;
371
372    template <typename Function>
373    invoker_function make_invoker(Function f);
374    template <typename Method, typename InstanceGetter>
375    invoker_function make_invoker(Method f, const InstanceGetter& getter);
376    void addArrayParamsDispatchEntry(const std::string& name,
377                                     const std::string& desc,
378                                     const invoker_function& invoker,
379                                     LLSD::Integer arity);
380    void addMapParamsDispatchEntry(const std::string& name,
381                                   const std::string& desc,
382                                   const invoker_function& invoker,
383                                   const LLSD& params,
384                                   const LLSD& defaults);
385};
386
387/*****************************************************************************
388*   LLEventDispatcher template implementation details
389*****************************************************************************/
390// Step 3 of parameter analysis, the recursive case.
391template<typename Function, class From, class To>
392struct LLEventDispatcher::invoker
393{
394    template<typename T>
395    struct remove_cv_ref
396        : boost::remove_cv< typename boost::remove_reference<T>::type >
397    { };
398
399    // apply() accepts an arbitrary boost::fusion sequence as args. It
400    // examines the next parameter type in the parameter-types sequence
401    // bounded by From and To, obtains the next LLSD object from the passed
402    // args_source and constructs an LLSDParam of appropriate type to try
403    // to convert the value. It then recurs with the next parameter-types
404    // iterator, passing the args sequence thus far.
405    template<typename Args>
406    static inline
407    void apply(Function func, const args_source& argsrc, Args const & args)
408    {
409        typedef typename boost::mpl::deref<From>::type arg_type;
410        typedef typename boost::mpl::next<From>::type next_iter_type;
411        typedef typename remove_cv_ref<arg_type>::type plain_arg_type;
412
413        invoker<Function, next_iter_type, To>::apply
414        ( func, argsrc, boost::fusion::push_back(args, LLSDParam<plain_arg_type>(argsrc())));
415    }
416
417    // Special treatment for instance (first) parameter of a non-static member
418    // function. Accept the instance-getter callable, calling that to produce
419    // the first args value. Since we know we're at the top of the recursion
420    // chain, we need not also require a partial args sequence from our caller.
421    template <typename InstanceGetter>
422    static inline
423    void method_apply(Function func, const args_source& argsrc, const InstanceGetter& getter)
424    {
425        typedef typename boost::mpl::next<From>::type next_iter_type;
426
427        // Instead of grabbing the first item from argsrc and making an
428        // LLSDParam of it, call getter() and pass that as the instance param.
429        invoker<Function, next_iter_type, To>::apply
430        ( func, argsrc, boost::fusion::push_back(boost::fusion::nil(), boost::ref(getter())));
431    }
432};
433
434// Step 4 of parameter analysis, the leaf case. When the general
435// invoker<Function, From, To> logic has advanced From until it matches To,
436// the compiler will pick this template specialization.
437template<typename Function, class To>
438struct LLEventDispatcher::invoker<Function,To,To>
439{
440    // the argument list is complete, now call the function
441    template<typename Args>
442    static inline
443    void apply(Function func, const args_source&, Args const & args)
444    {
445        boost::fusion::invoke(func, args);
446    }
447};
448
449template<typename Function>
450typename boost::enable_if< boost::function_types::is_nonmember_callable_builtin<Function> >::type
451LLEventDispatcher::add(const std::string& name, const std::string& desc, Function f)
452{
453    // Construct an invoker_function, a callable accepting const args_source&.
454    // Add to DispatchMap an ArrayParamsDispatchEntry that will handle the
455    // caller's LLSD::Array.
456    addArrayParamsDispatchEntry(name, desc, make_invoker(f),
457                                boost::function_types::function_arity<Function>::value);
458}
459
460template<typename Method, typename InstanceGetter>
461typename boost::enable_if< boost::function_types::is_member_function_pointer<Method> >::type
462LLEventDispatcher::add(const std::string& name, const std::string& desc, Method f,
463                       const InstanceGetter& getter)
464{
465    // Subtract 1 from the compile-time arity because the getter takes care of
466    // the first parameter. We only need (arity - 1) additional arguments.
467    addArrayParamsDispatchEntry(name, desc, make_invoker(f, getter),
468                                boost::function_types::function_arity<Method>::value - 1);
469}
470
471template<typename Function>
472typename boost::enable_if< boost::function_types::is_nonmember_callable_builtin<Function> >::type
473LLEventDispatcher::add(const std::string& name, const std::string& desc, Function f,
474                       const LLSD& params, const LLSD& defaults)
475{
476    // See comments for previous is_nonmember_callable_builtin add().
477    addMapParamsDispatchEntry(name, desc, make_invoker(f), params, defaults);
478}
479
480template<typename Method, typename InstanceGetter>
481typename boost::enable_if< boost::function_types::is_member_function_pointer<Method> >::type
482LLEventDispatcher::add(const std::string& name, const std::string& desc, Method f,
483                       const InstanceGetter& getter,
484                       const LLSD& params, const LLSD& defaults)
485{
486    addMapParamsDispatchEntry(name, desc, make_invoker(f, getter), params, defaults);
487}
488
489template <typename Function>
490LLEventDispatcher::invoker_function
491LLEventDispatcher::make_invoker(Function f)
492{
493    // Step 1 of parameter analysis, the top of the recursion. Passing a
494    // suitable f (see add()'s enable_if condition) to this method causes it
495    // to infer the function type; specifying that function type to invoker<>
496    // causes it to fill in the begin/end MPL iterators over the function's
497    // list of parameter types.
498    // While normally invoker::apply() could infer its template type from the
499    // boost::fusion::nil parameter value, here we must be explicit since
500    // we're boost::bind()ing it rather than calling it directly.
501    return boost::bind(&invoker<Function>::template apply<boost::fusion::nil>,
502                       f,
503                       _1,
504                       boost::fusion::nil());
505}
506
507template <typename Method, typename InstanceGetter>
508LLEventDispatcher::invoker_function
509LLEventDispatcher::make_invoker(Method f, const InstanceGetter& getter)
510{
511    // Use invoker::method_apply() to treat the instance (first) arg specially.
512    return boost::bind(&invoker<Method>::template method_apply<InstanceGetter>,
513                       f,
514                       _1,
515                       getter);
516}
517
518/*****************************************************************************
519*   LLDispatchListener
520*****************************************************************************/
521/**
522 * Bundle an LLEventPump and a listener with an LLEventDispatcher. A class
523 * that contains (or derives from) LLDispatchListener need only specify the
524 * LLEventPump name and dispatch key, and add() its methods. Incoming events
525 * will automatically be dispatched.
526 */
527class LL_COMMON_API LLDispatchListener: public LLEventDispatcher
528{
529public:
530    LLDispatchListener(const std::string& pumpname, const std::string& key);
531
532    std::string getPumpName() const { return mPump.getName(); }
533
534private:
535    bool process(const LLSD& event);
536
537    LLEventStream mPump;
538    LLTempBoundListener mBoundListener;
539};
540
541#endif /* ! defined(LL_LLEVENTDISPATCHER_H) */