summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorPatrick Ohly <patrick.ohly@intel.com>2018-01-12 21:18:23 +0100
committerPatrick Ohly <patrick.ohly@intel.com>2020-12-05 21:28:08 +0100
commit39bf3f12913dea4265d9e12bd54294acede22a72 (patch)
treeaf90cd78d2f20c09f483df536659a8fbfdc92796 /src
parent3729a239fc5ce39be2d60e1f2dbba204f710f7b0 (diff)
C++: variadic connectSignal()
By specifying the list of signal types as template parameters it becomes possible to use a single implementation. Lambdas can replace explicit callback methods. The reimplementation is more flexible and does not enforce the use of a boost::function. This matches how connectSignal() was used in practice. Thanks to universal references, the boost::bind instances get moved directly into the allocated instances that are attached to the signal handler. The downside is that the call syntax changes slightly, because partially specifying template parameters does not work. Signed-off-by: Patrick Ohly <patrick.ohly@intel.com>
Diffstat (limited to 'src')
-rw-r--r--src/backends/evolution/EvolutionCalendarSource.cpp12
-rw-r--r--src/backends/evolution/EvolutionContactSource.cpp12
-rw-r--r--src/syncevo/GLibSupport.h163
3 files changed, 54 insertions, 133 deletions
diff --git a/src/backends/evolution/EvolutionCalendarSource.cpp b/src/backends/evolution/EvolutionCalendarSource.cpp
index 3ec63c41..3be9cf01 100644
--- a/src/backends/evolution/EvolutionCalendarSource.cpp
+++ b/src/backends/evolution/EvolutionCalendarSource.cpp
@@ -318,12 +318,12 @@ class ECalClientViewSyncHandler {
bool processSync(GErrorCXX &gerror)
{
// Listen for view signals
- m_view.connectSignal<void (ECalClientView *ebookview,
- const GSList *contacts)>("objects-added",
- boost::bind(m_process, _2));
- m_view.connectSignal<void (EBookClientView *ebookview,
- const GError *error)>("complete",
- boost::bind(&ECalClientViewSyncHandler::completed, this, _2));
+ m_view.connectSignal<ECalClientView *,
+ const GSList *>()("objects-added",
+ boost::bind(m_process, _2));
+ m_view.connectSignal<EBookClientView *,
+ const GError *>()("complete",
+ boost::bind(&ECalClientViewSyncHandler::completed, this, _2));
// Start the view
e_cal_client_view_start (m_view, m_error);
diff --git a/src/backends/evolution/EvolutionContactSource.cpp b/src/backends/evolution/EvolutionContactSource.cpp
index d47a5bdc..492eb4cb 100644
--- a/src/backends/evolution/EvolutionContactSource.cpp
+++ b/src/backends/evolution/EvolutionContactSource.cpp
@@ -299,12 +299,12 @@ class EBookClientViewSyncHandler {
bool process(GErrorCXX &gerror) {
// Listen for view signals
- m_view.connectSignal<void (EBookClientView *ebookview,
- const GSList *contacts)>("objects-added",
- boost::bind(m_process, _2));
- m_view.connectSignal<void (EBookClientView *ebookview,
- const GError *error)>("complete",
- boost::bind(&EBookClientViewSyncHandler::completed, this, _2));
+ m_view.connectSignal<EBookClientView *,
+ const GSList *>()("objects-added",
+ boost::bind(m_process, _2));
+ m_view.connectSignal<EBookClientView *,
+ const GError *>()("complete",
+ boost::bind(&EBookClientViewSyncHandler::completed, this, _2));
// Start the view
e_book_client_view_start (m_view, m_error);
diff --git a/src/syncevo/GLibSupport.h b/src/syncevo/GLibSupport.h
index c683ed0f..af460517 100644
--- a/src/syncevo/GLibSupport.h
+++ b/src/syncevo/GLibSupport.h
@@ -76,103 +76,6 @@ GLibSelectResult GLibSelect(GMainLoop *loop, int fd, int direction, Timespec *ti
#ifdef HAVE_GLIB
-// Signal callback. Specializations will handle varying number of parameters.
-template<class S> struct GObjectSignalHandler {
- // static void handler();
- // No specialization defined for the requested function prototype.
-};
-
-template<> struct GObjectSignalHandler<void ()> {
- static void handler(gpointer data) throw () {
- try {
- (*reinterpret_cast< boost::function<void ()> *>(data))();
- } catch (...) {
- Exception::handle(HANDLE_EXCEPTION_FATAL);
- }
- }
-};
-template<class A1> struct GObjectSignalHandler<void (A1)> {
- static void handler(A1 a1, gpointer data) throw () {
- try {
- (*reinterpret_cast< boost::function<void (A1)> *>(data))(a1);
- } catch (...) {
- Exception::handle(HANDLE_EXCEPTION_FATAL);
- }
- }
-};
-template<class A1, class A2> struct GObjectSignalHandler<void (A1, A2)> {
- static void handler(A1 a1, A2 a2, gpointer data) throw () {
- try {
- (*reinterpret_cast< boost::function<void (A1, A2)> *>(data))(a1, a2);
- } catch (...) {
- Exception::handle(HANDLE_EXCEPTION_FATAL);
- }
- }
-};
-template<class A1, class A2, class A3> struct GObjectSignalHandler<void (A1, A2, A3)> {
- static void handler(A1 a1, A2 a2, A3 a3, gpointer data) throw () {
- try {
- (*reinterpret_cast< boost::function<void (A1, A2, A3)> *>(data))(a1, a2, a3);
- } catch (...) {
- Exception::handle(HANDLE_EXCEPTION_FATAL);
- }
- }
-};
-template<class A1, class A2, class A3, class A4> struct GObjectSignalHandler<void (A1, A2, A3, A4)> {
- static void handler(A1 a1, A2 a2, A3 a3, A4 a4, gpointer data) throw () {
- try {
- (*reinterpret_cast< boost::function<void (A1, A2, A3, A4)> *>(data))(a1, a2, a3, a4);
- } catch (...) {
- Exception::handle(HANDLE_EXCEPTION_FATAL);
- }
- }
-};
-template<class A1, class A2, class A3, class A4, class A5> struct GObjectSignalHandler<void (A1, A2, A3, A4, A5)> {
- static void handler(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, gpointer data) throw () {
- try {
- (*reinterpret_cast< boost::function<void (A1, A2, A3, A4, A5)> *>(data))(a1, a2, a3, a4, a5);
- } catch (...) {
- Exception::handle(HANDLE_EXCEPTION_FATAL);
- }
- }
-};
-template<class A1, class A2, class A3, class A4, class A5, class A6> struct GObjectSignalHandler<void (A1, A2, A3, A4, A5, A6)> {
- static void handler(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, gpointer data) throw () {
- try {
- (*reinterpret_cast< boost::function<void (A1, A2, A3, A4, A5, A6)> *>(data))(a1, a2, a3, a4, a5, a6);
- } catch (...) {
- Exception::handle(HANDLE_EXCEPTION_FATAL);
- }
- }
-};
-template<class A1, class A2, class A3, class A4, class A5, class A6, class A7> struct GObjectSignalHandler<void (A1, A2, A3, A4, A5, A6, A7)> {
- static void handler(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, gpointer data) throw () {
- try {
- (*reinterpret_cast< boost::function<void (A1, A2, A3, A4, A5, A6, A7)> *>(data))(a1, a2, a3, a4, a5, a6, a7);
- } catch (...) {
- Exception::handle(HANDLE_EXCEPTION_FATAL);
- }
- }
-};
-template<class A1, class A2, class A3, class A4, class A5, class A6, class A7, class A8> struct GObjectSignalHandler<void (A1, A2, A3, A4, A5, A6, A7, A8)> {
- static void handler(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, gpointer data) throw () {
- try {
- (*reinterpret_cast< boost::function<void (A1, A2, A3, A4, A5, A6, A7, A8)> *>(data))(a1, a2, a3, a4, a5, a6, a7, a8);
- } catch (...) {
- Exception::handle(HANDLE_EXCEPTION_FATAL);
- }
- }
-};
-template<class A1, class A2, class A3, class A4, class A5, class A6, class A7, class A8, class A9> struct GObjectSignalHandler<void (A1, A2, A3, A4, A5, A6, A7, A8, A9)> {
- static void handler(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, gpointer data) throw () {
- try {
- (*reinterpret_cast< boost::function<void (A1, A2, A3, A4, A5, A6, A7, A8, A9)> *>(data))(a1, a2, a3, a4, a5, a6, a7, a8, a9);
- } catch (...) {
- Exception::handle(HANDLE_EXCEPTION_FATAL);
- }
- }
-};
-
enum RefOwnership
{
TRANSFER_REF = false, /**<
@@ -185,20 +88,40 @@ enum RefOwnership
*/
};
+template<typename C, typename ...A> class ConnectHandle
+{
+ C *m_object;
+ public:
+ ConnectHandle(C *object) :
+ m_object(object)
+ {}
+
+ template<typename CB> guint operator () (const char *signal, CB &&callback) const {
+ auto c_callback = [] (A... a, gpointer data) noexcept {
+ try {
+ (*reinterpret_cast<CB *>(data))(a...);
+ } catch (...) {
+ Exception::handle(HANDLE_EXCEPTION_FATAL);
+ }
+ };
+ auto c_destroy = [] (gpointer data, GClosure *closure) noexcept {
+ try {
+ delete reinterpret_cast<CB *>(data);
+ } catch (...) {
+ Exception::handle(HANDLE_EXCEPTION_FATAL);
+ }
+ };
+ return g_signal_connect_data(m_object, signal,
+ G_CALLBACK(static_cast<void (*)(A..., gpointer)>(c_callback)),
+ new CB(std::forward<CB>(callback)),
+ c_destroy,
+ GConnectFlags(0));
+ }
+};
template<class C> class TrackGObject : public boost::intrusive_ptr<C> {
typedef boost::intrusive_ptr<C> Base_t;
- // Frees the instance of boost::function which was allocated
- // by connectSignal.
- template<class S> static void signalDestroy(gpointer data, GClosure *closure) throw () {
- try {
- delete reinterpret_cast< boost::function<void ()> *>(data);
- } catch (...) {
- Exception::handle(HANDLE_EXCEPTION_FATAL);
- }
- }
-
public:
TrackGObject(C *ptr, RefOwnership ownership) : Base_t(ptr, (bool)ownership) {}
TrackGObject() {}
@@ -209,13 +132,8 @@ template<class C> class TrackGObject : public boost::intrusive_ptr<C> {
static TrackGObject steal(C *ptr) { return TrackGObject(ptr, TRANSFER_REF); }
- template<class S> guint connectSignal(const char *signal,
- const boost::function<S> &callback) {
- return g_signal_connect_data(Base_t::get(), signal,
- G_CALLBACK(&GObjectSignalHandler<S>::handler),
- new boost::function<S>(callback),
- &signalDestroy<S>,
- GConnectFlags(0));
+ template<typename ...A> auto connectSignal() const {
+ return ConnectHandle<C, A...>(Base_t::get());
}
void disconnectSignal(guint handlerID) {
g_signal_handler_disconnect(static_cast<gpointer>(Base_t::get()),
@@ -258,8 +176,14 @@ template<class C> class StealGLib : public TrackGLib<C> {
* functions must be put into the boost namespace. The type itself is
* *inside* the SyncEvolution namespace.
*
- * connectSignal() connects a GObject signal to a boost::function with
- * the function signature S. Returns the handler ID, which can be
+ * connectSignal() connects a GObject signal to any callable.
+ * The type signature of the signal has to be specified explicitly
+ * as template parameters. The type of the callable then is
+ * determined automatically. To make this work, an temporary
+ * helper instance is needed, i.e. there are two function
+ * calls involved.
+ *
+ * Returns the handler ID, which can be
* passed to g_signal_handler_disconnect() to remove the connection.
*
* Example:
@@ -277,12 +201,9 @@ template<class C> class StealGLib : public TrackGLib<C> {
* GObjectCXX object(...);
* // Define signature explicitly because it cannot be guessed from
* // boost::bind() result.
- * object.connectSignal<void (GObject *gobject, GParamSpec *pspec)>("notify",
- * boost::bind(...));
- * // Signature is taken from boost::function parameter.
* guint handlerID =
- * object.connectSignal("notify",
- * boost::function<void (GObject *, GParamSpec *)>(boost::bind(...)));
+ * object.connectSignal<GObject *, GParamSpec *)>()("notify",
+ * boost::bind(...));
* object.disconnectSignal(handlerID);
* SE_END_CXX
*/