TGUI  0.8.0
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, // An error "variable 'func' has function type" here means you passed a reference instead of a function pointer to 'connect'
127  args...,
128  widget,
129  signalName,
130  internal_signal::dereference<UnboundArgs>(internal_signal::parameters[offset + Indices])...);
131  #else
132  return [=,o=offset](const std::shared_ptr<Widget>& widget, const std::string& signalName) { // MinGW TDM GCC 5.1 won't compile code without "o=offset" hack
133  invokeFunc(func, // An error "variable 'func' has function type" here means you passed a reference instead of a function pointer to 'connect'
134  args...,
135  widget,
136  signalName,
137  internal_signal::dereference<UnboundArgs>(internal_signal::parameters[o + Indices])...);
138  #endif
139  };
140  }
141  };
142 
143  template <typename... UnboundArgs>
144  struct binder<TypeSet<UnboundArgs...>, TypeSet<>>
145  {
146  template <typename Func, typename... BoundArgs>
147  static decltype(auto) bind(Signal& signal, Func&& func, BoundArgs&&... args)
148  {
149  return bindImpl(std::index_sequence_for<UnboundArgs...>{}, signal, std::forward<Func>(func), std::forward<BoundArgs>(args)...);
150  }
151 
152  private:
153 
154  template <typename Func, typename... BoundArgs, std::size_t... Indices>
155  static decltype(auto) bindImpl(std::index_sequence<Indices...>, Signal& signal, Func&& func, BoundArgs&&... args)
156  {
157  const std::size_t offset = (sizeof...(UnboundArgs) > 0) ? signal.validateTypes({typeid(UnboundArgs)...}) : 0;
158  #if defined TGUI_USE_CPP17
159  return [=]{
160  std::invoke(func, // An error "variable 'func' has function type" here means you passed a reference instead of a function pointer to 'connect'
161  args...,
162  internal_signal::dereference<UnboundArgs>(internal_signal::parameters[offset + Indices])...);
163  #else
164  return [=,o=offset]{ // MinGW TDM GCC 5.1 won't compile code without "o=offset" hack
165  invokeFunc(func, // An error "variable 'func' has function type" here means you passed a reference instead of a function pointer to 'connect'
166  args...,
167  internal_signal::dereference<UnboundArgs>(internal_signal::parameters[o + Indices])...);
168  #endif
169  };
170  }
171  };
172 
173 
174  #ifdef TGUI_USE_CPP17
175  // Error case (function signature did not match anything)
176  template <typename Enable, typename Func, typename... BoundArgs>
177  struct func_traits;
178 
179  // Free function
180  template <typename... Args, typename... BoundArgs>
181  struct func_traits<void, void(*)(Args...), BoundArgs...> : binder<TypeSet<std::decay_t<Args>...>, TypeSet<BoundArgs...>> {};
182  template <typename... Args, typename... BoundArgs>
183  struct func_traits<void, void(*)(Args...) noexcept, BoundArgs...> : binder<TypeSet<std::decay_t<Args>...>, TypeSet<BoundArgs...>> {};
184 
185  // Non-const member function
186  template <typename Class, typename... Args, typename... BoundArgs>
187  struct func_traits<void, void(Class::*)(Args...), 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...) noexcept, 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...) volatile, 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 noexcept, 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...) &, 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...) & noexcept, 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...) volatile &, 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 & noexcept, 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...) &&, BoundArgs...> : binder<TypeSet<Class*, std::decay_t<Args>...>, TypeSet<BoundArgs...>> {};
204  template <typename Class, typename... Args, typename... BoundArgs>
205  struct func_traits<void, void(Class::*)(Args...) && noexcept, BoundArgs...> : binder<TypeSet<Class*, std::decay_t<Args>...>, TypeSet<BoundArgs...>> {};
206  template <typename Class, typename... Args, typename... BoundArgs>
207  struct func_traits<void, void(Class::*)(Args...) volatile &&, BoundArgs...> : binder<TypeSet<Class*, std::decay_t<Args>...>, TypeSet<BoundArgs...>> {};
208  template <typename Class, typename... Args, typename... BoundArgs>
209  struct func_traits<void, void(Class::*)(Args...) volatile && noexcept, BoundArgs...> : binder<TypeSet<Class*, std::decay_t<Args>...>, TypeSet<BoundArgs...>> {};
210 
211  // Const member function
212  template <typename Class, typename... Args, typename... BoundArgs>
213  struct func_traits<void, void(Class::*)(Args...) const, 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 noexcept, 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...) volatile const, 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 noexcept, 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...) const &, 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 & noexcept, 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...) volatile const &, 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 & noexcept, 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...) const &&, BoundArgs...> : binder<TypeSet<const Class*, std::decay_t<Args>...>, TypeSet<BoundArgs...>> {};
230  template <typename Class, typename... Args, typename... BoundArgs>
231  struct func_traits<void, void(Class::*)(Args...) const && noexcept, BoundArgs...> : binder<TypeSet<const Class*, std::decay_t<Args>...>, TypeSet<BoundArgs...>> {};
232  template <typename Class, typename... Args, typename... BoundArgs>
233  struct func_traits<void, void(Class::*)(Args...) volatile const &&, BoundArgs...> : binder<TypeSet<const Class*, std::decay_t<Args>...>, TypeSet<BoundArgs...>> {};
234  template <typename Class, typename... Args, typename... BoundArgs>
235  struct func_traits<void, void(Class::*)(Args...) volatile const && noexcept, BoundArgs...> : binder<TypeSet<const Class*, std::decay_t<Args>...>, TypeSet<BoundArgs...>> {};
236 
237  // std::function or lambda
238  template <typename Func, typename... BoundArgs>
239  struct func_traits<void_t<decltype(&Func::operator())>, Func, BoundArgs...> : public func_traits<void, decltype(&Func::operator()), Func*, BoundArgs...> {};
240  #else
241  // Error case (function signature did not match anything)
242  template <typename Enable, typename Func, typename... BoundArgs>
243  struct func_traits;
244 
245  // std::function or lambda
246  template <typename Func, typename... BoundArgs>
247  struct func_traits<void_t<decltype(&Func::operator())>, Func, BoundArgs...>
248  : public func_traits<void, decltype(&Func::operator()), Func*, BoundArgs...>
249  {
250  };
251 
252  // Non-const member function
253  template <typename Class, typename... Args, typename... BoundArgs>
254  struct func_traits<void, void(Class::*)(Args...), BoundArgs...>
255  : binder<TypeSet<Class*, typename std::decay<Args>::type...>, TypeSet<BoundArgs...>>
256  {
257  };
258 
259  // Const member function
260  template <typename Class, typename... Args, typename... BoundArgs>
261  struct func_traits<void, void(Class::*)(Args...) const, BoundArgs...>
262  : binder<TypeSet<const Class*, typename std::decay<Args>::type...>, TypeSet<BoundArgs...>>
263  {
264  };
265 
266  // Free function
267  template <typename... Args, typename... BoundArgs>
268  struct func_traits<void, void(*)(Args...), BoundArgs...>
269  : binder<TypeSet<typename std::decay<Args>::type...>, TypeSet<BoundArgs...>>
270  {
271  };
272  #endif
273  }
274 
276 
277 #ifdef TGUI_USE_CPP17
278  template <typename Func, typename... BoundArgs>
279  unsigned int SignalWidgetBase::connect(std::string signalName, Func&& handler, const BoundArgs&... args)
280  {
281  unsigned int id;
282  Signal& signal = getSignal(toLower(signalName));
283 
284  if constexpr (std::is_convertible_v<Func, std::function<void(const BoundArgs&...)>>
285  && std::is_invocable_v<decltype(&handler), BoundArgs...>
286  && !std::is_function_v<Func>)
287  {
288  // Reference to function, all parameters bound
289  id = signal.connect([=, f=std::function<void(const BoundArgs&...)>(handler)]{ std::invoke(f, args...); });
290  }
291  else if constexpr (std::is_convertible_v<Func, std::function<void(const BoundArgs&...)>>)
292  {
293  // Function with all parameters bound
294  id = signal.connect([=]{ std::invoke(handler, args...); });
295  }
296  else if constexpr (std::is_convertible_v<Func, std::function<void(const BoundArgs&..., const std::shared_ptr<Widget>&, const std::string&)>>
297  && std::is_invocable_v<decltype(&handler), BoundArgs..., const std::shared_ptr<Widget>&, const std::string&>
298  && !std::is_function_v<Func>)
299  {
300  // Reference to function with caller arguments, all parameters bound
301  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); });
302  }
303  else if constexpr (std::is_convertible_v<Func, std::function<void(const BoundArgs&..., const std::shared_ptr<Widget>&, const std::string&)>>)
304  {
305  // Function with caller arguments, all parameters bound
306  id = signal.connect([=](const std::shared_ptr<Widget>& w, const std::string& s){ std::invoke(handler, args..., w, s); });
307  }
308  else
309  {
310  // Function with unbound arguments
311  using binder = internal_signal::func_traits<void, std::decay_t<Func>, BoundArgs...>;
312  id = signal.connect(binder::bind(signal, std::forward<Func>(handler), args...));
313  }
314 
315  m_connectedSignals[id] = toLower(signalName);
316  return id;
317  }
318 
319 #else
320  template <typename Func, typename... Args, typename std::enable_if<std::is_convertible<Func, std::function<void(const Args&...)>>::value>::type*>
321  unsigned int SignalWidgetBase::connect(std::string signalName, Func&& handler, const Args&... args)
322  {
323  const unsigned int id = getSignal(toLower(signalName)).connect([f=std::function<void(const Args&...)>(handler),args...](){ f(args...); });
324  m_connectedSignals[id] = toLower(signalName);
325  return id;
326  }
327 
328  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*>
329  unsigned int SignalWidgetBase::connect(std::string signalName, Func&& handler, BoundArgs&&... args)
330  {
331  const unsigned int id = getSignal(toLower(signalName)).connect(
332  [f=std::function<void(const BoundArgs&..., const std::shared_ptr<Widget>&, const std::string&)>(handler), args...]
333  (const std::shared_ptr<Widget>& w, const std::string& s)
334  { f(args..., w, s); }
335  );
336 
337  m_connectedSignals[id] = toLower(signalName);
338  return id;
339  }
340 
341  template <typename Func, typename... BoundArgs, typename std::enable_if<!std::is_convertible<Func, std::function<void(const BoundArgs&...)>>::value
342  && !std::is_convertible<Func, std::function<void(const BoundArgs&..., std::shared_ptr<Widget>, const std::string&)>>::value>::type*>
343  unsigned int SignalWidgetBase::connect(std::string signalName, Func&& handler, BoundArgs&&... args)
344  {
345  Signal& signal = getSignal(toLower(signalName));
346  using binder = internal_signal::func_traits<void, typename std::decay<Func>::type, BoundArgs...>;
347  const unsigned int id = signal.connect(binder::bind(signal, std::forward<Func>(handler), std::forward<BoundArgs>(args)...));
348  m_connectedSignals[id] = toLower(signalName);
349  return id;
350  }
351 #endif
352 
354 
355  template <typename Func, typename... BoundArgs>
356  unsigned int SignalWidgetBase::connect(std::initializer_list<std::string> signalNames, Func&& handler, BoundArgs&&... args)
357  {
358  unsigned int lastId = 0;
359  for (auto& signalName : signalNames)
360  lastId = connect(std::move(signalName), handler, args...);
361 
362  return lastId;
363  }
364 }
365 
367 
368 #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:321
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:59
unsigned int connect(const Delegate &handler)
Connects a signal handler that will be called when this signal is emitted.