TGUI  0.8.9
SignalImpl.hpp
1 //
3 // TGUI - Texus' Graphical User Interface
4 // Copyright (C) 2012-2020 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 #if defined(__cpp_lib_void_t) && (__cpp_lib_void_t >= 201411L)
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 #if defined(__cpp_if_constexpr) && (__cpp_if_constexpr >= 201606L)
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 if constexpr (std::is_same_v<Type, sf::Color>) // Signal handlers are allowed to have sf::Color parameters while the signal sends tgui::Color
59  return static_cast<sf::Color>(*static_cast<const Color*>(obj));
60  else
61  return *static_cast<const std::decay_t<Type>*>(obj);
62  }
63 #else
64  template <typename Type, typename std::enable_if<std::is_same<Type, std::string>::value>::type* = nullptr>
65  decltype(auto) dereference(const void* obj)
66  {
67  // Signal handlers are allowed to have std::string parameters while the signal sends sf::String
68  return static_cast<std::string>(*static_cast<const sf::String*>(obj));
69  }
70 
71  template <typename Type, typename std::enable_if<std::is_same<Type, sf::Vector2f>::value>::type* = nullptr>
72  decltype(auto) dereference(const void* obj)
73  {
74  // Signal handlers are allowed to have sf::Vector2f parameters while the signal sends tgui::Vector2f
75  return static_cast<sf::Vector2f>(*static_cast<const Vector2f*>(obj));
76  }
77 
78  template <typename Type, typename std::enable_if<std::is_same<Type, sf::Color>::value>::type* = nullptr>
79  decltype(auto) dereference(const void* obj)
80  {
81  // Signal handlers are allowed to have sf::Vector2f parameters while the signal sends tgui::Vector2f
82  return static_cast<sf::Color>(*static_cast<const Color*>(obj));
83  }
84 
85  template <typename Type, typename std::enable_if<!std::is_same<Type, std::string>::value && !std::is_same<Type, sf::Vector2f>::value && !std::is_same<Type, sf::Color>::value>::type* = nullptr>
86  decltype(auto) dereference(const void* obj)
87  {
88  return *static_cast<const typename std::decay<Type>::type*>(obj);
89  }
90 #endif
91 
92 #if !defined(__cpp_lib_invoke) || (__cpp_lib_invoke < 201411L)
93  // std::invoke only exists in c++17 so we use our own implementation to support c++14 compilers
94  // Visual Studio compiler did not like it when the function was called "invoke"
95  template <typename Func, typename... Args, typename std::enable_if<std::is_member_pointer<typename std::decay<Func>::type>::value>::type* = nullptr>
96  void invokeFunc(Func&& func, Args&&... args)
97  {
98  std::mem_fn(func)(std::forward<Args>(args)...);
99  }
100 
101  template <typename Func, typename... Args, typename std::enable_if<!std::is_member_pointer<typename std::decay<Func>::type>::value>::type* = nullptr>
102  void invokeFunc(Func&& func, Args&&... args)
103  {
104  std::forward<Func>(func)(std::forward<Args>(args)...);
105  }
106 #endif
107 
108  // The binder will figure out the unbound parameters and bind them if they correspond to what the signal sends
109  template <typename... Args>
110  struct binder;
111 
112  template <typename Arg, typename... AllArgs, typename BoundArg, typename... BoundArgs>
113  struct binder<TypeSet<Arg, AllArgs...>, TypeSet<BoundArg, BoundArgs...>>
114  : binder<TypeSet<AllArgs...>, TypeSet<BoundArgs...>>
115  {
116  };
117 
118  template <typename... UnboundArgs>
119  struct binder<TypeSet<std::shared_ptr<Widget>, std::string, UnboundArgs...>, TypeSet<>>
120  {
121  template <typename Func, typename... BoundArgs>
122  static decltype(auto) bind(Signal& signal, Func&& func, BoundArgs&&... args)
123  {
124  return bindImpl(std::index_sequence_for<UnboundArgs...>{}, signal, std::forward<Func>(func), std::forward<BoundArgs>(args)...);
125  }
126 
127  private:
128 
129  template <typename Func, typename... BoundArgs, std::size_t... Indices>
130  static decltype(auto) bindImpl(std::index_sequence<Indices...>, Signal& signal, Func&& func, BoundArgs&&... args)
131  {
132  const std::size_t offset = (sizeof...(UnboundArgs) > 0) ? signal.validateTypes({typeid(UnboundArgs)...}) : 0;
133 #if defined(__cpp_lib_invoke) && (__cpp_lib_invoke >= 201411L)
134  return [=,o=offset](const std::shared_ptr<Widget>& widget, const std::string& signalName) {
135  std::invoke(func, // An error "variable 'func' has function type" here means you passed a reference instead of a function pointer to 'connect'
136  args...,
137  widget,
138  signalName,
139  internal_signal::dereference<UnboundArgs>(internal_signal::parameters[o + Indices])...);
140 #else
141  return [=,o=offset](const std::shared_ptr<Widget>& widget, const std::string& signalName) {
142  invokeFunc(func, // An error "variable 'func' has function type" here means you passed a reference instead of a function pointer to 'connect'
143  args...,
144  widget,
145  signalName,
146  internal_signal::dereference<UnboundArgs>(internal_signal::parameters[o + Indices])...);
147 #endif
148  };
149  }
150  };
151 
152  template <typename... UnboundArgs>
153  struct binder<TypeSet<UnboundArgs...>, TypeSet<>>
154  {
155  template <typename Func, typename... BoundArgs>
156  static decltype(auto) bind(Signal& signal, Func&& func, BoundArgs&&... args)
157  {
158  return bindImpl(std::index_sequence_for<UnboundArgs...>{}, signal, std::forward<Func>(func), std::forward<BoundArgs>(args)...);
159  }
160 
161  private:
162 
163  template <typename Func, typename... BoundArgs, std::size_t... Indices>
164  static decltype(auto) bindImpl(std::index_sequence<Indices...>, Signal& signal, Func&& func, BoundArgs&&... args)
165  {
166  const std::size_t offset = (sizeof...(UnboundArgs) > 0) ? signal.validateTypes({typeid(UnboundArgs)...}) : 0;
167 #if defined(__cpp_lib_invoke) && (__cpp_lib_invoke >= 201411L)
168  return [=,o=offset]{
169  std::invoke(func, // An error "variable 'func' has function type" here means you passed a reference instead of a function pointer to 'connect'
170  args...,
171  internal_signal::dereference<UnboundArgs>(internal_signal::parameters[o + Indices])...);
172 #else
173  return [=,o=offset]{
174  invokeFunc(func, // An error "variable 'func' has function type" here means you passed a reference instead of a function pointer to 'connect'
175  args...,
176  internal_signal::dereference<UnboundArgs>(internal_signal::parameters[o + Indices])...);
177 #endif
178  };
179  }
180  };
181 
182 
183 #if defined(__cpp_noexcept_function_type) && (__cpp_noexcept_function_type >= 201510L)
184  // Error case (function signature did not match anything)
185  template <typename Enable, typename Func, typename... BoundArgs>
186  struct func_traits;
187 
188  // Free function
189  template <typename... Args, typename... BoundArgs>
190  struct func_traits<void, void(*)(Args...), BoundArgs...> : binder<TypeSet<std::decay_t<Args>...>, TypeSet<BoundArgs...>> {};
191  template <typename... Args, typename... BoundArgs>
192  struct func_traits<void, void(*)(Args...) noexcept, BoundArgs...> : binder<TypeSet<std::decay_t<Args>...>, TypeSet<BoundArgs...>> {};
193 
194  // Non-const member function
195  template <typename Class, typename... Args, typename... BoundArgs>
196  struct func_traits<void, void(Class::*)(Args...), BoundArgs...> : binder<TypeSet<Class*, std::decay_t<Args>...>, TypeSet<BoundArgs...>> {};
197  template <typename Class, typename... Args, typename... BoundArgs>
198  struct func_traits<void, void(Class::*)(Args...) noexcept, BoundArgs...> : binder<TypeSet<Class*, std::decay_t<Args>...>, TypeSet<BoundArgs...>> {};
199  template <typename Class, typename... Args, typename... BoundArgs>
200  struct func_traits<void, void(Class::*)(Args...) volatile, BoundArgs...> : binder<TypeSet<Class*, std::decay_t<Args>...>, TypeSet<BoundArgs...>> {};
201  template <typename Class, typename... Args, typename... BoundArgs>
202  struct func_traits<void, void(Class::*)(Args...) volatile noexcept, BoundArgs...> : binder<TypeSet<Class*, std::decay_t<Args>...>, TypeSet<BoundArgs...>> {};
203  template <typename Class, typename... Args, typename... BoundArgs>
204  struct func_traits<void, void(Class::*)(Args...) &, BoundArgs...> : binder<TypeSet<Class*, std::decay_t<Args>...>, TypeSet<BoundArgs...>> {};
205  template <typename Class, typename... Args, typename... BoundArgs>
206  struct func_traits<void, void(Class::*)(Args...) & noexcept, BoundArgs...> : binder<TypeSet<Class*, std::decay_t<Args>...>, TypeSet<BoundArgs...>> {};
207  template <typename Class, typename... Args, typename... BoundArgs>
208  struct func_traits<void, void(Class::*)(Args...) volatile &, BoundArgs...> : binder<TypeSet<Class*, std::decay_t<Args>...>, TypeSet<BoundArgs...>> {};
209  template <typename Class, typename... Args, typename... BoundArgs>
210  struct func_traits<void, void(Class::*)(Args...) volatile & noexcept, BoundArgs...> : binder<TypeSet<Class*, std::decay_t<Args>...>, TypeSet<BoundArgs...>> {};
211  template <typename Class, typename... Args, typename... BoundArgs>
212  struct func_traits<void, void(Class::*)(Args...) &&, BoundArgs...> : binder<TypeSet<Class*, std::decay_t<Args>...>, TypeSet<BoundArgs...>> {};
213  template <typename Class, typename... Args, typename... BoundArgs>
214  struct func_traits<void, void(Class::*)(Args...) && noexcept, BoundArgs...> : binder<TypeSet<Class*, std::decay_t<Args>...>, TypeSet<BoundArgs...>> {};
215  template <typename Class, typename... Args, typename... BoundArgs>
216  struct func_traits<void, void(Class::*)(Args...) volatile &&, BoundArgs...> : binder<TypeSet<Class*, std::decay_t<Args>...>, TypeSet<BoundArgs...>> {};
217  template <typename Class, typename... Args, typename... BoundArgs>
218  struct func_traits<void, void(Class::*)(Args...) volatile && noexcept, BoundArgs...> : binder<TypeSet<Class*, std::decay_t<Args>...>, TypeSet<BoundArgs...>> {};
219 
220  // Const member function
221  template <typename Class, typename... Args, typename... BoundArgs>
222  struct func_traits<void, void(Class::*)(Args...) const, BoundArgs...> : binder<TypeSet<const Class*, std::decay_t<Args>...>, TypeSet<BoundArgs...>> {};
223  template <typename Class, typename... Args, typename... BoundArgs>
224  struct func_traits<void, void(Class::*)(Args...) const noexcept, BoundArgs...> : binder<TypeSet<const Class*, std::decay_t<Args>...>, TypeSet<BoundArgs...>> {};
225  template <typename Class, typename... Args, typename... BoundArgs>
226  struct func_traits<void, void(Class::*)(Args...) volatile const, BoundArgs...> : binder<TypeSet<const Class*, std::decay_t<Args>...>, TypeSet<BoundArgs...>> {};
227  template <typename Class, typename... Args, typename... BoundArgs>
228  struct func_traits<void, void(Class::*)(Args...) volatile const noexcept, BoundArgs...> : binder<TypeSet<const Class*, std::decay_t<Args>...>, TypeSet<BoundArgs...>> {};
229  template <typename Class, typename... Args, typename... BoundArgs>
230  struct func_traits<void, void(Class::*)(Args...) const &, BoundArgs...> : binder<TypeSet<const Class*, std::decay_t<Args>...>, TypeSet<BoundArgs...>> {};
231  template <typename Class, typename... Args, typename... BoundArgs>
232  struct func_traits<void, void(Class::*)(Args...) const & noexcept, BoundArgs...> : binder<TypeSet<const Class*, std::decay_t<Args>...>, TypeSet<BoundArgs...>> {};
233  template <typename Class, typename... Args, typename... BoundArgs>
234  struct func_traits<void, void(Class::*)(Args...) volatile const &, BoundArgs...> : binder<TypeSet<const Class*, std::decay_t<Args>...>, TypeSet<BoundArgs...>> {};
235  template <typename Class, typename... Args, typename... BoundArgs>
236  struct func_traits<void, void(Class::*)(Args...) volatile const & noexcept, BoundArgs...> : binder<TypeSet<const Class*, std::decay_t<Args>...>, TypeSet<BoundArgs...>> {};
237  template <typename Class, typename... Args, typename... BoundArgs>
238  struct func_traits<void, void(Class::*)(Args...) const &&, BoundArgs...> : binder<TypeSet<const Class*, std::decay_t<Args>...>, TypeSet<BoundArgs...>> {};
239  template <typename Class, typename... Args, typename... BoundArgs>
240  struct func_traits<void, void(Class::*)(Args...) const && noexcept, BoundArgs...> : binder<TypeSet<const Class*, std::decay_t<Args>...>, TypeSet<BoundArgs...>> {};
241  template <typename Class, typename... Args, typename... BoundArgs>
242  struct func_traits<void, void(Class::*)(Args...) volatile const &&, BoundArgs...> : binder<TypeSet<const Class*, std::decay_t<Args>...>, TypeSet<BoundArgs...>> {};
243  template <typename Class, typename... Args, typename... BoundArgs>
244  struct func_traits<void, void(Class::*)(Args...) volatile const && noexcept, BoundArgs...> : binder<TypeSet<const Class*, std::decay_t<Args>...>, TypeSet<BoundArgs...>> {};
245 
246  // std::function or lambda
247  template <typename Func, typename... BoundArgs>
248  struct func_traits<void_t<decltype(&Func::operator())>, Func, BoundArgs...> : public func_traits<void, decltype(&Func::operator()), Func*, BoundArgs...> {};
249 #else
250  // Error case (function signature did not match anything)
251  template <typename Enable, typename Func, typename... BoundArgs>
252  struct func_traits;
253 
254  // std::function or lambda
255  template <typename Func, typename... BoundArgs>
256  struct func_traits<void_t<decltype(&Func::operator())>, Func, BoundArgs...>
257  : public func_traits<void, decltype(&Func::operator()), Func*, BoundArgs...>
258  {
259  };
260 
261  // Non-const member function
262  template <typename Class, typename... Args, typename... BoundArgs>
263  struct func_traits<void, void(Class::*)(Args...), BoundArgs...>
264  : binder<TypeSet<Class*, typename std::decay<Args>::type...>, TypeSet<BoundArgs...>>
265  {
266  };
267 
268  // Const member function
269  template <typename Class, typename... Args, typename... BoundArgs>
270  struct func_traits<void, void(Class::*)(Args...) const, BoundArgs...>
271  : binder<TypeSet<const Class*, typename std::decay<Args>::type...>, TypeSet<BoundArgs...>>
272  {
273  };
274 
275  // Free function
276  template <typename... Args, typename... BoundArgs>
277  struct func_traits<void, void(*)(Args...), BoundArgs...>
278  : binder<TypeSet<typename std::decay<Args>::type...>, TypeSet<BoundArgs...>>
279  {
280  };
281 #endif
282  }
283 
285 
286 #if defined(__cpp_if_constexpr) && (__cpp_if_constexpr >= 201606L)
287  template <typename Func, typename... BoundArgs>
288  unsigned int SignalWidgetBase::connect(std::string signalName, Func&& handler, const BoundArgs&... args)
289  {
290  unsigned int id;
291  Signal& signal = getSignal(toLower(signalName));
292 
293  if constexpr (std::is_convertible_v<Func, std::function<void(const BoundArgs&...)>>
294  && std::is_invocable_v<decltype(&handler), BoundArgs...>
295  && !std::is_function_v<Func>)
296  {
297  // Reference to function, all parameters bound
298  id = signal.connect([=, f=std::function<void(const BoundArgs&...)>(handler)]{ std::invoke(f, args...); });
299  }
300  else if constexpr (std::is_convertible_v<Func, std::function<void(const BoundArgs&...)>>)
301  {
302  // Function with all parameters bound
303  id = signal.connect([=]{ std::invoke(handler, args...); });
304  }
305  else if constexpr (std::is_convertible_v<Func, std::function<void(const BoundArgs&..., const std::shared_ptr<Widget>&, const std::string&)>>
306  && std::is_invocable_v<decltype(&handler), BoundArgs..., const std::shared_ptr<Widget>&, const std::string&>
307  && !std::is_function_v<Func>)
308  {
309  // Reference to function with caller arguments, all parameters bound
310  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); });
311  }
312  else if constexpr (std::is_convertible_v<Func, std::function<void(const BoundArgs&..., const std::shared_ptr<Widget>&, const std::string&)>>)
313  {
314  // Function with caller arguments, all parameters bound
315  id = signal.connect([=](const std::shared_ptr<Widget>& w, const std::string& s){ std::invoke(handler, args..., w, s); });
316  }
317  else
318  {
319  // Function with unbound arguments
320  using binder = internal_signal::func_traits<void, std::decay_t<Func>, BoundArgs...>;
321  id = signal.connect(binder::bind(signal, std::forward<Func>(handler), args...));
322  }
323 
324  m_connectedSignals[id] = toLower(signalName);
325  return id;
326  }
327 
328 #else
329  template <typename Func, typename... Args, typename std::enable_if<std::is_convertible<Func, std::function<void(const Args&...)>>::value>::type*>
330  unsigned int SignalWidgetBase::connect(std::string signalName, Func&& handler, const Args&... args)
331  {
332  const unsigned int id = getSignal(toLower(signalName)).connect([f=std::function<void(const Args&...)>(handler),args...](){ f(args...); });
333  m_connectedSignals[id] = toLower(signalName);
334  return id;
335  }
336 
337  template <typename Func, typename... BoundArgs, typename std::enable_if<!std::is_convertible<Func, std::function<void(const BoundArgs&...)>>::value // Ambigious otherwise when passing bind expression
338  && std::is_convertible<Func, std::function<void(const BoundArgs&..., std::shared_ptr<Widget>, const std::string&)>>::value>::type*>
339  unsigned int SignalWidgetBase::connect(std::string signalName, Func&& handler, BoundArgs&&... args)
340  {
341  const unsigned int id = getSignal(toLower(signalName)).connect(
342  [f=std::function<void(const BoundArgs&..., const std::shared_ptr<Widget>&, const std::string&)>(handler), args...]
343  (const std::shared_ptr<Widget>& w, const std::string& s)
344  { f(args..., w, s); }
345  );
346 
347  m_connectedSignals[id] = toLower(signalName);
348  return id;
349  }
350 
351  template <typename Func, typename... BoundArgs, typename std::enable_if<!std::is_convertible<Func, std::function<void(const BoundArgs&...)>>::value
352  && !std::is_convertible<Func, std::function<void(const BoundArgs&..., std::shared_ptr<Widget>, const std::string&)>>::value>::type*>
353  unsigned int SignalWidgetBase::connect(std::string signalName, Func&& handler, BoundArgs&&... args)
354  {
355  Signal& signal = getSignal(toLower(signalName));
356  using binder = internal_signal::func_traits<void, typename std::decay<Func>::type, BoundArgs...>;
357  const unsigned int id = signal.connect(binder::bind(signal, std::forward<Func>(handler), std::forward<BoundArgs>(args)...));
358  m_connectedSignals[id] = toLower(signalName);
359  return id;
360  }
361 #endif
362 
364 
365  template <typename Func, typename... BoundArgs>
366  unsigned int SignalWidgetBase::connect(std::initializer_list<std::string> signalNames, Func&& handler, BoundArgs&&... args)
367  {
368  unsigned int lastId = 0;
369  for (auto& signalName : signalNames)
370  lastId = connect(std::move(signalName), handler, args...);
371 
372  return lastId;
373  }
374 }
375 
377 
378 #endif // TGUI_SIGNAL_IMPL_HPP
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:330
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:231
unsigned int connect(const Delegate &handler)
Connects a signal handler that will be called when this signal is emitted.
Namespace that contains all TGUI functions and classes.
Definition: AbsoluteOrRelativeValue.hpp:37
TGUI_API std::string toLower(const std::string &str)
Converts a string to lowercase.