Est. 2011

Property Bindings and Declarative Syntax in C++

QtQuick and QML form a really nice language to develop user interfaces. The QML Bindings are very productive and convenient. The declarative syntax is really a pleasure to work with.
Would it be possible to do the same in C++? In this blog post, I will show a working implementation of property bindings in pure C++.

Disclaimer: This was done for the fun of it and is not made for production.

If you read this article from the RSS, you may want to open it in its original URL to see property formatted code.

Bindings

The goal of bindings is to have one property which depends on other properties. When its dependencies are changed, the property is automatically updated.

Here is an example inspired from the QML documentation.

int calculateArea(int width, int height) {
  return (width * height) * 0.5;
}

struct rectangle {
  property<rectangle*> parent = nullptr;
  property<int> width = 150;
  property<int> height = 75;
  property<int> area = [&]{ return calculateArea(width, height); };

  property<std::string> color = [&]{
    if (parent() && area > parent()->area)
      return std::string("blue");
    else
      return std::string("red");
  };
};

If you are not familiar with the [&]{ ... } syntax, this is a lambda function. I'm also using the fact that in C++11, you can initialize the members directly in the declaration.

Now, we'll see how this property class works. At the end I will show a cool demo of what you can do.

The code is using lots of C++11 constructs. It has been tested with GCC 4.7 and Clang 3.2.

Property

I have used my knowledge from QML and the QObject system to build something similar with C++ bindings.
The goal is to make a proof of concept. It is not optimized. I just wanted to have comprehensible code for this demo.

The idea behind the property class is the same as in QML. Each property keeps a list of its dependencies. When a binding is evaluated, all access to the property will be recorded as dependencies.

property<T> is a template class. The common part is put in a base class: property_base.

class property_base
{
  /* Set of properties which are subscribed to this one.
     When this property is changed, subscriptions are refreshed */
  std::unordered_set<property_base *> subscribers;

  /* Set of properties this property is depending on. */
  std::unordered_set<property_base *> dependencies;

public:
  virtual ~property_base()
  { clearSubscribers(); clearDependencies(); }

  // re-evaluate this property
  virtual void evaluate() = 0;
   
  // [...]
protected:
  /* This function is called by the derived class when the property has changed
     The default implementation re-evaluates all the property subscribed to this one. */
  virtual void notify() {
    auto copy = subscribers;
    for (property_base *p : copy) {
      p->evaluate();
    }
  }

  /* Derived class call this function whenever this property is accessed.
     It register the dependencies. */
  void accessed() {
    if (current && current != this) {
      subscribers.insert(current);
      current->dependencies.insert(this);
    }
  }

  void clearSubscribers() {
      for (property_base *p : subscribers)
          p->dependencies.erase(this);
      subscribers.clear();
  }
  void clearDependencies() {
      for (property_base *p : dependencies)
          p->subscribers.erase(this);
      dependencies.clear();
  }

  /* Helper class that is used on the stack to set the current property being evaluated */
  struct evaluation_scope {
    evaluation_scope(property_base *prop) : previous(current) {
      current = prop;
    }
    ~evaluation_scope() { current = previous; }
    property_base *previous;
  };
private:
  friend struct evaluation_scope;
  /* thread_local */ static property_base *current;
};

Then we have the implementation of the class property.

template <typename T>
struct property : property_base {
  typedef std::function<T()> binding_t;

  property() = default;
  property(const T &t) : value(t) {}
  property(const binding_t &b) : binding(b) { evaluate(); }

  void operator=(const T &t) {
      value = t;
      clearDependencies();
      notify();
  }
  void operator=(const binding_t &b) {
      binding = b;
      evaluate();
  }

  const T &get() const {
    const_cast<property*>(this)->accessed();
    return value;
  }

  //automatic conversions
  const T &operator()() const { return get();  }
  operator const T&() const { return get(); }

  void evaluate() override {
    if (binding) {
      clearDependencies();
      evaluation_scope scope(this);
      value = binding();
    }
    notify();
  }

protected:
  T value;
  binding_t binding;
};

property_hook

It is also desirable to be notified when a property is changed, so we can for example call update(). The property_hook class lets you specify a function which will be called when the property changes.

Qt bindings

