/* * Copyright (C) 2011 Intel Corporation * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) version 3. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA */ #ifndef INCL_GLIB_SUPPORT # define INCL_GLIB_SUPPORT #ifdef HAVE_CONFIG_H # include "config.h" #endif #include #ifdef HAVE_GLIB # include # include #else typedef void *GMainLoop; #endif #include #include #include #include #include #include #include #include #include #include #include #include SE_BEGIN_CXX enum { GLIB_SELECT_NONE = 0, GLIB_SELECT_READ = 1, GLIB_SELECT_WRITE = 2 }; enum GLibSelectResult { GLIB_SELECT_TIMEOUT, /**< returned because not ready after given amount of time */ GLIB_SELECT_READY, /**< fd is ready */ GLIB_SELECT_QUIT /**< something else caused the loop to quit, return to caller immediately */ }; /** * Waits for one particular file descriptor to become ready for reading * and/or writing. Keeps the given loop running while waiting. * * @param loop loop to keep running; must not be NULL * @param fd file descriptor to watch, -1 for none * @param direction read, write, both, or none (then fd is ignored) * @param timeout timeout in seconds + nanoseconds from now, NULL for no timeout, empty value for immediate return * @return see GLibSelectResult */ GLibSelectResult GLibSelect(GMainLoop *loop, int fd, int direction, Timespec *timeout); #ifdef HAVE_GLIB // Signal callback. Specializations will handle varying number of parameters. template struct GObjectSignalHandler { // static void handler(); // No specialization defined for the requested function prototype. }; template<> struct GObjectSignalHandler { static void handler(gpointer data) throw () { try { (*reinterpret_cast< boost::function *>(data))(); } catch (...) { Exception::handle(HANDLE_EXCEPTION_FATAL); } } }; template struct GObjectSignalHandler { static void handler(A1 a1, gpointer data) throw () { try { (*reinterpret_cast< boost::function *>(data))(a1); } catch (...) { Exception::handle(HANDLE_EXCEPTION_FATAL); } } }; template struct GObjectSignalHandler { static void handler(A1 a1, A2 a2, gpointer data) throw () { try { (*reinterpret_cast< boost::function *>(data))(a1, a2); } catch (...) { Exception::handle(HANDLE_EXCEPTION_FATAL); } } }; template struct GObjectSignalHandler { static void handler(A1 a1, A2 a2, A3 a3, gpointer data) throw () { try { (*reinterpret_cast< boost::function *>(data))(a1, a2, a3); } catch (...) { Exception::handle(HANDLE_EXCEPTION_FATAL); } } }; template struct GObjectSignalHandler { static void handler(A1 a1, A2 a2, A3 a3, A4 a4, gpointer data) throw () { try { (*reinterpret_cast< boost::function *>(data))(a1, a2, a3, a4); } catch (...) { Exception::handle(HANDLE_EXCEPTION_FATAL); } } }; template struct GObjectSignalHandler { static void handler(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, gpointer data) throw () { try { (*reinterpret_cast< boost::function *>(data))(a1, a2, a3, a4, a5); } catch (...) { Exception::handle(HANDLE_EXCEPTION_FATAL); } } }; template struct GObjectSignalHandler { static void handler(A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, gpointer data) throw () { try { (*reinterpret_cast< boost::function *>(data))(a1, a2, a3, a4, a5, a6); } catch (...) { Exception::handle(HANDLE_EXCEPTION_FATAL); } } }; template struct GObjectSignalHandler { 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 *>(data))(a1, a2, a3, a4, a5, a6, a7); } catch (...) { Exception::handle(HANDLE_EXCEPTION_FATAL); } } }; template struct GObjectSignalHandler { 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 *>(data))(a1, a2, a3, a4, a5, a6, a7, a8); } catch (...) { Exception::handle(HANDLE_EXCEPTION_FATAL); } } }; template struct GObjectSignalHandler { 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 *>(data))(a1, a2, a3, a4, a5, a6, a7, a8, a9); } catch (...) { Exception::handle(HANDLE_EXCEPTION_FATAL); } } }; enum RefOwnership { TRANSFER_REF = false, /**< * Create new smart pointer which steals an existing reference without * increasing the reference count of the object. */ ADD_REF = true /**< * Create new smart pointer which increases the reference count when * storing the pointer to the object. */ }; template class TrackGObject : public boost::intrusive_ptr { typedef boost::intrusive_ptr Base_t; // Frees the instance of boost::function which was allocated // by connectSignal. template static void signalDestroy(gpointer data, GClosure *closure) throw () { try { delete reinterpret_cast< boost::function *>(data); } catch (...) { Exception::handle(HANDLE_EXCEPTION_FATAL); } } public: TrackGObject(C *ptr, RefOwnership ownership) : Base_t(ptr, (bool)ownership) {} TrackGObject() {} TrackGObject(const TrackGObject &other) : Base_t(other) {} operator C * () const { return Base_t::get(); } operator bool () const { return Base_t::get() != NULL; } C * ref() const { return static_cast(g_object_ref(Base_t::get())); } static TrackGObject steal(C *ptr) { return TrackGObject(ptr, TRANSFER_REF); } template guint connectSignal(const char *signal, const boost::function &callback) { return g_signal_connect_data(Base_t::get(), signal, G_CALLBACK(&GObjectSignalHandler::handler), new boost::function(callback), &signalDestroy, GConnectFlags(0)); } void disconnectSignal(guint handlerID) { g_signal_handler_disconnect(static_cast(Base_t::get()), handlerID); } }; template class StealGObject : public TrackGObject { public: StealGObject(C *ptr) : TrackGObject(ptr, TRANSFER_REF) {} StealGObject() {} StealGObject(const StealGObject &other) : TrackGObject(other) {} }; template class TrackGLib : public boost::intrusive_ptr { typedef boost::intrusive_ptr Base_t; public: TrackGLib(C *ptr, RefOwnership ownership) : Base_t(ptr, (bool)ownership) {} TrackGLib() {} TrackGLib(const TrackGLib &other) : Base_t(other) {} operator C * () const { return Base_t::get(); } operator bool () const { return Base_t::get() != NULL; } C * ref() const { return static_cast(g_object_ref(Base_t::get())); } static TrackGLib steal(C *ptr) { return TrackGLib(ptr, TRANSFER_REF); } }; template class StealGLib : public TrackGLib { public: StealGLib(C *ptr) : TrackGLib(ptr, TRANSFER_REF) {} StealGLib() {} StealGLib(const StealGLib &other) : TrackGLib(other) {} }; /** * 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("notify", * boost::bind(...)); * // Signature is taken from boost::function parameter. * guint handlerID = * object.connectSignal("notify", * boost::function(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 \ 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(_x, _func_prefix) \ void inline intrusive_ptr_add_ref(_x *ptr) { _func_prefix ## _ref(ptr); } \ void inline intrusive_ptr_release(_x *ptr) { _func_prefix ## _unref(ptr); } \ SE_BEGIN_CXX \ typedef TrackGLib<_x> _x ## CXX; \ typedef StealGLib<_x> _x ## StealCXX; \ SE_END_CXX SE_END_CXX SE_GOBJECT_TYPE(GFile) SE_GOBJECT_TYPE(GFileMonitor) SE_GLIB_TYPE(GMainLoop, g_main_loop) SE_GLIB_TYPE(GAsyncQueue, g_async_queue) SE_BEGIN_CXX /** * Wrapper around g_file_monitor_file(). * Not copyable because monitor is tied to specific callback * via memory address. */ class GLibNotify : public boost::noncopyable { public: typedef boost::function callback_t; GLibNotify(const char *file, const callback_t &callback); private: GFileMonitorCXX m_monitor; callback_t m_callback; }; /** * Wraps GError. Where a GError** is expected, simply pass * a GErrorCXX instance. */ struct GErrorCXX { GError *m_gerror; /** empty error, NULL pointer */ GErrorCXX() : m_gerror(NULL) {} /** copies error content */ GErrorCXX(const GErrorCXX &other) : m_gerror(g_error_copy(other.m_gerror)) {} GErrorCXX &operator =(const GErrorCXX &other) { if (m_gerror != other.m_gerror) { if (m_gerror) { g_clear_error(&m_gerror); } if (other.m_gerror) { m_gerror = g_error_copy(other.m_gerror); } } return *this; } GErrorCXX &operator =(const GError* err) { if (err != m_gerror) { if (m_gerror) { g_clear_error(&m_gerror); } if (err) { m_gerror = g_error_copy(err); } } return *this; } /** For convenient access to GError members (message, domain, ...) */ const GError * operator-> () const { return m_gerror; } /** * For passing to C functions. They must not free the GError, * because GErrorCXX retains ownership. */ operator const GError * () const { return m_gerror; } /** error description, with fallback if not set (not expected, so not localized) */ operator const char * () { return m_gerror ? m_gerror->message : "<>"; } /** clear error */ ~GErrorCXX() { g_clear_error(&m_gerror); } /** clear error if any is set */ void clear() { g_clear_error(&m_gerror); } /** transfer ownership of error back to caller */ GError *release() { GError *gerror = m_gerror; m_gerror = NULL; return gerror; } /** checks whether the current error is the one passed as parameters */ bool matches(GQuark domain, gint code) { return g_error_matches(m_gerror, domain, code); } /** * Use this when passing GErrorCXX instance to C functions which need to set it. * Make sure the pointer isn't set yet (new GErrorCXX instance, reset if * an error was encountered before) or the GNOME functions will complain * when overwriting the existing error. */ operator GError ** () { return &m_gerror; } /** true if error set */ operator bool () { return m_gerror != NULL; } /** * always throws an exception, including information from GError if available: * : |failure */ void throwError(const std::string &action); static void throwError(const std::string &action, const GError *err); }; template void NoopDestructor(T *) {} template void GObjectDestructor(T *ptr) { g_object_unref(ptr); } template void GFreeDestructor(T *ptr) { g_free(static_cast(ptr)); } /** * Copies strings from a collection into a newly allocated, NULL * terminated array. Copying the strings is optional. Suggested * usage is: * * C collection; * collection.push_back(...); * boost::scoped_array array(AllocStringArray(collection)); * */ template char **AllocStringArray(const T &strings, const char **(*allocArray)(size_t) = NULL, void (*freeArray)(const char **) = NULL, const char *(*copyString)(const char *) = NULL, const void (*freeString)(char *) = NULL) { size_t arraySize = strings.size() + 1; const char **array = NULL; array = allocArray ? allocArray(arraySize) : new const char *[arraySize]; if (!array) { throw std::bad_alloc(); } try { memset(array, 0, sizeof(*array) * arraySize); size_t i = 0; BOOST_FOREACH(const std::string &str, strings) { array[i] = copyString ? copyString(str.c_str()) : str.c_str(); if (!array[i]) { throw std::bad_alloc(); } i++; } } catch (...) { if (freeString) { for (const char **ptr = array; *ptr; ptr++) { freeString(const_cast(*ptr)); } } if (freeArray) { freeArray(array); } throw; } return const_cast(array); } /** * Wraps a G[S]List of pointers to a specific type. * Can be used with boost::FOREACH and provides forward iterators * (two-way iterators and reverse iterators also possible, but not implemented). * Frees the list and optionally (not turned on by default) also frees * the data contained in it, using the provided destructor class. * Use GObjectDestructor for GObject instances. * * @param T the type of the instances pointed to inside the list * @param L GList or GSList * @param D destructor function freeing a T instance */ template< class T, class L, void (*D)(T*) = NoopDestructor > struct GListCXX : boost::noncopyable { L *m_list; static void listFree(GSList *l) { g_slist_free(l); } static void listFree(GList *l) { g_list_free(l); } static GSList *listPrepend(GSList *list, T *entry) { return g_slist_prepend(list, (gpointer)entry); } static GList *listPrepend(GList *list, T *entry) { return g_list_prepend(list, (gpointer)entry); } static GSList *listAppend(GSList *list, T *entry) { return g_slist_append(list, (gpointer)entry); } static GList *listAppend(GList *list, T *entry) { return g_list_append(list, (gpointer)entry); } public: typedef T * value_type; /** by default initialize an empty list; if parameter is not NULL, owership is transferred to the new instance of GListCXX */ GListCXX(L *list = NULL) : m_list(list) {} /** free list */ ~GListCXX() { clear(); } /** free old content, take owership of new one */ void reset(L *list = NULL) { clear(); m_list = list; } bool empty() { return m_list == NULL; } /** clear error if any is set */ void clear() { #if 1 BOOST_FOREACH(T *entry, *this) { D(entry); } #else for (iterator it = begin(); it != end(); ++it) { D(*it); } #endif listFree(m_list); m_list = NULL; } /** * Use this when passing GListCXX instance to C functions which need to set it. * Make sure the pointer isn't set yet (new GListCXX instance or cleared). */ operator L ** () { return &m_list; } /** * Cast to plain G[S]List, for use in functions which do not modify the list. */ operator L * () { return m_list; } class iterator : public std::iterator { L *m_entry; public: iterator(L *list) : m_entry(list) {} iterator(const iterator &other) : m_entry(other.m_entry) {} /** * boost::foreach needs a reference as return code here, * which forces us to do type casting on the address of the void * pointer, * then dereference the pointer. The reason is that typecasting the * pointer value directly yields an rvalue, which can't be used to initialize * the reference return value. */ T * &operator -> () const { return *getEntryPtr(); } T * &operator * () const { return *getEntryPtr(); } iterator & operator ++ () { m_entry = m_entry->next; return *this; } iterator operator ++ (int) { return iterator(m_entry->next); } bool operator == (const iterator &other) { return m_entry == other.m_entry; } bool operator != (const iterator &other) { return m_entry != other.m_entry; } private: /** * Used above, necessary to hide the fact that we do type * casting tricks. Otherwise the compiler will complain about * *(T **)&m_entry->data with "dereferencing type-punned * pointer will break strict-aliasing rules". * * That warning is about breaking assumptions that the compiler * uses for optimizations. The hope is that those optimzations * aren't done here, and/or are disabled by using a function. */ T** getEntryPtr() const { return (T **)&m_entry->data; } }; iterator begin() { return iterator(m_list); } iterator end() { return iterator(NULL); } class const_iterator : public std::iterator { L *m_entry; T *m_value; public: const_iterator(L *list) : m_entry(list) {} const_iterator(const const_iterator &other) : m_entry(other.m_entry) {} T * &operator -> () const { return *getEntryPtr(); } T * &operator * () const { return *getEntryPtr(); } const_iterator & operator ++ () { m_entry = m_entry->next; return *this; } const_iterator operator ++ (int) { return iterator(m_entry->next); } bool operator == (const const_iterator &other) { return m_entry == other.m_entry; } bool operator != (const const_iterator &other) { return m_entry != other.m_entry; } private: T** getEntryPtr() const { return (T **)&m_entry->data; } }; const_iterator begin() const { return const_iterator(m_list); } const_iterator end() const { return const_iterator(NULL); } void push_back(T *entry) { m_list = listAppend(m_list, entry); } void push_front(T *entry) { m_list = listPrepend(m_list, entry); } }; /** use this for a list which owns the strings it points to */ typedef GListCXX > GStringListFreeCXX; /** use this for a list which does not own the strings it points to */ typedef GListCXX GStringListNoFreeCXX; /** * Wraps a C gchar array and takes care of freeing the memory. */ class PlainGStr : public boost::shared_ptr { public: PlainGStr() {} PlainGStr(gchar *str) : boost::shared_ptr(str, g_free) {} PlainGStr(const PlainGStr &other) : boost::shared_ptr(other) {} operator const gchar *() const { return &**this; } const gchar *c_str() const { return &**this; } }; // empty template, need specialization based on parameter and return types template struct GAsyncReady5 {}; template struct GAsyncReady4 {}; template struct GAsyncReady3 {}; template struct GAsyncReady2 {}; template struct GAsyncReady1 {}; // empty template, need specializations based on arity template struct GAsyncReadyCXX {}; // five parameters of finish function template struct GAsyncReadyCXX : public GAsyncReady5::result_type, F, finish, typename boost::function_traits::arg1_type, typename boost::function_traits::arg2_type, typename boost::function_traits::arg3_type, typename boost::function_traits::arg4_type, typename boost::function_traits::arg5_type> { }; // four parameters template struct GAsyncReadyCXX : public GAsyncReady4::result_type, F, finish, typename boost::function_traits::arg1_type, typename boost::function_traits::arg2_type, typename boost::function_traits::arg3_type, typename boost::function_traits::arg4_type> { }; // three parameters template struct GAsyncReadyCXX : public GAsyncReady3::result_type, F, finish, typename boost::function_traits::arg1_type, typename boost::function_traits::arg2_type, typename boost::function_traits::arg3_type> { }; // two parameters template struct GAsyncReadyCXX : public GAsyncReady2::result_type, F, finish, typename boost::function_traits::arg1_type, typename boost::function_traits::arg2_type> { }; // one parameter template struct GAsyncReadyCXX : public GAsyncReady1::result_type, F, finish, typename boost::function_traits::arg1_type> { }; // For finish functions with return parameters the assumption is that // they have a non-void return value. Otherwise there would be no need // for the return parameters. // // result = GObject, GAsyncResult, A3, A4, A5 template struct GAsyncReady5 { typedef typename boost::remove_pointer::type A3_t; typedef typename boost::remove_pointer::type A4_t; typedef typename boost::remove_pointer::type A5_t; typedef boost::function CXXFunctionCB_t; static void handleGLibResult(GObject *sourceObject, GAsyncResult *result, gpointer userData) throw () { try { A3_t retval1 = boost::value_initialized(); A4_t retval2 = boost::value_initialized(); A5_t retval3 = boost::value_initialized(); T t = finish(reinterpret_cast(sourceObject), result, &retval1, &retval2, &retval3); std::auto_ptr cb(static_cast(userData)); (*cb)(t, retval1, retval2, retval3); } catch (...) { // called from C, must not let exception escape Exception::handle(HANDLE_EXCEPTION_FATAL); } } }; // result = GObject, GAsyncResult, A3, A4, GError template struct GAsyncReady5 { typedef typename boost::remove_pointer::type A3_t; typedef typename boost::remove_pointer::type A4_t; typedef boost::function CXXFunctionCB_t; static void handleGLibResult(GObject *sourceObject, GAsyncResult *result, gpointer userData) throw () { try { GErrorCXX gerror; A3_t retval1 = boost::value_initialized(); A4_t retval2 = boost::value_initialized(); T t = finish(reinterpret_cast(sourceObject), result, &retval1, &retval2, gerror); std::auto_ptr cb(static_cast(userData)); (*cb)(t, retval1, retval2, gerror); } catch (...) { // called from C, must not let exception escape Exception::handle(HANDLE_EXCEPTION_FATAL); } } }; // result = GObject, GAsyncResult, A3, A4 template struct GAsyncReady4 { typedef typename boost::remove_pointer::type A3_t; typedef typename boost::remove_pointer::type A4_t; typedef boost::function CXXFunctionCB_t; static void handleGLibResult(GObject *sourceObject, GAsyncResult *result, gpointer userData) throw () { try { A3_t retval1 = boost::value_initialized(); A4_t retval2 = boost::value_initialized(); T t = finish(reinterpret_cast(sourceObject), result, &retval1, &retval2); std::auto_ptr cb(static_cast(userData)); (*cb)(t, retval1, retval2); } catch (...) { // called from C, must not let exception escape Exception::handle(HANDLE_EXCEPTION_FATAL); } } }; // result = GObject, GAsyncResult, A3, GError template struct GAsyncReady4 { typedef typename boost::remove_pointer::type A3_t; typedef boost::function CXXFunctionCB_t; static void handleGLibResult(GObject *sourceObject, GAsyncResult *result, gpointer userData) throw () { try { GErrorCXX gerror; A3_t retval = boost::value_initialized(); T t = finish(reinterpret_cast(sourceObject), result, &retval, gerror); std::auto_ptr cb(static_cast(userData)); (*cb)(t, retval, gerror); } catch (...) { // called from C, must not let exception escape Exception::handle(HANDLE_EXCEPTION_FATAL); } } }; // res = GObject, GAsyncResult, GError template struct GAsyncReady3{ typedef boost::function CXXFunctionCB_t; static void handleGLibResult(GObject *sourceObject, GAsyncResult *result, gpointer userData) throw () { try { GErrorCXX gerror; T t = finish(reinterpret_cast(sourceObject), result, gerror); std::auto_ptr cb(static_cast(userData)); (*cb)(t, gerror); } catch (...) { // called from C, must not let exception escape Exception::handle(HANDLE_EXCEPTION_FATAL); } } }; // void = GObject, GAsyncResult, GError template struct GAsyncReady3 { typedef boost::function CXXFunctionCB_t; static void handleGLibResult(GObject *sourceObject, GAsyncResult *result, gpointer userData) throw () { try { GErrorCXX gerror; finish(reinterpret_cast(sourceObject), result, gerror); std::auto_ptr cb(static_cast(userData)); (*cb)(gerror); } catch (...) { // called from C, must not let exception escape Exception::handle(HANDLE_EXCEPTION_FATAL); } } }; // result = GObject, GAsyncResult template struct GAsyncReady2 { typedef boost::function CXXFunctionCB_t; static void handleGLibResult(GObject *sourceObject, GAsyncResult *result, gpointer userData) throw () { try { T t = finish(reinterpret_cast(sourceObject), result); std::auto_ptr cb(static_cast(userData)); (*cb)(t); } catch (...) { // called from C, must not let exception escape Exception::handle(HANDLE_EXCEPTION_FATAL); } } }; // result = GAsyncResult, GError template struct GAsyncReady2 { public: typedef boost::function CXXFunctionCB_t; static void handleGLibResult(GObject *sourceObject, GAsyncResult *result, gpointer userData) throw () { try { GErrorCXX gerror; T t = finish(result, gerror); std::auto_ptr cb(static_cast(userData)); (*cb)(t, gerror); } catch (...) { // called from C, must not let exception escape Exception::handle(HANDLE_EXCEPTION_FATAL); } } }; // void = GObject, GAsyncResult template struct GAsyncReady2 { typedef boost::function CXXFunctionCB_t; static void handleGLibResult(GObject *sourceObject, GAsyncResult *result, gpointer userData) throw () { try { finish(reinterpret_cast(sourceObject), result); std::auto_ptr cb(static_cast(userData)); (*cb)(); } catch (...) { // called from C, must not let exception escape Exception::handle(HANDLE_EXCEPTION_FATAL); } } }; // void = GAsyncResult, GError template struct GAsyncReady2 { typedef boost::function CXXFunctionCB_t; static void handleGLibResult(GObject *sourceObject, GAsyncResult *result, gpointer userData) throw () { try { GErrorCXX gerror; finish(result, gerror); std::auto_ptr cb(static_cast(userData)); (*cb)(gerror); } catch (...) { // called from C, must not let exception escape Exception::handle(HANDLE_EXCEPTION_FATAL); } } }; /** * convenience macro for picking the GAsyncReadyCXX that matches the _prepare call: * first switch based on arity of the finish function, then on its type */ #define SYNCEVO_GLIB_CALL_ASYNC_CXX(_prepare) \ GAsyncReadyCXX< boost::remove_pointer::type, \ & _prepare ## _finish, \ boost::function_traits::type>::arity > /** * Macro for asynchronous methods which use a GAsyncReadyCallback to * indicate completion. The assumption is that there is a matching * _finish function for the function which starts the operation. * * The boost::function callback will be called exactly once, with the * following parameters: * - return value of the _finish call, if non-void * - all return parameters of the _finish call, in the order * in which they appear there * - a GError is passed as "const GError *", with NULL if the * _finish function did not set an error; it does not have to * be freed. * * Other parameters must be freed if required by the _finish semantic. * * Use boost::bind() with a boost::weak_ptr as second * parameter when the callback belongs to an instance which is * not guaranteed to be around when the operation completes. * * Example: * * static void asyncCB(const GError *gerror, const char *func, bool &failed, bool &done) { * done = true; * if (gerror) { * failed = true; * // log gerror->message or store in GErrorCXX * } * } * * bool done = false, failed = false; * SYNCEVO_GLIB_CALL_ASYNC(folks_individual_aggregator_prepare, * boost::bind(asyncCB, _1, * "folks_individual_aggregator_prepare", * boost::ref(failed), boost::ref(done)), * aggregator); * * // Don't continue unless finished, because the callback will write * // into "done" and possibly "failed". * while (!done) { * g_main_context_iteration(NULL, true); * } * * @param _prepare name of the function which starts the operation * @param _cb boost::function with GError pointer and optional result value; * exceptions are considered fatal * @param _args parameters of _prepare, without the final GAsyncReadyCallback + user_data pair; * usually at least a GCancellable pointer is part of the arguments */ #define SYNCEVO_GLIB_CALL_ASYNC(_prepare, _cb, _args...) \ _prepare(_args, \ SYNCEVO_GLIB_CALL_ASYNC_CXX(_prepare)::handleGLibResult, \ new SYNCEVO_GLIB_CALL_ASYNC_CXX(_prepare)::CXXFunctionCB_t(_cb)) // helper class for finish method with some kind of result other than void template class GAsyncReadyDoneCXX { public: template static void storeResult(GErrorCXX &gerrorStorage, R &resultStorage, bool &done, T result, const GError *gerror) { done = true; gerrorStorage = gerror; resultStorage = result; } template static boost::function createCB(R &result, GErrorCXX &gerror, bool &done) { return boost::bind(storeResult, boost::ref(gerror), boost::ref(result), boost::ref(done), _1, _2); } }; // helper class for finish method with void result template<> class GAsyncReadyDoneCXX { public: static void storeResult(GErrorCXX &gerrorStorage, bool &done, const GError *gerror) { done = true; gerrorStorage = gerror; } static boost::function createCB(const int *dummy, GErrorCXX &gerror, bool &done) { return boost::bind(storeResult, boost::ref(gerror), boost::ref(done), _1); } }; /** * Like SYNCEVO_GLIB_CALL_ASYNC, but blocks until the operation * has finished. * * @param _res an instance which will hold the result when done, NULL when result is void * @param _gerror a GErrorCXX instance which will hold an error * pointer afterwards in case of a failure */ #define SYNCEVO_GLIB_CALL_SYNC(_res, _gerror, _prepare, _args...) \ do { \ bool done = false; \ SYNCEVO_GLIB_CALL_ASYNC(_prepare, \ GAsyncReadyDoneCXX::result_type>::createCB(_res, _gerror, done), \ _args); \ while (!done) { \ g_main_context_iteration(NULL, true); \ } \ } while (false); \ #endif SE_END_CXX #endif // INCL_GLIB_SUPPORT