Add support for overloaded C++ class methods.
This commit is contained in:
1 parent
9c63aa2654
commit
32bc7456a8
8 files changed
+111
-32
No files matched your search
@@ -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;
|
||||
}
|
||||
@@ -194,6 +194,9 @@ public:
|
||||
m_zbr->Ref ();
|
||||
}
|
||||
|
||||
int get_int (const char *from_string);
|
||||
int get_int (double from_float);
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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
@@ -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
@@ -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
|
||||
|
||||
@@ -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+'"')))
|
||||
|
||||
|
||||
|
||||
@@ -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;')
|
||||
|
||||
Reference in new issue
Block a user