Last active
August 29, 2015 13:57
-
-
Save albeva/9551571 to your computer and use it in GitHub Desktop.
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
#include <iostream> | |
#include <type_traits> | |
/** | |
* Property template encapsulates the callback | |
* functions to getter and setter of the property. | |
* it does not store the propertyu value itself | |
*/ | |
template<typename T, class U, T(U::*getter_fn)(), void(U::*setter_fn)(T&&)> | |
class Property | |
{ | |
public: | |
/** | |
* create property object from a pointer to the object, | |
* getter and setter proxy functions | |
*/ | |
Property(U * obj) : _obj(obj) {} | |
/** | |
* Allow assigning value to the property via '=' operator | |
* this should do perfect forwarding | |
*/ | |
template<typename V> | |
inline void operator = (V && value) | |
{ | |
(_obj->*setter_fn)(std::forward<V>(value)); | |
} | |
/** | |
* Overoad the operator for T so that propety can be simply | |
* assigned. | |
* int p = obj.int_prop; | |
*/ | |
inline operator T() const { | |
return (_obj->*getter_fn)(); | |
} | |
/** | |
* Override the () operators so that propert getter | |
* can be called like a functor | |
*/ | |
inline T operator()() const { | |
return (_obj->*getter_fn)(); | |
} | |
private: | |
U * _obj; | |
}; | |
/** | |
* Allow property to be used easily with output streams | |
* std::cout << obj.prop; | |
*/ | |
template<typename T, class U, T(U::*getter_fn)(), void(U::*setter_fn)(T&&)> | |
std::ostream& operator<<(std::ostream& os, const Property<T, U, getter_fn, setter_fn> & prop) | |
{ | |
os << prop(); | |
return os; | |
} | |
/** | |
* helper macro for creating the propert. | |
* usage: | |
* propert(int, age, { return _age; }, { _age = value; } | |
*/ | |
#define property(T, _name, _class, _getter, _setter) \ | |
public: \ | |
T _##_name; \ | |
T get_##_name() _getter; \ | |
void set_##_name(T && value) _setter; \ | |
public: \ | |
Property<T, _class, &_class::get_##_name, &_class::set_##_name> _name = this; | |
/** | |
* test properties | |
*/ | |
class Person | |
{ | |
// age | |
property(int, age, Person, { | |
return _age; | |
}, { | |
_age = value; | |
}); | |
// name | |
property(std::string, name, Person, { | |
return _name; | |
}, { | |
_name = value; | |
}); | |
// greeting | |
property(std::string, greeting, Person, { | |
return "Hello. My name is " + _name + " and I am " + std::to_string(_age) + " years old."; | |
}, {}); | |
}; | |
int main(int argc, char * argv[]) | |
{ | |
Person me; | |
me.age = 28; | |
me.name = "Albert"; | |
std::cout << me.greeting << '\n'; | |
std::cout << "sizeof(Person) = " << sizeof(Person) << '\n'; | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Careful with prefixing '_'. There are some rules.
So if I use
property(int, Age, ..., ...);
I get potential issues.