Freeciv21
Develop your civilization from humble roots to a global empire
listener.h
Go to the documentation of this file.
1 /**************************************************************************
2  Copyright (c) 1996-2020 Freeciv21 and Freeciv contributors. This file is
3  part of Freeciv21. Freeciv21 is free software: you can redistribute it
4  and/or modify it under the terms of the GNU General Public License as
5  published by the Free Software Foundation, either version 3 of the
6  License, or (at your option) any later version. You should have received
7  a copy of the GNU General Public License along with Freeciv21. If not,
8  see https://www.gnu.org/licenses/.
9 **************************************************************************/
10 #pragma once
11 
12 #include <set>
13 #include <utility>
14 
15 /***************************************************************************
16  Helper template to connect C and C++ code.
17 
18  This class is a helper to create Java-like "listeners" for "events"
19  generated in C code. If the C interface defines a callback foo() and
20  you want to use it as an event, you first declare a C++ interface:
21 
22  ~~~~~{.cpp}
23  class foo_listener : public listener<foo_listener>
24  {
25  public:
26  virtual void foo() = 0;
27  };
28  ~~~~~
29 
30  Then, you call the listeners from the implementation of the C interface:
31 
32  ~~~~~{.cpp}
33  void foo()
34  {
35  foo_listener::invoke(&foo_listener::foo);
36  }
37  ~~~~~
38 
39  This will invoke foo() on all foo_listener objects.
40 
41  == Listening to events
42 
43  Listening to events is done by inheriting from a listener. When your
44  object is ready to receive events, it should call the listener's @c listen
45  function. This will typically be done in the constructor:
46 
47  ~~~~~{.cpp}
48  class bar : private foo_listener
49  {
50  public:
51  explicit bar();
52  void foo();
53  };
54 
55  bar::bar()
56  {
57  // Initialize the object here
58  foo_listener::listen();
59  }
60  ~~~~~
61 
62  == Passing arguments
63 
64  Passing arguments to the listeners is very simple: you write them after
65  the function pointer. For instance, let's say foo() takes an int. It can
66  be passed to the listeners as follows:
67 
68  ~~~~~{.cpp}
69  void foo(int argument)
70  {
71  foo_listener::invoke(&foo_listener::foo, argument);
72  }
73  ~~~~~
74 
75  As there may be an arbitrary number of listeners, passing mutable data
76  through invoke() is discouraged.
77 
78  == Technical details
79 
80  This class achieves its purpose using the Curiously Recurring Template
81  Pattern, hence the weird parent for listeners:
82 
83  ~~~~~{.cpp}
84  class foo_listener : public listener<foo_listener>
85  ~~~~~
86 
87  The template argument is used to specialize object storage and member
88  function invocation. Compilers should be able to inline calls to invoke(),
89  leaving only the overhead of looping on all instances.
90 
91  @warning Implementation is not thread-safe.
92 ***************************************************************************/
93 template <class _type_> class listener {
94 public:
95  // The type a given specialization supports.
96  typedef _type_ type_t;
97 
98 private:
99  // All instances of type_t that have called listen().
100  inline static std::set<listener<type_t> *> instances{};
101 
102 protected:
103  explicit listener();
104  virtual ~listener();
105 
106  void listen();
107 
108 public:
109  template <class _member_fct_, class... _args_>
110  static void invoke(_member_fct_ function, _args_ &&...args);
111 };
112 
113 /***************************************************************************
114  Constructor
115 ***************************************************************************/
116 template <class _type_> listener<_type_>::listener() = default;
117 
118 /***************************************************************************
119  Starts listening to events
120 ***************************************************************************/
121 template <class _type_> void listener<_type_>::listen()
122 {
123  // If you get an error here, your listener likely doesn't inherit from the
124  // listener<> correctly. See the class documentation.
125  instances.insert(this);
126 }
127 
128 /***************************************************************************
129  Destructor
130 ***************************************************************************/
131 template <class _type_> listener<_type_>::~listener()
132 {
133  instances.erase(this);
134 }
135 
136 /***************************************************************************
137  Invokes a member function on all instances of a listener type. Template
138  parameters are meant to be automatically deduced.
139 
140  @param function The member function to call
141  @param args Arguments to pass ot the function.
142 ***************************************************************************/
143 template <class _type_>
144 template <class _member_fct_, class... _args_>
145 void listener<_type_>::invoke(_member_fct_ function, _args_ &&...args)
146 {
147  for (auto &instance : instances) {
148  (dynamic_cast<type_t *>(instance)->*function)(
149  std::forward<_args_>(args)...);
150  }
151 }
static std::set< listener< type_t > * > instances
Definition: listener.h:100
_type_ type_t
Definition: listener.h:96
void listen()
Definition: listener.h:121
static void invoke(_member_fct_ function, _args_ &&...args)
Definition: listener.h:145
virtual ~listener()
Definition: listener.h:131