2011-02-15 16:34:46 +01:00
|
|
|
/*
|
|
|
|
* 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
|
|
|
|
|
2011-02-16 12:07:20 +01:00
|
|
|
#include <syncevo/util.h>
|
|
|
|
|
2011-02-15 16:34:46 +01:00
|
|
|
#ifdef HAVE_GLIB
|
|
|
|
# include <glib-object.h>
|
2011-03-28 13:47:08 +02:00
|
|
|
# include <gio/gio.h>
|
2011-02-15 16:34:46 +01:00
|
|
|
#else
|
|
|
|
typedef void *GMainLoop;
|
|
|
|
#endif
|
|
|
|
|
2012-05-25 10:21:59 +02:00
|
|
|
#include <boost/shared_ptr.hpp>
|
2011-03-28 13:47:08 +02:00
|
|
|
#include <boost/intrusive_ptr.hpp>
|
|
|
|
#include <boost/utility.hpp>
|
2011-06-19 14:33:01 +02:00
|
|
|
#include <boost/foreach.hpp>
|
2012-08-14 09:31:17 +02:00
|
|
|
#include <boost/function.hpp>
|
2012-09-07 11:48:49 +02:00
|
|
|
#include <boost/bind.hpp>
|
2012-10-03 13:22:16 +02:00
|
|
|
#include <boost/type_traits/remove_pointer.hpp>
|
|
|
|
#include <boost/type_traits/function_traits.hpp>
|
2012-11-29 16:53:09 +01:00
|
|
|
#include <boost/utility/value_init.hpp>
|
2011-06-19 14:33:01 +02:00
|
|
|
|
|
|
|
#include <iterator>
|
2012-11-01 06:15:00 +01:00
|
|
|
#include <memory>
|
2011-03-28 13:47:08 +02:00
|
|
|
|
2011-02-15 16:34:46 +01:00
|
|
|
#include <syncevo/declarations.h>
|
|
|
|
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)
|
2011-02-16 12:07:20 +01:00
|
|
|
* @param timeout timeout in seconds + nanoseconds from now, NULL for no timeout, empty value for immediate return
|
2011-02-15 16:34:46 +01:00
|
|
|
* @return see GLibSelectResult
|
|
|
|
*/
|
2011-02-16 12:07:20 +01:00
|
|
|
GLibSelectResult GLibSelect(GMainLoop *loop, int fd, int direction, Timespec *timeout);
|
2011-02-15 16:34:46 +01:00
|
|
|
|
2011-03-28 13:47:08 +02:00
|
|
|
#ifdef HAVE_GLIB
|
|
|
|
|
2012-09-04 13:55:08 +02:00
|
|
|
// 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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
2012-09-25 11:01:35 +02:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
2012-09-04 13:55:08 +02:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2013-05-13 21:33:34 +02:00
|
|
|
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.
|
|
|
|
*/
|
|
|
|
};
|
|
|
|
|
|
|
|
|
2012-11-01 06:20:27 +01:00
|
|
|
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);
|
|
|
|
}
|
2012-09-04 13:55:08 +02:00
|
|
|
}
|
2012-11-01 06:20:27 +01:00
|
|
|
|
|
|
|
public:
|
2013-05-13 21:33:34 +02:00
|
|
|
TrackGObject(C *ptr, RefOwnership ownership) : Base_t(ptr, (bool)ownership) {}
|
2012-11-01 06:20:27 +01:00
|
|
|
TrackGObject() {}
|
|
|
|
TrackGObject(const TrackGObject &other) : Base_t(other) {}
|
2012-11-29 16:30:01 +01:00
|
|
|
operator C * () const { return Base_t::get(); }
|
2012-12-05 14:19:46 +01:00
|
|
|
operator bool () const { return Base_t::get() != NULL; }
|
2012-11-01 06:20:27 +01:00
|
|
|
C * ref() const { return static_cast<C *>(g_object_ref(Base_t::get())); }
|
|
|
|
|
2013-05-13 21:33:34 +02:00
|
|
|
static TrackGObject steal(C *ptr) { return TrackGObject(ptr, TRANSFER_REF); }
|
2012-11-01 06:20:27 +01:00
|
|
|
|
|
|
|
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:
|
2013-05-13 21:33:34 +02:00
|
|
|
StealGObject(C *ptr) : TrackGObject<C>(ptr, TRANSFER_REF) {}
|
2012-11-01 06:20:27 +01:00
|
|
|
StealGObject() {}
|
|
|
|
StealGObject(const StealGObject &other) : TrackGObject<C>(other) {}
|
|
|
|
};
|
|
|
|
|
2013-02-05 14:46:18 +01:00
|
|
|
template<class C> class TrackGLib : public boost::intrusive_ptr<C> {
|
|
|
|
typedef boost::intrusive_ptr<C> Base_t;
|
|
|
|
|
|
|
|
public:
|
2013-05-13 21:33:34 +02:00
|
|
|
TrackGLib(C *ptr, RefOwnership ownership) : Base_t(ptr, (bool)ownership) {}
|
2013-02-05 14:46:18 +01:00
|
|
|
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<C *>(g_object_ref(Base_t::get())); }
|
|
|
|
|
2013-05-13 21:33:34 +02:00
|
|
|
static TrackGLib steal(C *ptr) { return TrackGLib(ptr, TRANSFER_REF); }
|
2013-02-05 14:46:18 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
template<class C> class StealGLib : public TrackGLib<C> {
|
|
|
|
public:
|
2013-05-13 21:33:34 +02:00
|
|
|
StealGLib(C *ptr) : TrackGLib<C>(ptr, TRANSFER_REF) {}
|
2013-02-05 14:46:18 +01:00
|
|
|
StealGLib() {}
|
|
|
|
StealGLib(const StealGLib &other) : TrackGLib<C>(other) {}
|
|
|
|
};
|
2012-09-04 13:55:08 +02:00
|
|
|
|
|
|
|
/**
|
2012-11-01 06:20:27 +01:00
|
|
|
* 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
|
2012-09-04 13:55:08 +02:00
|
|
|
*/
|
2012-11-01 06:20:27 +01:00
|
|
|
#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)
|
|
|
|
*/
|
2013-02-05 14:46:18 +01:00
|
|
|
#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); } \
|
2012-11-01 06:20:27 +01:00
|
|
|
SE_BEGIN_CXX \
|
2013-02-05 14:46:18 +01:00
|
|
|
typedef TrackGLib<_x> _x ## CXX; \
|
|
|
|
typedef StealGLib<_x> _x ## StealCXX; \
|
2012-11-01 06:20:27 +01:00
|
|
|
SE_END_CXX
|
|
|
|
|
|
|
|
SE_END_CXX
|
|
|
|
|
|
|
|
SE_GOBJECT_TYPE(GFile)
|
|
|
|
SE_GOBJECT_TYPE(GFileMonitor)
|
|
|
|
SE_GLIB_TYPE(GMainLoop, g_main_loop)
|
2013-02-06 10:59:30 +01:00
|
|
|
SE_GLIB_TYPE(GAsyncQueue, g_async_queue)
|
2012-11-01 06:20:27 +01:00
|
|
|
|
|
|
|
SE_BEGIN_CXX
|
2012-09-04 13:55:08 +02:00
|
|
|
|
2011-03-28 13:47:08 +02:00
|
|
|
/**
|
|
|
|
* 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<void (GFile *, GFile *, GFileMonitorEvent)> callback_t;
|
|
|
|
|
|
|
|
GLibNotify(const char *file,
|
|
|
|
const callback_t &callback);
|
|
|
|
private:
|
|
|
|
GFileMonitorCXX m_monitor;
|
|
|
|
callback_t m_callback;
|
|
|
|
};
|
|
|
|
|
2011-06-19 14:33:01 +02:00
|
|
|
/**
|
|
|
|
* 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) {
|
2012-05-25 10:28:47 +02:00
|
|
|
if (m_gerror != other.m_gerror) {
|
2011-06-19 14:33:01 +02:00
|
|
|
if (m_gerror) {
|
|
|
|
g_clear_error(&m_gerror);
|
|
|
|
}
|
2012-05-25 10:28:47 +02:00
|
|
|
if (other.m_gerror) {
|
|
|
|
m_gerror = g_error_copy(other.m_gerror);
|
|
|
|
}
|
2011-06-19 14:33:01 +02:00
|
|
|
}
|
|
|
|
return *this;
|
|
|
|
}
|
2012-05-25 10:28:47 +02:00
|
|
|
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; }
|
2011-06-19 14:33:01 +02:00
|
|
|
|
|
|
|
/** error description, with fallback if not set (not expected, so not localized) */
|
|
|
|
operator const char * () { return m_gerror ? m_gerror->message : "<<no error>>"; }
|
|
|
|
|
|
|
|
/** clear error */
|
|
|
|
~GErrorCXX() { g_clear_error(&m_gerror); }
|
|
|
|
|
|
|
|
/** clear error if any is set */
|
|
|
|
void clear() { g_clear_error(&m_gerror); }
|
|
|
|
|
2012-05-25 10:28:47 +02:00
|
|
|
/** transfer ownership of error back to caller */
|
|
|
|
GError *release() { GError *gerror = m_gerror; m_gerror = NULL; return gerror; }
|
|
|
|
|
2011-11-04 16:25:13 +01:00
|
|
|
/** 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); }
|
|
|
|
|
2011-06-19 14:33:01 +02:00
|
|
|
/**
|
|
|
|
* 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; }
|
|
|
|
|
2012-04-13 13:54:09 +02:00
|
|
|
/** true if error set */
|
|
|
|
operator bool () { return m_gerror != NULL; }
|
|
|
|
|
2011-06-19 14:33:01 +02:00
|
|
|
/**
|
|
|
|
* always throws an exception, including information from GError if available:
|
|
|
|
* <action>: <error message>|failure
|
|
|
|
*/
|
|
|
|
void throwError(const std::string &action);
|
2012-10-23 11:47:24 +02:00
|
|
|
static void throwError(const std::string &action, const GError *err);
|
2011-06-19 14:33:01 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
template<class T> void NoopDestructor(T *) {}
|
2012-07-24 10:22:42 +02:00
|
|
|
template<class T> void GObjectDestructor(T *ptr) { g_object_unref(ptr); }
|
2011-11-30 16:14:15 +01:00
|
|
|
template<class T> void GFreeDestructor(T *ptr) { g_free(static_cast<void *>(ptr)); }
|
2011-06-19 14:33:01 +02:00
|
|
|
|
2011-12-21 14:12:43 +01:00
|
|
|
/**
|
|
|
|
* 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<char *> array(AllocStringArray(collection));
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
template<typename T> 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<char *>(*ptr));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (freeArray) {
|
|
|
|
freeArray(array);
|
|
|
|
}
|
|
|
|
throw;
|
|
|
|
}
|
|
|
|
return const_cast<char **>(array);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2011-06-19 14:33:01 +02:00
|
|
|
/**
|
|
|
|
* 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<T> > 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); }
|
|
|
|
|
2012-05-25 10:31:47 +02:00
|
|
|
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); }
|
2011-06-19 14:33:01 +02:00
|
|
|
|
2012-05-25 10:31:47 +02:00
|
|
|
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); }
|
2011-06-19 14:33:01 +02:00
|
|
|
|
|
|
|
public:
|
|
|
|
typedef T * value_type;
|
|
|
|
|
2011-11-30 16:14:15 +01:00
|
|
|
/** 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) {}
|
2011-06-19 14:33:01 +02:00
|
|
|
|
|
|
|
/** free list */
|
|
|
|
~GListCXX() { clear(); }
|
|
|
|
|
2012-11-29 16:31:39 +01:00
|
|
|
/** free old content, take owership of new one */
|
|
|
|
void reset(L *list = NULL) {
|
|
|
|
clear();
|
|
|
|
m_list = list;
|
|
|
|
}
|
|
|
|
|
2011-11-04 16:25:13 +01:00
|
|
|
bool empty() { return m_list == NULL; }
|
|
|
|
|
2011-06-19 14:33:01 +02:00
|
|
|
/** 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; }
|
|
|
|
|
2011-06-18 01:29:27 +02:00
|
|
|
class iterator : public std::iterator<std::forward_iterator_tag, T *> {
|
2011-06-19 14:33:01 +02:00
|
|
|
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.
|
|
|
|
*/
|
2011-09-01 12:28:04 +02:00
|
|
|
T * &operator -> () const { return *getEntryPtr(); }
|
|
|
|
T * &operator * () const { return *getEntryPtr(); }
|
2011-06-19 14:33:01 +02:00
|
|
|
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; }
|
2011-09-01 12:28:04 +02:00
|
|
|
|
|
|
|
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; }
|
2011-06-19 14:33:01 +02:00
|
|
|
};
|
|
|
|
iterator begin() { return iterator(m_list); }
|
|
|
|
iterator end() { return iterator(NULL); }
|
|
|
|
|
2011-06-18 01:29:27 +02:00
|
|
|
class const_iterator : public std::iterator<std::forward_iterator_tag, T *> {
|
2011-06-19 14:33:01 +02:00
|
|
|
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) {}
|
2011-09-01 12:28:04 +02:00
|
|
|
T * &operator -> () const { return *getEntryPtr(); }
|
|
|
|
T * &operator * () const { return *getEntryPtr(); }
|
2011-06-19 14:33:01 +02:00
|
|
|
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; }
|
2011-09-01 12:28:04 +02:00
|
|
|
|
|
|
|
private:
|
|
|
|
T** getEntryPtr() const { return (T **)&m_entry->data; }
|
2011-06-19 14:33:01 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
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); }
|
|
|
|
};
|
|
|
|
|
2011-11-30 16:14:15 +01:00
|
|
|
/** use this for a list which owns the strings it points to */
|
|
|
|
typedef GListCXX<char, GList, GFreeDestructor<char> > GStringListFreeCXX;
|
|
|
|
/** use this for a list which does not own the strings it points to */
|
|
|
|
typedef GListCXX<char, GList> GStringListNoFreeCXX;
|
2011-06-19 14:33:01 +02:00
|
|
|
|
2012-05-25 10:30:38 +02:00
|
|
|
/**
|
|
|
|
* Wraps a C gchar array and takes care of freeing the memory.
|
|
|
|
*/
|
|
|
|
class PlainGStr : public boost::shared_ptr<gchar>
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
PlainGStr() {}
|
|
|
|
PlainGStr(gchar *str) : boost::shared_ptr<char>(str, g_free) {}
|
|
|
|
PlainGStr(const PlainGStr &other) : boost::shared_ptr<gchar>(other) {}
|
|
|
|
operator const gchar *() const { return &**this; }
|
|
|
|
const gchar *c_str() const { return &**this; }
|
|
|
|
};
|
|
|
|
|
2012-11-29 16:53:09 +01:00
|
|
|
// empty template, need specialization based on parameter and return types
|
|
|
|
template <class T, class F, F *finish, class A1, class A2, class A3, class A4, class A5> struct GAsyncReady5 {};
|
|
|
|
template <class T, class F, F *finish, class A1, class A2, class A3, class A4> struct GAsyncReady4 {};
|
|
|
|
template <class T, class F, F *finish, class A1, class A2, class A3> struct GAsyncReady3 {};
|
|
|
|
template <class T, class F, F *finish, class A1, class A2> struct GAsyncReady2 {};
|
|
|
|
template <class T, class F, F *finish, class A1> struct GAsyncReady1 {};
|
|
|
|
|
|
|
|
// empty template, need specializations based on arity
|
|
|
|
template<class F, F *finish, int arity> struct GAsyncReadyCXX {};
|
|
|
|
|
|
|
|
// five parameters of finish function
|
|
|
|
template<class F, F *finish> struct GAsyncReadyCXX<F, finish, 5> :
|
|
|
|
public GAsyncReady5<typename boost::function_traits<F>::result_type,
|
|
|
|
F, finish,
|
|
|
|
typename boost::function_traits<F>::arg1_type,
|
|
|
|
typename boost::function_traits<F>::arg2_type,
|
|
|
|
typename boost::function_traits<F>::arg3_type,
|
|
|
|
typename boost::function_traits<F>::arg4_type,
|
|
|
|
typename boost::function_traits<F>::arg5_type>
|
|
|
|
{
|
|
|
|
};
|
|
|
|
|
|
|
|
// four parameters
|
|
|
|
template<class F, F *finish> struct GAsyncReadyCXX<F, finish, 4> :
|
|
|
|
public GAsyncReady4<typename boost::function_traits<F>::result_type,
|
|
|
|
F, finish,
|
|
|
|
typename boost::function_traits<F>::arg1_type,
|
|
|
|
typename boost::function_traits<F>::arg2_type,
|
|
|
|
typename boost::function_traits<F>::arg3_type,
|
|
|
|
typename boost::function_traits<F>::arg4_type>
|
|
|
|
{
|
|
|
|
};
|
|
|
|
|
|
|
|
// three parameters
|
|
|
|
template<class F, F *finish> struct GAsyncReadyCXX<F, finish, 3> :
|
|
|
|
public GAsyncReady3<typename boost::function_traits<F>::result_type,
|
|
|
|
F, finish,
|
|
|
|
typename boost::function_traits<F>::arg1_type,
|
|
|
|
typename boost::function_traits<F>::arg2_type,
|
|
|
|
typename boost::function_traits<F>::arg3_type>
|
|
|
|
{
|
|
|
|
};
|
|
|
|
|
|
|
|
// two parameters
|
|
|
|
template<class F, F *finish> struct GAsyncReadyCXX<F, finish, 2> :
|
|
|
|
public GAsyncReady2<typename boost::function_traits<F>::result_type,
|
|
|
|
F, finish,
|
|
|
|
typename boost::function_traits<F>::arg1_type,
|
|
|
|
typename boost::function_traits<F>::arg2_type>
|
|
|
|
{
|
|
|
|
};
|
|
|
|
|
|
|
|
// one parameter
|
|
|
|
template<class F, F *finish> struct GAsyncReadyCXX<F, finish, 1> :
|
|
|
|
public GAsyncReady1<typename boost::function_traits<F>::result_type,
|
|
|
|
F, finish,
|
|
|
|
typename boost::function_traits<F>::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<class T, class F, F *finish, class A1, class A3, class A4, class A5> struct GAsyncReady5<T, F, finish, A1, GAsyncResult *, A3, A4, A5>
|
|
|
|
{
|
|
|
|
typedef typename boost::remove_pointer<A3>::type A3_t;
|
|
|
|
typedef typename boost::remove_pointer<A4>::type A4_t;
|
|
|
|
typedef typename boost::remove_pointer<A5>::type A5_t;
|
|
|
|
typedef boost::function<void (T, A3_t, A4_t, A5_t)> CXXFunctionCB_t;
|
|
|
|
|
|
|
|
static void handleGLibResult(GObject *sourceObject,
|
|
|
|
GAsyncResult *result,
|
|
|
|
gpointer userData) throw () {
|
|
|
|
try {
|
|
|
|
A3_t retval1 = boost::value_initialized<A3_t>();
|
|
|
|
A4_t retval2 = boost::value_initialized<A4_t>();
|
|
|
|
A5_t retval3 = boost::value_initialized<A5_t>();
|
|
|
|
T t = finish(reinterpret_cast<A1>(sourceObject),
|
|
|
|
result, &retval1, &retval2, &retval3);
|
|
|
|
std::auto_ptr<CXXFunctionCB_t> cb(static_cast<CXXFunctionCB_t *>(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<class T, class F, F *finish, class A1, class A3, class A4> struct GAsyncReady5<T, F, finish, A1, GAsyncResult *, A3, A4, GError **>
|
|
|
|
{
|
|
|
|
typedef typename boost::remove_pointer<A3>::type A3_t;
|
|
|
|
typedef typename boost::remove_pointer<A4>::type A4_t;
|
|
|
|
typedef boost::function<void (T, A3_t, A4_t, const GError *)> CXXFunctionCB_t;
|
2012-08-14 09:31:17 +02:00
|
|
|
|
|
|
|
static void handleGLibResult(GObject *sourceObject,
|
|
|
|
GAsyncResult *result,
|
|
|
|
gpointer userData) throw () {
|
|
|
|
try {
|
|
|
|
GErrorCXX gerror;
|
2012-11-29 16:53:09 +01:00
|
|
|
A3_t retval1 = boost::value_initialized<A3_t>();
|
|
|
|
A4_t retval2 = boost::value_initialized<A4_t>();
|
2012-10-03 13:22:16 +02:00
|
|
|
T t = finish(reinterpret_cast<A1>(sourceObject),
|
2012-11-29 16:53:09 +01:00
|
|
|
result, &retval1, &retval2, gerror);
|
2012-11-01 06:15:00 +01:00
|
|
|
std::auto_ptr<CXXFunctionCB_t> cb(static_cast<CXXFunctionCB_t *>(userData));
|
2012-11-29 16:53:09 +01:00
|
|
|
(*cb)(t, retval1, retval2, gerror);
|
2012-08-14 09:31:17 +02:00
|
|
|
} catch (...) {
|
|
|
|
// called from C, must not let exception escape
|
|
|
|
Exception::handle(HANDLE_EXCEPTION_FATAL);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2012-11-29 16:53:09 +01:00
|
|
|
// result = GObject, GAsyncResult, A3, A4
|
|
|
|
template<class T, class F, F *finish, class A1, class A3, class A4> struct GAsyncReady4<T, F, finish, A1, GAsyncResult *, A3, A4>
|
|
|
|
{
|
|
|
|
typedef typename boost::remove_pointer<A3>::type A3_t;
|
|
|
|
typedef typename boost::remove_pointer<A4>::type A4_t;
|
|
|
|
typedef boost::function<void (T, A3_t, A4_t)> CXXFunctionCB_t;
|
2012-10-03 13:22:16 +02:00
|
|
|
|
|
|
|
static void handleGLibResult(GObject *sourceObject,
|
|
|
|
GAsyncResult *result,
|
|
|
|
gpointer userData) throw () {
|
|
|
|
try {
|
2012-11-29 16:53:09 +01:00
|
|
|
A3_t retval1 = boost::value_initialized<A3_t>();
|
|
|
|
A4_t retval2 = boost::value_initialized<A4_t>();
|
2012-10-03 13:22:16 +02:00
|
|
|
T t = finish(reinterpret_cast<A1>(sourceObject),
|
2012-11-29 16:53:09 +01:00
|
|
|
result, &retval1, &retval2);
|
2012-11-01 06:15:00 +01:00
|
|
|
std::auto_ptr<CXXFunctionCB_t> cb(static_cast<CXXFunctionCB_t *>(userData));
|
2012-11-29 16:53:09 +01:00
|
|
|
(*cb)(t, retval1, retval2);
|
2012-10-03 13:22:16 +02:00
|
|
|
} catch (...) {
|
|
|
|
// called from C, must not let exception escape
|
|
|
|
Exception::handle(HANDLE_EXCEPTION_FATAL);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2012-11-29 16:53:09 +01:00
|
|
|
// result = GObject, GAsyncResult, A3, GError
|
|
|
|
template<class T, class F, F *finish, class A1, class A3> struct GAsyncReady4<T, F, finish, A1, GAsyncResult *, A3, GError **>
|
|
|
|
{
|
|
|
|
typedef typename boost::remove_pointer<A3>::type A3_t;
|
|
|
|
typedef boost::function<void (T, A3_t, const GError *)> CXXFunctionCB_t;
|
2012-09-07 11:48:49 +02:00
|
|
|
|
|
|
|
static void handleGLibResult(GObject *sourceObject,
|
|
|
|
GAsyncResult *result,
|
|
|
|
gpointer userData) throw () {
|
|
|
|
try {
|
|
|
|
GErrorCXX gerror;
|
2012-11-29 16:53:09 +01:00
|
|
|
A3_t retval = boost::value_initialized<A3_t>();
|
|
|
|
T t = finish(reinterpret_cast<A1>(sourceObject),
|
|
|
|
result, &retval, gerror);
|
2012-11-01 06:15:00 +01:00
|
|
|
std::auto_ptr<CXXFunctionCB_t> cb(static_cast<CXXFunctionCB_t *>(userData));
|
2012-11-29 16:53:09 +01:00
|
|
|
(*cb)(t, retval, gerror);
|
2012-09-07 11:48:49 +02:00
|
|
|
} catch (...) {
|
|
|
|
// called from C, must not let exception escape
|
|
|
|
Exception::handle(HANDLE_EXCEPTION_FATAL);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2012-11-29 16:53:09 +01:00
|
|
|
// res = GObject, GAsyncResult, GError
|
|
|
|
template <class T, class F, F *finish, class A1> struct GAsyncReady3<T, F, finish, A1, GAsyncResult *, GError **>{
|
|
|
|
typedef boost::function<void (T, const GError *)> CXXFunctionCB_t;
|
|
|
|
|
|
|
|
static void handleGLibResult(GObject *sourceObject,
|
|
|
|
GAsyncResult *result,
|
|
|
|
gpointer userData) throw () {
|
|
|
|
try {
|
|
|
|
GErrorCXX gerror;
|
|
|
|
T t = finish(reinterpret_cast<A1>(sourceObject),
|
|
|
|
result, gerror);
|
|
|
|
std::auto_ptr<CXXFunctionCB_t> cb(static_cast<CXXFunctionCB_t *>(userData));
|
|
|
|
(*cb)(t, gerror);
|
|
|
|
} catch (...) {
|
|
|
|
// called from C, must not let exception escape
|
|
|
|
Exception::handle(HANDLE_EXCEPTION_FATAL);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
// void = GObject, GAsyncResult, GError
|
|
|
|
template<class F, F *finish, class A1> struct GAsyncReady3<void, F, finish, A1, GAsyncResult *, GError **>
|
|
|
|
{
|
2012-08-14 09:31:17 +02:00
|
|
|
typedef boost::function<void (const GError *)> CXXFunctionCB_t;
|
|
|
|
|
|
|
|
static void handleGLibResult(GObject *sourceObject,
|
|
|
|
GAsyncResult *result,
|
|
|
|
gpointer userData) throw () {
|
|
|
|
try {
|
|
|
|
GErrorCXX gerror;
|
2012-10-03 13:22:16 +02:00
|
|
|
finish(reinterpret_cast<A1>(sourceObject),
|
2012-08-14 09:31:17 +02:00
|
|
|
result, gerror);
|
2012-11-01 06:15:00 +01:00
|
|
|
std::auto_ptr<CXXFunctionCB_t> cb(static_cast<CXXFunctionCB_t *>(userData));
|
|
|
|
(*cb)(gerror);
|
2012-08-14 09:31:17 +02:00
|
|
|
} catch (...) {
|
|
|
|
// called from C, must not let exception escape
|
|
|
|
Exception::handle(HANDLE_EXCEPTION_FATAL);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2012-11-29 16:53:09 +01:00
|
|
|
// result = GObject, GAsyncResult
|
|
|
|
template<class T, class F, F *finish, class A1> struct GAsyncReady2<T, F, finish, A1, GAsyncResult *>
|
|
|
|
{
|
|
|
|
typedef boost::function<void (T)> CXXFunctionCB_t;
|
|
|
|
|
|
|
|
static void handleGLibResult(GObject *sourceObject,
|
|
|
|
GAsyncResult *result,
|
|
|
|
gpointer userData) throw () {
|
|
|
|
try {
|
|
|
|
T t = finish(reinterpret_cast<A1>(sourceObject),
|
|
|
|
result);
|
|
|
|
std::auto_ptr<CXXFunctionCB_t> cb(static_cast<CXXFunctionCB_t *>(userData));
|
|
|
|
(*cb)(t);
|
|
|
|
} catch (...) {
|
|
|
|
// called from C, must not let exception escape
|
|
|
|
Exception::handle(HANDLE_EXCEPTION_FATAL);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
// result = GAsyncResult, GError
|
|
|
|
template<class T, class F, F *finish> struct GAsyncReady2<T, F, finish, GAsyncResult *, GError **> {
|
2012-10-03 13:22:16 +02:00
|
|
|
public:
|
2012-11-29 16:53:09 +01:00
|
|
|
typedef boost::function<void (T, const GError *)> CXXFunctionCB_t;
|
|
|
|
|
|
|
|
static void handleGLibResult(GObject *sourceObject,
|
|
|
|
GAsyncResult *result,
|
|
|
|
gpointer userData) throw () {
|
|
|
|
try {
|
|
|
|
GErrorCXX gerror;
|
|
|
|
T t = finish(result, gerror);
|
|
|
|
std::auto_ptr<CXXFunctionCB_t> cb(static_cast<CXXFunctionCB_t *>(userData));
|
|
|
|
(*cb)(t, gerror);
|
|
|
|
} catch (...) {
|
|
|
|
// called from C, must not let exception escape
|
|
|
|
Exception::handle(HANDLE_EXCEPTION_FATAL);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
// void = GObject, GAsyncResult
|
|
|
|
template<class F, F *finish, class A1> struct GAsyncReady2<void, F, finish, A1, GAsyncResult *>
|
|
|
|
{
|
2012-10-03 13:22:16 +02:00
|
|
|
typedef boost::function<void ()> CXXFunctionCB_t;
|
|
|
|
|
|
|
|
static void handleGLibResult(GObject *sourceObject,
|
|
|
|
GAsyncResult *result,
|
|
|
|
gpointer userData) throw () {
|
|
|
|
try {
|
|
|
|
finish(reinterpret_cast<A1>(sourceObject),
|
|
|
|
result);
|
2012-11-01 06:15:00 +01:00
|
|
|
std::auto_ptr<CXXFunctionCB_t> cb(static_cast<CXXFunctionCB_t *>(userData));
|
|
|
|
(*cb)();
|
2012-10-03 13:22:16 +02:00
|
|
|
} catch (...) {
|
|
|
|
// called from C, must not let exception escape
|
|
|
|
Exception::handle(HANDLE_EXCEPTION_FATAL);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2012-11-29 16:53:09 +01:00
|
|
|
// void = GAsyncResult, GError
|
|
|
|
template<class F, F *finish> struct GAsyncReady2<void, F, finish, GAsyncResult *, GError **>
|
|
|
|
{
|
2012-09-07 11:48:49 +02:00
|
|
|
typedef boost::function<void (const GError *)> CXXFunctionCB_t;
|
|
|
|
|
|
|
|
static void handleGLibResult(GObject *sourceObject,
|
|
|
|
GAsyncResult *result,
|
|
|
|
gpointer userData) throw () {
|
|
|
|
try {
|
|
|
|
GErrorCXX gerror;
|
|
|
|
finish(result, gerror);
|
2012-11-01 06:15:00 +01:00
|
|
|
std::auto_ptr<CXXFunctionCB_t> cb(static_cast<CXXFunctionCB_t *>(userData));
|
|
|
|
(*cb)(gerror);
|
2012-09-07 11:48:49 +02:00
|
|
|
} catch (...) {
|
|
|
|
// called from C, must not let exception escape
|
|
|
|
Exception::handle(HANDLE_EXCEPTION_FATAL);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2012-11-29 16:53:09 +01:00
|
|
|
/**
|
|
|
|
* convenience macro for picking the GAsyncReadyCXX that matches the _prepare call:
|
|
|
|
* first switch based on arity of the finish function, then on its type
|
|
|
|
*/
|
2012-08-14 09:31:17 +02:00
|
|
|
#define SYNCEVO_GLIB_CALL_ASYNC_CXX(_prepare) \
|
2012-11-29 16:53:09 +01:00
|
|
|
GAsyncReadyCXX< boost::remove_pointer<typeof(_prepare ## _finish)>::type, \
|
2012-10-18 11:22:44 +02:00
|
|
|
& _prepare ## _finish, \
|
2012-10-03 13:22:16 +02:00
|
|
|
boost::function_traits<boost::remove_pointer<typeof(_prepare ## _finish)>::type>::arity >
|
2012-08-14 09:31:17 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* 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.
|
|
|
|
*
|
2012-11-29 16:53:09 +01:00
|
|
|
* 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.
|
2012-08-14 09:31:17 +02:00
|
|
|
*
|
|
|
|
* 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.
|
|
|
|
*
|
2012-09-07 11:48:49 +02:00
|
|
|
* 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);
|
|
|
|
* }
|
|
|
|
*
|
2012-08-14 09:31:17 +02:00
|
|
|
* @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
|
2012-09-07 11:48:49 +02:00
|
|
|
* @param _args parameters of _prepare, without the final GAsyncReadyCallback + user_data pair;
|
|
|
|
* usually at least a GCancellable pointer is part of the arguments
|
2012-08-14 09:31:17 +02:00
|
|
|
*/
|
|
|
|
#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))
|
|
|
|
|
2012-09-07 11:48:49 +02:00
|
|
|
// helper class for finish method with some kind of result other than void
|
|
|
|
template<class T> class GAsyncReadyDoneCXX
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
template<class R> static void storeResult(GErrorCXX &gerrorStorage,
|
|
|
|
R &resultStorage,
|
|
|
|
bool &done,
|
2012-11-29 16:53:09 +01:00
|
|
|
T result,
|
|
|
|
const GError *gerror) {
|
2012-09-07 11:48:49 +02:00
|
|
|
done = true;
|
|
|
|
gerrorStorage = gerror;
|
|
|
|
resultStorage = result;
|
|
|
|
}
|
|
|
|
|
2012-11-29 16:53:09 +01:00
|
|
|
template<class R> static boost::function<void (T, const GError *)> createCB(R &result, GErrorCXX &gerror, bool &done) {
|
2012-09-07 11:48:49 +02:00
|
|
|
return boost::bind(storeResult<R>, boost::ref(gerror), boost::ref(result), boost::ref(done), _1, _2);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
// helper class for finish method with void result
|
|
|
|
template<> class GAsyncReadyDoneCXX<void>
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
static void storeResult(GErrorCXX &gerrorStorage,
|
|
|
|
bool &done,
|
|
|
|
const GError *gerror) {
|
|
|
|
done = true;
|
|
|
|
gerrorStorage = gerror;
|
|
|
|
}
|
|
|
|
|
|
|
|
static boost::function<void (const GError *)> 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<boost::function<typeof(_prepare ## _finish)>::result_type>::createCB(_res, _gerror, done), \
|
|
|
|
_args); \
|
|
|
|
while (!done) { \
|
|
|
|
g_main_context_iteration(NULL, true); \
|
|
|
|
} \
|
|
|
|
} while (false); \
|
|
|
|
|
2011-03-28 13:47:08 +02:00
|
|
|
#endif
|
|
|
|
|
2011-02-15 16:34:46 +01:00
|
|
|
SE_END_CXX
|
|
|
|
|
|
|
|
#endif // INCL_GLIB_SUPPORT
|
|
|
|
|