Libpropc++�Properties in C++

Friday Sep 24th 2004 by Victor Porton
Share:

Learn about a template library for properties (used like 'widget.color = "red";') in C++. Supports "binding" several properties together to have their values synchronized automatically. Useful for GUI development and so forth.

Properties are pseudo-variables getting/setting which causes side-effects. For example, assignment to a color property of Widget class would cause the change of the color of a widget onscreen:

widget.color = "red";

Libpropc++ is the C++ library that implements properties in Standard C++.

The most interesting feature of libpropc++ is that it supports binding properties—that is, auto-synchronizing values of properties—so that when the value of one property changes, values of the other automatically changes accordingly.

Every time you write a GUI application, you in fact natively use binding properties: You need to synchronize e.g. the checked state of a checkbox and sensitivity or visibility of another conrol. You need to synchronize application preferences with the state of the UI and so forth.

With libpropc++, you can do the same more quickly, with fewer errors, more easily, and more maintainably. The most time spent while developing a GUI application is writing callbacks used to synchronize the values of several objects' properties. The most common cause of errors in GUI applications is incorrect synchronization of properties that should be synchronized. Save your time by using a ready, already debugged solution instead of repeated coding that's the same for every function.

Defining Properties in libpropc++

This example illustrates how to define properties in libpropc++:

#include <propc++/property.h>

class Widget
{
   // ...
   Widget() : color(*this) { }
   // ...
   Color get_color() const;
   void set_color(Color color);
   // ...
   prop::Property<Widget, Color, &Widget::set_color,
                  &Widget::get_color> color;
   // ...
};

After this, the property can be used just like a normal variable: assignment to it calls set_color() and reading its value calls get_color().

However, the above approach has the deficiency that a property unnecessarily holds a pointer to the object (to a widget, in our case), and so takes 4 bytes (for every object) on a 32-bit system.

As such, libpropc++ provides an alternative way to define properties that does not use any extra memory, so called simple properties:

#include <propc++/property_simple.h>

class Widget
{
   // ...
   Widget() : color(*this) { }
   // ...
   Color get_color() const;
   void set_color(Color color);
   // ...
   prop::simple::Property<Widget, Color> color() {
     return prop::property(*this, set_color, get_color);
   }
   // ...
};

Then, widget.color() = "red"; would mean the same as widget.set_color("red");.

Properties are classified to as read-write, read-only, and write-only properties. Read-write properties were demonstrated above; for read-only properties, use ReadProperty (with only a get_*() function) instead of Property and for write-only properties, use WriteProperty (with only a set_*() function).

Note that get_*() functions are called getter or read accessor and set_*() functions are called setter, write accessor, or modifier.

Binding Properties

Binding properties together is the most powerful feature of libpropc++. (Also see this article about binding properties in general and implementation strategies.)

Binding properties means that the user directs the library to automatically keep values of several properties synchronized with each other; that is, when one of the bound properties changes, the other automatically changes accordingly.

A typical example would be:

#include <propc++/bindable.h>

// ...
checkbox.checked.bind(frame.visible);
// ...

The above could be used to make a frame visible only when a checkbox is checked. The above statement is a warranty that the frame will be visible when and only when the checkbox is checked. Only one line of code, instead of about 6–7 or more lines for this as in most GUI toolkits! All automatically and no place for an error!

A common source of errors in GUI applications is that when a programmer tries to bind together two properties, A and B; he forgots the initial assignment B = A;. The above one-line example saves you from this error.

Well, if you want to make this error, you can:

checkbox.checked.bind_unsafe(frame.visible);

This binds the properties together but does not accomplish the initial assignment of frame.visible = checkbox.checked; before binding. Use bind_unsafe() at your own risk.

Sometimes, you need to bind two checkboxes (or more often a checkbox and a check menu item) to be checked/unchecked simulateously (when one is checked, the other should be checked also and vice versa). For this, use two-way or mutual binding:

prop::mutual::bind(checkbox1.checked, checkbox2.checked);

Want one to be checked when one is unchecked and vice versa? Use binding with the transforming functor:

prop::mutual::bind(checkbox1.checked, checkbox2.checked,
                   std::negate(), std::negate());

std::negate (defined in <functional> header) is an STL functor that negates a boolean value.

This can be simplified to:

prop::mutual::bind_negated(checkbox1.checked, checkbox2.checked);

For a more sophisticated example:

prop::mutual::bind(meters_entry.number, kilometers_entry.number,
                   std::bind2nd(std::multiply(), 1000),
                   std::bind2nd(std::divide  (), 1000));

This would bind values of meters and kilometers spin entries with transformation of the value by functors (multiply/divide by 1000). You provide two functors that are the inverse of each other.

By using transforming functors, you also can bind properties of different types. If the types are convertible to each other, you can omit transforming functors and the values will be converted to each other by using C++ type conversions.

Signalling

Signal programming (otherwise known as an observer pattern) is sending synchronous signals on some events (such as property value changes). So-called listeners register to hear a signal. One signal can be listened to by several listeners.

You can read more about signal programming in the documentation of the libsigc++ library.

Libpropc++ can be used either with or without libsigc++. (It depends simply on the include path settings of your compiler.)

Libpropc++ provides a partial re-implementation of libsigc++. Use this re-implementation if you want to use libpropc++ to write commercial software because the license of libsigc++ requires any software using libsigc++ to be open source.

Notes on Compilation and Bug Reporting

Libpropc++ is written in Standard C++ and should work with any comforming compiler.

Current status of compilers support:

GCC/G++
<3.0 unsupported, >=3.0 supported.

Please test libpropc++ with other compilers and report whether it compiles and any error/warning messages.

Report any bugs to Victor Porton, porton@ex-code.com.

Installation

Libpropc++ currently consists only of header files and needs no special installation. Just unpack it to the appropriate directory and point your compiler include path to it.

Licensing and Ordering

Libpropc++ has dual licensing:

LGPL 2.1
You are allowed to use libpropc++ only for open source software (because of LGPL issues related with templates).
Commercial License
Can be used to develop commercial applications.

Order/download libpropc++ at http://ex-code.com/propcpp/.

If you want to integrate libpropc++ into your own C++ library, contact Extreme Code.

References

  1. Binding Together Properties of Objects. Victor Porton. 2004.
  2. Online documentation of libpropc++.
  3. Online documentation of libsigc++.
Share:
Home
Mobile Site | Full Site
Copyright 2017 © QuinStreet Inc. All Rights Reserved