Another implementation of properties.

Hello. I've been trying to implement properties for c++0x, for now. I've come up with this solution for now. You need g++ svn to test it. It's a partial implementation, since not every operator is overloaded. Space overhead: - one pointer to the object per property instance. - the pointer to the member functions is shared among every instance of the class, so it's a per-class and not per-instance overhead, which is negligible. Usage: class Person { int getAge() const { .... }; void setAge(int age) { ... }; PROPS_RWPROPERTY(Age); Person() : Age(this) {} }; You can: - declare getters/setters const or nonconst. - getters must have one parameter. - setters must return void. - it has some operators overloaded, but the idea is to overload all of them if the implementation is all right. - the property returns whatever you put in your getter and gets as a parameter what you put in the setter. If you declare the getters and setters to be VIRTUAL, you get virtual behaviour, so you can use virtual properties, even with pure virtual functions, and it will work. Caveats: - Needs c++0x compiler for now. - Not enforced the read-only access for properties for now. I need extended friend declarations for that, and the operator= would be made private with friend class. But for now it's not reinforced. I think this implementation is pretty usable as-is. It's just a matter of extending it. I don't use the offset trick because I think it does not work with classes with virtual members, but I'm not sure. The implementation contains Trivial properties as well, which for now, are the same as fields I think. Implementation: #include <functional> #include <type_traits> #include <iostream> #ifndef _PROPS_PROPERTY_HPP_ #define _PROPS_PROPERTY_HPP_ namespace props { template <class GetterAddress, class SetterAddress> class RWProperty; template <class GetterAddress, class SetterAddress> class ROProperty; template <class PropType> class TROProperty; template <class PropType> class TRWProperty; namespace detail { struct unused {}; template <bool cond, class T = void> struct EnableIf { typedef T type; }; template <class T> struct EnableIf<false, T> {}; template <bool cond, class T = void> struct DisableIf {}; template <class T> struct DisableIf<false, T> { typedef T type; }; template <class ObjectType, class Ret, Ret (ObjectType::*Getter)()> struct WrapperGetter { typedef ObjectType object_t; typedef Ret result_type; Ret (ObjectType::*getter)(); WrapperGetter() : getter(Getter) {} }; template <class ObjectType, class Ret, Ret (ObjectType::*Getter)() const> struct WrapperGetterConst { typedef ObjectType object_t; typedef Ret result_type; Ret (ObjectType::*getter)() const; WrapperGetterConst() : getter(Getter) {} }; template <class ObjectType, class Arg, void (ObjectType::*Setter)(Arg)> struct WrapperSetter { typedef ObjectType object_t; typedef void result_type; typedef Arg argument_type; void (ObjectType::*setter)(Arg); WrapperSetter() : setter(Setter) {} }; template <class ObjectType, class Arg, void (ObjectType::*Setter)(Arg) const> struct WrapperSetterConst { typedef ObjectType object_t; typedef void result_type; typedef Arg argument_type; void (ObjectType::*setter)(Arg) const; WrapperSetterConst() : setter(Setter) {} }; template <class T> struct IsProperty : public std::false_type {}; template <class GetterAddress, class SetterAddress> struct IsProperty<RWProperty<GetterAddress, SetterAddress> > : public std::true_type {}; template <class GetterAddress, class SetterAddress> struct IsProperty<ROProperty<GetterAddress, SetterAddress> > : public std::true_type {}; template <class PropType> struct IsProperty<TRWProperty<PropType> > : public std::true_type {}; template <class PropType> struct IsProperty<TROProperty<PropType> > : public std::true_type {}; template <class T> struct IsConstMemberGetter : public std::false_type {}; template <class Ret, class Object> struct IsConstMemberGetter<Ret (Object::*)() const> : public std::true_type {}; template <class T> struct IsConstMemberSetter : public std::false_type {}; template <class ObjectType, class Arg> struct IsConstMemberSetter<void (ObjectType::*)(Arg) const> : public std::true_type {}; template <class ObjectType, class Ret, Ret (ObjectType::*Getter)()> WrapperGetter<ObjectType, Ret, Getter> deduceWrapperForGetter(); template <class ObjectType, class Ret, Ret (ObjectType::*Getter)() const> WrapperGetterConst<ObjectType, Ret, Getter> deduceWrapperForGetter(); template <class ObjectType, class Arg, void (ObjectType::*Setter)(Arg)> WrapperSetter<ObjectType, Arg, Setter> deduceWrapperForSetter(); template <class ObjectType, class Arg, void (ObjectType::*Setter)(Arg) const> WrapperSetterConst<ObjectType, Arg, Setter> deduceWrapperForSetter(); template <class ObjectType, class Arg> Arg deduceSetterArg(void (ObjectType::*Setter)(Arg) const); template <class ObjectType, class Arg> Arg deduceSetterArg(void (ObjectType::*Setter)(Arg)); template <class ObjectType, class PropType> auto deduceGetterRet(PropType (ObjectType::*Getter)() const, const ObjectType * obj = 0) -> typename EnableIf<IsConstMemberGetter<PropType (ObjectType::*)() const>::value, decltype((obj->*Getter)())>::type; template <class ObjectType, class PropType> auto deduceGetterRet(PropType (ObjectType::*Getter)(), ObjectType * obj = 0) -> typename EnableIf<!IsConstMemberGetter<PropType (ObjectType::*)()>::value, decltype((obj->*Getter)()) >::type; } //namespace detail template <class PropType> class TRWProperty { static_assert(!std::is_reference<PropType>::value, "Property types must not be reference types"); PropType value_; public: typedef typename std::remove_cv<PropType>::type value_type; typedef PropType & GetterReturnType; typedef const PropType & ConstGetterReturnType; /************************Constructors*************************************/ ///Default constructor TRWProperty() = default; ///Construct from other property (rvalue) template <class OtherProp> TRWProperty(OtherProp && other, typename detail::EnableIf<detail::IsProperty<OtherProp>::value>::type * = 0) : value_(std::move(other())) {} ///Construct from other value (rvalue) template <class PropVal> TRWProperty(PropVal && val, typename detail::EnableIf<!detail::IsProperty<PropVal>::value>::type * = 0) : value_(std::move(val)) {} ///Construct from other value (lvalue) template <class PropVal> TRWProperty(const PropVal & value, typename detail::EnableIf<!detail::IsProperty<PropVal>::value>::type * = 0) : value_(value) {} ///Construct from other property (lvalue) template <class OtherProp> TRWProperty(const OtherProp & prop, typename detail::EnableIf<detail::IsProperty<OtherProp>::value>::type * = 0) : value_(prop()) {} //***************************Assignment operators*****************************/ ///Assign from other property (rvalue) template <class OtherProp> typename detail::EnableIf<detail::IsProperty<OtherProp>::value, TRWProperty &>::type operator=(OtherProp && other) { value_ = std::move(other()); return *this; } ///Assign from other value (rvalue) template <class PropValue> typename detail::EnableIf<!detail::IsProperty<PropValue>::value, TRWProperty &>::type operator=(PropValue && val) { value_ = std::move(val); return *this; } ///Assign from other property (lvalue) template <class OtherProp> typename detail::EnableIf<detail::IsProperty<OtherProp>::value, TRWProperty &>::type operator=(const OtherProp & other) { value_ = other(); return *this; } ///Assign from other value (lvalue) template <class PropValue> typename detail::EnableIf<!detail::IsProperty<PropValue>::value, TRWProperty &>::type operator=(const PropValue & val) { value_ = val; return *this; } /***************************Obtain value operators*****************************/ ConstGetterReturnType operator()() const { return value_; } GetterReturnType operator()() { return value_; } /****************************Implicit conversions******************************/ operator ConstGetterReturnType() const { return value_; } operator GetterReturnType() { return value_; } /******************************Arithmetic Operators**********************************/ template <class Prop> typename detail::EnableIf<detail::IsProperty<Prop>::value, TRWProperty&>::type operator+=(const Prop & other) { (*this)() += other(); return *this; } template <class PropValue> typename detail::EnableIf<!detail::IsProperty<PropValue>::value, TRWProperty&>::type operator+=(const PropValue & value) { (*this)() += value; return *this; } TRWProperty & operator++() { ++((*this)()); return *this; } TRWProperty & operator++(int) { ((*this)())++; return *this; } TRWProperty & operator--() { --((*this)()); return *this; } TRWProperty & operator--(int) { ((*this)())--; return *this; } /*****************************Relational operators**************************************/ template <class Prop> typename detail::EnableIf<detail::IsProperty<Prop>::value, bool>::type operator<(const Prop & other) const { return (*this)() < other(); } template <class PropValue> typename detail::EnableIf<!detail::IsProperty<PropValue>::value, bool>::type operator<(const PropValue & value) const { return (*this)() < value; } template <class Prop> typename detail::EnableIf<detail::IsProperty<Prop>::value, bool>::type operator==(const Prop & other) const { return (*this)() == other(); } template <class PropValue> typename detail::EnableIf<!detail::IsProperty<PropValue>::value, bool>::type operator==(const PropValue & value) const { return (*this)() == value; } //******************************Swap*************************** template <class OtherProp> void swap(OtherProp & other) { TRWProperty tmp(std::move(other)); other = std::move(*this); *this = std::move(tmp); } }; template <class PropType> class TROProperty { //FIXME: Make private operator= and friend class the class containing the property static_assert(!std::is_reference<PropType>::value, "Property types must not be reference types"); PropType value_; public: typedef typename std::remove_cv<PropType>::type value_type; typedef PropType & GetterReturnType; typedef const PropType & ConstGetterReturnType; /************************Constructors*************************************/ ///Default constructor TROProperty() = default; ///Construct from other property (rvalue) template <class OtherProp> TROProperty(OtherProp && other, typename detail::EnableIf<detail::IsProperty<OtherProp>::value>::type * = 0) : value_(std::move(other())) {} ///Construct from other value (rvalue) template <class PropVal> TROProperty(PropVal && val, typename detail::EnableIf<!detail::IsProperty<PropVal>::value>::type * = 0) : value_(std::move(val)) {} ///Construct from other value (lvalue) template <class PropVal> TROProperty(const PropVal & value, typename detail::EnableIf<!detail::IsProperty<PropVal>::value>::type * = 0) : value_(value) {} ///Construct from other property (lvalue) template <class OtherProp> TROProperty(const OtherProp & prop, typename detail::EnableIf<detail::IsProperty<OtherProp>::value>::type * = 0) : value_(prop()) {} //***************************Assignment operators*****************************/ ///Assign from other property (rvalue) template <class OtherProp> typename detail::EnableIf<detail::IsProperty<OtherProp>::value, TROProperty &>::type operator=(OtherProp && other) { value_ = std::move(other()); return *this; } ///Assign from other value (rvalue) template <class PropValue> typename detail::EnableIf<!detail::IsProperty<PropValue>::value, TROProperty &>::type operator=(PropValue && val) { value_ = std::move(val); return *this; } ///Assign from other property (lvalue) template <class OtherProp> typename detail::EnableIf<detail::IsProperty<OtherProp>::value, TROProperty &>::type operator=(const OtherProp & other) { value_ = other(); return *this; } ///Assign from other value (lvalue) template <class PropValue> typename detail::EnableIf<!detail::IsProperty<PropValue>::value, TROProperty &>::type operator=(const PropValue & val) { value_ = val; return *this; } /***************************Obtain value operators*****************************/ ConstGetterReturnType operator()() const { return value_; } GetterReturnType operator()() { return value_; } /****************************Implicit conversions******************************/ operator ConstGetterReturnType() const { return value_; } operator GetterReturnType() { return value_; } /******************************Arithmetic Operators**********************************/ template <class Prop> typename detail::EnableIf<detail::IsProperty<Prop>::value, TROProperty&>::type operator+=(const Prop & other) { (*this)() += other(); return *this; } template <class PropValue> typename detail::EnableIf<!detail::IsProperty<PropValue>::value, TROProperty&>::type operator+=(const PropValue & value) { (*this)() += value; return *this; } TROProperty & operator++() { ++((*this)()); return *this; } TROProperty & operator++(int) { ((*this)())++; return *this; } TROProperty & operator--() { --((*this)()); return *this; } TROProperty & operator--(int) { ((*this)())--; return *this; } /*****************************Relational operators**************************************/ template <class Prop> typename detail::EnableIf<detail::IsProperty<Prop>::value, bool>::type operator<(const Prop & other) const { return (*this)() < other(); } template <class PropValue> typename detail::EnableIf<!detail::IsProperty<PropValue>::value, bool>::type operator<(const PropValue & value) const { return (*this)() < value; } template <class Prop> typename detail::EnableIf<detail::IsProperty<Prop>::value, bool>::type operator==(const Prop & other) const { return (*this)() == other(); } template <class PropValue> typename detail::EnableIf<!detail::IsProperty<PropValue>::value, bool>::type operator==(const PropValue & value) const { return (*this)() == value; } //******************************Swap*************************** template <class OtherProp> void swap(OtherProp & other) { TROProperty tmp(std::move(other)); other = std::move(*this); *this = std::move(tmp); } }; //***********************************************Elaborated properties******************************************* template <class GetterAddress, class SetterAddress> class RWProperty { typedef typename GetterAddress::object_t ObjectType; ObjectType * object_; static SetterAddress setter_; static GetterAddress getter_; typedef decltype((object_->*GetterAddress::getter)()) GetterReturnType; typedef const decltype((object_->*GetterAddress::getter)()) ConstGetterReturnType; typedef typename std::remove_cv<GetterReturnType>::type value_type; public: //Constructor without value RWProperty(ObjectType * object) : object_(object) { } //Constructor with value (lvalue) template <class PropValue> RWProperty(ObjectType * object, const PropValue & val, typename detail::EnableIf<!detail::IsProperty<PropValue>::value>::type * = 0) : object_(object) { (object_->*setter_.setter)(val); } //Constructor with other property (lvalue) template <class OtherProp> RWProperty(ObjectType * object, const OtherProp & other, typename detail::EnableIf<detail::IsProperty<OtherProp>::value>::type * = 0) : object_(object) { (object_->*setter_.setter)(other()); } //Constructor with value (rvalue) template <class PropValue> RWProperty(ObjectType * object, PropValue && val, typename detail::EnableIf<!detail::IsProperty<PropValue>::value>::type * = 0) : object_(object) { (object_->*setter_.setter)(std::move(val)); } //Constructor with other property (rvalue) template <class OtherProp> RWProperty(ObjectType * object, OtherProp && other, typename detail::EnableIf<detail::IsProperty<OtherProp>::value>::type * = 0) : object_(object) { (object_->*setter_.setter)(std::move(other())); } ConstGetterReturnType operator()() const { return (object_->*getter_.getter)(); } GetterReturnType operator()() { return (object_->*getter_.getter)(); } operator ConstGetterReturnType() const { (object_->*getter_.getter)(); } operator GetterReturnType() { return (object_->*getter_.getter)(); } ///Assign from other property (rvalue) template <class OtherProp> typename detail::EnableIf<detail::IsProperty<OtherProp>::value, RWProperty &>::type operator=(OtherProp && other) { (object_->*setter_.setter)(std::move(other())); return *this; } ///Assign from other value (rvalue) template <class PropValue> typename detail::EnableIf<!detail::IsProperty<PropValue>::value, RWProperty &>::type operator=(PropValue && val) { (object_->*setter_.setter)(std::move(val)); return *this; } ///Assign from other property (lvalue) template <class OtherProp> typename detail::EnableIf<detail::IsProperty<OtherProp>::value, RWProperty &>::type operator=(const OtherProp & other) { (object_->*setter_.setter)(other()); return *this; } ///Assign from other value (lvalue) template <class PropValue> typename detail::EnableIf<!detail::IsProperty<PropValue>::value, RWProperty &>::type operator=(const PropValue & val) { (object_->*setter_.setter)(val); return *this; } /******************************Arithmetic Operators**********************************/ template <class Prop> typename detail::EnableIf<detail::IsProperty<Prop>::value, RWProperty&>::type operator+=(const Prop & other) { auto res = (*this)() + other(); (object_->*setter_.setter)(res); return *this; } template <class PropValue> typename detail::EnableIf<!detail::IsProperty<PropValue>::value, RWProperty&>::type operator+=(const PropValue & value) { auto res = (*this)() + value; (object_->*setter_.setter)(res); return *this; } RWProperty & operator++() { auto val = (*this)() + 1; (object_->*setter_.setter)(val); return *this; } RWProperty & operator++(int) { ++(*this); return *this; } RWProperty & operator--() { auto val = (*this)() - 1; (object_->*setter_.setter)(val); return *this; } RWProperty & operator--(int) { --(*this); return *this; } /*****************************Relational operators**************************************/ template <class Prop> typename detail::EnableIf<detail::IsProperty<Prop>::value, bool>::type operator<(const Prop & other) const { return (*this)() < other(); } template <class PropValue> typename detail::EnableIf<!detail::IsProperty<PropValue>::value, bool>::type operator<(const PropValue & value) const { return (*this)() < value; } template <class Prop> typename detail::EnableIf<detail::IsProperty<Prop>::value, bool>::type operator==(const Prop & other) const { return (*this)() == other(); } template <class PropValue> typename detail::EnableIf<!detail::IsProperty<PropValue>::value, bool>::type operator==(const PropValue & value) const { return (*this)() == value; } //******************************Swap*************************** template <class OtherProp> void swap(OtherProp & other) { value_type tmp(std::move(other())); other = std::move(*this); *this = std::move(tmp); } }; template <class GetterAddress, class SetterAddress> GetterAddress RWProperty<GetterAddress, SetterAddress>::getter_; template <class GetterAddress, class SetterAddress> SetterAddress RWProperty<GetterAddress, SetterAddress>::setter_; //***********************************************Elaborated properties******************************************* template <class GetterAddress, class SetterAddress> class ROProperty { typedef typename GetterAddress::object_t ObjectType; ObjectType * object_; static SetterAddress setter_; static GetterAddress getter_; typedef decltype((object_->*GetterAddress::getter)()) GetterReturnType; typedef const decltype((object_->*GetterAddress::getter)()) ConstGetterReturnType; typedef typename std::remove_cv<GetterReturnType>::type value_type; public: //Constructor without value ROProperty(ObjectType * object) : object_(object) { } //Constructor with value (lvalue) template <class PropValue> ROProperty(ObjectType * object, const PropValue & val, typename detail::EnableIf<!detail::IsProperty<PropValue>::value>::type * = 0) : object_(object) { (object_->*setter_.setter)(val); } //Constructor with other property (lvalue) template <class OtherProp> ROProperty(ObjectType * object, const OtherProp & other, typename detail::EnableIf<detail::IsProperty<OtherProp>::value>::type * = 0) : object_(object) { (object_->*setter_.setter)(other()); } //Constructor with value (rvalue) template <class PropValue> ROProperty(ObjectType * object, PropValue && val, typename detail::EnableIf<!detail::IsProperty<PropValue>::value>::type * = 0) : object_(object) { (object_->*setter_.setter)(std::move(val)); } //Constructor with other property (rvalue) template <class OtherProp> ROProperty(ObjectType * object, OtherProp && other, typename detail::EnableIf<detail::IsProperty<OtherProp>::value>::type * = 0) : object_(object) { (object_->*setter_.setter)(std::move(other())); } ConstGetterReturnType operator()() const { return (object_->*getter_.getter)(); } GetterReturnType operator()() { return (object_->*getter_.getter)(); } operator ConstGetterReturnType() const { (object_->*getter_.getter)(); } operator GetterReturnType() { return (object_->*getter_.getter)(); } ///Assign from other property (rvalue) template <class OtherProp> typename detail::EnableIf<detail::IsProperty<OtherProp>::value, ROProperty &>::type operator=(OtherProp && other) { (object_->*setter_.setter)(std::move(other())); return *this; } ///Assign from other value (rvalue) template <class PropValue> typename detail::EnableIf<!detail::IsProperty<PropValue>::value, ROProperty &>::type operator=(PropValue && val) { (object_->*setter_.setter)(std::move(val)); return *this; } ///Assign from other property (lvalue) template <class OtherProp> typename detail::EnableIf<detail::IsProperty<OtherProp>::value, ROProperty &>::type operator=(const OtherProp & other) { (object_->*setter_.setter)(other()); return *this; } ///Assign from other value (lvalue) template <class PropValue> typename detail::EnableIf<!detail::IsProperty<PropValue>::value, ROProperty &>::type operator=(const PropValue & val) { (object_->*setter_.setter)(val); return *this; } /******************************Arithmetic Operators**********************************/ template <class Prop> typename detail::EnableIf<detail::IsProperty<Prop>::value, ROProperty&>::type operator+=(const Prop & other) { auto res = (*this)() + other(); (object_->*setter_.setter)(res); return *this; } template <class PropValue> typename detail::EnableIf<!detail::IsProperty<PropValue>::value, ROProperty&>::type operator+=(const PropValue & value) { auto res = (*this)() + value; (object_->*setter_.setter)(res); return *this; } ROProperty & operator++() { auto val = (*this)() + 1; (object_->*setter_.setter)(val); return *this; } ROProperty & operator++(int) { ++(*this); return *this; } ROProperty & operator--() { auto val = (*this)() - 1; (object_->*setter_.setter)(val); return *this; } ROProperty & operator--(int) { --(*this); return *this; } /*****************************Relational operators**************************************/ template <class Prop> typename detail::EnableIf<detail::IsProperty<Prop>::value, bool>::type operator<(const Prop & other) const { return (*this)() < other(); } template <class PropValue> typename detail::EnableIf<!detail::IsProperty<PropValue>::value, bool>::type operator<(const PropValue & value) const { return (*this)() < value; } template <class Prop> typename detail::EnableIf<detail::IsProperty<Prop>::value, bool>::type operator==(const Prop & other) const { return (*this)() == other(); } template <class PropValue> typename detail::EnableIf<!detail::IsProperty<PropValue>::value, bool>::type operator==(const PropValue & value) const { return (*this)() == value; } //******************************Swap*************************** template <class OtherProp> void swap(OtherProp & other) { value_type tmp(std::move(other())); other = std::move(*this); *this = std::move(tmp); } }; template <class GetterAddress, class SetterAddress> GetterAddress ROProperty<GetterAddress, SetterAddress>::getter_; template <class GetterAddress, class SetterAddress> SetterAddress ROProperty<GetterAddress, SetterAddress>::setter_; } //namespace props #define PROPS_RWPROPERTY(propname) \ ::props::RWProperty< \ decltype(::props::detail::deduceWrapperForGetter<ClassName, \ decltype(::props::detail::deduceGetterRet(&ClassName::get##propname)), \ &ClassName::get##propname>()), \ decltype(::props::detail::deduceWrapperForSetter<ClassName, \ decltype(::props::detail::deduceSetterArg(&ClassName::set##propname)), \ &ClassName::set##propname>())> propname #define PROPS_ROPROPERTY(propname) \ ::props::ROProperty< \ decltype(::props::detail::deduceWrapperForGetter<ClassName, \ decltype(::props::detail::deduceGetterRet(&ClassName::get##propname)), \ &ClassName::get##propname>()), \ decltype(::props::detail::deduceWrapperForSetter<ClassName, \ decltype(::props::detail::deduceSetterArg(&ClassName::set##propname)), \ &ClassName::set##propname>())> propname #endif