Now that we have the property class, we can build everything on top of that. We could build for example a set of widgets and use those. I'm going to use Qt Widgets for that. If the QtQuick elements had a C++ API, I could have used those instead.

The property_qobject

I introduce a property_qobject which is basically wrapping a property in a QObject. You initialize it by passing a pointer to the QObject and the string of the property you want to track, and voilà.

The implementation is not efficient and it could be optimized by sharing the QObject rather than having one for each property. With Qt5 I could also connect to lambda instead of doing this hack, but I used Qt 4.8 here.

Wrappers

Then I create a wrapper around each class I'm going to use that expose the properties in a property_qobject

A Demo

Now let's see what we are capable of doing:

This small demo just has a line edit which lets you specify a color and few sliders to change the rotation and the opacity of a graphics item.

Let the code speak for itself.

We need a Rectangle object with the proper bindings:

struct GraphicsRectObject : QGraphicsWidget {
  // bind the QObject properties.
  property_qobject<QRectF> geometry { this, "geometry" };
  property_qobject<qreal> opacity { this, "opacity" };
  property_qobject<qreal> rotation { this, "rotation" };

  // add a color property, with a hook to update when it changes
  property_hook<QColor> color { [this]{ this->update(); } };
private:
  void paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget*) override {
    painter->setBrush(color());
    painter->drawRect(boundingRect());
  }
};

Then we can proceed and declare a window object with all the subwidgets:

struct MyWindow : Widget {
  LineEdit colorEdit {this};

  Slider rotationSlider {Qt::Horizontal, this};
  Slider opacitySlider {Qt::Horizontal, this};

  QGraphicsScene scene;
  GraphicsView view {&scene, this};
  GraphicsRectObject rectangle;

  ::property<int> margin {10};

  MyWindow() {
    // Layout the items.  Not really as good as real layouts, but it demonstrates bindings
    colorEdit.geometry = [&]{ return QRect(margin, margin,
                                             geometry().width() - 2*margin,
                                             colorEdit.sizeHint().height()); };
    rotationSlider.geometry = [&]{ return QRect(margin,
                                                  colorEdit.geometry().bottom() + margin,
                                                  geometry().width() - 2*margin,
                                                  rotationSlider.sizeHint().height()); };
    opacitySlider.geometry = [&]{ return QRect(margin,
                                                 rotationSlider.geometry().bottom() + margin,
                                                 geometry().width() - 2*margin,
                                                 opacitySlider.sizeHint().height()); };
    view.geometry = [&]{
        int x = opacitySlider.geometry().bottom() + margin;
        return QRect(margin, x, width() - 2*margin, geometry().height() - x - margin); 
    };

    // Some proper default value
    colorEdit.text = QString("blue");
    rotationSlider.minimum = -180;
    rotationSlider.maximum = 180;
    opacitySlider.minimum = 0;
    opacitySlider.maximum = 100;
    opacitySlider.value = 100;

    scene.addItem(&rectangle);

    // now the 'cool' bindings
    rectangle.color = [&]{ return QColor(colorEdit.text);  };
    rectangle.opacity = [&]{ return qreal(opacitySlider.value/100.); };
    rectangle.rotation = [&]{ return rotationSlider.value(); };
  }
};

int main(int argc, char **argv)
{
    QApplication app(argc,argv);
    MyWindow window;
    window.show();
    return app.exec();
}

Conclusion

You can clone the code repository and try it for yourself.

Perhaps one day, a library will provide such property bindings.

Woboq is a software company that specializes in development and consulting around Qt and C++. Hire us!

If you like this blog and want to read similar articles, consider subscribing via our RSS feed (Via Google Feedburner, Privacy Policy), by e-mail (Via Google Feedburner, Privacy Policy) or follow us on twitter or add us on G+.

Submit on reddit Submit on reddit Tweet about it Share on Facebook Post on Google+

Article posted by Olivier Goffart on 28 February 2013

Load Comments...
Loading comments embeds an external widget from disqus.com.
Check disqus privacy policy for more information.
Get notified when we post a new interesting article!

Click to subscribe via RSS or e-mail on Google Feedburner. (external service).

Click for the privacy policy of Google Feedburner.
© 2011-2023 Woboq GmbH
Google Analytics Tracking Opt-Out