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

Charley Bay charleyb123 at gmail.com
Wed Jan 30 19:23:32 CET 2013


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

----------
Design goals:

 (a)- Expose individual C++ objects with minimal code

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

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

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

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

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

 (4) Is there interest in "formalizing" this for wider conventional use by
the Qt community?  (We are currently intending on using this for our code
base, but the adapter-layer itself is not specific to our domain.)

Thanks!

--charley
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.qt-project.org/pipermail/development/attachments/20130130/8b8fd2d8/attachment.html>


More information about the Development mailing list