]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - include/event.h
Add new cross-module event system
[user/henk/code/inspircd.git] / include / event.h
1 /*
2  * InspIRCd -- Internet Relay Chat Daemon
3  *
4  *   Copyright (C) 2015 Attila Molnar <attilamolnar@hush.com>
5  *
6  * This file is part of InspIRCd.  InspIRCd is free software: you can
7  * redistribute it and/or modify it under the terms of the GNU General Public
8  * License as published by the Free Software Foundation, version 2.
9  *
10  * This program is distributed in the hope that it will be useful, but WITHOUT
11  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
12  * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
13  * details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
17  */
18
19
20 #pragma once
21
22 namespace Events
23 {
24         class ModuleEventListener;
25         class ModuleEventProvider;
26 }
27
28 /** Provider of one or more cross-module events.
29  * Modules who wish to provide events for other modules create instances of this class and use
30  * one of the macros below to fire the event, passing the instance of the event provider class
31  * to the macro.
32  * Event providers are identified using a unique identifier string.
33  */
34 class Events::ModuleEventProvider : public ServiceProvider, private dynamic_reference_base::CaptureHook
35 {
36  public:
37         typedef std::vector<ModuleEventListener*> SubscriberList;
38
39         /** Constructor
40          * @param mod Module providing the event(s)
41          * @param eventid Identifier of the event or event group provided, must be unique
42          */
43         ModuleEventProvider(Module* mod, const std::string& eventid)
44                 : ServiceProvider(mod, eventid, SERVICE_DATA)
45                 , prov(mod, eventid)
46         {
47                 prov.SetCaptureHook(this);
48         }
49
50         /** Get list of objects subscribed to this event
51          * @return List of subscribed objects
52          */
53         const SubscriberList& GetSubscribers() const { return prov->subscribers; }
54
55         friend class ModuleEventListener;
56
57  private:
58         void OnCapture() CXX11_OVERRIDE
59         {
60                 // If someone else holds the list from now on, clear mine. See below for more info.
61                 if (*prov != this)
62                         subscribers.clear();
63         }
64
65         /** Reference to the active provider for this event. In case multiple event providers
66          * exist for the same event, only one of them contains the list of subscribers.
67          * To handle the case when we are not the ones with the list, we get it from the provider
68          * where the dynref points to.
69          */
70         dynamic_reference_nocheck<ModuleEventProvider> prov;
71
72         /** List of objects subscribed to the event(s) provided by us, or empty if multiple providers
73          * exist with the same name and we are not the ones holding the list.
74          */
75         SubscriberList subscribers;
76 };
77
78 /** Base class for abstract classes describing cross-module events.
79  * Subscribers should NOT inherit directly from this class.
80  */
81 class Events::ModuleEventListener : private dynamic_reference_base::CaptureHook
82 {
83         /** Reference to the provider, can be NULL if none of the provider modules are loaded
84          */
85         dynamic_reference_nocheck<ModuleEventProvider> prov;
86
87         /** Called by the dynref when the event provider becomes available
88          */
89         void OnCapture() CXX11_OVERRIDE
90         {
91                 prov->subscribers.push_back(this);
92         }
93
94  public:
95         /** Constructor
96          * @param mod Module subscribing
97          * @param eventid Identifier of the event to subscribe to
98          */
99         ModuleEventListener(Module* mod, const std::string& eventid)
100                 : prov(mod, eventid)
101         {
102                 prov.SetCaptureHook(this);
103                 // If the dynamic_reference resolved at construction our capture handler wasn't called
104                 if (prov)
105                         ModuleEventListener::OnCapture();
106         }
107
108         ~ModuleEventListener()
109         {
110                 if (prov)
111                         stdalgo::erase(prov->subscribers, this);
112         }
113 };
114
115 /**
116  * Run the given hook provided by a module
117  *
118  * FOREACH_MOD_CUSTOM(accountevprov, AccountEventListener, OnAccountChange, MOD_RESULT, (user, newaccount))
119  */
120 #define FOREACH_MOD_CUSTOM(prov, listenerclass, func, params) do { \
121         const Events::ModuleEventProvider::SubscriberList& _handlers = (prov).GetSubscribers(); \
122         for (Events::ModuleEventProvider::SubscriberList::const_iterator _i = _handlers.begin(); _i != _handlers.end(); ++_i) \
123         { \
124                 listenerclass* _t = static_cast<listenerclass*>(*_i); \
125                 _t->func params ; \
126         } \
127 } while (0);
128
129 /**
130  * Run the given hook provided by a module until some module returns MOD_RES_ALLOW or MOD_RES_DENY.
131  * If no module does that, result is set to MOD_RES_PASSTHRU.
132  *
133  * Example: ModResult MOD_RESULT;
134  * FIRST_MOD_RESULT_CUSTOM(httpevprov, HTTPRequestEventListener, OnHTTPRequest, MOD_RESULT, (request));
135  */
136 #define FIRST_MOD_RESULT_CUSTOM(prov, listenerclass, func, result, params) do { \
137         result = MOD_RES_PASSTHRU; \
138         const Events::ModuleEventProvider::SubscriberList& _handlers = (prov).GetSubscribers(); \
139         for (Events::ModuleEventProvider::SubscriberList::const_iterator _i = _handlers.begin(); _i != _handlers.end(); ++_i) \
140         { \
141                 listenerclass* _t = static_cast<listenerclass*>(*_i); \
142                 result = _t->func params ; \
143                 if (result != MOD_RES_PASSTHRU) \
144                         break; \
145         } \
146 } while (0);