TGUI  0.8-alpha
SignalImpl.hpp
1 //
3 // TGUI - Texus' Graphical User Interface
4 // Copyright (C) 2012-2018 Bruno Van de Velde (vdv_b@tgui.eu)
5 //
6 // This software is provided 'as-is', without any express or implied warranty.
7 // In no event will the authors be held liable for any damages arising from the use of this software.
8 //
9 // Permission is granted to anyone to use this software for any purpose,
10 // including commercial applications, and to alter it and redistribute it freely,
11 // subject to the following restrictions:
12 //
13 // 1. The origin of this software must not be misrepresented;
14 // you must not claim that you wrote the original software.
15 // If you use this software in a product, an acknowledgment
16 // in the product documentation would be appreciated but is not required.
17 //
18 // 2. Altered source versions must be plainly marked as such,
19 // and must not be misrepresented as being the original software.
20 //
21 // 3. This notice may not be removed or altered from any source distribution.
22 //
24 
25 
26 #ifndef TGUI_SIGNAL_IMPL_HPP
27 #define TGUI_SIGNAL_IMPL_HPP
28 
29 #include <TGUI/Signal.hpp>
30 
32 
33 namespace tgui
34 {
35  namespace internal_signal
36  {
37  #ifdef TGUI_USE_CPP17
38  using std::void_t;
39  #else
40  // void_t only exists in c++17 so we use our own implementation to support c++14 compilers
41  template<typename...>
42  using void_t = void;
43  #endif
44 
45  // Type to pass a list of template types
46  template <typename...>
47  struct TypeSet;
48 
49  // The dereference function turns the void* elements in the parameters list back into its original type right before calling the signal handler
50 #ifdef TGUI_USE_CPP17
51  template <typename Type>
52  decltype(auto) dereference(const void* obj)
53  {
54  if constexpr (std::is_same_v<Type, std::string>) // Signal handlers are allowed to have std::string parameters while the signal sends sf::String
55  return static_cast<std::string>(*static_cast<const sf::String*>(obj));
56  else if constexpr (std::is_same_v<Type, sf::Vector2f>) // Signal handlers are allowed to have sf::Vector2f parameters while the signal sends tgui::Vector2f
57  return static_cast<sf::Vector2f>(*static_cast<const Vector2f*>(obj));
58  else
59  return *static_cast<const std::decay_t<Type>*>(obj);
60  }
61 #else
62  template <typename Type, typename std::enable_if<std::is_same<Type, std::string>::value>::type* = nullptr>
63  decltype(auto) dereference(const void* obj)
64  {
65  // Signal handlers are allowed to have std::string parameters while the signal sends sf::String
66  return static_cast<std::string>(*static_cast<const sf::String*>(obj));
67  }
68 
69  template <typename Type, typename std::enable_if<std::is_same<Type, sf::Vector2f>::value>::type* = nullptr>
70  decltype(auto) dereference(const void* obj)
71  {
72  // Signal handlers are allowed to have sf::Vector2f parameters while the signal sends tgui::Vector2f
73  return static_cast<sf::Vector2f>(*static_cast<const Vector2f*>(obj));
74  }
75 
76  template <typename Type, typename std::enable_if<!std::is_same<Type, std::string>::value && !std::is_same<Type, sf::Vector2f>::value>::type* = nullptr>
77  decltype(auto) dereference(const void* obj)
78  {
79  return *static_cast<const typename std::decay<Type>::type*>(obj);
80  }
81 #endif
82 
83  #ifndef TGUI_USE_CPP17
84  // std::invoke only exists in c++17 so we use our own implementation to support c++14 compilers
85  // Visual Studio compiler did not like it when the function was called "invoke"
86  template <typename Func, typename... Args, typename std::enable_if<std::is_member_pointer<typename std::decay<Func>::type>::value>::type* = nullptr>
87  void invokeFunc(Func&& func, Args&&... args)
88  {
89  std::mem_fn(func)(std::forward<Args>(args)...);
90  }
91 
92  template <typename Func, typename... Args, typename std::enable_if<!std::is_member_pointer<typename std::decay<Func>::type>::value>::type* = nullptr>
93  void invokeFunc(Func&& func, Args&&... args)
94  {
95  std::forward<Func>(func)(std::forward<Args>(args)...);
96  }
97  #endif
98 
99  // The binder will figure out the unbound parameters and bind them if they correspond to what the signal sends
100  template <typename... Args>
101  struct binder;
102 
103  template <typename Arg, typename... AllArgs, typename BoundArg, typename... BoundArgs>
104  struct binder<TypeSet<Arg, AllArgs...>, TypeSet<BoundArg, BoundArgs...>>
105  : binder<TypeSet<AllArgs...>, TypeSet<BoundArgs...>>
106  {
107  };
108 
109  template <typename... UnboundArgs>
110  struct binder<TypeSet<std::shared_ptr<Widget>, std::string, UnboundArgs...>, TypeSet<>>
111  {
112  template <typename Func, typename... BoundArgs>
113  static decltype(auto) bind(Signal& signal, Func&& func, BoundArgs&&... args)
114  {
115  return bindImpl(std::index_sequence_for<UnboundArgs...>{}, signal, std::forward<Func>(func), std::forward<BoundArgs>(args)...);
116  }
117 
118  private:
119 
120  template <typename Func, typename... BoundArgs, std::size_t... Indices>
121  static decltype(auto) bindImpl(std::index_sequence<Indices...>, Signal& signal, Func&& func, BoundArgs&&... args)
122  {
123  const std::size_t offset = (sizeof...(UnboundArgs) > 0) ? signal.validateTypes({typeid(UnboundArgs)...}) : 0;
124  #if defined TGUI_USE_CPP17
125  return [=](const std::shared_ptr<Widget>& widget, const std::string& signalName) {
126  std::invoke(func,
127  #else
128  return [=, f=func](const std::shared_ptr<Widget>& widget, const std::string& signalName) { // f=func is needed to decay free functions
129  invokeFunc(f,
130  #endif
131  args...,
132  widget,
133  signalName,
134  internal_signal::dereference<UnboundArgs>(internal_signal::parameters[offset + Indices])...);
135  };
136  }
137  };
138 
139  template <typename... UnboundArgs>
140  struct binder<TypeSet<UnboundArgs...>, TypeSet<>>
141  {
142  template <typename Func, typename... BoundArgs>
143  static decltype(auto) bind(Signal& signal, Func&& func, BoundArgs&&... args)
144  {
145  return bindImpl(std::index_sequence_for<UnboundArgs...>{}, signal, std::forward<Func>(func), std::forward<BoundArgs>(args)...);
146  }
147 
148  private:
149 
150  template <typename Func, typename... BoundArgs, std::size_t... Indices>
151  static decltype(auto) bindImpl(std::index_sequence<Indices...>, Signal& signal, Func&& func, BoundArgs&&... args)
152  {
153  const std::size_t offset = (sizeof...(UnboundArgs) > 0) ? signal.validateTypes({typeid(UnboundArgs)...}) : 0;
154  #if defined TGUI_USE_CPP17
155  return [=]() {
156  std::invoke(func,
157  #else
158  return [=, f=func]() { // f=func is needed to decay free functions
159  invokeFunc(f,
160  #endif
161  args...,
162  internal_signal::dereference<UnboundArgs>(internal_signal::parameters[offset + Indices])...);
163  };
164  }
165  };
166 
167 
168  #ifdef TGUI_USE_CPP17
169  // Error case (function signature did not match anything)
170  template <typename Enable, typename Func, typename... BoundArgs>
171  struct func_traits;
172 
173  // Free function
174  template <typename... Args, typename... BoundArgs>
175  struct func_traits<void, void(*)(Args...), BoundArgs...> : binder<TypeSet<std::decay_t<Args>...>, TypeSet<BoundArgs...>> {};
176  template <typename... Args, typename... BoundArgs>
177  struct func_traits<void, void(*)(Args...) noexcept, BoundArgs...> : binder<TypeSet<std::decay_t<Args>...>, TypeSet<BoundArgs...>> {};
178 
179  // Non-const member function
180  template <typename Class, typename... Args, typename... BoundArgs>
181  struct func_traits<void, void(Class::*)(Args...), BoundArgs...> : binder<TypeSet<Class*, std::decay_t<Args>...>, TypeSet<BoundArgs...>> {};
182  template <typename Class, typename... Args, typename... BoundArgs>
183  struct func_traits<void, void(Class::*)(Args...) noexcept, BoundArgs...> : binder<TypeSet<Class*, std::decay_t<Args>...>, TypeSet<BoundArgs...>> {};
184  template <typename Class, typename... Args, typename... BoundArgs>
185  struct func_traits<void, void(Class::*)(Args...) volatile, BoundArgs...> : binder<TypeSet<Class*, std::decay_t<Args>...>, TypeSet<BoundArgs...>> {};
186  template <typename Class, typename... Args, typename... BoundArgs>
187  struct func_traits<void, void(Class::*)(Args...) volatile noexcept, BoundArgs...> : binder<TypeSet<Class*, std::decay_t<Args>...>, TypeSet<BoundArgs...>> {};
188  template <typename Class, typename... Args, typename... BoundArgs>
189  struct func_traits<void, void(Class::*)(Args...) &, BoundArgs...> : binder<TypeSet<Class*, std::decay_t<Args>...>, TypeSet<BoundArgs...>> {};
190  template <typename Class, typename... Args, typename... BoundArgs>
191  struct func_traits<void, void(Class::*)(Args...) & noexcept, BoundArgs...> : binder<TypeSet<Class*, std::decay_t<Args>...>, TypeSet<BoundArgs...>> {};
192  template <typename Class, typename... Args, typename... BoundArgs>
193  struct func_traits<void, void(Class::*)(Args...) volatile &, BoundArgs...> : binder<TypeSet<Class*, std::decay_t<Args>...>, TypeSet<BoundArgs...>> {};
194  template <typename Class, typename... Args, typename... BoundArgs>
195  struct func_traits<void, void(Class::*)(Args...) volatile & noexcept, BoundArgs...> : binder<TypeSet<Class*, std::decay_t<Args>...>, TypeSet<BoundArgs...>> {};
196  template <typename Class, typename... Args, typename... BoundArgs>
197  struct func_traits<void, void(Class::*)(Args...) &&, BoundArgs...> : binder<TypeSet<Class*, std::decay_t<Args>...>, TypeSet<BoundArgs...>> {};
198  template <typename Class, typename... Args, typename... BoundArgs>
199  struct func_traits<void, void(Class::*)(Args...) && noexcept, BoundArgs...> : binder<TypeSet<Class*, std::decay_t<Args>...>, TypeSet<BoundArgs...>> {};
200  template <typename Class, typename... Args, typename... BoundArgs>
201  struct func_traits<void, void(Class::*)(Args...) volatile &&, BoundArgs...> : binder<TypeSet<Class*, std::decay_t<Args>...>, TypeSet<BoundArgs...>> {};
202  template <typename Class, typename... Args, typename... BoundArgs>
203  struct func_traits<void, void(Class::*)(Args...) volatile && noexcept, BoundArgs...> : binder<TypeSet<Class*, std::decay_t<Args>...>, TypeSet<BoundArgs...>> {};
204 
205  // Const member function
206  template <typename Class, typename... Args, typename... BoundArgs>
207  struct func_traits<void, void(Class::*)(Args...) const, BoundArgs...> : binder<TypeSet<const Class*, std::decay_t<Args>...>, TypeSet<BoundArgs...>> {};
208  template <typename Class, typename... Args, typename... BoundArgs>
209  struct func_traits<void, void(Class::*)(Args...) const noexcept, BoundArgs...> : binder<TypeSet<const Class*, std::decay_t<Args>...>, TypeSet<BoundArgs...>> {};
210  template <typename Class, typename... Args, typename... BoundArgs>
211  struct func_traits<void, void(Class::*)(Args...) volatile const, BoundArgs...> : binder<TypeSet<const Class*, std::decay_t<Args>...>, TypeSet<BoundArgs...>> {};
212  template <typename Class, typename... Args, typename... BoundArgs>
213  struct func_traits<void, void(Class::*)(Args...) volatile const noexcept, BoundArgs...> : binder<TypeSet<const Class*, std::decay_t<Args>...>, TypeSet<BoundArgs...>> {};
214  template <typename Class, typename... Args, typename... BoundArgs>
215  struct func_traits<void, void(Class::*)(Args...) const &, BoundArgs...> : binder<TypeSet<const Class*, std::decay_t<Args>...>, TypeSet<BoundArgs...>> {};
216  template <typename Class, typename... Args, typename... BoundArgs>
217  struct func_traits<void, void(Class::*)(Args...) const & noexcept, BoundArgs...> : binder<TypeSet<const Class*, std::decay_t<Args>...>, TypeSet<BoundArgs...>> {};
218  template <typename Class, typename... Args, typename... BoundArgs>
219  struct func_traits<void, void(Class::*)(Args...) volatile const &, BoundArgs...> : binder<TypeSet<const Class*, std::decay_t<Args>...>, TypeSet<BoundArgs...>> {};
220  template <typename Class, typename... Args, typename... BoundArgs>
221  struct func_traits<void, void(Class::*)(Args...) volatile const & noexcept, BoundArgs...> : binder<TypeSet<const Class*, std::decay_t<Args>...>, TypeSet<BoundArgs...>> {};
222  template <typename Class, typename... Args, typename... BoundArgs>
223  struct func_traits<void, void(Class::*)(Args...) const &&, BoundArgs...> : binder<TypeSet<const Class*, std::decay_t<Args>...>, TypeSet<BoundArgs...>> {};
224  template <typename Class, typename... Args, typename... BoundArgs>
225  struct func_traits<void, void(Class::*)(Args...) const && noexcept, BoundArgs...> : binder<TypeSet<const Class*, std::decay_t<Args>...>, TypeSet<BoundArgs...>> {};
226  template <typename Class, typename... Args, typename... BoundArgs>
227  struct func_traits<void, void(Class::*)(Args...) volatile const &&, BoundArgs...> : binder<TypeSet<const Class*, std::decay_t<Args>...>, TypeSet<BoundArgs...>> {};
228  template <typename Class, typename... Args, typename... BoundArgs>
229  struct func_traits<void, void(Class::*)(Args...) volatile const && noexcept, BoundArgs...> : binder<TypeSet<const Class*, std::decay_t<Args>...>, TypeSet<BoundArgs...>> {};
230 
231  // std::function or lambda
232  template <typename Func, typename... BoundArgs>
233  struct func_traits<void_t<decltype(&Func::operator())>, Func, BoundArgs...> : public func_traits<void, decltype(&Func::operator()), Func*, BoundArgs...> {};
234  #else
235  // Error case (function signature did not match anything)
236  template <typename Enable, typename Func, typename... BoundArgs>
237  struct func_traits;
238 
239  // std::function or lambda
240  template <typename Func, typename... BoundArgs>
241  struct func_traits<void_t<decltype(&Func::operator())>, Func, BoundArgs...>
242  : public func_traits<void, decltype(&Func::operator()), Func*, BoundArgs...>
243  {
244  };
245 
246  // Non-const member function
247  template <typename Class, typename... Args, typename... BoundArgs>
248  struct func_traits<void, void(Class::*)(Args...), BoundArgs...>
249  : binder<TypeSet<Class*, typename std::decay<Args>::type...>, TypeSet<BoundArgs...>>
250  {
251  };
252 
253  // Const member function
254  template <typename Class, typename... Args, typename... BoundArgs>
255  struct func_traits<void, void(Class::*)(Args...) const, BoundArgs...>
256  : binder<TypeSet<const Class*, typename std::decay<Args>::type...>, TypeSet<BoundArgs...>>
257  {
258  };
259 
260  // Free function
261  template <typename... Args, typename... BoundArgs>
262  struct func_traits<void, void(*)(Args...), BoundArgs...>
263  : binder<TypeSet<typename std::decay<Args>::type...>, TypeSet<BoundArgs...>>
264  {
265  };
266  #endif
267  }
268 
270 
271 #ifdef TGUI_USE_CPP17
272  template <typename Func, typename... BoundArgs>
273  unsigned int SignalWidgetBase::connect(std::string signalName, Func&& handler, const BoundArgs&... args)
274  {
275  unsigned int id;
276  Signal& signal = getSignal(toLower(signalName));
277 
278  if constexpr (std::is_convertible_v<Func, std::function<void(const BoundArgs&...)>>
279  && std::is_invocable_v<decltype(&handler), BoundArgs...>
280  && !std::is_function_v<Func>)
281  {
282  // Reference to function, all parameters bound
283  id = signal.connect([=, f=std::function<void(const BoundArgs&...)>(handler)]{ std::invoke(f, args...); });
284  }
285  else if constexpr (std::is_convertible_v<Func, std::function<void(const BoundArgs&...)>>)
286  {
287  // Function with all parameters bound
288  id = signal.connect([=]{ std::invoke(handler, args...); });
289  }
290  else if constexpr (std::is_convertible_v<Func, std::function<void(const BoundArgs&..., const std::shared_ptr<Widget>&, const std::string&)>>
291  && std::is_invocable_v<decltype(&handler), BoundArgs..., const std::shared_ptr<Widget>&, const std::string&>
292  && !std::is_function_v<Func>)
293  {
294  // Reference to function with caller arguments, all parameters bound
295  id = signal.connect([=, f=std::function<void(const BoundArgs&..., const std::shared_ptr<Widget>& w, const std::string& s)>(handler)](const std::shared_ptr<Widget>& w, const std::string& s){ std::invoke(f, args..., w, s); });
296  }
297  else if constexpr (std::is_convertible_v<Func, std::function<void(const BoundArgs&..., const std::shared_ptr<Widget>&, const std::string&)>>)
298  {
299  // Function with caller arguments, all parameters bound
300  id = signal.connect([=](const std::shared_ptr<Widget>& w, const std::string& s){ std::invoke(handler, args..., w, s); });
301  }
302  else
303  {
304  // Function with unbound arguments
305  using binder = internal_signal::func_traits<void, std::decay_t<Func>, BoundArgs...>;
306  id = signal.connect(binder::bind(signal, std::forward<Func>(handler), args...));
307  }
308 
309  m_connectedSignals[id] = toLower(signalName);
310  return id;
311  }
312 
313 #else
314  template <typename Func, typename... Args, typename std::enable_if<std::is_convertible<Func, std::function<void(const Args&...)>>::value>::type*>
315  unsigned int SignalWidgetBase::connect(std::string signalName, Func&& handler, const Args&... args)
316  {
317  const unsigned int id = getSignal(toLower(signalName)).connect([f=std::function<void(const Args&...)>(handler),args...](){ f(args...); });
318  m_connectedSignals[id] = toLower(signalName);
319  return id;
320  }
321 
322  template <typename Func, typename... BoundArgs, typename std::enable_if<std::is_convertible<Func, std::function<void(const BoundArgs&..., std::shared_ptr<Widget>, const std::string&)>>::value>::type*>
323  unsigned int SignalWidgetBase::connect(std::string signalName, Func&& handler, BoundArgs&&... args)
324  {
325  const unsigned int id = getSignal(toLower(signalName)).connect(
326  [f=std::function<void(const BoundArgs&..., const std::shared_ptr<Widget>&, const std::string&)>(handler), args...]
327  (const std::shared_ptr<Widget>& w, const std::string& s)
328  { f(args..., w, s); }
329  );
330 
331  m_connectedSignals[id] = toLower(signalName);
332  return id;
333  }
334 
335  template <typename Func, typename... BoundArgs, typename std::enable_if<!std::is_convertible<Func, std::function<void(const BoundArgs&...)>>::value
336  && !std::is_convertible<Func, std::function<void(const BoundArgs&..., std::shared_ptr<Widget>, const std::string&)>>::value>::type*>
337  unsigned int SignalWidgetBase::connect(std::string signalName, Func&& handler, BoundArgs&&... args)
338  {
339  Signal& signal = getSignal(toLower(signalName));
340  using binder = internal_signal::func_traits<void, typename std::decay<Func>::type, BoundArgs...>;
341  const unsigned int id = signal.connect(binder::bind(signal, std::forward<Func>(handler), std::forward<BoundArgs>(args)...));
342  m_connectedSignals[id] = toLower(signalName);
343  return id;
344  }
345 #endif
346 
348 
349  template <typename Func, typename... BoundArgs>
350  unsigned int SignalWidgetBase::connect(std::initializer_list<std::string> signalNames, Func&& handler, BoundArgs&&... args)
351  {
352  unsigned int lastId = 0;
353  for (auto& signalName : signalNames)
354  lastId = connect(std::move(signalName), handler, args...);
355 
356  return lastId;
357  }
358 }
359 
361 
362 #endif // TGUI_SIGNAL_IMPL_HPP
Namespace that contains all TGUI functions and classes.
Definition: AbsoluteOrRelativeValue.hpp:36
unsigned int connect(std::string signalName, Func &&handler, const Args &... args)
Connects a signal handler that will be called when this signal is emitted.
Definition: SignalImpl.hpp:315
virtual Signal & getSignal(std::string signalName)=0
Retrieves a signal based on its name.
Signal to which the user can subscribe to get callbacks from.
Definition: Signal.hpp:58
unsigned int connect(const Delegate &handler)
Connects a signal handler that will be called when this signal is emitted.