diff --git a/pybindgen/cppattribute.py b/pybindgen/cppattribute.py new file mode 100644 index 0000000..c1086b9 --- /dev/null +++ b/pybindgen/cppattribute.py @@ -0,0 +1,148 @@ +""" +Wraps C++ class instance/static attributes. +""" + +from typehandlers.base import ForwardWrapperBase +from typehandlers import codesink +import settings + + + +class CppInstanceAttributeGetter(ForwardWrapperBase): + ''' + A getter 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(CppInstanceAttributeGetter, self).__init__( + value_type, [], "return NULL;", "return NULL;", no_c_retval=True) + self.class_ = class_ + self.attribute_name = attribute_name + self.c_function_name = "_wrap_%s__get_%s" % (self.class_.pystruct, + self.attribute_name) + value_type.value = "self->obj->%s" % self.attribute_name + + def generate_call(self): + "virtual method implementation; do not call" + pass + + def generate(self, code_sink): + """ + code_sink -- a CodeSink instance that will receive the generated code + """ + tmp_sink = codesink.MemoryCodeSink() + self.generate_body(tmp_sink) + code_sink.writeln("static PyObject* %s(%s *self)" % (self.c_function_name, + self.class_.pystruct)) + code_sink.writeln('{') + code_sink.indent() + tmp_sink.flush_to(code_sink) + code_sink.unindent() + code_sink.writeln('}') + + +class CppStaticAttributeGetter(ForwardWrapperBase): + ''' + A getter for a C++ class static attribute. + ''' + def __init__(self, value_type, class_, attribute_name): + """ + value_type -- a ReturnValue object handling the value type; + c_value_expression -- C value expression + """ + super(CppStaticAttributeGetter, self).__init__( + value_type, [], "return NULL;", "return NULL;", no_c_retval=True) + self.class_ = class_ + self.attribute_name = attribute_name + self.c_function_name = "_wrap_%s__get_%s" % (self.class_.pystruct, + self.attribute_name) + def generate_call(self): + "virtual method implementation; do not call" + self.before_call.write_code( + "#define retval %s::%s" % (self.class_.name, self.attribute_name)) + self.before_call.add_cleanup_code('#undef retval') + + def generate(self, code_sink): + """ + code_sink -- a CodeSink instance that will receive the generated code + """ + tmp_sink = codesink.MemoryCodeSink() + self.generate_body(tmp_sink) + code_sink.writeln("static PyObject* %s(void)" % self.c_function_name) + code_sink.writeln('{') + code_sink.indent() + tmp_sink.flush_to(code_sink) + code_sink.unindent() + code_sink.writeln('}') + + +class PyDescrGenerator(object): + """ + Class that generates a Python descriptor type. + """ + def __init__(self, name, descr_get, descr_set): + self.name = name + self.descr_get = descr_get + self.descr_set = descr_set + prefix = settings.name_prefix.capitalize() + self.pytypestruct = "Py%s%s_Type" % (prefix, self.name) + + def generate(self, code_sink): + code_sink.writeln(''' +PyTypeObject %(pytypestruct)s = { + PyObject_HEAD_INIT(NULL) + 0, /* ob_size */ + "%(name)s", /* tp_name */ + 0, /* tp_basicsize */ + 0, /* tp_itemsize */ + 0, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_compare */ + 0, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + 0, /* tp_hash */ + 0, /* tp_call */ + 0, /* tp_str */ + 0, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT, /* tp_flags */ + 0, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + 0, /* tp_methods */ + 0, /* tp_members */ + 0, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + (descrgetfunc) %(descr_get)s, /* tp_descr_get */ + (descrsetfunc) %(descr_set)s, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + 0, /* tp_new */ + 0, /* tp_free */ + 0, /* tp_is_gc */ + 0, /* tp_bases */ + 0, /* tp_mro */ + 0, /* tp_cache */ + 0, /* tp_subclasses */ + 0, /* tp_weaklist */ + 0 /* tp_del */ +}; +''' % dict(pytypestruct=self.pytypestruct, name=self.name, + descr_get=(self.descr_get or '0'), + descr_set=(self.descr_set or '0'))) + diff --git a/pybindgen/cppclass.py b/pybindgen/cppclass.py index 7fe9303..cf5748c 100644 --- a/pybindgen/cppclass.py +++ b/pybindgen/cppclass.py @@ -3,338 +3,14 @@ Wrap C++ classes and methods """ from typehandlers.base import ForwardWrapperBase, Parameter, ReturnValue -from typehandlers import codesink + +from cppmethod import CppMethod, CppConstructor, CppNoConstructor +from cppattribute import (CppInstanceAttributeGetter, CppStaticAttributeGetter, + PyDescrGenerator) + + import settings - - - -class CppMethod(ForwardWrapperBase): - """ - Class that generates a wrapper to a C++ class method - """ - - def __init__(self, return_value, method_name, parameters, is_static=False): - """ - return_value -- the method return value - method_name -- name of the method - parameters -- the method parameters - is_static -- whether it is a static method - """ - super(CppMethod, self).__init__( - return_value, parameters, - "return NULL;", "return NULL;") - self.method_name = method_name - self.is_static = is_static - - - def generate_call(self, class_): - "virtual method implementation; do not call" - assert isinstance(class_, CppClass) - if self.is_static: - method = '%s::%s' % (class_.name, self.method_name) - else: - method = 'self->obj->%s' % self.method_name - if self.return_value.ctype == 'void': - self.before_call.write_code( - '%s(%s);' % - (method, ", ".join(self.call_params))) - else: - self.before_call.write_code( - 'retval = %s(%s);' % - (method, ", ".join(self.call_params))) - - - def generate(self, code_sink, class_, method_name, docstring=None): - """ - Generates the wrapper code - code_sink -- a CodeSink instance that will receive the generated code - class_ -- the c++ class wrapper the method belongs to - method_name -- actual name the method will get - docstring -- optional documentation string - - Returns the corresponding PyMethodDef entry string. - """ - assert isinstance(class_, CppClass) - tmp_sink = codesink.MemoryCodeSink() - - self.generate_body(tmp_sink, gen_call_params=[class_]) - - wrapper_function_name = "_wrap_%s_%s" % ( - class_.name, self.method_name) - - flags = self.get_py_method_def_flags() - if self.is_static: - flags.append('METH_STATIC') - - code_sink.writeln("static PyObject *") - if 'METH_STATIC' in flags: - _self_name = 'dummy PYBINDGEN_UNUSED' - else: - _self_name = 'self' - - if 'METH_VARARGS' in flags: - if 'METH_KEYWORDS' in flags: - code_sink.writeln( - "%s(%s *%s, PyObject *args, PyObject *kwargs)" - % (wrapper_function_name, class_.pystruct, _self_name)) - else: - code_sink.writeln( - "%s(%s *%s, PyObject *args)" - % (wrapper_function_name, class_.pystruct, _self_name)) - else: - if 'METH_STATIC' in flags: - code_sink.writeln("%s(void)" % (wrapper_function_name,)) - else: - code_sink.writeln( - "%s(%s *%s)" - % (wrapper_function_name, class_.pystruct, _self_name)) - - code_sink.writeln('{') - code_sink.indent() - tmp_sink.flush_to(code_sink) - code_sink.unindent() - code_sink.writeln('}') - - return "{\"%s\", (PyCFunction) %s, %s, %s }," % \ - (method_name, wrapper_function_name, '|'.join(flags), - (docstring is None and "NULL" or ('"'+docstring+'"'))) - - -class CppConstructor(ForwardWrapperBase): - """ - Class that generates a wrapper to a C++ class constructor. Such - wrapper is used as the python class __init__ method. - """ - - def __init__(self, parameters): - """ - parameters -- the constructor parameters - """ - super(CppConstructor, self).__init__( - None, parameters, - "return -1;", "return -1;", - force_parse=ForwardWrapperBase.PARSE_TUPLE_AND_KEYWORDS) - - def generate_call(self, class_): - "virtual method implementation; do not call" - assert isinstance(class_, CppClass) - self.before_call.write_code( - 'self->obj = new %s(%s);' % - (class_.name, ", ".join(self.call_params))) - - def generate(self, code_sink, class_): - """ - Generates the wrapper code - code_sink -- a CodeSink instance that will receive the generated code - class_ -- the c++ class wrapper the method belongs to - - Returns the wrapper function name. - """ - assert isinstance(class_, CppClass) - tmp_sink = codesink.MemoryCodeSink() - - self.generate_body(tmp_sink, gen_call_params=[class_]) - - assert ((self.parse_params.get_parameters() == ['""']) - or self.parse_params.get_keywords() is not None), \ - ("something went wrong with the type handlers;" - " constructors need parameter names, " - "yet no names were given for the class %s constructor" - % class_.name) - - wrapper_function_name = "_wrap_%s__tp_init" % ( - class_.name,) - code_sink.writeln("static int") - code_sink.writeln( - "%s(%s *self, PyObject *args, PyObject *kwargs)" - % (wrapper_function_name, class_.pystruct)) - code_sink.writeln('{') - code_sink.indent() - tmp_sink.flush_to(code_sink) - code_sink.writeln('return 0;') - code_sink.unindent() - code_sink.writeln('}') - - return wrapper_function_name - - -class CppNoConstructor(ForwardWrapperBase): - """ - - Class that generates a constructor that raises an exception saying - that the class has no constructor. - - """ - - def __init__(self): - """ - """ - super(CppNoConstructor, self).__init__( - None, [], - "return -1;", "return -1;") - - def generate_call(self, code_sink): - "dummy method, not really called" - pass - - def generate(self, code_sink, class_): - """ - Generates the wrapper code - code_sink -- a CodeSink instance that will receive the generated code - class_ -- the c++ class wrapper the method belongs to - - Returns the wrapper function name. - """ - assert isinstance(class_, CppClass) - - wrapper_function_name = "_wrap_%s__tp_init" % ( - class_.name,) - code_sink.writeln("static int") - code_sink.writeln("%s(void)" % wrapper_function_name) - code_sink.writeln('{') - code_sink.indent() - code_sink.writeln('PyErr_SetString(PyExc_TypeError, "class \'%s\' ' - 'cannot be constructed");' % class_.name) - code_sink.writeln('return -1;') - code_sink.unindent() - code_sink.writeln('}') - - return wrapper_function_name - -class CppInstanceAttributeGetter(ForwardWrapperBase): - ''' - A getter 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(CppInstanceAttributeGetter, self).__init__( - value_type, [], "return NULL;", "return NULL;", no_c_retval=True) - self.class_ = class_ - self.attribute_name = attribute_name - self.c_function_name = "_wrap_%s__get_%s" % (self.class_.pystruct, - self.attribute_name) - value_type.value = "self->obj->%s" % self.attribute_name - - def generate_call(self): - "virtual method implementation; do not call" - pass - - def generate(self, code_sink): - """ - code_sink -- a CodeSink instance that will receive the generated code - """ - tmp_sink = codesink.MemoryCodeSink() - self.generate_body(tmp_sink) - code_sink.writeln("static PyObject* %s(%s *self)" % (self.c_function_name, - self.class_.pystruct)) - code_sink.writeln('{') - code_sink.indent() - tmp_sink.flush_to(code_sink) - code_sink.unindent() - code_sink.writeln('}') - -class CppStaticAttributeGetter(ForwardWrapperBase): - ''' - A getter for a C++ class static attribute. - ''' - def __init__(self, value_type, class_, attribute_name): - """ - value_type -- a ReturnValue object handling the value type; - c_value_expression -- C value expression - """ - super(CppStaticAttributeGetter, self).__init__( - value_type, [], "return NULL;", "return NULL;", no_c_retval=True) - self.class_ = class_ - self.attribute_name = attribute_name - self.c_function_name = "_wrap_%s__get_%s" % (self.class_.pystruct, - self.attribute_name) - def generate_call(self): - "virtual method implementation; do not call" - self.before_call.write_code( - "#define retval %s::%s" % (self.class_.name, self.attribute_name)) - self.before_call.add_cleanup_code('#undef retval') - - def generate(self, code_sink): - """ - code_sink -- a CodeSink instance that will receive the generated code - """ - tmp_sink = codesink.MemoryCodeSink() - self.generate_body(tmp_sink) - code_sink.writeln("static PyObject* %s(void)" % self.c_function_name) - code_sink.writeln('{') - code_sink.indent() - tmp_sink.flush_to(code_sink) - code_sink.unindent() - code_sink.writeln('}') - - -class PyDescrGenerator(object): - def __init__(self, name, descr_get, descr_set): - self.name = name - self.descr_get = descr_get - self.descr_set = descr_set - prefix = settings.name_prefix.capitalize() - self.pytypestruct = "Py%s%s_Type" % (prefix, self.name) - - def generate(self, code_sink): - code_sink.writeln(''' -PyTypeObject %(pytypestruct)s = { - PyObject_HEAD_INIT(NULL) - 0, /* ob_size */ - "%(name)s", /* tp_name */ - 0, /* tp_basicsize */ - 0, /* tp_itemsize */ - 0, /* tp_dealloc */ - 0, /* tp_print */ - 0, /* tp_getattr */ - 0, /* tp_setattr */ - 0, /* tp_compare */ - 0, /* tp_repr */ - 0, /* tp_as_number */ - 0, /* tp_as_sequence */ - 0, /* tp_as_mapping */ - 0, /* tp_hash */ - 0, /* tp_call */ - 0, /* tp_str */ - 0, /* tp_getattro */ - 0, /* tp_setattro */ - 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT, /* tp_flags */ - 0, /* tp_doc */ - 0, /* tp_traverse */ - 0, /* tp_clear */ - 0, /* tp_richcompare */ - 0, /* tp_weaklistoffset */ - 0, /* tp_iter */ - 0, /* tp_iternext */ - 0, /* tp_methods */ - 0, /* tp_members */ - 0, /* tp_getset */ - 0, /* tp_base */ - 0, /* tp_dict */ - (descrgetfunc) %(descr_get)s, /* tp_descr_get */ - (descrsetfunc) %(descr_set)s, /* tp_descr_set */ - 0, /* tp_dictoffset */ - 0, /* tp_init */ - 0, /* tp_alloc */ - 0, /* tp_new */ - 0, /* tp_free */ - 0, /* tp_is_gc */ - 0, /* tp_bases */ - 0, /* tp_mro */ - 0, /* tp_cache */ - 0, /* tp_subclasses */ - 0, /* tp_weaklist */ - 0 /* tp_del */ -}; -''' % dict(pytypestruct=self.pytypestruct, name=self.name, - descr_get=(self.descr_get or '0'), - descr_set=(self.descr_set or '0'))) - + class CppClass(object): @@ -477,8 +153,7 @@ class CppClass(object): def add_static_attribute(self, value_type, name): """ - XXX: not working correctly: 1. getter only works from - instances, not classes. 2. setter not yet implemented. + XXX: setter not yet implemented. value_type -- a ReturnValue object """ @@ -699,7 +374,6 @@ static void code_sink.writeln(self.TYPE_TMPL % dict_) - class CppClassParameter(Parameter): "Class handlers" CTYPES = [] diff --git a/pybindgen/cppmethod.py b/pybindgen/cppmethod.py new file mode 100644 index 0000000..38b8291 --- /dev/null +++ b/pybindgen/cppmethod.py @@ -0,0 +1,204 @@ +""" +Wrap C++ class methods and constructods. +""" + +from typehandlers.base import ForwardWrapperBase +from typehandlers import codesink +#from cppclass import CppClass + + + +class CppMethod(ForwardWrapperBase): + """ + Class that generates a wrapper to a C++ class method + """ + + def __init__(self, return_value, method_name, parameters, is_static=False): + """ + return_value -- the method return value + method_name -- name of the method + parameters -- the method parameters + is_static -- whether it is a static method + """ + super(CppMethod, self).__init__( + return_value, parameters, + "return NULL;", "return NULL;") + self.method_name = method_name + self.is_static = is_static + + + def generate_call(self, class_): + "virtual method implementation; do not call" + #assert isinstance(class_, CppClass) + if self.is_static: + method = '%s::%s' % (class_.name, self.method_name) + else: + method = 'self->obj->%s' % self.method_name + if self.return_value.ctype == 'void': + self.before_call.write_code( + '%s(%s);' % + (method, ", ".join(self.call_params))) + else: + self.before_call.write_code( + 'retval = %s(%s);' % + (method, ", ".join(self.call_params))) + + + def generate(self, code_sink, class_, method_name, docstring=None): + """ + Generates the wrapper code + code_sink -- a CodeSink instance that will receive the generated code + class_ -- the c++ class wrapper the method belongs to + method_name -- actual name the method will get + docstring -- optional documentation string + + Returns the corresponding PyMethodDef entry string. + """ + #assert isinstance(class_, CppClass) + tmp_sink = codesink.MemoryCodeSink() + + self.generate_body(tmp_sink, gen_call_params=[class_]) + + wrapper_function_name = "_wrap_%s_%s" % ( + class_.name, self.method_name) + + flags = self.get_py_method_def_flags() + if self.is_static: + flags.append('METH_STATIC') + + code_sink.writeln("static PyObject *") + if 'METH_STATIC' in flags: + _self_name = 'dummy PYBINDGEN_UNUSED' + else: + _self_name = 'self' + + if 'METH_VARARGS' in flags: + if 'METH_KEYWORDS' in flags: + code_sink.writeln( + "%s(%s *%s, PyObject *args, PyObject *kwargs)" + % (wrapper_function_name, class_.pystruct, _self_name)) + else: + code_sink.writeln( + "%s(%s *%s, PyObject *args)" + % (wrapper_function_name, class_.pystruct, _self_name)) + else: + if 'METH_STATIC' in flags: + code_sink.writeln("%s(void)" % (wrapper_function_name,)) + else: + code_sink.writeln( + "%s(%s *%s)" + % (wrapper_function_name, class_.pystruct, _self_name)) + + code_sink.writeln('{') + code_sink.indent() + tmp_sink.flush_to(code_sink) + code_sink.unindent() + code_sink.writeln('}') + + return "{\"%s\", (PyCFunction) %s, %s, %s }," % \ + (method_name, wrapper_function_name, '|'.join(flags), + (docstring is None and "NULL" or ('"'+docstring+'"'))) + + +class CppConstructor(ForwardWrapperBase): + """ + Class that generates a wrapper to a C++ class constructor. Such + wrapper is used as the python class __init__ method. + """ + + def __init__(self, parameters): + """ + parameters -- the constructor parameters + """ + super(CppConstructor, self).__init__( + None, parameters, + "return -1;", "return -1;", + force_parse=ForwardWrapperBase.PARSE_TUPLE_AND_KEYWORDS) + + def generate_call(self, class_): + "virtual method implementation; do not call" + #assert isinstance(class_, CppClass) + self.before_call.write_code( + 'self->obj = new %s(%s);' % + (class_.name, ", ".join(self.call_params))) + + def generate(self, code_sink, class_): + """ + Generates the wrapper code + code_sink -- a CodeSink instance that will receive the generated code + class_ -- the c++ class wrapper the method belongs to + + Returns the wrapper function name. + """ + #assert isinstance(class_, CppClass) + tmp_sink = codesink.MemoryCodeSink() + + self.generate_body(tmp_sink, gen_call_params=[class_]) + + assert ((self.parse_params.get_parameters() == ['""']) + or self.parse_params.get_keywords() is not None), \ + ("something went wrong with the type handlers;" + " constructors need parameter names, " + "yet no names were given for the class %s constructor" + % class_.name) + + wrapper_function_name = "_wrap_%s__tp_init" % ( + class_.name,) + code_sink.writeln("static int") + code_sink.writeln( + "%s(%s *self, PyObject *args, PyObject *kwargs)" + % (wrapper_function_name, class_.pystruct)) + code_sink.writeln('{') + code_sink.indent() + tmp_sink.flush_to(code_sink) + code_sink.writeln('return 0;') + code_sink.unindent() + code_sink.writeln('}') + + return wrapper_function_name + + +class CppNoConstructor(ForwardWrapperBase): + """ + + Class that generates a constructor that raises an exception saying + that the class has no constructor. + + """ + + def __init__(self): + """ + Constructor. + """ + super(CppNoConstructor, self).__init__( + None, [], + "return -1;", "return -1;") + + def generate_call(self, code_sink): + "dummy method, not really called" + pass + + def generate(self, code_sink, class_): + """ + Generates the wrapper code + code_sink -- a CodeSink instance that will receive the generated code + class_ -- the c++ class wrapper the method belongs to + + Returns the wrapper function name. + """ + #assert isinstance(class_, CppClass) + + wrapper_function_name = "_wrap_%s__tp_init" % ( + class_.name,) + code_sink.writeln("static int") + code_sink.writeln("%s(void)" % wrapper_function_name) + code_sink.writeln('{') + code_sink.indent() + code_sink.writeln('PyErr_SetString(PyExc_TypeError, "class \'%s\' ' + 'cannot be constructed");' % class_.name) + code_sink.writeln('return -1;') + code_sink.unindent() + code_sink.writeln('}') + + return wrapper_function_name +