Skip to content

Instantly share code, notes, and snippets.

@cheind
Created December 13, 2011 19:29
Show Gist options
  • Star 5 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save cheind/1473513 to your computer and use it in GitHub Desktop.
Save cheind/1473513 to your computer and use it in GitHub Desktop.
C++ Policy Based Property Implementation
/**
* C++ property implementation
* Christoph Heindl 2011
* christoph.heindl@gmail.com
*/
#pragma once
#include <cheind/properties/property.h>
#include <cheind/properties/policy_optional_value.h>
namespace properties {
/** Decorates basic property for optional value handling. */
template
<
class T,
class StoragePolicy = policies::optional_value<T>
>
class optional_property : public basic_property<T, StoragePolicy> {
public:
/** Default constructor */
optional_property()
{}
/** Construct from policy objects */
optional_property(const StoragePolicy &s)
: basic_property<T, StoragePolicy>(s)
{}
/** Test if property is set */
bool empty() const
{ return storage().empty(); }
/** Reset to uninitialized state */
void clear()
{ return storage().clear(); }
/** Test whether property is set.
*
* \note Use safe-bool idiom instead of this quick-hack
* http://www.artima.com/cppsource/safebool.html
*/
operator bool() const
{ return !empty(); }
};
}
/**
* C++ property implementation
* Christoph Heindl 2011
* christoph.heindl@gmail.com
*/
// MSVC
#pragma once
#include <boost/optional.hpp>
#include <boost/serialization/optional.hpp>
#include <boost/serialization/nvp.hpp>
#include <boost/serialization/access.hpp>
namespace properties {
namespace policies {
/** Read/write/empty policy with automatic data storage */
template<class T>
class optional_value {
public:
/** Default construct */
optional_value()
{}
/** Construct with initial property value */
optional_value(const T &v)
: _data(v)
{}
/** Getter */
T get() const
{ return *_data; }
/** Setter */
void set(const T &v)
{ _data = v; }
/** Test if value is set */
bool empty() const
{ return !_data; }
/** Reset to empty state */
void clear()
{ _data.reset(); }
private:
friend class boost::serialization::access;
template<class Archive>
void serialize(Archive & ar, unsigned int file_version)
{
ar & boost::serialization::make_nvp("value", _data);
}
boost::optional<T> _data;
};
}
}
/**
* 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"); }
get_fnc_type _get;
set_fnc_type _set;
};
}
}
/**
* C++ property implementation
* Christoph Heindl 2011
* christoph.heindl@gmail.com
*/
// MSVC
#pragma once
#include <boost/utility.hpp>
namespace properties {
namespace policies {
/** Read/write policy through target reference */
template<class T>
class reference {
public:
/** Default construct */
reference()
{}
/** Construct from value */
reference(T &v)
: _data(boost::addressof(v))
{}
/** Getter */
T get() const
{ return *_data; }
/** Setter */
void set(const T &v)
{ *_data = v; }
/** Set the referenced object */
void bind_reference(T &v)
{ _data = boost::addressof(v); }
private:
T *_data;
};
}
}
/**
* 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;
};
}
}
/**
* C++ property implementation
* Christoph Heindl 2011
* christoph.heindl@gmail.com
*
* No warranty of any kind
*/
#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;
};
}
/**
* C++ property implementation
* Christoph Heindl 2011
* christoph.heindl@gmail.com
*/
#pragma once
#include <fundament/property.h>
#include <boost/bind.hpp>
#pragma warning(push)
#pragma warning(disable : 4275 4251)
#include <boost/program_options/value_semantic.hpp>
#pragma warning(pop)
// direct binders for boost program options
namespace boost {
namespace program_options {
template<class T, class S>
typed_value<T>*
value_from_property(::properties::basic_property<T, S> &p)
{
return value<T>()->notifier(boost::bind(&::properties::basic_property<T, S>::set, &p, _1));
}
template<class T, class S>
typed_value<T, wchar_t>*
wvalue_from_property(::properties::basic_property<T, S> &p)
{
return wvalue<T>()->notifier(boost::bind(&::properties::basic_property<T, S>::set, &p, _1));
}
}
}
/**
* C++ property implementation
* Christoph Heindl 2011
* christoph.heindl@gmail.com
*
* No warranty of any kind
*/
#include <boost/test/unit_test.hpp>
#include <boost/bind.hpp>
#include <cheind/properties/property.h>
#include <cheind/properties/optional_property.h>
#include <cheind/properties/policy_value.h>
#include <cheind/properties/policy_proxy.h>
#include <cheind/properties/policy_reference.h>
#include <cheind/properties/property_po.h>
#pragma warning(push)
#pragma warning(disable : 4275 4251)
#include <boost/program_options.hpp>
#include <boost/archive/xml_oarchive.hpp>
#include <boost/archive/xml_iarchive.hpp>
#pragma warning(pop)
#include <fstream>
#include <boost/serialization/nvp.hpp>
BOOST_AUTO_TEST_SUITE(cheind_test_properties)
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());
}
/** 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());
}
struct size {
int width;
int height;
};
class camera2 {
public:
p::basic_property<int, p::policies::reference<int> > width;
p::basic_property<int, p::policies::reference<int> > height;
camera2()
: width(_s.width), height(_s.height)
{}
camera2(const camera2 &o)
: _s(o._s)
{
update_references();
}
camera2 &operator=(const camera2 &rhs)
{
_s = rhs._s;
update_references();
}
private:
void update_references() {
width.storage().bind_reference(_s.width);
height.storage().bind_reference(_s.height);
}
size _s;
};
BOOST_AUTO_TEST_CASE(reference_properties)
{
camera2 c;
c.width(320);
c.height(200);
BOOST_REQUIRE_EQUAL(320, c.width());
BOOST_REQUIRE_EQUAL(200, c.height());
camera2 c2(c);
}
BOOST_AUTO_TEST_CASE(program_opts)
{
namespace po = boost::program_options;
char *av[] = {"app.exe", "--width", "100", "--height", "100"};
int ac = 5;
camera o;
po::options_description opts_desc("Options");
opts_desc.add_options()
("width", po::value_from_property(o.width), "Set width.")
("height", po::value_from_property(o.height), "Set height.");
boost::program_options::variables_map vm;
po::store(po::command_line_parser(ac, av).options(opts_desc).run(), vm);
po::notify(vm);
BOOST_REQUIRE_EQUAL(100, o.width());
BOOST_REQUIRE_EQUAL(100, o.height());
}
class camera3 {
public:
p::optional_property<int> fps;
};
BOOST_AUTO_TEST_CASE(optional_properties)
{
camera3 c;
BOOST_REQUIRE(c.fps.empty());
c.fps(30);
BOOST_REQUIRE(!c.fps.empty());
BOOST_REQUIRE_EQUAL(30, c.fps());
c.fps.clear();
BOOST_REQUIRE(c.fps.empty());
}
class camera4 {
public:
p::basic_property<int> width;
p::basic_property<int> height;
p::basic_property<std::string> name;
p::optional_property<int> fps;
template<class Archive>
void serialize(Archive &ar, unsigned int fileversion)
{
ar & BOOST_SERIALIZATION_NVP(width);
ar & BOOST_SERIALIZATION_NVP(height);
ar & BOOST_SERIALIZATION_NVP(name);
ar & BOOST_SERIALIZATION_NVP(fps);
}
};
BOOST_AUTO_TEST_CASE(serialize)
{
camera4 c;
c.width(640);
c.height(480);
std::ofstream ofs("camera.xml", std::ios::out);
{
boost::archive::xml_oarchive oa(ofs);
oa << boost::serialization::make_nvp("camera", c);
}
ofs.close();
camera4 c2;
std::ifstream ifs("camera.xml", std::ios::in);
{
boost::archive::xml_iarchive ia(ifs);
ia >> boost::serialization::make_nvp("camera", c2);
}
ifs.close();
BOOST_REQUIRE_EQUAL(640, c2.width());
BOOST_REQUIRE_EQUAL(480, c2.height());
}
BOOST_AUTO_TEST_SUITE_END()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment