diff options
-rw-r--r-- | src/QGlib/value.cpp | 208 | ||||
-rw-r--r-- | src/QGlib/value.h | 248 |
2 files changed, 273 insertions, 183 deletions
diff --git a/src/QGlib/value.cpp b/src/QGlib/value.cpp index 2b9616e..e353ca0 100644 --- a/src/QGlib/value.cpp +++ b/src/QGlib/value.cpp @@ -1,5 +1,7 @@ /* Copyright (C) 2009-2010 George Kiagiadakis <kiagiadakis.george@gmail.com> + Copyright (C) 2010 Collabora Ltd. + @author George Kiagiadakis <george.kiagiadakis@collabora.co.uk> 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 @@ -18,8 +20,91 @@ #include "string.h" #include <glib-object.h> #include <QtCore/QDebug> +#include <QtCore/QReadWriteLock> namespace QGlib { +namespace Private { + +class Dispatcher +{ +public: + Dispatcher(); + + ValueVTable getVTable(Type t) const; + void setVTable(Type t, const ValueVTable & vtable); + +private: + mutable QReadWriteLock lock; + QHash<Type, ValueVTable> dispatchTable; +}; + +Dispatcher::Dispatcher() +{ +#define DECLARE_VTABLE(T, NICK, GTYPE) \ + struct ValueVTable_##NICK \ + { \ + static void get(const ValueBase & value, void *data) \ + { \ + *reinterpret_cast<T*>(data) = g_value_get_##NICK(value); \ + }; \ + \ + static void set(ValueBase & value, const void *data) \ + { \ + g_value_set_##NICK(value, *reinterpret_cast<T const *>(data)); \ + }; \ + }; \ + setVTable(GTYPE, ValueVTable(ValueVTable_##NICK::set, ValueVTable_##NICK::get)); + + DECLARE_VTABLE(char, char, Type::Char) + DECLARE_VTABLE(unsigned char, uchar, Type::Uchar) + DECLARE_VTABLE(bool, boolean, Type::Boolean) + DECLARE_VTABLE(int, int, Type::Int) + DECLARE_VTABLE(unsigned int, uint, Type::Uint) + DECLARE_VTABLE(long, long, Type::Long) + DECLARE_VTABLE(unsigned long, ulong, Type::Ulong) + DECLARE_VTABLE(qint64, int64, Type::Int64) + DECLARE_VTABLE(quint64, uint64, Type::Uint64) + DECLARE_VTABLE(int, enum, Type::Enum); + DECLARE_VTABLE(uint, flags, Type::Flags) + DECLARE_VTABLE(float, float, Type::Float) + DECLARE_VTABLE(double, double, Type::Double) + DECLARE_VTABLE(QByteArray, string, Type::String) + DECLARE_VTABLE(void*, pointer, Type::Pointer) + DECLARE_VTABLE(void*, boxed, Type::Boxed) + DECLARE_VTABLE(GParamSpec*, param, Type::Param) + DECLARE_VTABLE(void*, object, Type::Object) + DECLARE_VTABLE(QGlib::Type, gtype, GetType<QGlib::Type>()) + +#undef DECLARE_VTABLE +} + +ValueVTable Dispatcher::getVTable(Type t) const +{ + QReadLocker l(&lock); + + if (dispatchTable.contains(t)) { + return dispatchTable[t]; + } + + while (t.isDerived()) { + t = t.parent(); + if (dispatchTable.contains(t)) { + return dispatchTable[t]; + } + } + + return ValueVTable(); +} + +void Dispatcher::setVTable(Type t, const ValueVTable & vtable) +{ + QWriteLocker l(&lock); + dispatchTable[t] = vtable; +} + +} //namespace Private + +Q_GLOBAL_STATIC(Private::Dispatcher, s_dispatcher); //BEGIN ValueBase @@ -64,6 +149,70 @@ Value ValueBase::transformTo(Type t) const return dest; } +//static +void ValueBase::registerValueVTable(Type type, const ValueVTable & vtable) +{ + s_dispatcher()->setVTable(type, vtable); +} + +void ValueBase::getData(Type dataType, void *data) const +{ + if (!isValid()) { + throw InvalidValueException(); + } else if (g_value_type_compatible(type(), dataType)) { + ValueVTable vtable = s_dispatcher()->getVTable(dataType); + if (vtable.get != NULL) { + vtable.get(*this, data); + } else { + throw std::logic_error("Unable to handle the given type. Type is not registered"); + } + } else if (dataType.isValueType() && g_value_type_transformable(type(), dataType)) { + Value v; + v.init(dataType); + + if (!g_value_transform(m_value, v.m_value)) { + throw InvalidTypeException(); + } + + try { + v.getData(dataType, data); + } catch (const InvalidTypeException &) { + Q_ASSERT(false); //This must never happen + } + } else { + throw InvalidTypeException(); + } +} + +void ValueBase::setData(Type dataType, const void *data) +{ + if (!isValid()) { + throw InvalidValueException(); + } else if (g_value_type_compatible(dataType, type())) { + ValueVTable vtable = s_dispatcher()->getVTable(dataType); + if (vtable.set != NULL) { + vtable.set(*this, data); + } else { + throw std::logic_error("Unable to handle the given type. Type is not registered"); + } + } else if (dataType.isValueType() && g_value_type_transformable(dataType, type())) { + Value v; + v.init(dataType); + + try { + v.setData(dataType, data); + } catch (const InvalidTypeException &) { + Q_ASSERT(false); //This must never happen + } + + if (!g_value_transform(v.m_value, m_value)) { + throw InvalidTypeException(); + } + } else { + throw InvalidTypeException(); + } +} + //END ValueBase //BEGIN Value @@ -164,42 +313,7 @@ SharedValue & SharedValue::operator=(const SharedValue & other) //END SharedValue - -//BEGIN ValueImpl internal helpers - -uint ValueImpl_Flags::get(const ValueBase & value) -{ - return g_value_get_flags(value); -} - -void ValueImpl_Flags::set(ValueBase & value, uint data) -{ - g_value_set_flags(value, data); -} - -int ValueImpl_Enum::get(const ValueBase & value) -{ - return g_value_get_enum(value); -} - -void ValueImpl_Enum::set(ValueBase & value, int data) -{ - g_value_set_enum(value, data); -} - -void *ValueImpl_Boxed::get(const ValueBase & value) -{ - return g_value_get_boxed(value); -} - -void ValueImpl_Boxed::set(ValueBase & value, void *data) -{ - g_value_set_boxed(value, data); -} - -//END ValueImpl internal helpers - -} +} //namespace QGlib QDebug & operator<<(QDebug debug, const QGlib::ValueBase & value) @@ -225,25 +339,3 @@ QDebug & operator<<(QDebug debug, const QGlib::ValueBase & value) return debug.space(); } } - -#define SHORT_VALUEIMPL_IMPLEMENTATION(T, NICK) \ - QGLIB_REGISTER_VALUEIMPL_IMPLEMENTATION(T, g_value_get_##NICK(value), g_value_set_##NICK(value, data)) - -SHORT_VALUEIMPL_IMPLEMENTATION(bool, boolean) -SHORT_VALUEIMPL_IMPLEMENTATION(char, char) -SHORT_VALUEIMPL_IMPLEMENTATION(unsigned char, uchar) -SHORT_VALUEIMPL_IMPLEMENTATION(int, int) -SHORT_VALUEIMPL_IMPLEMENTATION(unsigned int, uint) -SHORT_VALUEIMPL_IMPLEMENTATION(long, long) -SHORT_VALUEIMPL_IMPLEMENTATION(unsigned long, ulong) -SHORT_VALUEIMPL_IMPLEMENTATION(qint64, int64) -SHORT_VALUEIMPL_IMPLEMENTATION(quint64, uint64) -SHORT_VALUEIMPL_IMPLEMENTATION(float, float) -SHORT_VALUEIMPL_IMPLEMENTATION(double, double) -SHORT_VALUEIMPL_IMPLEMENTATION(void*, pointer) -SHORT_VALUEIMPL_IMPLEMENTATION(QGlib::Type, gtype) -SHORT_VALUEIMPL_IMPLEMENTATION(const char*, string) -SHORT_VALUEIMPL_IMPLEMENTATION(QByteArray, string) - -QGLIB_REGISTER_VALUEIMPL_IMPLEMENTATION(QString, QString::fromUtf8(g_value_get_string(value)), - g_value_set_string(value, data.toUtf8().constData())) diff --git a/src/QGlib/value.h b/src/QGlib/value.h index 66f0f04..061dbc4 100644 --- a/src/QGlib/value.h +++ b/src/QGlib/value.h @@ -1,5 +1,7 @@ /* Copyright (C) 2009-2010 George Kiagiadakis <kiagiadakis.george@gmail.com> + Copyright (C) 2010 Collabora Ltd. + @author George Kiagiadakis <george.kiagiadakis@collabora.co.uk> 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 @@ -19,11 +21,28 @@ #include "global.h" #include "type.h" +#include "refpointer.h" +#include <boost/mpl/if.hpp> #include <boost/type_traits.hpp> #include <stdexcept> +#include <QtCore/QString> +#include <QtCore/QDebug> namespace QGlib { +struct ValueVTable +{ + typedef void (*SetFunction)(ValueBase & value, const void *data); + typedef void (*GetFunction)(const ValueBase & value, void *data); + + inline ValueVTable() : set(NULL), get(NULL) {} + inline ValueVTable(SetFunction s, GetFunction g) : set(s), get(g) {} + + SetFunction set; + GetFunction get; +}; + + /*! \headerfile value.h <QGlib/Value> * \brief Common base class for Value and SharedValue, wrappers for GValue */ @@ -105,7 +124,19 @@ public: inline operator GValue*() { return m_value; } inline operator const GValue*() const { return m_value; } + /*! \internal */ + static void registerValueVTable(Type type, const ValueVTable & vtable); + protected: + template <typename T> + friend struct ValueImpl; + + /*! \internal */ + void getData(Type dataType, void *data) const; + /*! \internal */ + void setData(Type dataType, const void *data); + + ValueBase(GValue *val); virtual ~ValueBase(); Q_DISABLE_COPY(ValueBase) @@ -182,101 +213,27 @@ struct ValueImpl static inline void set(ValueBase & value, const T & data); }; -/*! \addtogroup macros Internal macros */ -//@{ - -/*! This macro declares a specialization for ValueImpl for the type \a T. - * It should be used in a header, in combination with QGLIB_REGISTER_VALUEIMPL_IMPLEMENTATION - * in the respective source file, which will define the implementation of this specialization. - * \sa QGlib::ValueImpl, QGLIB_REGISTER_VALUEIMPL_IMPLEMENTATION - */ -#define QGLIB_REGISTER_VALUEIMPL(T) \ - namespace QGlib { \ - template <> \ - struct ValueImpl<T> \ - { \ - static T get(const ValueBase & value); \ - static void set(ValueBase & value, T const & data); \ - }; \ - } - -/*! This macro defines the implementation of a ValueImpl specialization for type \a T. - * \param GET_IMPL should be an expression that evaluates to type T and extracts a value from - * a const ValueBase refererence called 'value' - * \param SET_IMPL should be an expression that evaluates to void and sets the data from a - * const T reference called 'data' to a ValueBase reference called 'value' - * \sa QGLIB_REGISTER_VALUEIMPL, QGlib::ValueImpl, QGlib::ValueBase - */ -#define QGLIB_REGISTER_VALUEIMPL_IMPLEMENTATION(T, GET_IMPL, SET_IMPL) \ - namespace QGlib { \ - T ValueImpl<T>::get(const ValueBase & value) \ - { \ - return (GET_IMPL);\ - } \ - void ValueImpl<T>::set(ValueBase & value, T const & data) \ - { \ - (SET_IMPL);\ - } \ - } - -/*! This macro declares a specialization (with its inline implementation) - * for ValueImpl for the type \a T, where \a T is a Boxed type. - * You can use this macro to register any boxed type in your code, in case - * you need to use it with an element property or with a signal. - * You will also need to register type \a T with the type system first, - * using the QGLIB_REGISTER_BOXED_TYPE macro. - * - * Example: - * \code - * QGLIB_REGISTER_BOXED_TYPE(GList*) - * QGLIB_REGISTER_VALUEIMPL_FOR_BOXED_TYPE(GList*) - * \endcode - * - * \note \a T must be a pointer - * \sa QGLIB_REGISTER_BOXED_TYPE - */ -#define QGLIB_REGISTER_VALUEIMPL_FOR_BOXED_TYPE(T) \ - namespace QGlib { \ - template <> \ - struct ValueImpl<T> \ - { \ - static T get(const ValueBase & value) { \ - return static_cast<T>(ValueImpl_Boxed::get(value)); \ - } \ - static void set(ValueBase & value, T const & data) { \ - ValueImpl_Boxed::set(value, static_cast<void*>(data)); \ - } \ - }; \ - } - -//@} - // -- template implementations -- template <typename T> T ValueBase::get() const { - if (!isValid()) { - throw InvalidValueException(); - } - if (!QGlib::Private::CanConvertTo<T>::from(type())) { - throw InvalidTypeException(); + try { + return ValueImpl<T>::get(*this); + } catch (const std::exception & e) { + qWarning() << "QGlib::ValueBase::get:" << e.what(); + return T(); } - - return ValueImpl<T>::get(*this); } template <typename T> void ValueBase::set(const T & data) { - if (!isValid()) { - throw InvalidValueException(); - } - if (!QGlib::Private::CanConvertFrom<T>::to(type())) { - throw InvalidTypeException(); + try { + ValueImpl<T>::set(*this, data); + } catch (const std::exception & e) { + qWarning() << "QGlib::ValueBase::set:" << e.what(); } - - ValueImpl<T>::set(*this, data); } @@ -294,58 +251,116 @@ inline void Value::init() init(GetType<T>()); } -// -- default ValueImpl implementation (handles enums) -- - -struct ValueImpl_Enum -{ - static int get(const ValueBase & value); - static void set(ValueBase & value, int data); -}; +// -- default ValueImpl implementation -- template <typename T> inline T ValueImpl<T>::get(const ValueBase & value) { - QGLIB_STATIC_ASSERT(boost::is_enum<T>::value, - "No QGlib::ValueImpl<T> specialization has been registered for this type"); - return static_cast<T>(ValueImpl_Enum::get(value)); + //Use int for enums, T for everything else + typename boost::mpl::if_< + boost::is_enum<T>, + int, T + >::type result; + + value.getData(GetType<T>(), &result); + return static_cast<T>(result); } template <typename T> inline void ValueImpl<T>::set(ValueBase & value, const T & data) { - QGLIB_STATIC_ASSERT(boost::is_enum<T>::value, - "No QGlib::ValueImpl<T> specialization has been registered for this type"); - ValueImpl_Enum::set(value, static_cast<int>(data)); + //Use const int for enums, const T for everything else + typename boost::mpl::if_< + boost::is_enum<T>, + const int, const T & + >::type dataRef = data; + + value.setData(GetType<T>(), &dataRef); } // -- ValueImpl specialization for QFlags -- -struct ValueImpl_Flags -{ - static uint get(const ValueBase & value); - static void set(ValueBase & value, uint data); -}; - template <class T> struct ValueImpl< QFlags<T> > { static inline QFlags<T> get(const ValueBase & value) { - return QFlags<T>(QFlag(ValueImpl_Flags::get(value))); + uint flags; + value.getData(GetType< QFlags<T> >(), &flags); + return QFlags<T>(QFlag(flags)); } static inline void set(ValueBase & value, const QFlags<T> & data) { - ValueImpl_Flags::set(value, static_cast<uint>(data)); + uint flags = data; + value.setData(GetType< QFlags<T> >(), &flags); + } +}; + +// -- ValueImpl specialization for RefPointer -- + +template <class T> +struct ValueImpl< RefPointer<T> > +{ + static inline RefPointer<T> get(const ValueBase & value) + { + typename T::CType *gobj; + value.getData(GetType<T>(), &gobj); + return RefPointer<T>::wrap(gobj); + } + + static inline void set(ValueBase & value, const RefPointer<T> & data) + { + typename T::CType *gobj = static_cast<typename T::CType*>(data); + value.setData(GetType<T>(), &gobj); + } +}; + +// -- ValueImpl specialization for string literals -- + +template <int N> +struct ValueImpl<char[N]> +{ + //No get method, obviously. + + static inline void set(ValueBase & value, const char (&data)[N]) + { + QByteArray str = QByteArray::fromRawData(data, N); + value.setData(Type::String, &str); + } +}; + +// -- ValueImpl specialization for const char* -- + +template <> +struct ValueImpl<const char*> +{ + //No get method, obviously. + + static inline void set(ValueBase & value, const char *data) + { + QByteArray str = QByteArray::fromRawData(data, qstrlen(data)); + value.setData(Type::String, &str); } }; -// -- ValueImpl helper for using boxed types -- +// -- ValueImpl specialization for QString -- -struct ValueImpl_Boxed +template <> +struct ValueImpl<QString> { - static void *get(const ValueBase & value); - static void set(ValueBase & value, void *data); + static inline QString get(const ValueBase & value) + { + QByteArray str; + value.getData(Type::String, &str); + return QString::fromUtf8(str); + } + + static inline void set(ValueBase & value, const QString & data) + { + QByteArray str = data.toUtf8(); + value.setData(Type::String, &str); + } }; } //namespace QGlib @@ -357,21 +372,4 @@ QGLIB_REGISTER_TYPE(QGlib::ValueBase) //codegen: GType=G_TYPE_VALUE QGLIB_REGISTER_TYPE(QGlib::Value) QGLIB_REGISTER_TYPE(QGlib::SharedValue) //codegen: GType=G_TYPE_VALUE -QGLIB_REGISTER_VALUEIMPL(bool) -QGLIB_REGISTER_VALUEIMPL(char) -QGLIB_REGISTER_VALUEIMPL(unsigned char) -QGLIB_REGISTER_VALUEIMPL(int) -QGLIB_REGISTER_VALUEIMPL(unsigned int) -QGLIB_REGISTER_VALUEIMPL(long) -QGLIB_REGISTER_VALUEIMPL(unsigned long) -QGLIB_REGISTER_VALUEIMPL(qint64) -QGLIB_REGISTER_VALUEIMPL(quint64) -QGLIB_REGISTER_VALUEIMPL(float) -QGLIB_REGISTER_VALUEIMPL(double) -QGLIB_REGISTER_VALUEIMPL(void*) -QGLIB_REGISTER_VALUEIMPL(QGlib::Type) -QGLIB_REGISTER_VALUEIMPL(const char*) -QGLIB_REGISTER_VALUEIMPL(QByteArray) -QGLIB_REGISTER_VALUEIMPL(QString) - #endif |