Germán Diago wrote:
Hello. I've been trying to implement properties for c++0x, for now. I've come up with this solution for now.
You need g++ svn to test it. It's a partial implementation, since not every operator is overloaded.
Space overhead:
- one pointer to the object per property instance. - the pointer to the member functions is shared among every instance of the class, so it's a per-class and not per-instance overhead, which is negligible.
Usage:
class Person {
int getAge() const { .... }; void setAge(int age) { ... };
PROPS_RWPROPERTY(Age);
Person() : Age(this) {} };
You can: - declare getters/setters const or nonconst. - getters must have one parameter. - setters must return void. - it has some operators overloaded, but the idea is to overload all of them if the implementation is all right.
- the property returns whatever you put in your getter and gets as a parameter what you put in the setter.
If you declare the getters and setters to be VIRTUAL, you get virtual behaviour, so you can use virtual properties, even with pure virtual functions, and it will work.
So you have a lot of code here, but I am confused what problem you are solving. What is the practical difference between using your property and just declaring a data member: class Person { public: int getAge() const { return Age; }; void setAge(int age) { Age = age; }; private: Age; public: Person() : Age() {} }; By the way, your EnableIf that doesn't use enable_if_c will eventually run into compiler bugs in MSVC8 and 9 because of the order it evaluates template instantiations. Usually I think of properties as something attached to an instance of a class without the need to modify the class. class Person { string Name; Person() : Name() {} }; Person p; Property<int, Person> Age(-1); //default age is -1, an error code Age.set(10, &p); assert(Age.get(&p) == 10); Such a thing becomes rather complicated once you try to wire in removal of property values to the destructor of your object type and duplication of property values to the copy constructor without the need to know about all the kinds of properties that might be later associated with the class at the time the class is defined. Add to that custom heaps for storing property values and clever schemes to look them up and you quickly run into a system that is quite complicated with singletons running around and issues with thread safety etc etc. I think it is better to keep things simple where possible and understand clearly what the problem is and why the problem requires a complicated solution before implementing or using a complicated solution. Regards, Luke

