summaryrefslogtreecommitdiff
path: root/indra/llcommon/lleventdispatcher.h
blob: ef83ebabc1fa60cb0b5a5a0c2eb713e6304eba5e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
/**
 * @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=viewergpl$
 * Copyright (c) 2009, Linden Research, Inc.
 * $/LicenseInfo$
 */

#if ! defined(LL_LLEVENTDISPATCHER_H)
#define LL_LLEVENTDISPATCHER_H

#include <string>
#include <map>
#include <boost/function.hpp>
#include <boost/bind.hpp>
#include <typeinfo>
#include "llevents.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();

    /// Accept any C++ callable, typically a boost::bind() expression
    typedef boost::function<void(const LLSD&)> Callable;

    /**
     * Register a @a callable by @a name. 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 Callable& callable, const LLSD& required=LLSD());

    /**
     * Special case: a subclass of this class can pass an unbound member
     * function pointer without explicitly specifying the
     * <tt>boost::bind()</tt> expression.
     */
    template <class CLASS>
    void add(const std::string& name, void (CLASS::*method)(const LLSD&),
             const LLSD& required=LLSD())
    {
        addMethod<CLASS>(name, method, required);
    }

    /// Overload for both const and non-const methods
    template <class CLASS>
    void add(const std::string& name, void (CLASS::*method)(const LLSD&) const,
             const LLSD& required=LLSD())
    {
        addMethod<CLASS>(name, method, required);
    }

    /// 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;

    /// 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;

private:
    template <class CLASS, typename METHOD>
    void addMethod(const std::string& name, const METHOD& method, const LLSD& required)
    {
        CLASS* downcast = dynamic_cast<CLASS*>(this);
        if (! downcast)
        {
            addFail(name, typeid(CLASS).name());
        }
        else
        {
            add(name, boost::bind(method, downcast, _1), required);
        }
    }
    void addFail(const std::string& name, const std::string& classname) const;
    /// try to dispatch, return @c true if success
    bool attemptCall(const std::string& name, const LLSD& event) const;

    std::string mDesc, mKey;
    typedef std::map<std::string, std::pair<Callable, LLSD> > DispatchMap;
    DispatchMap mDispatch;
};

/**
 * 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) */