From bdbd7926dc7cea5a68f9774cdb719abdb112386a Mon Sep 17 00:00:00 2001 From: "Gustavo J. A. M. Carneiro" Date: Sun, 10 Jun 2007 19:22:51 +0100 Subject: [PATCH] Generate setters for C++ class instance attributes. --- examples/footest.py | 2 ++ pybindgen/cppattribute.py | 53 +++++++++++++++++++++++++++++++++- pybindgen/cppclass.py | 21 ++++++++++++-- pybindgen/typehandlers/base.py | 13 +++++---- 4 files changed, 79 insertions(+), 10 deletions(-) diff --git a/examples/footest.py b/examples/footest.py index d7f7c9d..a408795 100644 --- a/examples/footest.py +++ b/examples/footest.py @@ -79,6 +79,8 @@ class TestFoo(unittest.TestCase): def test_instance_attribute(self): obj = foo.SomeObject("Hello") self.assertEqual(obj.m_prefix, "Hello") + obj.m_prefix = "World" + self.assertEqual(obj.m_prefix, "World") def test_static_attribute(self): self.assertEqual(foo.SomeObject.staticData, "Hello Static World!") diff --git a/pybindgen/cppattribute.py b/pybindgen/cppattribute.py index c1086b9..3ac3943 100644 --- a/pybindgen/cppattribute.py +++ b/pybindgen/cppattribute.py @@ -2,7 +2,7 @@ Wraps C++ class instance/static attributes. """ -from typehandlers.base import ForwardWrapperBase +from typehandlers.base import ForwardWrapperBase, ReverseWrapperBase from typehandlers import codesink import settings @@ -80,6 +80,57 @@ class CppStaticAttributeGetter(ForwardWrapperBase): code_sink.writeln('}') +class CppInstanceAttributeSetter(ReverseWrapperBase): + ''' + A setter for a C++ instance attribute. + ''' + def __init__(self, value_type, class_, attribute_name): + """ + value_type -- a ReturnValue object handling the value type; + class_ -- the class (CppClass object) + attribute_name -- name of attribute + """ + super(CppInstanceAttributeSetter, self).__init__( + value_type, [], "return -1;") + self.class_ = class_ + self.attribute_name = attribute_name + 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): + """ + code_sink -- a CodeSink instance that will receive the generated code + """ + + self.declarations.declare_variable('PyObject*', 'py_retval') + self.before_call.write_code( + 'py_retval = Py_BuildValue("(O)", value);') + self.before_call.add_cleanup_code('Py_DECREF(py_retval);') + 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),)) + ## cleanup and return + self.after_call.write_cleanup() + self.after_call.write_code('return 0;') + + ## now generate the function itself + code_sink.writeln("static int %s(%s *self, PyObject *value)" + % (self.c_function_name, self.class_.pystruct)) + code_sink.writeln('{') + code_sink.indent() + + self.declarations.get_code_sink().flush_to(code_sink) + code_sink.writeln() + self.before_call.sink.flush_to(code_sink) + self.after_call.sink.flush_to(code_sink) + + code_sink.unindent() + code_sink.writeln('}') + + class PyDescrGenerator(object): """ Class that generates a Python descriptor type. diff --git a/pybindgen/cppclass.py b/pybindgen/cppclass.py index cf5748c..1666ad4 100644 --- a/pybindgen/cppclass.py +++ b/pybindgen/cppclass.py @@ -5,7 +5,8 @@ Wrap C++ classes and methods from typehandlers.base import ForwardWrapperBase, Parameter, ReturnValue from cppmethod import CppMethod, CppConstructor, CppNoConstructor -from cppattribute import (CppInstanceAttributeGetter, CppStaticAttributeGetter, +from cppattribute import (CppInstanceAttributeGetter, CppInstanceAttributeSetter, + CppStaticAttributeGetter, PyDescrGenerator) @@ -169,7 +170,8 @@ class CppClass(object): """ assert isinstance(value_type, ReturnValue) getter = CppInstanceAttributeGetter(value_type, self, name) - self.attributes.append((name, getter, None)) + setter = CppInstanceAttributeSetter(value_type, self, name) + self.attributes.append((name, getter, setter)) def generate_forward_declarations(self, code_sink): """Generates forward declarations for the instance and type @@ -274,6 +276,11 @@ typedef struct { module.after_init.write_code( 'PyDict_SetItemString(%s.tp_dict, "%s", PyObject_New(PyObject, &%s));' % (self.pytypestruct, name, descriptor.pytypestruct)) + + ## generate setter wrapper function body + if isinstance(setter, CppInstanceAttributeSetter): + have_instance_attributes = True + setter.generate(code_sink) if have_instance_attributes: getset_table_name = "_%s__getsets" % self.pystruct @@ -287,6 +294,8 @@ typedef struct { code_sink.writeln('{') code_sink.indent() code_sink.writeln('"%s", /* attribute name */' % name) + + ## getter if isinstance(getter, CppInstanceAttributeGetter): getter_c_name = getter.c_function_name else: @@ -294,10 +303,16 @@ typedef struct { code_sink.writeln( '(getter) %s, /* C function to get the attribute */' % getter_c_name) - setter_c_name = "NULL" + + ## setter + if isinstance(setter, CppInstanceAttributeSetter): + setter_c_name = setter.c_function_name + else: + setter_c_name = "NULL" code_sink.writeln( '(setter) %s, /* C function to set the attribute */' % setter_c_name) + code_sink.writeln('NULL, /* optional doc string */') code_sink.writeln('NULL /* optional additional data ' 'for getter and setter */') diff --git a/pybindgen/typehandlers/base.py b/pybindgen/typehandlers/base.py index 94f3144..c17fa08 100644 --- a/pybindgen/typehandlers/base.py +++ b/pybindgen/typehandlers/base.py @@ -443,7 +443,7 @@ class ReverseWrapperBase(object): """ - def __init__(self, return_value, parameters): + def __init__(self, return_value, parameters, error_return=None): ''' Base constructor @@ -459,7 +459,8 @@ class ReverseWrapperBase(object): self.return_value = return_value self.parameters = parameters - error_return = return_value.get_c_error_return() + if error_return is None: + error_return = return_value.get_c_error_return() self.declarations = DeclarationsScope() self.before_call = CodeBlock(error_return, self.declarations) self.after_call = CodeBlock(error_return, self.declarations, @@ -467,10 +468,6 @@ class ReverseWrapperBase(object): self.build_params = BuildValueParameters() self.parse_params = ParseTupleParameters() - self.declarations.declare_variable('PyObject*', 'py_retval') - if return_value.ctype != 'void': - self.declarations.declare_variable(return_value.ctype, 'retval') - def generate_python_call(self): """Generates the code (into self.before_call) to call into Python, storing the result in the variable 'py_retval'; should @@ -487,6 +484,10 @@ class ReverseWrapperBase(object): assert isinstance(decl_modifiers, list) assert all([isinstance(mod, str) for mod in decl_modifiers]) + self.declarations.declare_variable('PyObject*', 'py_retval') + if return_value.ctype != 'void': + self.declarations.declare_variable(return_value.ctype, 'retval') + ## convert the input parameters for param in self.parameters: param.convert_c_to_python(self)