<snip>
I pushed this for distraction and I had a bit of fun creating it, but I
would never use something like this, I'll simply stick to the functional
notation wherever I need to intercept the property change.
Disclaimer: the code below is _intentionally_ littered with macros.
Don't waste your time /criticizing it for me/: I _do_ consider it a very
expensive and silly way to skin this very cat.
Still, an interesting exercise for me.
//-------
/*
Assigning to a Property within the setter callback
or reading the same Property from the getter callback
will result in an infinite loop, either:
- keep the following macro defined and
pay for the additional checks
- undefine the following macro and be sure:
- to not assign to the Property in the setter callback
- to not read from the Property in the getter callback
*/
#define PREVENT_CALLBACK_LOOP
#undef PREVENT_CALLBACK_LOOP
template<class Object, class Type> class Property {
public:
typedef Type (Object::*setter_callback)(Type, Type);
typedef void (Object::*getter_callback)(Type);
Property(Type value = Type(),
Object* object = 0,
setter_callback setter = 0,
getter_callback getter = 0):
#ifdef PREVENT_CALLBACK_LOOP
_updating(false),
#endif
_value(value),
_object(object),
_setter_callback(setter),
_getter_callback(getter) {}
Property& operator=(const Property& p) {
return this->operator=(Type(p));
}
Property& operator=(Type newvalue) {
#ifdef PREVENT_CALLBACK_LOOP
if (!_updating) {
_updating = true;
#endif
if (_object && _setter_callback) {
newvalue = (_object->*_setter_callback)(_value, newvalue);
};
_value = newvalue;
#ifdef PREVENT_CALLBACK_LOOP
_updating = false;
}
#endif
return *this;
}
operator const Type&() const {
#ifdef PREVENT_CALLBACK_LOOP
if (!_updating) {
_updating = true;
#endif
if (_object && _getter_callback) {
(_object->*_getter_callback)(_value);
}
#ifdef PREVENT_CALLBACK_LOOP
_updating = false;
}
#endif
return _value;
}
private:
Property(const Property& p);
#ifdef PREVENT_CALLBACK_LOOP
mutable bool _updating;
#endif
Type _value;
Object* _object;
setter_callback _setter_callback;
getter_callback _getter_callback;
};
//-------
// test case:
#include <iostream>
using namespace std;
#define INITIALIZE_PROPERTY(property_name, parent_type) \
this, \
&parent_type:
roperty_name##_write, \
&parent_type:
roperty_name##_read
static int id_count = 1;
struct Tester {
Property<Tester, int> i;
Tester(int i = 0) :
i(i, INITIALIZE_PROPERTY(i, Tester) ),
id(id_count++) {}
Tester(const Tester& t) :
i(t.i, INITIALIZE_PROPERTY(i, Tester) ),
id(id_count++) {}
Tester& operator=(const Tester& t) {
i = t.i;
return *this;
}
int i_write(int oldvalue, int newvalue) {
cout << "[t" << id << "] ";
if (newvalue > 10) {
cout << "write: refused change from " << oldvalue
<< " to " << newvalue
<< ", let's make it 10" << endl;
return 10;
} else {
cout << "write: accepted change from " << oldvalue
<< " to " << newvalue << endl;
return newvalue;
}
#ifdef PREVENT_CALLBACK_LOOP
i = 78; // will never do anything, of course
#endif
}
void i_read(int current) {
cout << "[t" << id << "] read: "
#ifdef PREVENT_CALLBACK_LOOP
<< i
#else
<< current
#endif
<< endl;
}
private:
int id;
};
int main() {
cout << "\n# Tester t1(1);" << endl;
Tester t1(1);
cout << "\n# Tester t2(t1);" << endl;
Tester t2(t1);
cout << "\n# t2.i = 8;" << endl;
t2.i = 8;
cout << "\n# t2.i = t1.i;" << endl;
t2.i = t1.i;
cout << "\n# t2.i = 2;" << endl;
t2.i = 2;
cout << "\n# Tester t3(3);" << endl;
Tester t3(3);
cout << "\n# t3 = t2;" << endl;
t3 = t2;
cout << "\n# t3.i = 3;" << endl;
t3.i = 3;
cout << "\n# t1.i = t2.i = t3.i = 42;" << endl;
t1.i = t2.i = t3.i = 42;
cout << "\n# print'em all " << endl;
cout << t1.i << " " << t2.i << " " << t3.i << endl;
return 0;
}
/*
OUTPUT:
# Tester t1(1);
# Tester t2(t1);
[t1] read: 1
# t2.i = 8;
[t2] write: accepted change from 1 to 8
# t2.i = t1.i;
[t1] read: 1
[t2] write: accepted change from 8 to 1
# t2.i = 2;
[t2] write: accepted change from 1 to 2
# Tester t3(3);
# t3 = t2;
[t2] read: 2
[t3] write: accepted change from 3 to 2
# t3.i = 3;
[t3] write: accepted change from 2 to 3
# t1.i = t2.i = t3.i = 42;
[t3] write: refused change from 3 to 42, let's make it 10
[t3] read: 10
[t2] write: accepted change from 2 to 10
[t2] read: 10
[t1] write: accepted change from 1 to 10
# print'em all
[t3] read: 10
[t2] read: 10
[t1] read: 10
10 10 10
*/
//-------