siplasplas
A library for C++ reflection and introspection
emitter.hpp
1 #ifndef SIPLASPLAS_SIGNALS_EMITTER_HPP
2 #define SIPLASPLAS_SIGNALS_EMITTER_HPP
3 
4 #include <siplasplas/utility/function_traits.hpp>
5 #include <siplasplas/utility/hash.hpp>
6 #include <siplasplas/reflection/static/api.hpp>
7 #include "syncsink.hpp"
8 #include "asyncsink.hpp"
9 #include "logger.hpp"
10 #include <siplasplas/signals/export.hpp>
11 
12 #include <memory>
13 #include <mutex>
14 
48 namespace cpp
49 {
50 
64 class SIPLASPLAS_SIGNALS_EXPORT SignalEmitter
65 {
66 public:
67 
86  template<typename Caller, typename Function, typename R, typename Class, typename... Args>
87  static std::shared_ptr<const SignalSink> connect(Caller& caller, R(Class::*source)(Args...), Function function)
88  {
89  static_assert(equal_signature<decltype(source), Function>::value, "Signal vs slot signatures don't match");
90  std::shared_ptr<SignalSink> sink{ new SyncSink{caller, function} };
91 
92  caller.registerConnection(source, sink);
93 
94  return sink;
95  }
96 
117  template<typename Caller, typename Callee, typename Function, typename R, typename Class, typename... Args>
118  static std::shared_ptr<const SignalSink> connect(Caller& caller, R(Class::*source)(Args...), Callee& callee, Function function)
119  {
120  static_assert(equal_signature<decltype(source), Function>::value, "Signal vs slot signatures don't match");
121  std::shared_ptr<SignalSink> sink{ new SyncSink{caller, callee, function} };
122 
123  caller.registerConnection(source, sink);
124  callee.registerIncommingConnection(sink);
125 
126  return sink;
127  }
128 
148  template<typename Caller, typename Function, typename R, typename Class, typename... Args>
149  static std::shared_ptr<const SignalSink> connect_async(Caller& caller, R(Class::*source)(Args...), Function function)
150  {
151  static_assert(equal_signature<decltype(source), Function>::value, "Signal vs slot signatures don't match");
152  std::shared_ptr<SignalSink> sink{ new AsyncSink{caller, function} };
153 
154  caller.registerConnection(source, sink);
155 
156  return sink;
157  }
158 
176  template<typename Caller, typename Callee, typename Function, typename R, typename Class, typename... Args>
177  static std::shared_ptr<const SignalSink> connect_async(Caller& caller, R(Class::*source)(Args...), Callee& callee, Function function)
178  {
179  static_assert(equal_signature<decltype(source), Function>::value, "Signal vs slot signatures don't match");
180  std::shared_ptr<SignalSink> sink{ new AsyncSink{caller, callee, function} };
181 
182  caller.registerConnection(source, sink);
183  callee.registerIncommingConnection(sink);
184 
185  return sink;
186  }
187 
203  template<typename Caller, typename Callee, typename R, typename... Args>
204  static std::shared_ptr<const SignalSink> bypass(Caller& caller, R(Caller::*source)(Args...), Callee& callee, R(Callee::*dest)(Args...))
205  {
206  static_assert(equal_signature<decltype(source), decltype(dest)>::value, "Signal vs slot signatures don't match");
207  return connect(caller, source, callee, [&callee, dest](Args... args)
208  {
209  emit(callee, dest, args...);
210  });
211  }
212 
228  template<typename Caller, typename Callee, typename R, typename... Args>
229  static std::shared_ptr<const SignalSink> bypass_async(Caller& caller, R(Caller::*source)(Args...), Callee& callee, R(Callee::*dest)(Args...))
230  {
231  static_assert(equal_signature<decltype(source), decltype(dest)>::value, "Signal vs slot signatures don't match");
232  return connect_async(caller, source, callee, [&callee, dest](Args... args)
233  {
234  emit(callee, dest, args...);
235  });
236  }
237 
245  template<typename Class, typename R, typename... FArgs, typename... Args>
246  static void emit(Class& emitter, R(Class::*function)(FArgs...), Args&&... args)
247  {
248  emitter.invoke(function, std::forward<Args>(args)...);
249  }
250 
251  SignalEmitter() = default;
252  SignalEmitter(SignalEmitter&&) = default;
253  SignalEmitter& operator=(SignalEmitter&&) = default;
254  ~SignalEmitter();
255 
262  void poll();
263 
264 protected:
265  template<typename Function, typename... Args>
266  void invoke(Function function, Args&&... args)
267  {
268  auto it = _connections.find(::cpp::hash(function));
269 
270  if(it != _connections.end())
271  {
272  auto& sinks = it->second;
273 
274 #ifdef SIPLASPLAS_LOG_SIGNALS
275  {
276  static ctti::type_id_t argsTypes[] = {ctti::type_id<decltype(std::forward<Args>(args))>()..., ctti::type_id<void>()};
277 
278  signals::log().debug("Emitting signal from @{}. Args (count={})", static_cast<void*>(this), sizeof...(args));
279 
280  for(std::size_t i = 0; i < sizeof...(Args); ++i)
281  {
282  signals::log().debug(
283  " Arg({}): value '{}', type '{}'",
284  i,
285  argsTypes[i].name()
286  );
287  }
288  }
289 #endif
290  for(auto& sink : sinks)
291  {
292  (*sink)(
293  std::forward<Args>(args)...
294  );
295  }
296  }
297  }
298 
299 private:
300  cpp::HashSet<std::shared_ptr<SignalSink>> _incomingConnections;
302  std::mutex _lockConnections;
303  std::mutex _lockIncommingConnections;
304 
305  void disconnectCallee(SignalEmitter* callee);
306 
307  template<typename Function>
308  void registerConnection(Function function, const std::shared_ptr<SignalSink>& sink)
309  {
310  std::lock_guard<std::mutex> guard{_lockConnections};
311  _connections[::cpp::hash(function)].push_back(sink);
312  }
313 
314  void registerIncommingConnection(const std::shared_ptr<SignalSink>& sink);
315 };
316 
348 template<typename Class>
349 auto emit(const Class& object)
350 {
351  return ::cpp::static_reflection::Class<Class>::fakeObject(
352  object,
353  [](const Class& object, auto method, auto&&... args)
354  {
355  SignalEmitter::emit(object, method, std::forward<decltype(args)>(args)...);
356  }
357  );
358 }
359 
391 template<typename Class>
392 auto emit(Class& object)
393 {
394  return ::cpp::static_reflection::Class<Class>::fakeObject(
395  object,
396  [](Class& object, auto method, auto&&... args)
397  {
398  SignalEmitter::emit(object, method, std::forward<decltype(args)>(args)...);
399  }
400  );
401 }
402 
403 
404 }
405 
406 #endif // SIPLASPLAS_SIGNALS_EMITTER_HPP
Class that can send and receive signals from other emitters.
Definition: emitter.hpp:64
static void emit(Class &emitter, R(Class::*function)(FArgs...), Args &&...args)
Emits a signal on the given emitter.
Definition: emitter.hpp:246
Implements a direct connection to the destination function.
Definition: syncsink.hpp:20
std::unordered_set< Key, Hash< Key >> HashSet
std::set alias using cpp::Hash as hash.
Definition: hash.hpp:249
Definition: canary_allocator.hpp:7
static std::shared_ptr< const SignalSink > connect_async(Caller &caller, R(Class::*source)(Args...), Callee &callee, Function function)
Creates an asynchronous connection between a signal and a function using an specific callee object...
Definition: emitter.hpp:177
static std::shared_ptr< const SignalSink > connect(Caller &caller, R(Class::*source)(Args...), Function function)
Creates a direct connection from the given emitter object to the given function.
Definition: emitter.hpp:87
static std::shared_ptr< const SignalSink > bypass_async(Caller &caller, R(Caller::*source)(Args...), Callee &callee, R(Callee::*dest)(Args...))
Connects two signals asynchronously.
Definition: emitter.hpp:229
std::unordered_map< Key, Value, Hash< Key >> HashTable
std::unordered_map alias using cpp::Hash as hash.
Definition: hash.hpp:240
static std::shared_ptr< const SignalSink > connect(Caller &caller, R(Class::*source)(Args...), Callee &callee, Function function)
Creates a direct connection from the given emitter object to the given function, using an specific ca...
Definition: emitter.hpp:118
constexpr std::size_t hash(const T &value)
Implements a hash function for values of type T.
Definition: hash.hpp:180
static std::shared_ptr< const SignalSink > bypass(Caller &caller, R(Caller::*source)(Args...), Callee &callee, R(Callee::*dest)(Args...))
Connects two signals synchronously.
Definition: emitter.hpp:204
decltype(auto) invoke(Callable &&callable, const ::cpp::SimpleAny< Storages > &...args)
Invokes a callable object with the given type-erased arguments.
Definition: invoke.hpp:214
Implements an asynchronous signal sink suited for communication between different threads...
Definition: asyncsink.hpp:23
static std::shared_ptr< const SignalSink > connect_async(Caller &caller, R(Class::*source)(Args...), Function function)
Creates an asynchronous connection between a signal and a function.
Definition: emitter.hpp:149
auto emit(Class &object)
Emits a signal from the given SignalEmitter.
Definition: emitter.hpp:392