Fcitx
signals.h
Go to the documentation of this file.
1 /*
2  * SPDX-FileCopyrightText: 2016-2016 CSSlayer <wengxt@gmail.com>
3  *
4  * SPDX-License-Identifier: LGPL-2.1-or-later
5  *
6  */
7 #ifndef _FCITX_UTILS_SIGNALS_H_
8 #define _FCITX_UTILS_SIGNALS_H_
9 
10 /// \addtogroup FcitxUtils
11 /// \{
12 /// \file
13 /// \brief A signal-slot implemention.
14 
15 #include <functional>
16 #include <memory>
17 #include <utility>
18 #include <fcitx-utils/handlertable.h>
19 #include <fcitx-utils/intrusivelist.h>
20 #include <fcitx-utils/macros.h>
21 #include <fcitx-utils/signals_details.h> // IWYU pragma: export
23 #include <fcitx-utils/tuplehelpers.h>
24 
25 namespace fcitx {
26 
27 /// \brief Combiner that return the last value.
28 template <typename T>
29 class LastValue {
30 public:
31  LastValue(T defaultValue = T()) : initial_(defaultValue) {}
32 
33  template <typename InputIterator>
34  T operator()(InputIterator begin, InputIterator end) {
35  T v = initial_;
36  for (; begin != end; begin++) {
37  v = *begin;
38  }
39  return v;
40  }
41 
42 private:
43  T initial_;
44 };
45 
46 template <>
47 class LastValue<void> {
48 public:
49  LastValue() = default;
50  template <typename InputIterator>
51  void operator()(InputIterator begin, InputIterator end) {
52  for (; begin != end; begin++) {
53  *begin;
54  }
55  }
56 };
57 
58 /// \brief A connection instance. Can be used to query the existence of
59 /// connection.
60 class Connection {
61 public:
62  Connection() = default;
64  : body_(std::move(body)) {}
65 
66  FCITX_INLINE_DEFINE_DEFAULT_DTOR_COPY_AND_MOVE(Connection)
67 
68  // FIXME: merge this
69  bool connected() { return body_.isValid(); }
70  bool connected() const { return body_.isValid(); }
71 
72  void disconnect() {
73  auto *body = body_.get();
74  // delete nullptr is no-op;
75  delete body;
76  }
77  bool operator==(const Connection &other) const {
78  return body_.get() == other.body_.get();
79  }
80  bool operator!=(const Connection &other) const { return !(*this == other); }
81 
82 protected:
84 };
85 
86 /// \brief Connection that will disconnection when it goes out of scope.
87 /// \see Connection
88 class ScopedConnection : public Connection {
89 public:
90  // You must create two Connection if you really want two ScopedConnection
91  // for same actual connection
92  ScopedConnection(ScopedConnection &&other) noexcept
93  : Connection(std::move(other)) {}
94  ScopedConnection(Connection &&other) noexcept
95  : Connection(std::move(other)) {}
96  ScopedConnection(const ScopedConnection &) = delete;
97  ScopedConnection() = default;
98 
99  ScopedConnection &operator=(ScopedConnection &&other) noexcept {
100  if (&other == this) {
101  return *this;
102  }
103  disconnect();
104  Connection::operator=(std::move(other));
105  return *this;
106  }
107  ScopedConnection &operator=(const ScopedConnection &other) = delete;
108 
109  virtual ~ScopedConnection() { disconnect(); }
110 
111  Connection release() {
112  Connection conn(body_);
113  body_.unwatch();
114  return conn;
115  }
116 };
117 
118 class SignalBase {
119 public:
120  virtual ~SignalBase() = default;
121 };
122 
123 /// \brief Class to represent a signal. May be used like a functor.
124 template <typename T,
126 class Signal;
127 
128 template <typename Ret, typename Combiner, typename... Args>
129 class Signal<Ret(Args...), Combiner> : public SignalBase {
130  struct SignalData {
131  SignalData(Combiner combiner) : combiner_(std::move(combiner)) {}
132 
133  HandlerTable<std::function<Ret(Args...)>> table_;
134  IntrusiveList<ConnectionBody> connections_;
135  Combiner combiner_;
136  };
137 
138 public:
139  using return_type = Ret;
140  using function_type = Ret(Args...);
141  Signal(Combiner combiner = Combiner())
142  : d_ptr(std::make_unique<SignalData>(std::move(combiner))) {}
143  ~Signal() override {
144  if (d_ptr) {
145  disconnectAll();
146  }
147  }
148  Signal(Signal &&other) noexcept { operator=(std::move(other)); }
149  Signal &operator=(Signal &&other) noexcept {
150  using std::swap;
151  swap(d_ptr, other.d_ptr);
152  return *this;
153  }
154 
155  Ret operator()(Args... args) {
156  auto view = d_ptr->table_.view();
157  Invoker<Ret, Args...> invoker(args...);
158  auto iter = MakeSlotInvokeIterator(invoker, view.begin());
159  auto end = MakeSlotInvokeIterator(invoker, view.end());
160  return d_ptr->combiner_(iter, end);
161  }
162 
163  template <typename Func>
164  Connection connect(Func &&func) {
165  auto *body =
166  new ConnectionBody(d_ptr->table_.add(std::forward<Func>(func)));
167  d_ptr->connections_.push_back(*body);
168  return Connection{body->watch()};
169  }
170 
171  void disconnectAll() {
172  while (!d_ptr->connections_.empty()) {
173  delete &d_ptr->connections_.front();
174  }
175  }
176 
177 private:
178  // store data in a unique_ptr to speed up move.
179  std::unique_ptr<SignalData> d_ptr;
180 };
181 } // namespace fcitx
182 
183 #endif // _FCITX_UTILS_SIGNALS_H_
Utility class provides a weak reference to the object.
Utitliy classes for statically tracking the life of a object.
Definition: action.cpp:17
A connection instance.
Definition: signals.h:60
Connection that will disconnection when it goes out of scope.
Definition: signals.h:88
Class to represent a signal. May be used like a functor.
Definition: signals.h:126
Combiner that return the last value.
Definition: signals.h:29
T * get() const
Get the referenced object. Return nullptr if it is not available.