A Policy-Based Property Implementation in C++

Having read a proof-of-concept C++ Property implementation by Lois Goldthwaite as published in ACCU’s Overload journal #65, I decided to evaluate the usefulness of such an approach for various projects I’m currently involved in.

I started to fiddle around with the code provided and ended up with a more general policy based design for property classes. In the end however, I decided not to propose the following property library to my team-mates as it would take much more effort to create an industrial strength implementation.

Basically, my approach differs from the Goldthwaite’s in way different property behaviors are handled. While Goldthwaite suggests implementing different behaviours by different property classes, I decided to go with a single property class, basic_property that offers orthogonal policies to configure its behavior.

One such behavior is data storage. The data storage policy defines how the property data is stored. Among others, the following manifestations are conceivable

  • policies::value Keeps a copy of the property value type internally.
  • policies::optional_value Keeps a copy of the property value type internally and might be set to an uninitialized state.
  • policies::proxy Keeps pointers to getter and setter functions to modify and query the value of the property.
  • policies::reference Keeps a reference to an external value.

Besides data storage, orthogonal policies define the way user-defined hooks (pre-set and post-get) are handled, or the way transactions are implemented (commit and rollback).

For the remainder of this post, we will focus on the data storage policy.

As stated earlier, basic_property is a generic read/write property implementation which takes its value type and policies as template parameters. Implementation is given below

/** 
  * property.h
  * C++ property implementation
  * Christoph Heindl 2011
  * christoph.heindl@gmail.com
  */

#pragma once

#include <cheind/properties/policy_value.h>
#include <boost/serialization/nvp.hpp>
#include <boost/serialization/access.hpp>

namespace properties {

  /** Read/write policy based property */
  template
  <
    class T, 
    class StoragePolicy = policies::value<T>
  >
  class basic_property {
  public:
    /** Default constructor */
    basic_property()
    {}

    /** Construct from policy objects */
    basic_property(const StoragePolicy &s)
      :_s(s)
    {}

    /** Getter */
    T get() const 
    { return _s.get(); }

    /** Setter */
    void set(const T &v) 
    { _s.set(v); }

    /** Getter */
    T operator()() const 
    { return get(); }

    /** Setter */
    void operator()(const T &v) 
    { set(v); }

    /** Assignment */
    T operator=(const T &v) 
    { set(v); }

    /** Access to storage policy */
    StoragePolicy &storage()
    { return _s; }

    /** Access to storage policy */
    const StoragePolicy &storage() const
    { return _s; }
  
  private:
    friend class boost::serialization::access;
    
    template<class Archive>
    void serialize(Archive & ar, unsigned int file_version)
    {
      ar & boost::serialization::make_nvp("storage", _s);
    }

    StoragePolicy _s;
  };
}

basic_property simply forwards getter and setter operations to its underlying storage policy object. Additionally, basic_property supports serialization through Boost.Serialization.

Here’s the code for the policies::value policy


/** 
  * policy_value.h
  * C++ property implementation
  * Christoph Heindl 2011
  * christoph.heindl@gmail.com
  */

// MSVC
#pragma once

#include <boost/serialization/nvp.hpp>
#include <boost/serialization/access.hpp>

namespace properties {

  namespace policies {

    /** Read/write policy with automatic data storage. */
    template<class T>
    class value {
    public:
      /** Default construct */
      value()
      {}

      /** Construct with initial property value */
      value(const T &v) 
      : _data(v) 
      {}

      /** Getter */
      T get() const 
      { return _data; }

      /** Setter */
      void set(const T &v) 
      { _data = v; }

    private:
      friend class boost::serialization::access;

      template<class Archive>
      void serialize(Archive & ar, unsigned int file_version)
      {
        ar & boost::serialization::make_nvp("value", _data);
      }

      T _data;
    };

  }
}

Pretty straight forward.

Usage is as follows

namespace p = properties;

class camera {
public:
  /* The following properties have automatic storage */
  p::basic_property<int> width;
  p::basic_property<int> height;
  p::basic_property<std::string> name;

  camera()
    : width(320), height(200), name(std::string("camera"))
  {}

};

