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/reflection/dynamic/object_manip.hpp>
5 #include <siplasplas/reflection/dynamic/function_pointer.hpp>
6 #include <siplasplas/utility/function_traits.hpp>
7 #include <siplasplas/utility/hash.hpp>
8 #include <siplasplas/reflection/api.hpp>
9 #include "syncsink.hpp"
10 #include "asyncsink.hpp"
11 #include "logger.hpp"
12 #include <siplasplas/signals/export.hpp>
13 
14 #include <memory>
15 #include <mutex>
16 
50 namespace cpp
51 {
52 
66 class SIPLASPLAS_SIGNALS_EXPORT SignalEmitter
67 {
68 public:
69 
88  template<typename Caller, typename Function, typename R, typename Class, typename... Args>
89  static std::shared_ptr<const SignalSink> connect(Caller& caller, R(Class::*source)(Args...), Function function)
90  {
91  static_assert(equal_signature<decltype(source), Function>::value, "Signal vs slot signatures don't match");
92  std::shared_ptr<SignalSink> sink{ new SyncSink{caller, function} };
93 
94  caller.registerConnection(source, sink);
95 
96  return sink;
97  }
98 
119  template<typename Caller, typename Callee, typename Function, typename R, typename Class, typename... Args>
120  static std::shared_ptr<const SignalSink> connect(Caller& caller, R(Class::*source)(Args...), Callee& callee, Function function)
121  {
122  static_assert(equal_signature<decltype(source), Function>::value, "Signal vs slot signatures don't match");
123  std::shared_ptr<SignalSink> sink{ new SyncSink{caller, callee, function} };
124 
125  caller.registerConnection(source, sink);
126  callee.registerIncommingConnection(sink);
127 
128  return sink;
129  }
130 
150  template<typename Caller, typename Function, typename R, typename Class, typename... Args>
151  static std::shared_ptr<const SignalSink> connect_async(Caller& caller, R(Class::*source)(Args...), Function function)
152  {
153  static_assert(equal_signature<decltype(source), Function>::value, "Signal vs slot signatures don't match");
154  std::shared_ptr<SignalSink> sink{ new AsyncSink{caller, function} };
155 
156  caller.registerConnection(source, sink);
157 
158  return sink;
159  }
160 
178  template<typename Caller, typename Callee, typename Function, typename R, typename Class, typename... Args>
179  static std::shared_ptr<const SignalSink> connect_async(Caller& caller, R(Class::*source)(Args...), Callee& callee, Function function)
180  {
181  static_assert(equal_signature<decltype(source), Function>::value, "Signal vs slot signatures don't match");
182  std::shared_ptr<SignalSink> sink{ new AsyncSink{caller, callee, function} };
183 
184  caller.registerConnection(source, sink);
185  callee.registerIncommingConnection(sink);
186 
187  return sink;
188  }
189 
205  template<typename Caller, typename Callee, typename R, typename... Args>
206  static std::shared_ptr<const SignalSink> bypass(Caller& caller, R(Caller::*source)(Args...), Callee& callee, R(Callee::*dest)(Args...))
207  {
208  static_assert(equal_signature<decltype(source), decltype(dest)>::value, "Signal vs slot signatures don't match");
209  return connect(caller, source, callee, [&callee, dest](Args... args)
210  {
211  emit(callee, dest, args...);
212  });
213  }
214 
230  template<typename Caller, typename Callee, typename R, typename... Args>
231  static std::shared_ptr<const SignalSink> bypass_async(Caller& caller, R(Caller::*source)(Args...), Callee& callee, R(Callee::*dest)(Args...))
232  {
233  static_assert(equal_signature<decltype(source), decltype(dest)>::value, "Signal vs slot signatures don't match");
234  return connect_async(caller, source, callee, [&callee, dest](Args... args)
235  {
236  emit(callee, dest, args...);
237  });
238  }
239 
247  template<typename Class, typename R, typename... FArgs, typename... Args>
248  static void emit(Class& emitter, R(Class::*function)(FArgs...), Args&&... args)
249  {
250  emitter.invoke(function, std::forward<Args>(args)...);
251  }
252 
253  SignalEmitter() = default;
254  SignalEmitter(SignalEmitter&&) = default;
255  SignalEmitter& operator=(SignalEmitter&&) = default;
256  ~SignalEmitter();
257 
264  void poll();
265 
266 protected:
267  template<typename Function, typename... Args>
268  void invoke(Function function, Args&&... args)
269  {
270  auto it = _connections.find(::cpp::hash(function));
271 
272  if(it != _connections.end())
273  {
274  auto& sinks = it->second;
275 
276 #ifdef SIPLASPLAS_LOG_SIGNALS
277  {
278  static ctti::type_id_t argsTypes[] = {ctti::type_id<decltype(std::forward<Args>(args))>()..., ctti::type_id<void>()};
279  cpp::dynamic_reflection::Object objectArgs[] = {std::forward<Args>(args)..., cpp::dynamic_reflection::Object()};
280 
281  signals::log().debug("Emitting signal from @{}. Args (count={})", static_cast<void*>(this), sizeof...(args));
282 
283  for(std::size_t i = 0; i < sizeof...(Args); ++i)
284  {
285  signals::log().debug(
286  " Arg({}): value '{}', type '{}'",
287  i,
288  objectArgs[i].toString(),
289  argsTypes[i].name()
290  );
291  }
292  }
293 #endif
294  for(auto& sink : sinks)
295  {
296  (*sink)(
297  std::forward<Args>(args)...
298  );
299  }
300  }
301  }
302 
303 private:
304  cpp::HashSet<std::shared_ptr<SignalSink>> _incomingConnections;
306  std::mutex _lockConnections;
307  std::mutex _lockIncommingConnections;
308 
309  void disconnectCallee(SignalEmitter* callee);
310 
311  template<typename Function>
312  void registerConnection(Function function, const std::shared_ptr<SignalSink>& sink)
313  {
314  std::lock_guard<std::mutex> guard{_lockConnections};
315  _connections[::cpp::hash(function)].push_back(sink);
316  }
317 
318  void registerIncommingConnection(const std::shared_ptr<SignalSink>& sink);
319 };
320 
352 template<typename Class>
353 auto emit(const Class& object)
354 {
355  return ::cpp::static_reflection::Class<Class>::fakeObject(
356  object,
357  [](const Class& object, auto method, auto&&... args)
358  {
359  SignalEmitter::emit(object, method, std::forward<decltype(args)>(args)...);
360  }
361  );
362 }
363 
395 template<typename Class>
396 auto emit(Class& object)
397 {
398  return ::cpp::static_reflection::Class<Class>::fakeObject(
399  object,
400  [](Class& object, auto method, auto&&... args)
401  {
402  SignalEmitter::emit(object, method, std::forward<decltype(args)>(args)...);
403  }
404  );
405 }
406 
407 
408 }
409 
410 #endif // SIPLASPLAS_SIGNALS_EMITTER_HPP
Class that can send and receive signals from other emitters.
Definition: emitter.hpp:66
static void emit(Class &emitter, R(Class::*function)(FArgs...), Args &&...args)
Emits a signal on the given emitter.
Definition: emitter.hpp:248
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:179
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:89
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:231
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:120
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:206
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:151
auto emit(Class &object)
Emits a signal from the given SignalEmitter.
Definition: emitter.hpp:396