glib: use template class for GObject intrusive pointer + "steal" references
The main motivation is that it allows other template classes to define a smart tracking pointer for an arbitrary GObject-derived type. Adding a second flavor of the class where the constructor with a plain pointer takes over ("steals") the reference becomes easy. The caveat is that the template is still unusable without the SE_GOBJECT_TYPE macro for the type, because without the macro there is no suitable intrusive_ptr_add_ref/release. Using a template instead of expanding source code might also be slightly more efficient regarding compile time. It allows setting breakpoints. It would be nice to have a default implementation of that which the C++ compiler picks if and only if the pointer is derived from GObject. Unfortunately that relationship is opaque to the compiler (struct starts with GObject, but that does not enable any special relationship at the semantic level). Perhaps play tricks based the common conventions for naming that first instance ("parent", "parent_instance", "parent_object")? The change is almost transparent for users of SE_GOBJECT_TYPE, except for one thing: the defined CXX name is now a typedef instead of a class. This prevents simple forward declarations, thus folks.h had to include individual-traits.h.
This commit is contained in:
parent
9630aef090
commit
36d7e5f64b
|
@ -29,12 +29,11 @@
|
|||
|
||||
#include "gdbus-cxx-bridge.h"
|
||||
#include "../dbus-callbacks.h"
|
||||
#include "folks.h"
|
||||
|
||||
#include <syncevo/declarations.h>
|
||||
SE_BEGIN_CXX
|
||||
|
||||
class PersonaDetails;
|
||||
class FolksIndividualCXX;
|
||||
void DBus2PersonaDetails(GDBusCXX::ExtractArgs &context, GDBusCXX::reader_type &iter, PersonaDetails &details);
|
||||
void FolksIndividual2DBus(const FolksIndividualCXX &individual, GDBusCXX::builder_type &builder);
|
||||
void Details2Persona(const Result<void ()> &result, const PersonaDetails &details, FolksPersona *persona);
|
||||
|
|
|
@ -74,90 +74,6 @@ GLibSelectResult GLibSelect(GMainLoop *loop, int fd, int direction, Timespec *ti
|
|||
|
||||
#ifdef HAVE_GLIB
|
||||
|
||||
/**
|
||||
* Defines a shared pointer for a GObject-based type, with intrusive
|
||||
* reference counting. Use *outside* of SyncEvolution namespace
|
||||
* (i.e. outside of SE_BEGIN/END_CXX. This is necessary because some
|
||||
* 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
|
||||
* passed to g_signal_handler_disconnect() to remove the connection.
|
||||
*
|
||||
* Example:
|
||||
* SE_GOBJECT_TYPE(GFile)
|
||||
* SE_GOBJECT_TYPE(GObject)
|
||||
* SE_BEGIN_CXX
|
||||
* {
|
||||
* // reference normally increased during construction,
|
||||
* // steal() avoids that
|
||||
* GFileCXX filecxx = GFileCXX::steal(g_file_new_for_path("foo"));
|
||||
* GFile *filec = filecxx.get(); // does not increase reference count
|
||||
* // file freed here as filecxx gets destroyed
|
||||
* }
|
||||
*
|
||||
* 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.disconnectSignal(handlerID);
|
||||
* SE_END_CXX
|
||||
*/
|
||||
#define SE_GOBJECT_TYPE(_x) \
|
||||
void inline intrusive_ptr_add_ref(_x *ptr) { g_object_ref(ptr); } \
|
||||
void inline intrusive_ptr_release(_x *ptr) { g_object_unref(ptr); } \
|
||||
SE_BEGIN_CXX \
|
||||
class _x ## CXX : public boost::intrusive_ptr<_x> { \
|
||||
public: \
|
||||
_x ## CXX(_x *ptr, bool add_ref = true) : boost::intrusive_ptr<_x>(ptr, add_ref) {} \
|
||||
_x ## CXX() {} \
|
||||
_x ## CXX(const _x ## CXX &other) : boost::intrusive_ptr<_x>(other) {} \
|
||||
operator _x * () { return get(); } \
|
||||
_x * ref() const { return static_cast<_x *>(g_object_ref(get())); } \
|
||||
\
|
||||
static _x ## CXX steal(_x *ptr) { return _x ## CXX(ptr, false); } \
|
||||
\
|
||||
template<class S> guint connectSignal(const char *signal, \
|
||||
const boost::function<S> &callback) \
|
||||
{ \
|
||||
return ConnectGObjectSignal(static_cast<gpointer>(get()), signal, callback); \
|
||||
} \
|
||||
void disconnectSignal(guint handlerID) { \
|
||||
g_signal_handler_disconnect(static_cast<gpointer>(get()), \
|
||||
handlerID); \
|
||||
} \
|
||||
}; \
|
||||
SE_END_CXX \
|
||||
|
||||
/**
|
||||
* Defines a CXX smart pointer similar to SE_GOBJECT_TYPE,
|
||||
* but for types which have their own _ref and _unref
|
||||
* calls.
|
||||
*
|
||||
* Example:
|
||||
* SE_GLIB_TYPE(GMainLoop, g_main_loop)
|
||||
*/
|
||||
#define SE_GLIB_TYPE(_type, _func_prefix) \
|
||||
void inline intrusive_ptr_add_ref(_type *ptr) { _func_prefix ## _ref(ptr); } \
|
||||
void inline intrusive_ptr_release(_type *ptr) { _func_prefix ## _unref(ptr); } \
|
||||
SE_BEGIN_CXX \
|
||||
typedef boost::intrusive_ptr<_type> _type ## CXX; \
|
||||
SE_END_CXX
|
||||
|
||||
SE_END_CXX
|
||||
|
||||
SE_GOBJECT_TYPE(GFile)
|
||||
SE_GOBJECT_TYPE(GFileMonitor)
|
||||
SE_GLIB_TYPE(GMainLoop, g_main_loop)
|
||||
|
||||
SE_BEGIN_CXX
|
||||
|
||||
// Signal callback. Specializations will handle varying number of parameters.
|
||||
template<class S> struct GObjectSignalHandler {
|
||||
// static void handler();
|
||||
|
@ -255,30 +171,115 @@ template<class A1, class A2, class A3, class A4, class A5, class A6, class A7, c
|
|||
}
|
||||
};
|
||||
|
||||
// Frees the instance of boost::function which was allocated
|
||||
// by ConnectGObjectSignal.
|
||||
template<class S> void GObjectSignalDestroy(gpointer data, GClosure *closure) throw ()
|
||||
{
|
||||
try {
|
||||
delete reinterpret_cast< boost::function<void ()> *>(data);
|
||||
} catch (...) {
|
||||
Exception::handle(HANDLE_EXCEPTION_FATAL);
|
||||
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, bool add_ref = true) : Base_t(ptr, add_ref) {}
|
||||
TrackGObject() {}
|
||||
TrackGObject(const TrackGObject &other) : Base_t(other) {}
|
||||
operator C * () { return Base_t::get(); }
|
||||
C * ref() const { return static_cast<C *>(g_object_ref(Base_t::get())); }
|
||||
|
||||
static TrackGObject steal(C *ptr) { return TrackGObject(ptr, false); }
|
||||
|
||||
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));
|
||||
}
|
||||
void disconnectSignal(guint handlerID) {
|
||||
g_signal_handler_disconnect(static_cast<gpointer>(Base_t::get()),
|
||||
handlerID);
|
||||
}
|
||||
};
|
||||
|
||||
template<class C> class StealGObject : public TrackGObject<C> {
|
||||
public:
|
||||
StealGObject(C *ptr) : TrackGObject<C>(ptr, false) {}
|
||||
StealGObject() {}
|
||||
StealGObject(const StealGObject &other) : TrackGObject<C>(other) {}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* implements SE_GOBJECT_TYPE() connectSignal()
|
||||
* Defines a shared pointer for a GObject-based type, with intrusive
|
||||
* reference counting. Use *outside* of SyncEvolution namespace
|
||||
* (i.e. outside of SE_BEGIN/END_CXX. This is necessary because some
|
||||
* 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
|
||||
* passed to g_signal_handler_disconnect() to remove the connection.
|
||||
*
|
||||
* Example:
|
||||
* SE_GOBJECT_TYPE(GFile)
|
||||
* SE_GOBJECT_TYPE(GObject)
|
||||
* SE_BEGIN_CXX
|
||||
* {
|
||||
* // reference normally increased during construction,
|
||||
* // steal() avoids that
|
||||
* GFileCXX filecxx = GFileCXX::steal(g_file_new_for_path("foo"));
|
||||
* GFile *filec = filecxx.get(); // does not increase reference count
|
||||
* // file freed here as filecxx gets destroyed
|
||||
* }
|
||||
*
|
||||
* 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.disconnectSignal(handlerID);
|
||||
* SE_END_CXX
|
||||
*/
|
||||
template<class S> guint ConnectGObjectSignal(gpointer instance,
|
||||
const char *signal,
|
||||
const boost::function<S> &callback)
|
||||
{
|
||||
return g_signal_connect_data(instance, signal,
|
||||
G_CALLBACK(&GObjectSignalHandler<S>::handler),
|
||||
new boost::function<S>(callback),
|
||||
&GObjectSignalDestroy<S>,
|
||||
GConnectFlags(0));
|
||||
}
|
||||
#define SE_GOBJECT_TYPE(_x) \
|
||||
void inline intrusive_ptr_add_ref(_x *ptr) { g_object_ref(ptr); } \
|
||||
void inline intrusive_ptr_release(_x *ptr) { g_object_unref(ptr); } \
|
||||
SE_BEGIN_CXX \
|
||||
typedef TrackGObject<_x> _x ## CXX; \
|
||||
typedef StealGObject<_x> _x ## StealCXX; \
|
||||
SE_END_CXX \
|
||||
|
||||
/**
|
||||
* Defines a CXX smart pointer similar to SE_GOBJECT_TYPE,
|
||||
* but for types which have their own _ref and _unref
|
||||
* calls.
|
||||
*
|
||||
* Example:
|
||||
* SE_GLIB_TYPE(GMainLoop, g_main_loop)
|
||||
*/
|
||||
#define SE_GLIB_TYPE(_type, _func_prefix) \
|
||||
void inline intrusive_ptr_add_ref(_type *ptr) { _func_prefix ## _ref(ptr); } \
|
||||
void inline intrusive_ptr_release(_type *ptr) { _func_prefix ## _unref(ptr); } \
|
||||
SE_BEGIN_CXX \
|
||||
typedef boost::intrusive_ptr<_type> _type ## CXX; \
|
||||
SE_END_CXX
|
||||
|
||||
SE_END_CXX
|
||||
|
||||
SE_GOBJECT_TYPE(GFile)
|
||||
SE_GOBJECT_TYPE(GFileMonitor)
|
||||
SE_GLIB_TYPE(GMainLoop, g_main_loop)
|
||||
|
||||
SE_BEGIN_CXX
|
||||
|
||||
/**
|
||||
* Wrapper around g_file_monitor_file().
|
||||
|
|
Loading…
Reference in a new issue