diff --git a/SWIG/Examples/test-suite/python/attributetest.i b/SWIG/Examples/test-suite/python/attributetest.i new file mode 100644 index 000000000..aac4b977e --- /dev/null +++ b/SWIG/Examples/test-suite/python/attributetest.i @@ -0,0 +1,50 @@ +%module attributetest + +%include attribute.i + +%attribute(A, int, a, get_a, set_a); +%attribute_ref(A, int, b); + + +%attribute(A, int, c, get_c); /* read-only */ +%attribute_ref(A, int, d, b); /* different attribute name 'd' */ + +%inline +{ + struct A + { + A(int a, int b, int c) : _a(a), _b(b), _c(c) + { + } + + int get_a() const + { + return _a; + } + + void set_a(int aa) + { + _a = aa; + } + + const int& b() const + { + return _b; + } + + int& b() + { + return _b; + } + + int get_c() const + { + return _c; + } + private: + int _a; + int _b; + int _c; + }; +} + diff --git a/SWIG/Examples/test-suite/python/attributetest_runme.py b/SWIG/Examples/test-suite/python/attributetest_runme.py new file mode 100644 index 000000000..38a94992a --- /dev/null +++ b/SWIG/Examples/test-suite/python/attributetest_runme.py @@ -0,0 +1,27 @@ +import attributetest + +aa = attributetest.A(1,2,3) + +if aa.a != 1: + raise RuntimeError +aa.a = 3 +if aa.a != 3: + raise RuntimeError + + +if aa.b != 2: + raise RuntimeError +aa.b = 5 +if aa.b != 5: + raise RuntimeError + + + +if aa.d != aa.b: + raise RuntimeError + +if aa.c != 3: + raise RuntimeError +#aa.c = 5 +#if aa.c != 3: +# raise RuntimeError diff --git a/SWIG/Lib/python/attribute.i b/SWIG/Lib/python/attribute.i new file mode 100644 index 000000000..af0f638ba --- /dev/null +++ b/SWIG/Lib/python/attribute.i @@ -0,0 +1,121 @@ +%{ +#include +%} + +/* + Attribute implementation using JOHN E LENZ ideas. + + The following macros convert a pair of set/get methods + into a "native" python attribute. + + Use %attribute when you have a pair of get/set methods + like in: + + %attribute(A, int, a, get_a, set_a); + + struct A + { + int get_a() const + { + return _a; + } + + void set_a(int aa) + { + _a = aa; + } + }; + + If you don't provide a 'set' method, a 'read-only' attribute + is generated, ie, like in: + + %attribute(A, int, c, get_c); + + + Use %attribute_ref when you have const/non-const reference + access methods, like in: + + %attribute_ref(A, int, b); + + struct A + { + const int& b() const + { + return _b; + } + + int& b() + { + return _b; + } + }; + + You can also use + + %attribute_ref(class, type, pyattr, cppame); + + if the internal C++ reference methods have a different + name from the python attribute you want. + + Then you can use the instances like: + + x = A() + x.a = 3 # calls A::set_a + print x.a # calls A::get_a + + x.b = 3 # calls A::b() + print x.b # calls A::b() const + +*/ + + +%define %_attribute(Class, type, attr, get, set) +%extend Class { + type attr; +} +%{ + template + inline type Class ##_## attr ## _get(C *t) { + return get; + } + template + inline type Class ##_## attr ## _set(C *t, const type& val) { + set; + } +%} +%enddef + +%define %attribute(Class, type, attr, get, ...) +#if #__VA_ARGS__ != "" + %_attribute(Class, type, attr, t->get(), t->__VA_ARGS__(val)) +#else + %_attribute(Class, type, attr, t->get(), + std::cerr << "'attr' is a read-only attribute" << std::endl); +#endif +%enddef + +%define %_attribute_ref(Class, type, attr, ref_name) +%extend Class { + type attr; +} +%ignore Class::ref_name(); +%ignore Class::ref_name() const; +%{ + template + inline type Class ##_## attr ## _get(C *t) { + return t->ref_name(); + } + template + inline type Class ##_## attr ## _set(C *t, const type& val) { + t->ref_name() = val; + } +%} +%enddef + +%define %attribute_ref(Class, type, attr, ...) +#if #__VA_ARGS__ == "" +%_attribute_ref(Class, type, attr, attr) +#else +%_attribute_ref(Class, type, attr, __VA_ARGS__) +#endif +%enddef