Add support for overloaded C++ class methods.

This commit is contained in:
Gustavo J. A. M. Carneiro committed 2007-08-13 17:57:47 +01:00
1 parent 9c63aa2654
commit 32bc7456a8
8 files changed
+111 -32

No files matched your search

+9
View File
@@ -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;
}
+3
View File
@@ -194,6 +194,9 @@ public:
m_zbr->Ref ();
}
int get_int (const char *from_string);
int get_int (double from_float);
};
+9
View File
@@ -146,6 +146,15 @@ def my_module_gen(out_file):
SomeObject.add_method(CppMethod(
ReturnValue.new('PointerHolder<Zbr>'), '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))
+11
View File
@@ -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__':
+15 -6
View File
@@ -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,))
+49 -15
View File
@@ -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
+7 -6
View File
@@ -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+'"')))
+8 -5
View File
@@ -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;')