Fcitx
objectvtable.h
Go to the documentation of this file.
1 /*
2  * SPDX-FileCopyrightText: 2016-2016 CSSlayer <wengxt@gmail.com>
3  *
4  * SPDX-License-Identifier: LGPL-2.1-or-later
5  *
6  */
7 #ifndef _FCITX_UTILS_DBUS_OBJECTVTABLE_H_
8 #define _FCITX_UTILS_DBUS_OBJECTVTABLE_H_
9 
10 #include <cstdint>
11 #include <exception>
12 #include <functional>
13 #include <memory>
14 #include <mutex>
15 #include <string>
16 #include <tuple>
17 #include <type_traits>
19 #include <fcitx-utils/fcitxutils_export.h>
20 #include <fcitx-utils/flags.h>
21 #include <fcitx-utils/macros.h>
23 
24 /// \addtogroup FcitxUtils
25 /// \{
26 /// \file
27 /// \brief High level API for dbus objects.
28 
29 namespace fcitx::dbus {
30 class Message;
31 class ObjectVTableBase;
32 class Slot;
33 class Bus;
34 class ObjectVTablePrivate;
35 
36 using ObjectMethod = std::function<bool(Message)>;
37 using ObjectMethodClosure = std::function<bool(Message, const ObjectMethod &)>;
38 using PropertyGetMethod = std::function<void(Message &)>;
39 using PropertySetMethod = std::function<bool(Message &)>;
40 
41 /**
42  * An exception if you want message to return a DBus error.
43  *
44  * In the registered property or method, you may throw this exception if a DBus
45  * error happens.
46  *
47  * E.g.
48  * @code
49  * throw dbus::MethodCallError("org.freedesktop.DBus.Error.InvalidArgs", ...);
50  * @endcode
51  */
52 class FCITXUTILS_EXPORT MethodCallError : public std::exception {
53 public:
54  MethodCallError(const char *name, const char *error)
55  : name_(name), error_(error) {}
56 
57  const char *what() const noexcept override { return error_.c_str(); }
58 
59  const char *name() const { return name_.c_str(); }
60 
61 private:
62  std::string name_;
63  std::string error_;
64 };
65 
66 /**
67  * An exception to not reply a the D-Bus method call.
68  *
69  * This can be useful if there is a cascade of method calls and you want to
70  * reply later.
71  */
72 class FCITXUTILS_EXPORT MethodCallNoReply : public std::exception {
73 public:
74  explicit MethodCallNoReply();
75 
76  const char *what() const noexcept override { return "MethodCallNoReply"; }
77 };
78 
79 class FCITXUTILS_EXPORT MethodReturnTypeMismatch : public std::exception {
80 public:
81  explicit MethodReturnTypeMismatch();
82 
83  const char *what() const noexcept override;
84 };
85 
86 class ObjectVTableMethodPrivate;
87 
88 /**
89  * Register a DBus method to current DBus VTable.
90  *
91  * Usually this class should not be used directly in the code.
92  *
93  * @see FCITX_OBJECT_VTABLE_METHOD
94  */
95 class FCITXUTILS_EXPORT ObjectVTableMethod {
96 public:
97  ObjectVTableMethod(ObjectVTableBase *vtable, const std::string &name,
98  const std::string &signature, const std::string &ret,
99  ObjectMethod handler);
100 
101  virtual ~ObjectVTableMethod();
102 
103  const std::string &name() const;
104  const std::string &signature() const;
105  const std::string &ret() const;
106  const ObjectMethod &handler() const;
107  ObjectVTableBase *vtable() const;
108 
109  /**
110  * Set a closure function to call the handler with in it.
111  *
112  * This is useful when you want to do something before and after the dbus
113  * message delivery.
114  *
115  * @param wrapper wrapper function.
116  */
117  void setClosureFunction(ObjectMethodClosure closure);
118 
119 private:
120  std::unique_ptr<ObjectVTableMethodPrivate> d_ptr;
121  FCITX_DECLARE_PRIVATE(ObjectVTableMethod);
122 };
123 
124 template <typename T>
126  using type = T;
127  type ret;
128 
129  template <typename U>
130  void call(U u) {
131  ret = u();
132  }
133 };
134 
135 template <>
136 struct ReturnValueHelper<void> {
137  using type = std::tuple<>;
138  type ret;
139  template <typename U>
140  void call(U u) {
141  u();
142  }
143 };
144 
145 /**
146  * Register a class member function as a DBus method.
147  *
148  * It will also check if the dbus signature matches the function type.
149  *
150  * @param FUNCTION a member function of the class
151  * @param FUNCTION_NAME a string of DBus method name
152  * @param SIGNATURE The dbus signature of arguments.
153  * @param RET The dbus signature of the return value.
154  *
155  * @see https://dbus.freedesktop.org/doc/dbus-specification.html#type-system
156  */
157 #define FCITX_OBJECT_VTABLE_METHOD(FUNCTION, FUNCTION_NAME, SIGNATURE, RET) \
158  ::fcitx::dbus::ObjectVTableMethod FUNCTION##Method { \
159  this, FUNCTION_NAME, SIGNATURE, RET, \
160  ::fcitx::dbus::makeObjectVTablePropertyObjectMethodAdaptor< \
161  FCITX_STRING_TO_DBUS_TYPE(RET), \
162  FCITX_STRING_TO_DBUS_TUPLE(SIGNATURE)>( \
163  this, [this](auto &&...args) { \
164  return this->FUNCTION( \
165  std::forward<decltype(args)>(args)...); \
166  }) \
167  }
168 
169 /**
170  * Register a new DBus signal.
171  *
172  * This macro will define two new function, SIGNAL and SIGNALTo.
173  *
174  * The latter one will only be send to one DBus destination.
175  *
176  * @param SIGNAL will be used to define two member functions.
177  * @param SIGNAL_NAME a string of DBus signal name
178  * @param SIGNATURE The dbus signature of the signal.
179  *
180  * @see https://dbus.freedesktop.org/doc/dbus-specification.html#type-system
181  */
182 #define FCITX_OBJECT_VTABLE_SIGNAL(SIGNAL, SIGNAL_NAME, SIGNATURE) \
183  ::fcitx::dbus::ObjectVTableSignal SIGNAL##Signal{this, SIGNAL_NAME, \
184  SIGNATURE}; \
185  using SIGNAL##ArgType = FCITX_STRING_TO_DBUS_TUPLE(SIGNATURE); \
186  template <typename... Args> \
187  void SIGNAL(Args &&...args) { \
188  auto msg = SIGNAL##Signal.createSignal(); \
189  SIGNAL##ArgType tupleArg{std::forward<Args>(args)...}; \
190  msg << tupleArg; \
191  msg.send(); \
192  } \
193  template <typename... Args> \
194  void SIGNAL##To(const std::string &dest, Args &&...args) { \
195  auto msg = SIGNAL##Signal.createSignal(); \
196  msg.setDestination(dest); \
197  SIGNAL##ArgType tupleArg{std::forward<Args>(args)...}; \
198  msg << tupleArg; \
199  msg.send(); \
200  }
201 
202 /**
203  * Register a new DBus read-only property.
204  *
205  * @param PROPERTY will be used to define class member.
206  * @param NAME a string of DBus property name
207  * @param SIGNATURE The dbus signature of the property.
208  * @param GETMETHOD The method used to return the value of the property
209  *
210  * @see https://dbus.freedesktop.org/doc/dbus-specification.html#type-system
211  */
212 #define FCITX_OBJECT_VTABLE_PROPERTY(PROPERTY, NAME, SIGNATURE, GETMETHOD, \
213  ...) \
214  ::fcitx::dbus::ObjectVTableProperty PROPERTY##Property{ \
215  this, NAME, SIGNATURE, \
216  ::fcitx::dbus::makeObjectVTablePropertyGetMethodAdaptor< \
217  FCITX_STRING_TO_DBUS_TUPLE(SIGNATURE)>(this, GETMETHOD), \
218  ::fcitx::dbus::PropertyOptions{__VA_ARGS__}};
219 
220 /**
221  * Register a new DBus read-only property.
222  *
223  * @param PROPERTY will be used to define class member.
224  * @param NAME a string of DBus property name
225  * @param SIGNATURE The dbus signature of the property.
226  * @param GETMETHOD The method used to return the value of the property
227  * @param SETMETHOD The method used to update the value of the property
228  *
229  * @see https://dbus.freedesktop.org/doc/dbus-specification.html#type-system
230  */
231 #define FCITX_OBJECT_VTABLE_WRITABLE_PROPERTY(PROPERTY, NAME, SIGNATURE, \
232  GETMETHOD, SETMETHOD, ...) \
233  ::fcitx::dbus::ObjectVTableWritableProperty PROPERTY##Property{ \
234  this, \
235  NAME, \
236  SIGNATURE, \
237  ::fcitx::dbus::makeObjectVTablePropertyGetMethodAdaptor< \
238  FCITX_STRING_TO_DBUS_TUPLE(SIGNATURE)>(this, GETMETHOD), \
239  ::fcitx::dbus::makeObjectVTablePropertySetMethodAdaptor< \
240  FCITX_STRING_TO_DBUS_TUPLE(SIGNATURE)>(this, SETMETHOD), \
241  ::fcitx::dbus::PropertyOptions{__VA_ARGS__}};
242 
243 class ObjectVTableSignalPrivate;
244 
245 /**
246  * Register a DBus signal to current DBus VTable.
247  *
248  * Usually this class should not be used directly in the code.
249  *
250  * @see FCITX_OBJECT_VTABLE_SIGNAL
251  */
252 class FCITXUTILS_EXPORT ObjectVTableSignal {
253 public:
254  ObjectVTableSignal(ObjectVTableBase *vtable, std::string name,
255  std::string signature);
256  virtual ~ObjectVTableSignal();
257 
258  Message createSignal();
259  const std::string &name() const;
260  const std::string &signature() const;
261 
262 private:
263  std::unique_ptr<ObjectVTableSignalPrivate> d_ptr;
264  FCITX_DECLARE_PRIVATE(ObjectVTableSignal);
265 };
266 
267 enum class PropertyOption : uint32_t { Hidden = (1 << 0) };
268 
270 
271 class ObjectVTablePropertyPrivate;
272 
273 /**
274  * Register a DBus read-only property to current DBus VTable.
275  *
276  * Usually this class should not be used directly in the code.
277  *
278  * @see FCITX_OBJECT_VTABLE_PROPERTY
279  */
280 class FCITXUTILS_EXPORT ObjectVTableProperty {
281 public:
282  ObjectVTableProperty(ObjectVTableBase *vtable, std::string name,
283  std::string signature, PropertyGetMethod getMethod,
284  PropertyOptions options);
285  virtual ~ObjectVTableProperty();
286 
287  const std::string &name() const;
288  const std::string &signature() const;
289  bool writable() const;
290  const PropertyGetMethod &getMethod() const;
291  const PropertyOptions &options() const;
292 
293 protected:
294  ObjectVTableProperty(std::unique_ptr<ObjectVTablePropertyPrivate> d);
295 
296  std::unique_ptr<ObjectVTablePropertyPrivate> d_ptr;
297  FCITX_DECLARE_PRIVATE(ObjectVTableProperty);
298 };
299 
300 /**
301  * Register a DBus property to current DBus VTable.
302  *
303  * Usually this class should not be used directly in the code.
304  *
305  * @see FCITX_OBJECT_VTABLE_WRITABLE_PROPERTY
306  */
307 class FCITXUTILS_EXPORT ObjectVTableWritableProperty
308  : public ObjectVTableProperty {
309 public:
310  ObjectVTableWritableProperty(ObjectVTableBase *vtable, std::string name,
311  std::string signature,
312  PropertyGetMethod getMethod,
313  PropertySetMethod setMethod,
314  PropertyOptions options);
315 
316  const PropertySetMethod &setMethod() const;
317 };
318 
320 class MessageSetter;
321 
322 class FCITXUTILS_EXPORT ObjectVTableBase
323  : public TrackableObject<ObjectVTableBase> {
324  friend class Bus;
325  friend class MessageSetter;
326 
327 public:
329  virtual ~ObjectVTableBase();
330 
331  void addMethod(ObjectVTableMethod *method);
332  void addSignal(ObjectVTableSignal *sig);
333  void addProperty(ObjectVTableProperty *property);
334 
335  /**
336  * Unregister the dbus object from the bus.
337  *
338  * The object will automatically unregister itself upon destruction. So this
339  * method should only be used if you want to temporarily remove a object
340  * from dbus.
341  */
342  void releaseSlot();
343 
344  /// Return the bus that the object is registered to.
345  Bus *bus();
346  Bus *bus() const;
347  /// Return whether this object is registered to a bus.
348  bool isRegistered() const;
349  /// Return the registered dbus object path of the object.
350  const std::string &path() const;
351  /// Return the registered dbus interface of the object.
352  const std::string &interface() const;
353 
354  /**
355  * Return the current dbus message for current method.
356  *
357  * This should only be used with in a registered callback.
358  *
359  * @return DBus message
360  */
361  Message *currentMessage() const;
362 
363  /**
364  * Set the current dbus message.
365  *
366  * This is only used by internal dbus class and not supposed to be used
367  * anywhere else.
368  *
369  * @param message current message.
370  */
371  void setCurrentMessage(Message *message);
372 
373  ObjectVTableMethod *findMethod(const std::string &name);
374  ObjectVTableProperty *findProperty(const std::string &name);
375 
376 protected:
377  virtual std::mutex &privateDataMutexForType() = 0;
378  virtual ObjectVTablePrivate *privateDataForType() = 0;
379  static std::shared_ptr<ObjectVTablePrivate> newSharedPrivateData();
380 
381 private:
382  void setSlot(Slot *slot);
383 
384  std::unique_ptr<ObjectVTableBasePrivate> d_ptr;
385  FCITX_DECLARE_PRIVATE(ObjectVTableBase);
386 };
387 
388 /**
389  * Base class of any DBus object.
390  *
391  * This should be used with curiously recurring template pattern. Like:
392  *
393  * @code
394  * class Object : public ObjectVTable<OBject> {};
395  * @endcode
396  *
397  * It will instantiate the related shared data for this type.
398  *
399  */
400 template <typename T>
402 public:
403  std::mutex &privateDataMutexForType() override {
404  return privateDataMutex();
405  }
406  ObjectVTablePrivate *privateDataForType() override { return privateData(); }
407  static std::mutex &privateDataMutex() {
408  static std::mutex mutex;
409  return mutex;
410  }
411  static ObjectVTablePrivate *privateData() {
412  static std::shared_ptr<ObjectVTablePrivate> d(newSharedPrivateData());
413  return d.get();
414  }
415 };
416 
417 template <typename Ret, typename Args, typename Callback>
419 public:
421  Callback callback)
422  : base_(base), callback_(std::move(callback)) {}
423 
424  FCITX_INLINE_DEFINE_DEFAULT_DTOR_COPY_AND_MOVE(
426 
427  bool operator()(Message msg) {
428  base_->setCurrentMessage(&msg);
429  auto watcher = base_->watch();
430  Args args;
431  msg >> args;
432  try {
433  using ReturnType = decltype(callWithTuple(callback_, args));
434  static_assert(std::is_same<Ret, ReturnType>::value,
435  "Return type does not match.");
437  helper.call(
438  [this, &args]() { return callWithTuple(callback_, args); });
439  auto reply = msg.createReply();
440  reply << helper.ret;
441  reply.send();
442  } catch (const MethodCallError &error) {
443  auto reply = msg.createError(error.name(), error.what());
444  reply.send();
445  } catch (
446  const MethodCallNoReply &noReply) { // NOLINT(bugprone-empty-catch)
447  }
448  if (watcher.isValid()) {
449  watcher.get()->setCurrentMessage(nullptr);
450  }
451  return true;
452  }
453 
454 private:
455  ObjectVTableBase *base_;
456  Callback callback_;
457 };
458 
459 template <typename Ret, typename Args, typename Callback>
460 auto makeObjectVTablePropertyObjectMethodAdaptor(ObjectVTableBase *base,
461  Callback &&callback) {
463  base, std::forward<Callback>(callback));
464 }
465 
466 template <typename Ret, typename Callback>
468 public:
470  Callback callback)
471  : base_(base), callback_(std::move(callback)) {}
472 
473  FCITX_INLINE_DEFINE_DEFAULT_DTOR_COPY_AND_MOVE(
475 
476  void operator()(Message &msg) {
477  Ret property = callback_();
478  msg << property;
479  }
480 
481 private:
482  ObjectVTableBase *base_;
483  Callback callback_;
484 };
485 
486 template <typename Ret, typename Callback>
487 auto makeObjectVTablePropertyGetMethodAdaptor(ObjectVTableBase *base,
488  Callback &&callback) {
490  base, std::forward<Callback>(callback));
491 }
492 
493 template <typename Ret, typename Callback>
495 public:
497  Callback callback)
498  : base_(base), callback_(std::move(callback)) {}
499 
500  FCITX_INLINE_DEFINE_DEFAULT_DTOR_COPY_AND_MOVE(
502 
503  bool operator()(Message &msg) {
504  base_->setCurrentMessage(&msg);
505  auto watcher = base_->watch();
506  Ret args;
507  msg >> args;
508  callWithTuple(callback_, args);
509  auto reply = msg.createReply();
510  reply.send();
511  if (watcher.isValid()) {
512  watcher.get()->setCurrentMessage(nullptr);
513  }
514  return true;
515  }
516 
517 private:
518  ObjectVTableBase *base_;
519  Callback callback_;
520 };
521 
522 template <typename Ret, typename Callback>
523 auto makeObjectVTablePropertySetMethodAdaptor(ObjectVTableBase *base,
524  Callback &&callback) {
526  base, std::forward<Callback>(callback));
527 }
528 
529 } // namespace fcitx::dbus
530 
531 #endif // _FCITX_UTILS_DBUS_OBJECTVTABLE_H_
bool send()
Send this message.
Definition: message.cpp:277
Basic DBus type of a DBus message.
Definition: message.h:224
Base class of any DBus object.
Definition: objectvtable.h:401
Register a DBus read-only property to current DBus VTable.
Definition: objectvtable.h:280
Message createError(const char *name, const char *message) const
Create a error reply to this message.
Definition: message.cpp:62
Virtual base class represent some internal registration of the bus.
Definition: bus.h:33
Utitliy classes for statically tracking the life of a object.
A class that represents a connection to the Bus.
Definition: bus.h:51
API for DBus message.
Message createReply() const
Create a reply to this message.
Definition: message.cpp:53
Register a DBus property to current DBus VTable.
Definition: objectvtable.h:307
An exception if you want message to return a DBus error.
Definition: objectvtable.h:52
Helper class to be used with TrackableObjectReference.
Register a DBus method to current DBus VTable.
Definition: objectvtable.h:95
Class provides bit flag support for Enum.
Definition: flags.h:33
Helper template class to make easier to use type safe enum flags.
An exception to not reply a the D-Bus method call.
Definition: objectvtable.h:72
Register a DBus signal to current DBus VTable.
Definition: objectvtable.h:252