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.
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.
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() 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.