On Mar 3, 2010, at 12:07 PM, "Simonson, Lucanus J" <lucanus.j.simonson@intel.com
wrote:
Usually I think of properties as something attached to an instance of a class without the need to modify the class.
class Person { string Name; Person() : Name() {} };
Person p;
Property<int, Person> Age(-1); //default age is -1, an error code
Age.set(10, &p); assert(Age.get(&p) == 10);
Such a thing becomes rather complicated once you try to wire in removal of property values to the destructor of your object type and duplication of property values to the copy constructor without the need to know about all the kinds of properties that might be later associated with the class at the time the class is defined. Add to that custom heaps for storing property values and clever schemes to look them up and you quickly run into a system that is quite complicated with singletons running around and issues with thread safety etc etc. I think it is better to keep things simple where possible and understand clearly what the problem is and why the problem requires a complicated solution before implementing or using a complicated solution.
Regards, Luke
I think conceptually the proposed abstraction fits my mental image of "properties" pretty closely, and probably the same for anyone who has made significant use of C#. The main problem they address is providing a level of abstraction over data members allowing you to have logical properties as opposed to physical properties. This allows you to change the implementation details of a class without changing its interface. Aside from syntactic sugar (in C# anyway) there is no difference between properties and get/ set methods. In C++ i can see potential benefits with respect to generic programming-- if data members are get/ set with an identical syntax to how functions are called yhey can be used as policy classes which expect certain members being present. Im not defending or criticizing this implementation, just speaking in general. Zach

