QtGStreamer  1.2.0
 All Classes Namespaces Functions Variables Typedefs Enumerations Enumerator Pages
Internal Design Details

This page documents various implementation details for people that are interested in contributing to the library itself or who are just interesting in learning how some things work.

Wrapping System Design

GObject
-------
Every GObject is attached a C++ class instance that provides the C++ bindings
for this object. This is done using the system described below.
Every C++ binding class has a "constructor" method which is autogenerated by codegen
and it simply creates an instance of this class. This method is not a member of the
class, but it is a friend of the class, so that it can call the private constructor.
The constructor and the friend declaration are both declared inside the Q*_WRAPPER
macro at the top of the class definition. A pointer to this constructor method is then
saved inside the quark data of the GType that this class provides bindings for. The
code that does this is also generated by codegen and it is called from the global init()
function of the relevant namespace (QGlib::init() and QGst::init()).
When the C object is first wrapped using RefPointer::wrap(), the wrap method
checks the GType of this C object and constructs a C++ class instance that best
matches the GType of the object. That means that if the given GType has a pointer to
a constructor method saved in its qdata, then it is used, otherwise, it tries the
GType of the parent and this goes on recursively. In the worst case, a QGlib::Object
instance will be created, which is the wrapper for GObject, the parent of all object
types.
After the C++ class instance has been created, a pointer to it is saved inside the
GObject instance qdata. This allows it to be reused; if for some reason all the references
to this instance are dropped from the C++ side and then later RefPointer::wrap() is
called again on the same C instance, it reuses the same C++ instance that was created
earlier. The C++ instance is finally deleted when the C instance is also deleted. A
GObject weak reference is used internally to get a notification when the C instance has
been deleted.
GObject Interfaces
------------------
Interfaces are handled in a similar way, but not exactly the same.
Interfaces are generally implemented by GObject subclasses in a way that resembles
multiple inheritance. In C++ we use multiple inheritance to simulate this. So for
example, because GstBin implements the GstChildProxy interface and inherits GstElement,
in C++ the class is:
class Bin : public Element, public ChildProxy
Now in this case, if we have a QGst::Element pointer that is actually a QGst::Bin instance,
the simplest way to cast to the ChildProxy interface is to use C++'s dynamic_cast().
However, it is not always possible to have a class that inherits from all the supported
interfaces, since in many cases those interfaces are either dynamic or implemented in
unknown subclasses. This is the case for example with the various GStreamer elements which
are implemented inside plugins and as a result, their class definition is not known before
runtime.
So, for all those GStreamer elements, the C++ class that is generally created is
QGst::Element - which does not inherit from any interface - and the interfaces are added
dynamically when needed, in the following way: First, a GType check is performed to see if
the element really implements the requested interface. If it does, a constructor method for
the C++ class that wraps the interface is requested from the interface's GType qdata
and this method is used to construct the interface's C++ instance, as it is done with GObject.
This interface C++ instance is then also saved inside the GObject instance qdata, but using
a different quark as a key for every interface, so that multiple interface C++ instances can
be saved.
This functionality is all hidden inside RefPointer::dynamicCast(), which hides the complexity
from the user and offers it as if it was part of the programming language.
GstMiniObject
-------------
GstMiniObject is a special case, since it does not inherit from GObject, but is a boxed type
instead. In general, the same technique as in GObject is used, with the most notable difference
being that GstMiniObject has no qdata on its instance and no weak references, so the C++ class
instance cannot be saved on the C instance and cannot be deleted when the C instance is deleted.
To work this around, an external reference count is added on the C++ class instance, which is
incremented and deincremented from RefPointer. Unfortunately this whole technique does not allow
reuse of the C++ instance; the C++ instance is deleted as soon as all the RefPointers that point
to it are destroyed, just like it would happen with QSharedPointer.

Value Design

What is interesting about QGlib::Value is how its set/get methods handle all possible
kinds of data in a unified way and call the appropriate g(st)_value_set/get_* methods
internally. This is handled like this: Every type passed to set/get is converted into
a void* and then it is passed to setData/getData, which use the GType of this type to
determine if it can be directly assigned to the GValue or if it needs conversion.
If it can be directly assigned, a dispatcher that holds set/get methods for the various
GTypes is used to get the appropriate set/get method that knows how to convert void* back
to the original type and handle it. A method (Value::registerValueVTable()) is
provided to register additional such methods from client code, if necessary.
If a conversion is required, it is handled by g_value_transform. This of course means
that an intermediate GValue that holds the given GType is created and transformed
accordingly.
Between Value::set/get and Value::setData/getData, there is an intermediate layer,
struct ValueImpl<T>. This struct provides the actual implementation of set() and get().
This is provided as an external template, so that it is possible to specialize it for
specific C++ types that need special handling. Examples include RefPointer<T>, QFlags<T>,
the various string types (internally, the dispatcher's set/get methods only handle QByteArray
for Type::String) and others. This is also useful to prevent a certain C++ type from
being used with Value, if necessary (for example, get<const char*>() is disabled and
will result in compilation error).
One more thing to note here is that Value::setData/getData are implemented to throw exceptions
when something goes wrong. These exceptions are later catched in Value::set/get, so that users
are not faced with exceptions, however, they are used internally from the signals/slots code
to handle errors and show friendlier error messages. Because Value::set/get catch these
exceptions, the signals/slots code uses directly ValueImpl<T>.

Connect Design

connect() is a template method. When it is used, it takes the arguments of the given
slot and instantiates a template marshaller that is responsible for converting
the GValues that the signal provides to the C++ types that the slot expects.
This marshaller and the structure holding the receiver & slot are then attached on
the data of a GClosure and this GClosure is connected to the signal.