BOOST_AUTO_TEST_CASE(scalar_properties)
{
  camera c;

  // Method oriented get
  BOOST_REQUIRE_EQUAL(320, c.width());
  BOOST_REQUIRE_EQUAL(200, c.height());
  BOOST_REQUIRE_EQUAL(std::string("camera"), c.name());

  // Traditional get
  BOOST_REQUIRE_EQUAL(320, c.width.get());
  BOOST_REQUIRE_EQUAL(200, c.height.get());
  BOOST_REQUIRE_EQUAL(std::string("camera"), c.name.get());

  // Set
  c.width(640);
  c.height(480);
  BOOST_REQUIRE_EQUAL(640, c.width());
  BOOST_REQUIRE_EQUAL(480, c.height());

  // Copy construction
  camera c2(c);
  BOOST_REQUIRE_EQUAL(640, c2.width());
  BOOST_REQUIRE_EQUAL(480, c2.height());
}

Next, the policies::proxy policy is outlined

/** 
  * C++ property implementation
  * Christoph Heindl 2011
  * christoph.heindl@gmail.com
  */

#pragma once

#include <boost/function.hpp>
#include <stdexcept>

namespace properties {

  namespace policies {

    /** Storage policy refering to external getter/setter implementations. */
    template<class T>
    class proxy {
    public:
      /** Prototype of getter function */
      typedef boost::function< T(void) > get_fnc_type;
      /** Prototype of setter function */
      typedef boost::function< void(const T &) > set_fnc_type;

      /** Default constructor */
      proxy()
        : _get(&proxy<T>::default_get),
          _set(&proxy<T>::default_set)
      {}

      /** Construct from bindings */
      proxy(const get_fnc_type &get_fnc, const set_fnc_type &set_fnc)
        : _get(get_fnc),
          _set(set_fnc)
      {}

      /** Bind getter to proxy */
      void bind_get(const get_fnc_type &f)
      { _get = f; }

      /** Bind setter to proxy */
      void bind_set(const set_fnc_type &f)
      { _set = f; }

      /** Getter */
      T get() const 
      { return _get(); }

      /** Setter */
      void set(const T &v) 
      { _set(v); }

    private:
      static T default_get() { throw std::runtime_error("Proxy getter not bound"); }
      static void default_set(const T &) { throw std::runtime_error("Proxy setter not bound"); }

      // not serializable

      get_fnc_type _get;
      set_fnc_type _set;
    };

  }
}

This policy delegates the getter and setter operation to two bound Boost.Function objects. Here’s a use case for the proxy policy.

namespace p = properties;

/** Camera driver, not modifyable, provided by third-party */
class device_driver {
public:
  void set_target_fps(int fps) { _fps = fps; }
  int get_current_fps() const { return _fps; }
private:
  int _fps;
};

class fps_camera : public camera {
public:

  /** Target FPS */
  p::basic_property<int, p::policies::proxy<int> > fps;
  
  fps_camera()
  {
    update_bindings();
  }

  fps_camera(const fps_camera &other)
    : _d(other._d)
  {
    update_bindings();
  }

  fps_camera &operator=(const fps_camera &rhs)
  {
    _d = rhs._d;
    update_bindings();
  }

private:

  void update_bindings() {
    fps.storage().bind_get(boost::bind(&device_driver::get_current_fps, &_d));
    fps.storage().bind_set(boost::bind(&device_driver::set_target_fps, &_d, _1));
  }

  device_driver _d;
};

BOOST_AUTO_TEST_CASE(proxy_properties)
{
  fps_camera c;
  
  // Inherited properties
  c.width(640);
  c.height(480);
  BOOST_REQUIRE_EQUAL(640, c.width());
  BOOST_REQUIRE_EQUAL(480, c.height());

  // Proxy
  c.fps(30);
  BOOST_REQUIRE_EQUAL(30, c.fps());

  fps_camera c2 = c;
  BOOST_REQUIRE_EQUAL(30, c2.fps());
  c2.fps(10);
  BOOST_REQUIRE_EQUAL(10, c2.fps());
  BOOST_REQUIRE_EQUAL(30, c.fps());
}

Note the flexibility of the approach, but also note the tedious work of rebinding to the correct instance of the device_driver.

If you find the above useful and would like to dig deeper into the code, go ahead and browse the entire code at Gist.

About these ads

3 thoughts on “A Policy-Based Property Implementation in C++

  1. I actually Feel blog post, “A Policy-Based Property
    Implementation in C++ Christoph Heindl” was in fact perfect!
    I personallycan’t agree along with you more! At last looks like I actuallydiscovered a blog site very well worth checking out. Thanks for your time, Suzanne

  2. Exactly what really stimulated u to post “A Policy-Based Property
    Implementation in C++ | Christoph Heindl” hwor ? Ireally liked the blog post!
    Thanks ,Joy

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s