diff --git a/examples/foomodulegen.py b/examples/foomodulegen.py index e957054..05a6307 100755 --- a/examples/foomodulegen.py +++ b/examples/foomodulegen.py @@ -114,6 +114,9 @@ def my_module_gen(out_file): SomeObject = CppClass('SomeObject', allow_subclassing=True) mod.add_class(SomeObject) + SomeObject.add_instance_attribute(ReturnValue.new('Foo'), 'foo', + getter='get_foo_value', + setter='set_foo_value') SomeObject.add_instance_attribute(ReturnValue.new('std::string'), 'm_prefix') SomeObject.add_static_attribute(ReturnValue.new('std::string'), 'staticData') diff --git a/examples/footest.py b/examples/footest.py index 6669ac9..bba69cb 100644 --- a/examples/footest.py +++ b/examples/footest.py @@ -536,6 +536,13 @@ class TestFoo(unittest.TestCase): prefix = t.get_prefix_with_foo_ptr(foo1) self.assertEqual(prefix, "123zbr456zbr") + def test_attribute_with_getset(self): + obj = foo.SomeObject("") + f1 = foo.Foo("hello") + obj.foo = f1 + f2 = obj.foo + self.assertEqual(f2.get_datum(), "hello") + if __name__ == '__main__': unittest.main() diff --git a/pybindgen/cppattribute.py b/pybindgen/cppattribute.py index be06a8f..1ece1a1 100644 --- a/pybindgen/cppattribute.py +++ b/pybindgen/cppattribute.py @@ -30,19 +30,24 @@ class CppInstanceAttributeGetter(PyGetter): ''' A getter for a C++ instance attribute. ''' - def __init__(self, value_type, class_, attribute_name): + def __init__(self, value_type, class_, attribute_name, getter=None): """ value_type -- a ReturnValue object handling the value type; class_ -- the class (CppClass object) attribute_name -- name of attribute + getter -- None, or name of a method of the class used to get the value """ super(CppInstanceAttributeGetter, self).__init__( value_type, [], "return NULL;", "return NULL;", no_c_retval=True) self.class_ = class_ self.attribute_name = attribute_name + self.getter = getter self.c_function_name = "_wrap_%s__get_%s" % (self.class_.pystruct, self.attribute_name) - value_type.value = "self->obj->%s" % self.attribute_name + if self.getter is None: + value_type.value = "self->obj->%s" % self.attribute_name + else: + value_type.value = "self->obj->%s()" % self.getter def generate_call(self): "virtual method implementation; do not call" @@ -104,19 +109,20 @@ class CppInstanceAttributeSetter(PySetter): ''' A setter for a C++ instance attribute. ''' - def __init__(self, value_type, class_, attribute_name): + def __init__(self, value_type, class_, attribute_name, setter=None): """ value_type -- a ReturnValue object handling the value type; class_ -- the class (CppClass object) attribute_name -- name of attribute + setter -- None, or name of a method of the class used to set the value """ super(CppInstanceAttributeSetter, self).__init__( value_type, [], "return -1;") self.class_ = class_ self.attribute_name = attribute_name + self.setter = setter self.c_function_name = "_wrap_%s__set_%s" % (self.class_.pystruct, self.attribute_name) - value_type.value = "self->obj->%s" % self.attribute_name def generate(self, code_sink): """ @@ -127,11 +133,26 @@ class CppInstanceAttributeSetter(PySetter): self.before_call.write_code( 'py_retval = Py_BuildValue("(O)", value);') self.before_call.add_cleanup_code('Py_DECREF(py_retval);') + + if self.setter is not None: + ## if we have a setter method, redirect the value to a temporary variable + value_var = self.declarations.declare_variable(self.return_value.ctype, 'tmp_value') + self.return_value.value = value_var + else: + ## else the value is written directly to a C++ instance attribute + self.return_value.value = "self->obj->%s" % self.attribute_name + self.return_value.convert_python_to_c(self) + parse_tuple_params = ['py_retval'] parse_tuple_params.extend(self.parse_params.get_parameters()) self.before_call.write_error_check('!PyArg_ParseTuple(%s)' % (', '.join(parse_tuple_params),)) + + if self.setter is not None: + ## if we have a setter method, now is the time to call it + self.after_call.write_code("self->obj->%s(%s);" % (self.setter, value_var)) + ## cleanup and return self.after_call.write_cleanup() self.after_call.write_code('return 0;') diff --git a/pybindgen/cppclass.py b/pybindgen/cppclass.py index b70e91e..471d408 100644 --- a/pybindgen/cppclass.py +++ b/pybindgen/cppclass.py @@ -472,19 +472,23 @@ public: setter = CppStaticAttributeSetter(value_type, self, name) self.static_attributes.add_attribute(name, getter, setter) - def add_instance_attribute(self, value_type, name, is_const=False): + def add_instance_attribute(self, value_type, name, is_const=False, + getter=None, setter=None): """ value_type -- a ReturnValue object name -- attribute name (i.e. the name of the class member variable) is_const -- True if the attribute is const, i.e. cannot be modified + getter -- None, or name of a method of this class used to get the value + setter -- None, or name of a method of this class used to set the value """ assert isinstance(value_type, ReturnValue) - getter = CppInstanceAttributeGetter(value_type, self, name) + getter_wrapper = CppInstanceAttributeGetter(value_type, self, name, getter=getter) if is_const: - setter = None + setter_wrapper = None + assert setter is None else: - setter = CppInstanceAttributeSetter(value_type, self, name) - self.instance_attributes.add_attribute(name, getter, setter) + setter_wrapper = CppInstanceAttributeSetter(value_type, self, name, setter=setter) + self.instance_attributes.add_attribute(name, getter_wrapper, setter_wrapper) def generate_forward_declarations(self, code_sink): """Generates forward declarations for the instance and type