[Development] Design review: Adapter layer, C++ to QML

Alan Alpert 416365416c at gmail.com
Wed Jan 30 20:23:50 CET 2013


On Wed, Jan 30, 2013 at 10:23 AM, Charley Bay <charleyb123 at gmail.com> wrote:
> I've implemented a C++ "adapter-layer" (mostly template-based) to expose C++
> objects to QML.
>
> We are in the "early-stages" for its use, and (of course) the "final-API"
> will significantly impact how we expose our (domain-specific) C++ classes to
> QML.
>
> My manager suggested I ask others what they are doing to solve similar
> problems (C++/QML adapter layer).  IMHO, this could reasonably be a
> "standardized" adapter-API for generalized use in applications with rich C++
> classes that must be exposed to QML (that's us).

Sounds like this is for developers using Qt, not working on Qt.
qt-interest is the correct ML for that (although given the youthful
state of QML, this could also suggest improvements to QML later. But
we can worry about that later).

>
> ----------
> Design goals:
>
>  (a)- Expose individual C++ objects with minimal code

There are other related goals that could be involved in the C++/QML
adaption layer. The last generator script I wrote for this (
http://alan.imagin-itis.net/?p=166 ) was about non-Qt C++ code (with a
GUI focus), but I assume you're talking about exposing QObject
subclasses in a Qt project from the start.

>  (b)- Individual "QML-instances" can "contain" a "C++ back-end-instance"
> that is not shared with other QML instances.

Is this just creating a subclass with Q_PROPERTY macros?

>  (c)- Multiple "QML-instances" can "share" access to the same C++ back-end
> instance, with coordinated updates (e.g., as one QML-instance triggers a C++
> object "property-value-change", all QML instances are "notified" of that
> change)

Perhaps you should just be using the new QML Singleton type, used like so:

MyType.myProp = foo; //Engine creates an instance of the C++ type
first time MyType is used, all references use that instance.

>  (d)- Enable nested QML-wrapped-object-access to nested C++ instances that
> are natively stored (e.g., no refactoring of C++ code to incrementally
> expose nested C++ objects to QML)

I don't understand what you're saying here. Do you mean that adding a
QML adaption layer requires no changes to the backing C++ class?

>  (e)- Expose "sets" of C++ instances to QML (e.g., through
> "QAbstractListModel") with add/remove notifications to QML
>
> ----------
> Conceptual design:
>
> Each "C++ instance" is "wrapped" by a "singleton-QmlBoxBack" instance with
> application-specific properties exposed.  Many "QmlBox" instances will
> "reference" that single "QmlBoxBack" instance for "ultimate-state", and the
> "QmlBoxBack" will maintain the "registered-set" of "QmlBox" instances, so
> that when one "QmlBox" changes a property-value, all "QmlBox" instances are
> "notified" of the change.  Similarly, the "QmlBoxBack" instance is
> charged-with explicit notification of all "QmlBox" instances when the
> back-end C++ model changes asynchronously.  (We are using this for
> monitoring back-end instrumentation.)
>
> For logistical reasons, a "QmlBoxBacksManager<>" is needed to manage the
> (registered) "QmlBoxBack" instances, to preserve the "singleton" nature of
> the "QmlBoxBack" for a give C++ instance.
>
> ----------
> High level design is:
>
>  (1) The following C++ key abstractions are implemented:
>
>     - QmlBox (derives from QObject)
>     - QmlBoxBack (derives from nothing)
>     - QmlBoxBacksManager<> (template base class)
>
>  (2) Application-specific type definition:
>
>     - Implement your C++ class (e.g., "MyDog")
>     - Derive "MyQmlDogBox" from "QmlBox" to expose type-specific properties
>     - Derive "MyQmlDogBoxBack" from "QmlBoxBack" to propagate type-specific
> properties
>     - Use macro to define "MyQmlBoxBacksManagerDog" (which parameterizes
> "QmlBoxBacksManager<>")
>
>  (3) Application-specific use:
>
>     - QML can instantiate instances, or reference instances on the C++ side
>     - QML is "notified" when items are added/removed from the C++ side,
> which triggers instantiation of QML instances (e.g., through
> "QAbstractListModel" semantics)
>
>  (4) Internal details:
>
>     - Each C++ instance is "wrapped-by" a SINGLE "QmlBoxBack" instance,
> which "owns" or "references" that C++ instance (e.g., like a
> "smart-pointer")
>     - Many "QmlBox" instances are "registered" within a given "QmlBoxBack";
> when property values "change", all "QmlBox" instances are "notified" of that
> property-change
>     - All "QmlBoxBack" instances for a given "type" are "registered" with
> the "QmlBoxBacksManager<>" for that type, which deletes the "QmlBoxBack"
> instances when the C++ object is "destroyed", and un-registeres "QmlBox"
> instances as-needed.
>
>   SUMMARY:
>     - One "QmlBoxBack" for each C++ instance
>     - Many "QmlBox" instances "reference" a given "QmlBoxBack"
>
>  (5) Practical use:
>
>     - ...where "myInstrument", "laser", and "wavelength" are implemented
> through application-specific C++ types:
>
>         Item {
>            color:  myInstrument.laser.wavelength.color
>         }
>
>     - ...where a "list-model" exists to expose many instances of the
> application-specific type "detector", which has properties "channel" and
> "filter":
>
>         ListView {
>           model:  myInstrument.detectors
>           delegate: Text { text: "(" + channel + ")" + filter.text }
>         }
>
> ----------
>
> QUESTIONS:
>
>  (1) Are you doing "heavy-duty-wrapping" of rich C++ models, without
> refactoring the C++ code?  What was the design, and did it work for you?
>
>  (2) Have you a critique for the design listed above (how to improve it, if
> it seems a reasonable approach)?

I'm not sure why you needed the multiple QML instances <-> single C++
instance thing in the first place. Even in Qt 4, you can have a single
C++ instance that's exposed but as an uncreatable type. Just put that
in your root context as myInstrument and you're good to go.

The idea of conveniently exposing sets of objects with
QAbstractListModel like behaviour is interesting, but I don't think it
should be mixed up with the exposing single instances question. We
already had a thread on improving QAbstractListModel's utility in QML,
which I think is a more promising approach.

>  (3) Have I missed similar approaches that already exist, which we could
> otherwise use directly?

Yeah, you might just want to use qmlRegisterSingletonType (new in Qt 5).

--
Alan Alpert



More information about the Development mailing list