This page contains notes on a research I am doing to see if it is possible to bring QtGLib closer to Qt, make it more generic and more feature-complete, so that it can be used for anything besides GStreamer. The bullets below describe possible solutions to the various problems that have been showstoppers until now. == GValue <-> QVariant == * Converting GValue to QVariant: * For native types: switch() and convert. * For Object/Interface/ParamSpec pointers: --(Store a QGlib::Value into the QVariant and provide template specializations for QVariant::value / qvariant_cast / qVariantValue (qt4 only) that will convert using QGlib::Value::get)-- Screw that. There is no such thing as function template partial specializations. * For Enum/Flag types: See below * For G_TYPE_POINTER: convert to QMetaType::!VoidStar * For Boxed types: provide hooks * Converting QVariant to GValue: * For native types: ditto. * If the QVariant contains a QGlib::Value (object/interface/paramspec ptr that comes from a method in the bindings): obvious. * For Object/Interface/ParamSpec pointers: --(Override QVariant::setValue / QVariant::fromValue / qVariantFromValue / qVariantSetValue to store a QGlib::Value directly into the QVariant -> as a result there will be no way to have a QVariant that stores an !ObjectPtr directly.)-- * For Enum/Flag types: See below * For void* / QObject*: store into a G_TYPE_POINTER GValue. Unfortunately we can't do much about other kinds of pointers. * For Boxed types: provide hooks * Enums/Flags: * --(Method 1: Store them as QGlib::Value inside the QVariant and provide template specializations for all the related QVariant methods (using type traits) which will convert to/from QGlib::Value, just like with the object types.)-- * --(Requires tagging template-wise all the enums/flags that belong to the bindings)-- * --(Requires also handling other kinds of enums/flags by copying QVariant's own implementation of those methods inside the specializations.)-- * Method 2: Register qMetaTypeId <-> GType pairs in global hashtables and convert like native types. * Problems: * QVariant::convert / QVariant::canConvert will not work correctly. This will need to be documented. * QVariant::value will also do conversions for types that a QGlib::Value is stored inside the variant. This is wanted for object types, but maybe not for enums/flags (+ not all conversions may be wanted). * QVariant::userType() will always return qMetaTypeId for all object types... * Pointer types are a bit problematic... Imagine setting a !VideoWidget* to the "widget" property of qwidgetvideosink. The latter expects a G_TYPE_POINTER, but the conversion cannot be made automatically. * Bonus: * With QVariant method specializations for QGlib::Value, it will be easy to get the original GValue from a QVariant. == GObject <-> QObject == * A QMetaObject needs to be generated for every GObject subclass. * Can be generated either at runtime or at compile time, but the runtime part has to be there anyway so that we can use signals/slots with classes that are unknown at compile time (gst elements...). * It can either be generated from GI or by introspecting manually the signals/properties of a class. The advantage of GI is that we can also bind non-signal methods as Q_SLOTS, although I'm not sure if we want this. * There is a QMetaObjectBuilder class in various places (including !QtCore in qt5), but it's not public. -> Copy until it becomes public. * Store the metaobject in the GType quark data, since it is not static data in the binary. * We need an ObjectBase superclass that will inherit QObject and will define the moc methods: * !ObjectBase::metaObject: * return g_type_get_qdata(G_TYPE_FROM_INSTANCE(m_object), "_QGLib_QMetaObject"); * !ObjectBase::qt_metacast: * Easy to implement, get the QMetaObject and compare class names like the moc-generated qt_metacast does. * !ObjectBase::qt_metacall: * For method (signal) calls, we need to convert the void* array to a GValue array and emit the signal. See below. * For property reads/writes, we need to convert GValue to/from QVariant. See above. * Ignore anything else. * !ObjectBase::connectNotify: * This is not a moc-generated method, but needs to be overriden so that we know when somebody has connected to a GObject signal. * Connect a generic GClosure on the real signal with a marshaller that converts the GValue array to a void* array and calls QMetaObject::activate. * ''' BIG POSSIBLE ISSUE HERE: I have never seen QObject signals having return values. We need to check if this is supported ''' * Turns out it is supported, but does not blend in very well. The return value is not part of the function signature check + you would have to write something like int r = Q_EMIT foobar(); (or whatever... Q_EMIT is a dummy macro that doesn't do anything at all anyway, but most people think it does magic and probably wouldn't know how to use it in a case like this) === void* <-> GValue === * QObject::qt_metacall uses a void* array that contains pointers to the actual parameters of the signal. Moc normally knows about their types, so it does a reinterpret_cast and writes on them. In our case, we don't know the types, so we have to do some extra work: * For native types (int/long/string/etc...): switch() and convert appropriately. * Strings will always be mangled as QStrings, so the dillema with const char* and QByteArray is gone. * For pointer types: assume void* and convert like the other native types. * For Object/Interface/ParamSpec types: The target will always be a RefPointer, which has constant size because it contains only a SomeObjectClass* member, so we can reinterpret_cast to RefPointer and be done with it. * This requires changing RefPointer to hold a RefCountedObject* instead of a T* because the pointer might be different for interfaces etc due to multiple inheritance * For Boxed types: We need to provide conversion hooks. * For enums: Enums don't necessarily have a constant size. They are required to have the size of an integer type, but it can be anything. Solution: * For generated static bindings, pad all enums to the size of int and use reinterpret_cast to write in them. This will also need static assertions at compile time to make sure that all the enums used in the bindings have the same size. * For enums that are not known at compile time (say an enum in a gst element), we will let QMetaObject present the argument as being a simple int. * For flags: QFlag is just an int, so we can use reinterpret_cast here as well. * For flags that are not known at compile time, do the same as with enums. == Callbacks == * For every function that takes a callback argument, the public bindings interface will need two functions: * A template one that takes a functor as an argument * A non-template one that takes a functor wrapper as an argument. The functor wrapper will have some standard interface for calling the functor using a void* array for the arguments. See QSlotObject in qt5's qobject.h to understand what I have in mind. * The non-template method will internally have a C callback that will take the functor wrapper in its user_data argument, will convert between C types and C++ types and will call the functor. * For dynamic bindings (in case we do that), we can assume that the caller already gives us a functor wrapper as a void*. * Step 1: cast to functor wrapper * Step 2: generate a C callback using libffi and g-i which will in turn call a generic callback marshaller * Step 3: pass the functor and the GIFooInfo (whatever is needed) to the marshaller in its user_data argument. * Step 4: pass the generated C callback down to the C method call. * Now when our callback is called, the generic callback marshaller will be invoked and all that it will need to do is to convert from C types (given in a void* array) to C++ types (which will also be given to the functor in a void* array). * This can basically be done the same way it is done for void* <-> GValue conversion, only that there is no GValue. Not sure if enums coming from the C interface can be trusted to have a specific size, though. == Subclassing == One solution might be the following: * Make a subclass that inherits from the wrapper class type that we want to inherit in C, i.e. class MyElement : public QGst::Element * Force the programmer to use some macro or even write some very small piece of code that will implement the _get_type() function * This will internally register the GType using some boilerplate code wrapped up somewhere in QtGLib * This will also need to add the interfaces on the GType, which may be possible to do with some template magic on the list of inherited classes * This will also have to register the Foo_new function that is currently used for the wrapping system * Implement virtual functions by declaring protected Q_SLOTS with a certain name, for example: vfunc_addElement for GstBin's add_element vfunc. * The boilerplate class_init function will use the staticMetaObject to introspect the available slots and for every vfunc_* slot that matches some vfunc in C (use GI to find out what is available) it will generate a C function using libffi that will call some dispatcher which in turn will call the slot using the QMetaObject api. * This adds the convenience that nothing has to be declared as virtual on the C++ side, since slots are virtual by design.