Zachary Turner wrote:
On Mar 3, 2010, at 12:07 PM, "Simonson, Lucanus J" <lucanus.j.simonson@intel.com> wrote:
Usually I think of properties as something attached to an instance of a class without the need to modify the class.
class Person { string Name; Person() : Name() {} };
Person p;
Property<int, Person> Age(-1); //default age is -1, an error code
Age.set(10, &p); assert(Age.get(&p) == 10);
Such a thing becomes rather complicated once you try to wire in removal of property values to the destructor of your object type and duplication of property values to the copy constructor without the need to know about all the kinds of properties that might be later associated with the class at the time the class is defined. Add to that custom heaps for storing property values and clever schemes to look them up and you quickly run into a system that is quite complicated with singletons running around and issues with thread safety etc etc. I think it is better to keep things simple where possible and understand clearly what the problem is and why the problem requires a complicated solution before implementing or using a complicated solution.
Regards, Luke
I think conceptually the proposed abstraction fits my mental image of "properties" pretty closely, and probably the same for anyone who has made significant use of C#.
The main problem they address is providing a level of abstraction over data members allowing you to have logical properties as opposed to physical properties. This allows you to change the implementation details of a class without changing its interface.
I'm not sure about that. I don't recognize a big difference between use of C# properties: int age = person.Age; and C++ member function: int age = person.get_age(); In both cases change of implementation details without touching the interface is possible. It feels more like a syntactic sugar. Though, properties in C# seem to provide better self-containment and property isolation than regular methods.
Aside from syntactic sugar (in C# anyway) there is no difference between properties and get/ set methods.
That's right. C# compiler generates proper setters/getters for properties anyway.
In C++ i can see potential benefits with respect to generic programming-- if data members are get/set with an identical syntax to how functions are called yhey can be used as policy classes which expect certain members being present.
It is still possible, by defining a convention as it happened with well known members like size(), begin(), end() or definitions like value_type, size_type, and so one.
Im not defending or criticizing this implementation, just speaking in general.
So do I. By the way, Visual C++ provides an interesting attribute: __declspec( property(...)) http://msdn.microsoft.com/en-us/library/yhfk0thd%28VS.80%29.aspx Best regards, -- Mateusz Loskot, http://mateusz.loskot.net Charter Member of OSGeo, http://osgeo.org

Mateusz Loskot wrote:
Zachary Turner wrote:
On Mar 3, 2010, at 12:07 PM, "Simonson, Lucanus J" <lucanus.j.simonson@intel.com> wrote:
Usually I think of properties as something attached to an instance of a class without the need to modify the class.
class Person { string Name; Person() : Name() {} };
Person p;
Property<int, Person> Age(-1); //default age is -1, an error code
Age.set(10, &p); assert(Age.get(&p) == 10);
Such a thing becomes rather complicated once you try to wire in removal of property values to the destructor of your object type and duplication of property values to the copy constructor without the need to know about all the kinds of properties that might be later associated with the class at the time the class is defined. Add to that custom heaps for storing property values and clever schemes to look them up and you quickly run into a system that is quite complicated with singletons running around and issues with thread safety etc etc. I think it is better to keep things simple where possible and understand clearly what the problem is and why the problem requires a complicated solution before implementing or using a complicated solution.
I think conceptually the proposed abstraction fits my mental image of "properties" pretty closely, and probably the same for anyone who has made significant use of C#.
The main problem they address is providing a level of abstraction over data members allowing you to have logical properties as opposed to physical properties. This allows you to change the implementation details of a class without changing its interface.
I'm not sure about that. I don't recognize a big difference between use of C# properties:
int age = person.Age;
and C++ member function:
int age = person.get_age();
In fact, I should write it this way, to mimic C# syntax closer: int age = person.age(); person.age() = age; Best regards, -- Mateusz Loskot, http://mateusz.loskot.net Charter Member of OSGeo, http://osgeo.org

Hi Germán, ----- Original Message ----- From: "Germán Diago" <germandiago@gmail.com> To: <boost@lists.boost.org> Sent: Wednesday, March 03, 2010 5:33 PM Subject: [boost] Another implementation of properties.
Hello. I've been trying to implement properties for c++0x, for now. I've come up with this solution for now.
You need g++ svn to test it. It's a partial implementation, since not every operator is overloaded.
Space overhead:
- one pointer to the object per property instance.
I don't like this as the size of each property is increased by sizeof(void*).
- the pointer to the member functions is shared among every instance of the class, so it's a per-class and not per-instance overhead, which is negligible.
You should be able to don't store them at all using the template parameters (see attached files)
Usage:
class Person {
int getAge() const { .... }; void setAge(int age) { ... };
PROPS_RWPROPERTY(Age);
Person() : Age(this) {} };
You can: - declare getters/setters const or nonconst. - getters must have one parameter. - setters must return void. - it has some operators overloaded, but the idea is to overload all of them if the implementation is all right.
I don't think this is necessary if you have an operator T&(), but maybe I'm wrong.
- the property returns whatever you put in your getter and gets as a parameter what you put in the setter.
If you declare the getters and setters to be VIRTUAL, you get virtual behaviour, so you can use virtual properties, even with pure virtual functions, and it will work.
Caveats: - Needs c++0x compiler for now.
I have not seen at the implementation but I suppose you use it to retrieve the property type. Have you tried with Boost.Typeof?
- Not enforced the read-only access for properties for now. I need extended friend declarations for that, and the operator= would be made private with friend class. But for now it's not reinforced.
I think this implementation is pretty usable as-is. It's just a matter of extending it. I don't use the offset trick because I think it does not work with classes with virtual members, but I'm not sure.
Why offsetof is a C++ standard macro that should work in any case. if f is a field of a type T reinterpret_cast<T*>(reinterpret_cast<char*>(this)-offsetof(T,f))) should point to the T instance, isn't it? Someone tolds that this works only for POD. Could you clarify? I'm not telling that the use of this trick is good. I just wants to know if this can be used as proposed. I think that properties that have no associated storage should take no space on the class instance. If we want a syntax such as c.prop = x we need a 'prop' member that will take at least 1 byte(if no more). For example struct P1 {}; struct P2 {}; struct S { int i; P1 p1; P1 p2; }; sizeof(P1)=1 sizeof(P2)=1 sizeof(S)=8 Is for this reason that I think we need a function syntax: c.prop() = x In this case, I agree with Stefan, I don't see a real advantge of class Person { int getAge() const { .... }; void setAge(int age) { ... }; PROPS_RWPROPERTY(Age); Person() : Age(this) {} }; over class person { int age_; public: int age() const { return Age_ }; int& age() { return Age_ }; person() : age(0) {} }; I don't think that I will use a property library that uses more memory than needed, if the library doesn't provides more features that just access and modification of the property. I have attached an implementation that uses template parameter member functions, maybe this can help you for your library. Note that I'm not proposing the attached files as an alternative proposal, as I have said, the flat implementation is enough to me if properties don't offer more. It is just to show another way to implement what you are looking for. Best, Vicente

El 03/03/2010 06:15 p.m., vicente.botet escribió:
Why offsetof is a C++ standard macro that should work in any case.
if f is a field of a type T reinterpret_cast<T*>(reinterpret_cast<char*>(this)-offsetof(T,f)))
should point to the T instance, isn't it? Someone tolds that this works only for POD. Could you clarify?
[18.2.4] "The macro offsetof(type, member-designator) accepts a restricted set of type arguments in this International Standard. If type is not a standard-layout class (Clause 9), the results are undefined. (...)" Agustín K-ballo Bergé.- http://talesofcpp.blogspot.com
participants (6)
-
Agustín K-ballo Bergé
-
Germán Diago
-
Mateusz Loskot
-
Simonson, Lucanus J
-
vicente.botet
-
Zachary Turner