diff --git a/examples/foo.cc b/examples/foo.cc index a00c8a9..7cecc58 100644 --- a/examples/foo.cc +++ b/examples/foo.cc @@ -28,3 +28,12 @@ int get_int_from_float(double from_float) std::string SomeObject::staticData = std::string("Hello Static World!"); +int SomeObject::get_int (const char *from_string) +{ + return atoi(from_string); +} + +int SomeObject::get_int (double from_float) +{ + return (int) from_float; +} diff --git a/examples/foo.h b/examples/foo.h index 47c86c4..49da91f 100644 --- a/examples/foo.h +++ b/examples/foo.h @@ -194,6 +194,9 @@ public: m_zbr->Ref (); } + int get_int (const char *from_string); + int get_int (double from_float); + }; diff --git a/examples/foomodulegen.py b/examples/foomodulegen.py index 5709882..adcd05e 100755 --- a/examples/foomodulegen.py +++ b/examples/foomodulegen.py @@ -146,6 +146,15 @@ def my_module_gen(out_file): SomeObject.add_method(CppMethod( ReturnValue.new('PointerHolder'), 'get_zbr_pholder', [])) + ## test overloaded methods + SomeObject.add_method(CppMethod(ReturnValue.new('int'), 'get_int', + [Parameter.new('const char*', 'from_string')]), + name="get_int") + SomeObject.add_method(CppMethod(ReturnValue.new('int'), 'get_int', + [Parameter.new('double', 'from_float')]), + name="get_int") + + mod.add_class(SomeObject) mod.generate(FileCodeSink(out_file)) diff --git a/examples/footest.py b/examples/footest.py index b0f091a..efad0d2 100644 --- a/examples/footest.py +++ b/examples/footest.py @@ -100,6 +100,17 @@ class TestFoo(unittest.TestCase): self.assertEqual(v2, 123) self.assertRaises(TypeError, foo.get_int, [123]) + + def test_overloaded_methods(self): + obj = foo.SomeObject("zbr") + + v1 = obj.get_int(123.0) + self.assertEqual(v1, 123) + + v2 = obj.get_int("123") + self.assertEqual(v2, 123) + + self.assertRaises(TypeError, obj.get_int, [123]) if __name__ == '__main__': diff --git a/pybindgen/cppclass.py b/pybindgen/cppclass.py index 321cc51..3ed86c2 100644 --- a/pybindgen/cppclass.py +++ b/pybindgen/cppclass.py @@ -6,7 +6,7 @@ import warnings from typehandlers.base import ForwardWrapperBase, Parameter, ReturnValue -from cppmethod import CppMethod, CppConstructor, CppNoConstructor +from cppmethod import CppMethod, CppConstructor, CppNoConstructor, CppOverloadedMethod from cppattribute import (CppInstanceAttributeGetter, CppInstanceAttributeSetter, CppStaticAttributeGetter, CppStaticAttributeSetter, PyGetSetDef, PyMetaclass) @@ -86,7 +86,7 @@ class CppClass(object): if not given) """ self.name = name - self.methods = [] # (name, wrapper) pairs + self.methods = {} # name => OverloadedMethod self.constructors = [] # (name, wrapper) pairs self.slots = dict() @@ -145,7 +145,16 @@ class CppClass(object): assert isinstance(wrapper, CppMethod) if name is None: name = wrapper.method_name - self.methods.append((name, wrapper)) + + try: + overload = self.methods[name] + except KeyError: + overload = CppOverloadedMethod(name) + overload.pystruct = self.pystruct + self.methods[name] = overload + + wrapper.class_ = self + overload.add(wrapper) def add_constructor(self, wrapper): @@ -314,10 +323,10 @@ typedef struct { def _generate_methods(self, code_sink): """generate the method wrappers""" method_defs = [] - for meth_name, meth_wrapper in self.methods: + for meth_name, overload in self.methods.iteritems(): code_sink.writeln() - method_defs.append(meth_wrapper.generate( - code_sink, self, meth_name)) + overload.generate(code_sink) + method_defs.append(overload.get_py_method_def(meth_name)) code_sink.writeln() ## generate the method table code_sink.writeln("static PyMethodDef %s_methods[] = {" % (self.name,)) diff --git a/pybindgen/cppmethod.py b/pybindgen/cppmethod.py index 38b8291..c1fabd6 100644 --- a/pybindgen/cppmethod.py +++ b/pybindgen/cppmethod.py @@ -4,7 +4,7 @@ Wrap C++ class methods and constructods. from typehandlers.base import ForwardWrapperBase from typehandlers import codesink -#from cppclass import CppClass +import overloading @@ -25,6 +25,19 @@ class CppMethod(ForwardWrapperBase): "return NULL;", "return NULL;") self.method_name = method_name self.is_static = is_static + self._class = None + self.docstring = None + self.wrapper_base_name = None + self.wrapper_actual_name = None + + + def set_class(self, class_): + self._class = class_ + self.wrapper_base_name = "_wrap_%s_%s" % ( + class_.name, self.method_name) + def get_class(self): + return self._class + class_ = property(get_class, set_class) def generate_call(self, class_): @@ -44,27 +57,27 @@ class CppMethod(ForwardWrapperBase): (method, ", ".join(self.call_params))) - def generate(self, code_sink, class_, method_name, docstring=None): + def generate(self, code_sink, wrapper_name=None, extra_wrapper_params=()): """ 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 + extra_wrapper_params -- extra parameters the wrapper function should receive Returns the corresponding PyMethodDef entry string. """ + class_ = self.class_ #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) + if wrapper_name is None: + self.wrapper_actual_name = self.wrapper_base_name + else: + self.wrapper_actual_name = wrapper_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: @@ -72,22 +85,30 @@ class CppMethod(ForwardWrapperBase): else: _self_name = 'self' + if extra_wrapper_params: + extra = ', '.join([''] + list(extra_wrapper_params)) + else: + extra = '' 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)) + "%s(%s *%s, PyObject *args, PyObject *kwargs%s)" + % (self.wrapper_actual_name, class_.pystruct, _self_name, extra)) else: + assert not extra_wrapper_params, \ + "extra_wrapper_params can only be used with full varargs/kwargs wrappers" code_sink.writeln( "%s(%s *%s, PyObject *args)" - % (wrapper_function_name, class_.pystruct, _self_name)) + % (self.wrapper_actual_name, class_.pystruct, _self_name)) else: + assert not extra_wrapper_params, \ + "extra_wrapper_params can only be used with full varargs/kwargs wrappers" if 'METH_STATIC' in flags: - code_sink.writeln("%s(void)" % (wrapper_function_name,)) + code_sink.writeln("%s(void)" % (self.wrapper_actual_name,)) else: code_sink.writeln( "%s(%s *%s)" - % (wrapper_function_name, class_.pystruct, _self_name)) + % (self.wrapper_actual_name, class_.pystruct, _self_name)) code_sink.writeln('{') code_sink.indent() @@ -95,11 +116,24 @@ class CppMethod(ForwardWrapperBase): code_sink.unindent() code_sink.writeln('}') + def get_py_method_def_flags(self): + flags = super(CppMethod, self).get_py_method_def_flags() + if self.is_static: + flags.append('METH_STATIC') + return flags + + def get_py_method_def(self, method_name): + flags = self.get_py_method_def_flags() return "{\"%s\", (PyCFunction) %s, %s, %s }," % \ - (method_name, wrapper_function_name, '|'.join(flags), - (docstring is None and "NULL" or ('"'+docstring+'"'))) + (method_name, self.wrapper_actual_name, '|'.join(flags), + (self.docstring is None and "NULL" or ('"'+self.docstring+'"'))) +class CppOverloadedMethod(overloading.OverloadedWrapper): + RETURN_TYPE = 'PyObject *' + ERROR_RETURN = 'return NULL;' + + class CppConstructor(ForwardWrapperBase): """ Class that generates a wrapper to a C++ class constructor. Such diff --git a/pybindgen/function.py b/pybindgen/function.py index 1bf591d..c1188ff 100644 --- a/pybindgen/function.py +++ b/pybindgen/function.py @@ -22,7 +22,8 @@ class Function(ForwardWrapperBase): parse_error_return="return NULL;", error_return="return NULL;") self.function_name = function_name - self.wrapper_name = None + self.wrapper_base_name = "_wrap_%s" % (self.function_name,) + self.wrapper_actual_name = None self.docstring = docstring def generate_call(self): @@ -41,13 +42,14 @@ class Function(ForwardWrapperBase): wrapper_name -- name of wrapper function """ if wrapper_name is None: - wrapper_name = "_wrap_%s" % (self.function_name,) - self.wrapper_name = wrapper_name + self.wrapper_actual_name = self.wrapper_base_name + else: + self.wrapper_actual_name = wrapper_name tmp_sink = codesink.MemoryCodeSink() self.generate_body(tmp_sink) code_sink.writeln("static PyObject *") prototype_line = ("%s(PyObject *dummy PYBINDGEN_UNUSED, " - "PyObject *args, PyObject *kwargs") % (wrapper_name,) + "PyObject *args, PyObject *kwargs") % (self.wrapper_actual_name,) if extra_wrapper_params: prototype_line += ", " + ", ".join(extra_wrapper_params) prototype_line += ')' @@ -57,7 +59,6 @@ class Function(ForwardWrapperBase): tmp_sink.flush_to(code_sink) code_sink.unindent() code_sink.writeln('}') - return wrapper_name def get_py_method_def(self, name): @@ -68,7 +69,7 @@ class Function(ForwardWrapperBase): """ flags = self.get_py_method_def_flags() return "{\"%s\", (PyCFunction) %s, %s, %s }," % \ - (name, self.wrapper_name, '|'.join(flags), + (name, self.wrapper_actual_name, '|'.join(flags), (self.docstring is None and "NULL" or ('"'+self.docstring+'"'))) diff --git a/pybindgen/overloading.py b/pybindgen/overloading.py index 298614d..6144ce6 100644 --- a/pybindgen/overloading.py +++ b/pybindgen/overloading.py @@ -24,6 +24,7 @@ class OverloadedWrapper(object): self.wrappers = [] self.wrapper_name = wrapper_name self.wrapper_function_name = None + self.pystruct = 'PyObject' def add(self, wrapper): """ @@ -40,12 +41,14 @@ class OverloadedWrapper(object): if len(self.wrappers) == 1: ## special case when there's only one wrapper; keep ## simple things simple - self.wrapper_function_name = self.wrappers[0].generate(code_sink) + self.wrappers[0].generate(code_sink) + self.wrapper_function_name = self.wrappers[0].wrapper_actual_name + assert self.wrapper_function_name is not None else: ## multiple overloaded wrappers case.. ## Generate the individual "low level" wrappers that handle a single prototype - self.wrapper_function_name = "_wrap_%s" % (self.wrapper_name,) + self.wrapper_function_name = self.wrappers[0].wrapper_base_name delegate_wrappers = [] for number, wrapper in enumerate(self.wrappers): ## enforce uniform method flags @@ -62,14 +65,14 @@ return NULL;""" wrapper_name = "%s__%i" % (self.wrapper_function_name, number) wrapper.set_parse_error_return(error_return) wrapper.generate(code_sink, wrapper_name, - extra_wrapper_params=["PyObject **return_exception"]) + extra_wrapper_params=["PyObject **return_exception"]) delegate_wrappers.append(wrapper_name) ## Generate the 'main wrapper' that calls the other ones code_sink.writeln("static " + self.RETURN_TYPE) - code_sink.writeln("%s(PyObject *self," + code_sink.writeln("%s(%s *self," " PyObject *args, PyObject *kwargs)" - % (self.wrapper_function_name,)) + % (self.wrapper_function_name, self.pystruct)) code_sink.writeln('{') code_sink.indent() code_sink.writeln(self.RETURN_TYPE + ' retval;')