Created
December 13, 2011 19:29
-
-
Save cheind/1473513 to your computer and use it in GitHub Desktop.
C++ Policy Based Property Implementation
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* 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(); } | |
}; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* 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; | |
}; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* 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; | |
}; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* 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; | |
}; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* 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; | |
}; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* 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; | |
}; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* 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)); | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* 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