diff options
author | George Kiagiadakis <george.kiagiadakis@collabora.co.uk> | 2011-01-06 22:05:23 +0200 |
---|---|---|
committer | George Kiagiadakis <george.kiagiadakis@collabora.co.uk> | 2011-01-06 22:05:23 +0200 |
commit | 86725fc8d6475279d50ac0a86cd0271dc971ce83 (patch) | |
tree | 7de6bb74dfe0f2acd66df6704af58aa09971a39a | |
parent | e43f0c3f28086fd070db60ea24cedb68b80a0f69 (diff) |
Implement a new way of wraping objects, similar to the one glibmm uses.
Features:
* C++ wrappers are reused among several RefPointer instances.
* C++ wrappers are even saved in qdata where applicable, to make
sure they are only created once for each object. (This does
not apply to MiniObjects and Caps though)
* dynamicCast should be faster in certain cases, since it no longer
queries the GType of the instance if it is not necessary.
-rw-r--r-- | src/CMakeLists.txt | 3 | ||||
-rw-r--r-- | src/QGlib/global.h | 5 | ||||
-rw-r--r-- | src/QGlib/init.cpp | 32 | ||||
-rw-r--r-- | src/QGlib/object.h | 1 | ||||
-rw-r--r-- | src/QGlib/paramspec.h | 1 | ||||
-rw-r--r-- | src/QGlib/refpointer.h | 151 | ||||
-rw-r--r-- | src/QGlib/wrap.cpp | 97 | ||||
-rw-r--r-- | src/QGlib/wrap.h | 74 | ||||
-rw-r--r-- | src/QGst/caps.cpp | 43 | ||||
-rw-r--r-- | src/QGst/caps.h | 11 | ||||
-rw-r--r-- | src/QGst/childproxy.h | 1 | ||||
-rw-r--r-- | src/QGst/colorbalance.h | 1 | ||||
-rw-r--r-- | src/QGst/global.cpp | 3 | ||||
-rw-r--r-- | src/QGst/global.h | 2 | ||||
-rw-r--r-- | src/QGst/miniobject.cpp | 43 | ||||
-rw-r--r-- | src/QGst/miniobject.h | 9 | ||||
-rw-r--r-- | src/QGst/propertyprobe.h | 1 | ||||
-rw-r--r-- | src/QGst/streamvolume.h | 1 | ||||
-rw-r--r-- | src/QGst/urihandler.h | 1 | ||||
-rw-r--r-- | src/QGst/videoorientation.h | 1 | ||||
-rw-r--r-- | src/QGst/xoverlay.h | 1 | ||||
-rw-r--r-- | tests/auto/capstest.cpp | 2 | ||||
-rw-r--r-- | tests/auto/refpointertest.cpp | 112 | ||||
-rw-r--r-- | tests/auto/urihandlertest.cpp | 12 |
24 files changed, 530 insertions, 78 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 066d8e7..ff32b14 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -7,6 +7,8 @@ set(QtGstreamer_SRCS QGlib/signal.cpp QGlib/error.cpp QGlib/connect.cpp + QGlib/wrap.cpp + QGlib/init.cpp QGst/global.cpp QGst/objectstore.cpp @@ -48,6 +50,7 @@ set(INSTALLED_HEADERS QGlib/quark.h QGlib/Quark QGlib/type.h QGlib/Type QGlib/refpointer.h QGlib/RefPointer + QGlib/wrap.h QGlib/paramspec.h QGlib/ParamSpec QGlib/object.h QGlib/Object QGlib/value.h QGlib/Value diff --git a/src/QGlib/global.h b/src/QGlib/global.h index 7fa5c72..793735c 100644 --- a/src/QGlib/global.h +++ b/src/QGlib/global.h @@ -40,6 +40,10 @@ typedef RefPointer<ParamSpec> ParamSpecPtr; class Object; typedef RefPointer<Object> ObjectPtr; +/*! Initializes the type system. You must call + * this function before using any QtGLib API. */ +void init(); + } //namespace QGlib @@ -51,7 +55,6 @@ typedef RefPointer<Object> ObjectPtr; CppClass(const CppClass &); \ CppClass & operator=(const CppClass &); \ ~CppClass() {} \ - template <class T> friend class QGlib::RefPointer; \ friend QGlib::RefCountedObject* FakeSuperClass##_new(void*); \ private: diff --git a/src/QGlib/init.cpp b/src/QGlib/init.cpp new file mode 100644 index 0000000..5dfc1fa --- /dev/null +++ b/src/QGlib/init.cpp @@ -0,0 +1,32 @@ +/* + Copyright (C) 2010 Collabora Ltd. <info@collabora.co.uk> + @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 + by the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + This program 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 General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ +#include <glib-object.h> + +namespace QGlib { + +namespace Private { + void registerWrapperConstructors(); //generated by codegen +} + +void init() +{ + g_type_init(); + Private::registerWrapperConstructors(); +} + +} //namespace QGlib diff --git a/src/QGlib/object.h b/src/QGlib/object.h index 94578e3..9d1d14d 100644 --- a/src/QGlib/object.h +++ b/src/QGlib/object.h @@ -99,5 +99,6 @@ void ObjectBase::setProperty(const char *name, const T & value) QGLIB_REGISTER_TYPE(QGlib::Object) QGLIB_REGISTER_TYPE(QGlib::Interface) +QGLIB_REGISTER_WRAPIMPL_FOR_SUBCLASSES_OF(QGlib::Object, QGlib::Private::wrapObject) #endif diff --git a/src/QGlib/paramspec.h b/src/QGlib/paramspec.h index 4cadb2e..11bb567 100644 --- a/src/QGlib/paramspec.h +++ b/src/QGlib/paramspec.h @@ -70,5 +70,6 @@ Q_DECLARE_OPERATORS_FOR_FLAGS(ParamSpec::ParamFlags) QGLIB_REGISTER_TYPE(QGlib::ParamSpec) //codegen: GType=G_TYPE_PARAM QGLIB_REGISTER_TYPE(QGlib::ParamSpec::ParamFlags) +QGLIB_REGISTER_WRAPIMPL_FOR_SUBCLASSES_OF(QGlib::ParamSpec, QGlib::Private::wrapParamSpec) #endif diff --git a/src/QGlib/refpointer.h b/src/QGlib/refpointer.h index b42bc4d..25bf606 100644 --- a/src/QGlib/refpointer.h +++ b/src/QGlib/refpointer.h @@ -21,10 +21,18 @@ #include "global.h" #include "type.h" +#include "wrap.h" #include <cstddef> #include <boost/type_traits.hpp> +#include <boost/utility/enable_if.hpp> namespace QGlib { + +//forward declarations +class Object; +class Interface; + + namespace Private { template <class T, class X> @@ -82,6 +90,9 @@ public: inline RefPointer(); inline ~RefPointer(); + /*! \internal */ + explicit inline RefPointer(T *cppClass); + inline RefPointer(const RefPointer<T> & other); template <class X> inline RefPointer(const RefPointer<X> & other); @@ -149,12 +160,13 @@ private: */ class RefCountedObject { +public: + virtual ~RefCountedObject() {} + protected: template <class T> friend class RefPointer; template <class T, class X> friend struct Private::RefPointerEqualityCheck; - virtual ~RefCountedObject() {} - virtual void ref(bool increaseRef) = 0; virtual void unref() = 0; @@ -184,6 +196,12 @@ inline RefPointer<T>::~RefPointer() } template <class T> +inline RefPointer<T>::RefPointer(T *cppClass) + : m_class(cppClass) +{ +} + +template <class T> template <class X> inline RefPointer<T>::RefPointer(const RefPointer<X> & other) : m_class(NULL) @@ -221,11 +239,10 @@ void RefPointer<T>::assign(const RefPointer<X> & other) { //T should be a base class of X QGLIB_STATIC_ASSERT((boost::is_base_of<T, X>::value), - "Cannot upcast a RefPointer without using dynamicCast()"); + "Cannot implicitly cast a RefPointer down the hierarchy"); if (!other.isNull()) { - m_class = new T(); - m_class->m_object = other.m_class->m_object; + m_class = static_cast<T*>(other.m_class); static_cast<RefCountedObject*>(m_class)->ref(true); } } @@ -262,8 +279,7 @@ template <class T> void RefPointer<T>::clear() { if (!isNull()) { - static_cast<RefCountedObject*>(m_class)->unref(); - delete m_class; + static_cast<RefCountedObject*>(m_class)->unref(); //this may delete m_class at this point m_class = NULL; } } @@ -274,9 +290,10 @@ RefPointer<T> RefPointer<T>::wrap(typename T::CType *nativePtr, bool increaseRef { RefPointer<T> ptr; if (nativePtr != NULL) { - ptr.m_class = new T(); - ptr.m_class->m_object = nativePtr; - static_cast<RefCountedObject*>(ptr.m_class)->ref(increaseRef); + RefCountedObject *cppObj = WrapImpl<T>::wrap(nativePtr); + cppObj->ref(increaseRef); + ptr.m_class = dynamic_cast<T*>(cppObj); + Q_ASSERT(ptr.m_class); } return ptr; } @@ -304,26 +321,126 @@ inline T *RefPointer<T>::operator->() const template <class T> inline RefPointer<T>::operator typename T::CType*() const { - return m_class ? static_cast<typename T::CType*>(m_class->m_object) : NULL; + return m_class ? static_cast<RefCountedObject*>(m_class)->object<typename T::CType>() : NULL; } template <class T> template <class X> RefPointer<X> RefPointer<T>::staticCast() const { - return isNull() ? RefPointer<X>() - : RefPointer<X>::wrap(static_cast<typename X::CType*>(static_cast<X*>(m_class)->m_object)); + RefPointer<X> result; + if (m_class) { + static_cast<RefCountedObject*>(m_class)->ref(true); + result.m_class = static_cast<X*>(m_class); + } + return result; } + +namespace Private { + +template <typename T, typename X, typename Enable = void> +struct IfaceDynamicCastImpl +{ + static inline X *doCast(typename X::CType *obj) + { + Q_UNUSED(obj); + return NULL; + } +}; + +//this version is compiled if X is an interface and T is an object, +//i.e. we are dynamically casting from an object to an interface. +template <typename T, typename X> +struct IfaceDynamicCastImpl<T, X, + typename boost::enable_if_c< + //to check if something is an interface, we need to also verify that it does + //not inherit Object, since derived object classes may also derive from interfaces. + (boost::is_base_of<Interface, X>::value && + !boost::is_base_of<Object, X>::value && + boost::is_base_of<Object, T>::value) + >::type + > +{ + static inline X *doCast(typename X::CType *obj) + { + X *targetClass = NULL; + + //Check that instanceType implements (isA) the interface + //and if it does, return a wrapper for that interface. + if (Type::fromInstance(obj).isA(GetType<X>())) + { + targetClass = dynamic_cast<X*>(Private::wrapInterface(GetType<X>(), obj)); + Q_ASSERT(targetClass); + } + + return targetClass; + } +}; + +//this version is compiled if T is an interface, +//i.e. we are dynamically casting from an interface to either an object or another interface. +template <typename T, typename X> +struct IfaceDynamicCastImpl<T, X, + typename boost::enable_if_c< + //to check if something is an interface, we need to also verify that it does + //not inherit Object, since derived object classes may also derive from interfaces. + (boost::is_base_of<Interface, T>::value && + !boost::is_base_of<Object, T>::value) + >::type + > +{ + static inline X *doCast(typename X::CType *obj) + { + //get the instance type and try to create (or rather fetch from the GObject qdata) + //the C++ wrapper class for this type of object. + RefCountedObject *cppClass = Private::wrapObject(obj); + + //attempt to cast it to X + X *targetClass = dynamic_cast<X*>(cppClass); + + if (!targetClass) { + //Cast failed. This either means that X is something that our instance is not + //or that X is another interface that is not inherited by the wrapper class + //for this instance type, but it is possible that our instance actually + //implements it, so let's check it. + if (boost::is_base_of<Interface, X>::value && + !boost::is_base_of<Object, X>::value && + Type::fromInstance(obj).isA(GetType<X>())) + { + targetClass = dynamic_cast<X*>(Private::wrapInterface(GetType<X>(), obj)); + Q_ASSERT(targetClass); + } + } + + return targetClass; + } +}; + +} //namespace Private + + template <class T> template <class X> RefPointer<X> RefPointer<T>::dynamicCast() const { - if (!isNull() && QGlib::Private::CanConvertTo<X>::from(m_class->m_object)) { - return RefPointer<X>::wrap(static_cast<typename X::CType*>(m_class->m_object)); - } else { - return RefPointer<X>(); + RefPointer<X> result; + if (m_class) { + X *targetClass = dynamic_cast<X*>(m_class); + if (!targetClass) { + //in case either X or T is an interface, we need to do some extra checks. + //this is a template to optimize the compiled code depending on what X and T are. + typename X::CType *obj = static_cast<RefCountedObject*>(m_class)->object<typename X::CType>(); + targetClass = Private::IfaceDynamicCastImpl<T, X>::doCast(obj); + } + + if (targetClass) { + static_cast<RefCountedObject*>(targetClass)->ref(true); + result.m_class = targetClass; + } } + + return result; } // trick GetType to return the same type for GetType<T>() and GetType< RefPointer<T> >() diff --git a/src/QGlib/wrap.cpp b/src/QGlib/wrap.cpp new file mode 100644 index 0000000..857e9cb --- /dev/null +++ b/src/QGlib/wrap.cpp @@ -0,0 +1,97 @@ +/* + Copyright (C) 2010 Collabora Ltd. <info@collabora.co.uk> + @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 + by the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + This program 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 General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ +#include "refpointer.h" +#include "quark.h" +#include <glib-object.h> + +namespace QGlib { + +RefCountedObject *constructWrapper(Type instanceType, void *instance) +{ + Quark q = g_quark_from_static_string("QGlib__wrapper_constructor"); + RefCountedObject *cppClass = NULL; + + for(Type t = instanceType; t.isValid(); t = t.parent()) { + void *funcPtr = t.quarkData(q); + if (funcPtr) { + cppClass = (reinterpret_cast<RefCountedObject *(*)(void*)>(funcPtr))(instance); + Q_ASSERT_X(cppClass, "QGlib::constructWrapper", + "Failed to wrap instance. This is a bug in the bindings library."); + return cppClass; + } + } + + Q_ASSERT_X(false, "QGlib::constructWrapper", + "No wrapper constructor found for this type. This is a bug in the bindings library."); + return cppClass; +} + +namespace Private { + +static void qdataDestroyNotify(void *cppInstance) +{ + delete static_cast<RefCountedObject*>(cppInstance); +} + +RefCountedObject *wrapObject(void *gobject) +{ + Q_ASSERT(gobject); + + Quark q = g_quark_from_static_string("QGlib__object_wrapper"); + RefCountedObject *obj = static_cast<RefCountedObject*>(g_object_get_qdata(G_OBJECT(gobject), q)); + + if (!obj) { + obj = constructWrapper(Type::fromInstance(gobject), gobject); + g_object_set_qdata_full(G_OBJECT(gobject), q, obj, &qdataDestroyNotify); + } + + return obj; +} + +RefCountedObject *wrapParamSpec(void *param) +{ + Q_ASSERT(param); + + Quark q = g_quark_from_static_string("QGlib__paramspec_wrapper"); + RefCountedObject *obj = static_cast<RefCountedObject*>(g_param_spec_get_qdata(G_PARAM_SPEC(param), q)); + + if (!obj) { + obj = constructWrapper(Type::fromInstance(param), param); + g_param_spec_set_qdata_full(G_PARAM_SPEC(param), q, obj, &qdataDestroyNotify); + } + + return obj; +} + +RefCountedObject *wrapInterface(Type interfaceType, void *gobject) +{ + Q_ASSERT(gobject); + + Quark q = Quark::fromString(QLatin1String("QGlib__interface_wrapper__") + interfaceType.name()); + RefCountedObject *obj = static_cast<RefCountedObject*>(g_object_get_qdata(G_OBJECT(gobject), q)); + + if (!obj) { + obj = constructWrapper(interfaceType, gobject); + g_object_set_qdata_full(G_OBJECT(gobject), q, obj, &qdataDestroyNotify); + } + + return obj; +} + +} //namespace Private +} //namespace QGlib diff --git a/src/QGlib/wrap.h b/src/QGlib/wrap.h new file mode 100644 index 0000000..8f2ce4b --- /dev/null +++ b/src/QGlib/wrap.h @@ -0,0 +1,74 @@ +/* + Copyright (C) 2010 Collabora Ltd. <info@collabora.co.uk> + @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 + by the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + This program 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 General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ +#ifndef QGLIB_WRAP_H +#define QGLIB_WRAP_H + +namespace QGlib { + +class RefCountedObject; +class Type; + +/*! This function constructs a RefCountedObject that wraps the given \a instance, + * which is of type \a instanceType. It returns a C++ wrapper class that inherits + * RefCountedObject and wraps \a instanceType objects in the best possible way. + * + * This function is provided for implementing bindings that use QtGLib. It is only + * needed if those bindings have some reference counted instance type that is not + * an Object or Interface or ParamSpec (like QGst::MiniObject and QGst::Caps). + * You should \em not otherwise call this function directly. + */ +RefCountedObject *constructWrapper(Type instanceType, void *instance); + +template <typename T, typename Enable = void> +struct WrapImpl {}; + +#define QGLIB_REGISTER_WRAPIMPL_FOR_SUBCLASSES_OF(BaseClass, WrapFunc) \ + namespace QGlib { \ + template <typename T> \ + struct WrapImpl<T, typename boost::enable_if< boost::is_base_of<BaseClass, T> >::type > \ + { \ + static inline RefCountedObject *wrap(typename T::CType *object) \ + { \ + return WrapFunc(object); \ + } \ + }; \ + } //namespace QGlib + +#define QGLIB_REGISTER_INTERFACE(IfaceClass) \ + namespace QGlib { \ + template <> \ + struct WrapImpl<IfaceClass, void> \ + { \ + static inline RefCountedObject *wrap(IfaceClass::CType *object) \ + { \ + return Private::wrapInterface(GetType<IfaceClass>(), object); \ + } \ + }; \ + } //namespace QGlib + +namespace Private { + +RefCountedObject *wrapObject(void *gobject); +RefCountedObject *wrapParamSpec(void *param); +RefCountedObject *wrapInterface(Type interfaceType, void *gobject); + +} //namespace Private +} //namespace QGlib + + +#endif // QGLIB_WRAP_H diff --git a/src/QGst/caps.cpp b/src/QGst/caps.cpp index 08d0e34..d3971c3 100644 --- a/src/QGst/caps.cpp +++ b/src/QGst/caps.cpp @@ -185,7 +185,7 @@ CapsPtr Caps::copyNth(uint index) const void Caps::ref(bool increaseRef) { - if (Private::ObjectStore::put(m_object)) { + if (Private::ObjectStore::put(this)) { if (increaseRef) { gst_caps_ref(GST_CAPS(m_object)); } @@ -194,32 +194,24 @@ void Caps::ref(bool increaseRef) void Caps::unref() { - if (Private::ObjectStore::take(m_object)) { + if (Private::ObjectStore::take(this)) { gst_caps_unref(GST_CAPS(m_object)); + delete this; } } -void Caps::makeWritable() +CapsPtr Caps::makeWritable() const { + /* + * Calling gst_*_make_writable() below is tempting but wrong. + * Since MiniObjects and Caps do not share the same C++ instance in various wrappings, calling + * gst_*_make_writable() on an already writable object and wrapping the result is wrong, + * since it would just return the same pointer and we would wrap it in a new C++ instance. + */ if (!isWritable()) { - //m_object will change, need to deal with the reference count properly - unref(); - - /* - * Calling gst_*_make_writable() below is tempting but wrong, as the above unref() might have - * dropped the gst refcount from 2 to 1 temporarily. When this happens gst_*_make_writable() - * will do nothing, return the same object, and the refcount will go back to 2 when we ref() - * it again below. - * So the right thing to do is to copy() here to make sure we get a new object in this case. - * - * Note that if the external refCount is 1 then the gst_*_make_writable() semantics is - * preserved (nothing is copied, same object is used) as we tested for this condition - * before entering this code path. - */ - m_object = gst_caps_copy(GST_CAPS(m_object)); - - //Manage our reference count for the new m_object - ref(false); + return copy(); + } else { + return CapsPtr(const_cast<Caps*>(this)); } } @@ -229,4 +221,13 @@ QDebug operator<<(QDebug debug, const CapsPtr & caps) return debug.space(); } + +namespace Private { + +QGlib::RefCountedObject *wrapCaps(void *caps) +{ + return QGlib::constructWrapper(GST_CAPS(caps)->type, caps); +} + +} //namespace Private } //namespace QGst diff --git a/src/QGst/caps.h b/src/QGst/caps.h index a83b6e1..4a11f38 100644 --- a/src/QGst/caps.h +++ b/src/QGst/caps.h @@ -69,7 +69,7 @@ public: CapsPtr copyNth(uint index) const; bool isWritable() const; - void makeWritable(); + CapsPtr makeWritable() const; protected: virtual void ref(bool increaseRef); @@ -79,8 +79,15 @@ protected: /*! \relates QGst::Caps */ QDebug operator<<(QDebug debug, const CapsPtr & caps); -} + +namespace Private { + +QGlib::RefCountedObject *wrapCaps(void *caps); + +} //namespace Private +} //namespace QGst QGLIB_REGISTER_TYPE(QGst::Caps) +QGLIB_REGISTER_WRAPIMPL_FOR_SUBCLASSES_OF(QGst::Caps, QGst::Private::wrapCaps) #endif diff --git a/src/QGst/childproxy.h b/src/QGst/childproxy.h index 107f3f0..91d43b3 100644 --- a/src/QGst/childproxy.h +++ b/src/QGst/childproxy.h @@ -53,5 +53,6 @@ void ChildProxy::setChildProperty(const char *name, const T & value) } QGLIB_REGISTER_TYPE(QGst::ChildProxy) +QGLIB_REGISTER_INTERFACE(QGst::ChildProxy) #endif // QGST_CHILDPROXY_H diff --git a/src/QGst/colorbalance.h b/src/QGst/colorbalance.h index c8bf262..7e54099 100644 --- a/src/QGst/colorbalance.h +++ b/src/QGst/colorbalance.h @@ -51,5 +51,6 @@ public: QGLIB_REGISTER_TYPE(QGst::ColorBalanceChannel) QGLIB_REGISTER_TYPE(QGst::ColorBalance) +QGLIB_REGISTER_INTERFACE(QGst::ColorBalance) #endif // QGST_COLORBALANCE_H diff --git a/src/QGst/global.cpp b/src/QGst/global.cpp index 0f5fcf2..9299db0 100644 --- a/src/QGst/global.cpp +++ b/src/QGst/global.cpp @@ -22,6 +22,7 @@ namespace QGst { namespace Private { void registerValueVTables(); + void registerWrapperConstructors(); //generated by codegen } void init() @@ -31,11 +32,13 @@ void init() void init(int *argc, char **argv[]) { + QGlib::init(); GError *error; if (!gst_init_check(argc, argv, &error)) { throw QGlib::Error(error); } Private::registerValueVTables(); + Private::registerWrapperConstructors(); } void cleanup() diff --git a/src/QGst/global.h b/src/QGst/global.h index b5a8865..5c74c35 100644 --- a/src/QGst/global.h +++ b/src/QGst/global.h @@ -164,6 +164,8 @@ namespace QGst { /*! Initializes the GStreamer library, setting up internal path lists, * registering built-in elements, and loading standard plugins. + * \note This function also calls QGlib::init(), + * so there is no need to call it explicitly. * \param argc pointer to the application's argc * \param argv pointer to the application's argv * \throws QGlib::Error when initialization fails diff --git a/src/QGst/miniobject.cpp b/src/QGst/miniobject.cpp index 4a063d9..9f5c461 100644 --- a/src/QGst/miniobject.cpp +++ b/src/QGst/miniobject.cpp @@ -52,7 +52,7 @@ void MiniObject::unsetFlag(MiniObjectFlag flag) void MiniObject::ref(bool increaseRef) { - if (Private::ObjectStore::put(m_object)) { + if (Private::ObjectStore::put(this)) { if (increaseRef) { gst_mini_object_ref(GST_MINI_OBJECT(m_object)); } @@ -61,33 +61,34 @@ void MiniObject::ref(bool increaseRef) void MiniObject::unref() { - if (Private::ObjectStore::take(m_object)) { + if (Private::ObjectStore::take(this)) { gst_mini_object_unref(GST_MINI_OBJECT(m_object)); + delete this; } } -void MiniObject::makeWritable() +MiniObjectPtr MiniObject::makeWritable() const { + /* + * Calling gst_*_make_writable() below is tempting but wrong. + * Since MiniObjects and Caps do not share the same C++ instance in various wrappings, calling + * gst_*_make_writable() on an already writable object and wrapping the result is wrong, + * since it would just return the same pointer and we would wrap it in a new C++ instance. + */ if (!isWritable()) { - //m_object will change, need to deal with the reference count properly - unref(); - - /* - * Calling gst_*_make_writable() below is tempting but wrong, as the above unref() might have - * dropped the gst refcount from 2 to 1 temporarily. When this happens gst_*_make_writable() - * will do nothing, return the same object, and the refcount will go back to 2 when we ref() - * it again below. - * So the right thing to do is to copy() here to make sure we get a new object in this case. - * - * Note that if the external refCount is 1 then the gst_*_make_writable() semantics is - * preserved (nothing is copied, same object is used) as we tested for this condition - * before entering this code path. - */ - m_object = gst_mini_object_copy(GST_MINI_OBJECT(m_object)); - - //Manage our reference count for the new m_object - ref(false); + return copy(); + } else { + return MiniObjectPtr(const_cast<MiniObject*>(this)); } } + +namespace Private { + +QGlib::RefCountedObject *wrapMiniObject(void *miniObject) +{ + return QGlib::constructWrapper(QGlib::Type::fromInstance(miniObject), miniObject); +} + +} //namespace Private } //namespace QGst diff --git a/src/QGst/miniobject.h b/src/QGst/miniobject.h index feb8ae6..61690c7 100644 --- a/src/QGst/miniobject.h +++ b/src/QGst/miniobject.h @@ -32,7 +32,7 @@ class MiniObject : public QGlib::RefCountedObject public: MiniObjectPtr copy() const; bool isWritable() const; - void makeWritable(); + MiniObjectPtr makeWritable() const; MiniObjectFlags flags() const; bool flagIsSet(MiniObjectFlag flag) const; @@ -44,8 +44,15 @@ protected: virtual void unref(); }; + +namespace Private { + +QGlib::RefCountedObject *wrapMiniObject(void *miniObject); + +} //namespace Private } //namespace QGst QGLIB_REGISTER_TYPE(QGst::MiniObject) +QGLIB_REGISTER_WRAPIMPL_FOR_SUBCLASSES_OF(QGst::MiniObject, QGst::Private::wrapMiniObject) #endif diff --git a/src/QGst/propertyprobe.h b/src/QGst/propertyprobe.h index 75714e6..1385add 100644 --- a/src/QGst/propertyprobe.h +++ b/src/QGst/propertyprobe.h @@ -50,5 +50,6 @@ public: } QGLIB_REGISTER_TYPE(QGst::PropertyProbe) +QGLIB_REGISTER_INTERFACE(QGst::PropertyProbe) #endif // QGST_PROPERTYPROBE_H diff --git a/src/QGst/streamvolume.h b/src/QGst/streamvolume.h index b908f66..e646eca 100644 --- a/src/QGst/streamvolume.h +++ b/src/QGst/streamvolume.h @@ -41,5 +41,6 @@ public: } //namespace QGst QGLIB_REGISTER_TYPE(QGst::StreamVolume) +QGLIB_REGISTER_INTERFACE(QGst::StreamVolume) #endif // QGST_STREAMVOLUME_H diff --git a/src/QGst/urihandler.h b/src/QGst/urihandler.h index eeb2538..eafd3a5 100644 --- a/src/QGst/urihandler.h +++ b/src/QGst/urihandler.h @@ -42,5 +42,6 @@ public: } //namespace QGst QGLIB_REGISTER_TYPE(QGst::UriHandler) +QGLIB_REGISTER_INTERFACE(QGst::UriHandler) #endif // QGST_URIHANDLER_H diff --git a/src/QGst/videoorientation.h b/src/QGst/videoorientation.h index 4764ee1..60c5ff1 100644 --- a/src/QGst/videoorientation.h +++ b/src/QGst/videoorientation.h @@ -44,5 +44,6 @@ public: } //namespace QGst QGLIB_REGISTER_TYPE(QGst::VideoOrientation) +QGLIB_REGISTER_INTERFACE(QGst::VideoOrientation) #endif // QGST_VIDEOORIENTATION_H diff --git a/src/QGst/xoverlay.h b/src/QGst/xoverlay.h index f8d665b..2db89f9 100644 --- a/src/QGst/xoverlay.h +++ b/src/QGst/xoverlay.h @@ -42,5 +42,6 @@ public: } //namespace QGst QGLIB_REGISTER_TYPE(QGst::XOverlay) +QGLIB_REGISTER_INTERFACE(QGst::XOverlay) #endif // QGST_XOVERLAY_H diff --git a/tests/auto/capstest.cpp b/tests/auto/capstest.cpp index 9bf8163..558803c 100644 --- a/tests/auto/capstest.cpp +++ b/tests/auto/capstest.cpp @@ -107,7 +107,7 @@ void CapsTest::writabilityTest() QVERIFY(GST_CAPS_REFCOUNT_VALUE(caps) == 2); QVERIFY(!caps->isWritable()); - caps->makeWritable(); //creates a copy + caps = caps->makeWritable(); //creates a copy QVERIFY(caps->isWritable()); QVERIFY(oldPtr != static_cast<GstCaps*>(caps)); //no longer same gstcaps object } diff --git a/tests/auto/refpointertest.cpp b/tests/auto/refpointertest.cpp index 206922c..bbb28ff 100644 --- a/tests/auto/refpointertest.cpp +++ b/tests/auto/refpointertest.cpp @@ -1,5 +1,5 @@ /* - Copyright (C) 2010 George Kiagiadakis <kiagiadakis.george@gmail.com> + Copyright (C) 2010 George Kiagiadakis <kiagiadakis.george@gmail.com> Copyright (C) 2010 Collabora Ltd. @author George Kiagiadakis <george.kiagiadakis@collabora.co.uk> @@ -18,9 +18,11 @@ */ #include "qgsttest.h" #include <QGst/Object> -#include <QGst/Bin> #include <QGst/Message> #include <QGst/Pipeline> +#include <QGst/ElementFactory> +#include <QGst/UriHandler> +#include <QGst/StreamVolume> class RefPointerTest : public QGstTest { @@ -29,6 +31,11 @@ private Q_SLOTS: void refTest1(); void refTest2(); void dynamicCastTest(); + void dynamicCastDownObjectTest(); + void dynamicCastUpObjectTest(); + void dynamicCastObjectToIfaceTest(); + void dynamicCastIfaceToObjectTest(); + void cppWrappersTest(); void messageDynamicCastTest(); void equalityTest(); }; @@ -75,6 +82,107 @@ void RefPointerTest::dynamicCastTest() gst_object_unref(bin); } +void RefPointerTest::dynamicCastDownObjectTest() +{ + GstObject *bin = GST_OBJECT(gst_object_ref(gst_bin_new(NULL))); + gst_object_sink(bin); + + { + QGlib::ObjectPtr object = QGlib::ObjectPtr::wrap(G_OBJECT(bin)); + QVERIFY(!object.dynamicCast<QGst::Object>().isNull()); + QVERIFY(!object.dynamicCast<QGst::Bin>().isNull()); + QVERIFY(object.dynamicCast<QGst::Pipeline>().isNull()); + } + + gst_object_unref(bin); +} + +void RefPointerTest::dynamicCastUpObjectTest() +{ + GstBin *bin = GST_BIN(gst_object_ref(gst_bin_new(NULL))); + gst_object_sink(bin); + + { + QGst::BinPtr object = QGst::BinPtr::wrap(bin); + QVERIFY(!object.dynamicCast<QGst::Element>().isNull()); + QVERIFY(!object.dynamicCast<QGlib::Object>().isNull()); + QVERIFY(!object.dynamicCast<QGst::ChildProxy>().isNull()); + } + + gst_object_unref(bin); +} + +void RefPointerTest::dynamicCastObjectToIfaceTest() +{ + QGst::ElementPtr e = QGst::ElementFactory::make("fakesrc"); + QGst::UriHandlerPtr u = e.dynamicCast<QGst::UriHandler>(); + QVERIFY(u.isNull()); + + e = QGst::ElementFactory::make("filesrc"); + u = e.dynamicCast<QGst::UriHandler>(); + QVERIFY(!u.isNull()); +} + +void RefPointerTest::dynamicCastIfaceToObjectTest() +{ + GstElement *e = gst_element_factory_make("filesrc", NULL); + gst_object_ref_sink(e); + + QGst::UriHandlerPtr u = QGst::UriHandlerPtr::wrap(GST_URI_HANDLER(e), false); + QVERIFY(!u.isNull()); + QVERIFY(!u.dynamicCast<QGst::Element>().isNull()); +} + +void RefPointerTest::cppWrappersTest() +{ + QGst::ElementPtr e = QGst::ElementFactory::make("playbin2"); + QVERIFY(!e.isNull()); + + { + QGst::PipelinePtr pipeline = e.dynamicCast<QGst::Pipeline>(); + QVERIFY(!pipeline.isNull()); + //the C++ wrappers must be the same + QCOMPARE(static_cast<QGlib::RefCountedObject*>(pipeline.operator->()), + static_cast<QGlib::RefCountedObject*>(e.operator->())); + } + + { + QGst::ChildProxyPtr proxy = e.dynamicCast<QGst::ChildProxy>(); + QVERIFY(!proxy.isNull()); + //the C++ wrappers must be the same + QCOMPARE(static_cast<QGlib::RefCountedObject*>(proxy.operator->()), + static_cast<QGlib::RefCountedObject*>(e.operator->())); + } + + { //new wrap() should give the same C++ instance + GstElement *gobj = e; + QGst::ElementPtr e2 = QGst::ElementPtr::wrap(gobj); + QCOMPARE(static_cast<QGlib::RefCountedObject*>(e2.operator->()), + static_cast<QGlib::RefCountedObject*>(e.operator->())); + } + + { + QGst::StreamVolumePtr sv = e.dynamicCast<QGst::StreamVolume>(); + QVERIFY(!sv.isNull()); + //now the C++ wrapper must not be the same, since Pipeline does not inherit StreamVolume + QVERIFY(static_cast<QGlib::RefCountedObject*>(sv.operator->()) + != static_cast<QGlib::RefCountedObject*>(e.operator->())); + } + + { + QGst::MessagePtr msg = QGst::ApplicationMessage::create(e); + QGst::MessagePtr msg2 = msg; + QCOMPARE(static_cast<QGlib::RefCountedObject*>(msg.operator->()), + static_cast<QGlib::RefCountedObject*>(msg2.operator->())); + QVERIFY(msg2 == msg); + + QGst::MessagePtr msg3 = QGst::MessagePtr::wrap(msg2); + QVERIFY(static_cast<QGlib::RefCountedObject*>(msg3.operator->()) + != static_cast<QGlib::RefCountedObject*>(msg2.operator->())); + QVERIFY(msg3 == msg2); + } +} + void RefPointerTest::messageDynamicCastTest() { QGst::BinPtr bin = QGst::Bin::create(); diff --git a/tests/auto/urihandlertest.cpp b/tests/auto/urihandlertest.cpp index c722e87..1ec9792 100644 --- a/tests/auto/urihandlertest.cpp +++ b/tests/auto/urihandlertest.cpp @@ -23,22 +23,10 @@ class UriHandlerTest : public QGstTest { Q_OBJECT private Q_SLOTS: - void castTest(); void interfaceTest(); void makeTest(); }; -void UriHandlerTest::castTest() -{ - QGst::ElementPtr e = QGst::ElementFactory::make("audioconvert"); - QGst::UriHandlerPtr u = e.dynamicCast<QGst::UriHandler>(); - QVERIFY(u.isNull()); - - e = QGst::ElementFactory::make("filesrc"); - u = e.dynamicCast<QGst::UriHandler>(); - QVERIFY(!u.isNull()); -} - void UriHandlerTest::interfaceTest() { QGst::ElementPtr e = QGst::ElementFactory::make("filesrc"); |