Skip to content

Commit

Permalink
More flexible python builtin slots
Browse files Browse the repository at this point in the history
The closure names used for builtin slots are mangled with their functype so
 that overloaded C++ method names can be used for multiple slots.
For example:
%feature("python:slot", "mp_subscript", functype="binaryfunc") SimpleArray::__getitem__;
%feature("python:slot", "sq_item", functype="ssizeargfunc") SimpleArray::__getitem__(Py_ssize_t n);
will generate closures:
  SWIGPY_SSIZEARGFUNC_CLOSURE(_wrap_SimpleArray___getitem__) /* defines _wrap_SimpleArray___getitem___ssizeargfunc_closure */
  SWIGPY_BINARYFUNC_CLOSURE(_wrap_SimpleArray___getitem__) /* defines _wrap_SimpleArray___getitem___binaryfunc_closure */
  • Loading branch information
wsfulton committed Sep 25, 2016
1 parent 5b8e8fa commit 848628a
Show file tree
Hide file tree
Showing 5 changed files with 140 additions and 24 deletions.
18 changes: 18 additions & 0 deletions CHANGES.current
Expand Up @@ -11,6 +11,24 @@ Version 3.0.11 (in progress)
we checked an uninitialised value instead. Fixes #627. Based on
patch from Sergey Seroshtan.

2016-09-22: wsfulton
[Python] More flexible python builtin slots for overloaded C++ function.

The closure names used for builtin slots are mangled with their functype so
that overloaded C++ method names can be used for multiple slots.
For example:

%feature("python:slot", "mp_subscript", functype="binaryfunc") SimpleArray::__getitem__;
%feature("python:slot", "sq_item", functype="ssizeargfunc") SimpleArray::__getitem__(Py_ssize_t n);

will generate closures:

SWIGPY_SSIZEARGFUNC_CLOSURE(_wrap_SimpleArray___getitem__) /* defines _wrap_SimpleArray___getitem___ssizeargfunc_closure */
SWIGPY_BINARYFUNC_CLOSURE(_wrap_SimpleArray___getitem__) /* defines _wrap_SimpleArray___getitem___binaryfunc_closure */

Previously only one name was defined: _wrap_SimpleArray___getitem___closure.
Hence the overloaded __getitem__ method can be used to support both mp_subscript and sq_item slots.

2016-09-17: wsfulton
[Python] Fix iterators for containers of NULL pointers (or Python None) when using
-builtin. Previously iteration would stop at the first element that was NULL.
Expand Down
13 changes: 13 additions & 0 deletions Examples/test-suite/python/python_builtin_runme.py
Expand Up @@ -79,3 +79,16 @@
if MyClass.less_than_counts != 6:
raise RuntimeError("python:compare feature not working")

sa = SimpleArray(5)
elements = [x for x in sa]
if elements != [0, 10, 20, 30, 40]:
raise RuntimeError("Iteration not working")
if len(sa) != 5:
raise RuntimeError("len not working")
for i in range(5):
if sa[i] != i*10:
raise RuntimeError("indexing not working")
subslice = sa[1:3]
elements = [x for x in subslice]
if elements != [10, 20]:
raise RuntimeError("slice not working")
63 changes: 63 additions & 0 deletions Examples/test-suite/python_builtin.i
Expand Up @@ -136,3 +136,66 @@ void Dealloc2Destroyer(PyObject *v) {
};
int MyClass::less_than_counts = 0;
%}

// Test 6 add in container __getitem__ to support basic sequence protocol
// Tests overloaded functions being used for more than one slot (mp_subscript and sq_item)
%include <exception.i>
%include <std_except.i>
%apply int {Py_ssize_t}
%typemap(in) PySliceObject * {
if (!PySlice_Check($input))
SWIG_exception(SWIG_TypeError, "in method '$symname', argument $argnum of type '$type'");
$1 = (PySliceObject *)$input;
}
%typemap(typecheck,precedence=300) PySliceObject* {
$1 = PySlice_Check($input);
}

%feature("python:slot", "mp_subscript", functype="binaryfunc") SimpleArray::__getitem__(PySliceObject *slice);
%feature("python:slot", "sq_item", functype="ssizeargfunc") SimpleArray::__getitem__(Py_ssize_t n);
%feature("python:slot", "sq_length", functype="lenfunc") SimpleArray::__len__;
%inline %{
class SimpleArray {
size_t size;
int numbers[5];
public:
SimpleArray(size_t size) : size(size) {
for (size_t x = 0; x<size; ++x)
numbers[x] = x*10;
}

Py_ssize_t __len__() {
return size;
}

int __getitem__(Py_ssize_t n) throw (std::out_of_range) {
if (n >= (int)size)
throw std::out_of_range("Index too large");
return numbers[n];
}

SimpleArray __getitem__(PySliceObject *slice) throw (std::out_of_range, std::invalid_argument) {
if (!PySlice_Check(slice))
throw std::invalid_argument("Slice object expected");
Py_ssize_t i, j, step;
#if PY_VERSION_HEX >= 0x03020000
PySlice_GetIndices((PyObject *)slice, size, &i, &j, &step);
#else
PySlice_GetIndices((PySliceObject *)slice, size, &i, &j, &step);
#endif
if (step != 1)
throw std::invalid_argument("Only a step size of 1 is implemented");

{
Py_ssize_t ii = i<0 ? 0 : i>=size ? size-1 : i;
Py_ssize_t jj = j<0 ? 0 : j>=size ? size-1 : j;
if (ii > jj)
throw std::invalid_argument("getitem i should not be larger than j");
SimpleArray n(jj-ii);
for (size_t x = 0; x<size; ++x)
n.numbers[x] = numbers[x+ii];
return n;
}
}
};
%}
42 changes: 27 additions & 15 deletions Lib/python/builtin.swg
@@ -1,12 +1,12 @@
#define SWIGPY_UNARYFUNC_CLOSURE(wrapper) \
SWIGINTERN PyObject * \
wrapper##_closure(PyObject *a) { \
wrapper##_unaryfunc_closure(PyObject *a) { \
return wrapper(a, NULL); \
}

#define SWIGPY_DESTRUCTOR_CLOSURE(wrapper) \
SWIGINTERN void \
wrapper##_closure(PyObject *a) { \
wrapper##_destructor_closure(PyObject *a) { \
SwigPyObject *sobj; \
sobj = (SwigPyObject *)a; \
Py_XDECREF(sobj->dict); \
Expand All @@ -32,7 +32,7 @@ wrapper##_closure(PyObject *a) { \

#define SWIGPY_INQUIRY_CLOSURE(wrapper) \
SWIGINTERN int \
wrapper##_closure(PyObject *a) { \
wrapper##_inquiry_closure(PyObject *a) { \
PyObject *pyresult; \
int result; \
pyresult = wrapper(a, NULL); \
Expand All @@ -41,9 +41,15 @@ wrapper##_closure(PyObject *a) { \
return result; \
}

#define SWIGPY_GETITERFUNC_CLOSURE(wrapper) \
SWIGINTERN PyObject * \
wrapper##_getiterfunc_closure(PyObject *a) { \
return wrapper(a, NULL); \
}

#define SWIGPY_BINARYFUNC_CLOSURE(wrapper) \
SWIGINTERN PyObject * \
wrapper##_closure(PyObject *a, PyObject *b) { \
wrapper##_binaryfunc_closure(PyObject *a, PyObject *b) { \
PyObject *tuple, *result; \
tuple = PyTuple_New(1); \
assert(tuple); \
Expand All @@ -58,7 +64,7 @@ typedef ternaryfunc ternarycallfunc;

#define SWIGPY_TERNARYFUNC_CLOSURE(wrapper) \
SWIGINTERN PyObject * \
wrapper##_closure(PyObject *a, PyObject *b, PyObject *c) { \
wrapper##_ternaryfunc_closure(PyObject *a, PyObject *b, PyObject *c) { \
PyObject *tuple, *result; \
tuple = PyTuple_New(2); \
assert(tuple); \
Expand All @@ -73,13 +79,13 @@ wrapper##_closure(PyObject *a, PyObject *b, PyObject *c) { \

#define SWIGPY_TERNARYCALLFUNC_CLOSURE(wrapper) \
SWIGINTERN PyObject * \
wrapper##_closure(PyObject *callable_object, PyObject *args, PyObject *) { \
wrapper##_ternarycallfunc_closure(PyObject *callable_object, PyObject *args, PyObject *) { \
return wrapper(callable_object, args); \
}

#define SWIGPY_LENFUNC_CLOSURE(wrapper) \
SWIGINTERN Py_ssize_t \
wrapper##_closure(PyObject *a) { \
wrapper##_lenfunc_closure(PyObject *a) { \
PyObject *resultobj; \
Py_ssize_t result; \
resultobj = wrapper(a, NULL); \
Expand All @@ -90,7 +96,7 @@ wrapper##_closure(PyObject *a) { \

#define SWIGPY_SSIZESSIZEARGFUNC_CLOSURE(wrapper) \
SWIGINTERN PyObject * \
wrapper##_closure(PyObject *a, Py_ssize_t b, Py_ssize_t c) { \
wrapper##_ssizessizeargfunc_closure(PyObject *a, Py_ssize_t b, Py_ssize_t c) { \
PyObject *tuple, *result; \
tuple = PyTuple_New(2); \
assert(tuple); \
Expand All @@ -103,7 +109,7 @@ wrapper##_closure(PyObject *a, Py_ssize_t b, Py_ssize_t c) { \

#define SWIGPY_SSIZESSIZEOBJARGPROC_CLOSURE(wrapper) \
SWIGINTERN int \
wrapper##_closure(PyObject *a, Py_ssize_t b, Py_ssize_t c, PyObject *d) { \
wrapper##_ssizessizeobjargproc_closure(PyObject *a, Py_ssize_t b, Py_ssize_t c, PyObject *d) { \
PyObject *tuple, *resultobj; \
int result; \
tuple = PyTuple_New(d ? 3 : 2); \
Expand All @@ -123,7 +129,7 @@ wrapper##_closure(PyObject *a, Py_ssize_t b, Py_ssize_t c, PyObject *d) { \

#define SWIGPY_SSIZEARGFUNC_CLOSURE(wrapper) \
SWIGINTERN PyObject * \
wrapper##_closure(PyObject *a, Py_ssize_t b) { \
wrapper##_ssizeargfunc_closure(PyObject *a, Py_ssize_t b) { \
PyObject *tuple, *result; \
tuple = PyTuple_New(1); \
assert(tuple); \
Expand All @@ -135,7 +141,7 @@ wrapper##_closure(PyObject *a, Py_ssize_t b) { \

#define SWIGPY_FUNPACK_SSIZEARGFUNC_CLOSURE(wrapper) \
SWIGINTERN PyObject * \
wrapper##_closure(PyObject *a, Py_ssize_t b) { \
wrapper##_ssizeargfunc_closure(PyObject *a, Py_ssize_t b) { \
PyObject *arg, *result; \
arg = _PyLong_FromSsize_t(b); \
result = wrapper(a, arg); \
Expand All @@ -145,7 +151,7 @@ wrapper##_closure(PyObject *a, Py_ssize_t b) { \

#define SWIGPY_SSIZEOBJARGPROC_CLOSURE(wrapper) \
SWIGINTERN int \
wrapper##_closure(PyObject *a, Py_ssize_t b, PyObject *c) { \
wrapper##_ssizeobjargproc_closure(PyObject *a, Py_ssize_t b, PyObject *c) { \
PyObject *tuple, *resultobj; \
int result; \
tuple = PyTuple_New(2); \
Expand All @@ -162,7 +168,7 @@ wrapper##_closure(PyObject *a, Py_ssize_t b, PyObject *c) { \

#define SWIGPY_OBJOBJARGPROC_CLOSURE(wrapper) \
SWIGINTERN int \
wrapper##_closure(PyObject *a, PyObject *b, PyObject *c) { \
wrapper##_objobjargproc_closure(PyObject *a, PyObject *b, PyObject *c) { \
PyObject *tuple, *resultobj; \
int result; \
tuple = PyTuple_New(c ? 2 : 1); \
Expand All @@ -182,13 +188,13 @@ wrapper##_closure(PyObject *a, PyObject *b, PyObject *c) { \

#define SWIGPY_REPRFUNC_CLOSURE(wrapper) \
SWIGINTERN PyObject * \
wrapper##_closure(PyObject *a) { \
wrapper##_reprfunc_closure(PyObject *a) { \
return wrapper(a, NULL); \
}

#define SWIGPY_HASHFUNC_CLOSURE(wrapper) \
SWIGINTERN Py_hash_t \
wrapper##_closure(PyObject *a) { \
wrapper##_hashfunc_closure(PyObject *a) { \
PyObject *pyresult; \
Py_hash_t result; \
pyresult = wrapper(a, NULL); \
Expand All @@ -199,6 +205,12 @@ wrapper##_closure(PyObject *a) { \
return result; \
}

#define SWIGPY_ITERNEXTFUNC_CLOSURE(wrapper) \
SWIGINTERN PyObject * \
wrapper##_iternextfunc_closure(PyObject *a) { \
return wrapper(a, NULL); \
}

#ifdef __cplusplus
extern "C" {
#endif
Expand Down
28 changes: 19 additions & 9 deletions Source/Modules/python.cxx
Expand Up @@ -47,11 +47,13 @@ static Hash *f_shadow_imports = 0;
static String *f_shadow_builtin_imports = 0;
static String *f_shadow_stubs = 0;
static Hash *builtin_getset = 0;
static Hash *builtin_closures = 0;
static Hash *class_members = 0;
static File *f_builtins = 0;
static String *builtin_tp_init = 0;
static String *builtin_methods = 0;
static String *builtin_default_unref = 0;
static String *builtin_closures_code = 0;

static String *methods;
static String *class_name;
Expand Down Expand Up @@ -188,7 +190,7 @@ static String *getClosure(String *functype, String *wrapper, int funpack = 0) {
"unaryfunc", "SWIGPY_UNARYFUNC_CLOSURE",
"destructor", "SWIGPY_DESTRUCTOR_CLOSURE",
"inquiry", "SWIGPY_INQUIRY_CLOSURE",
"getiterfunc", "SWIGPY_UNARYFUNC_CLOSURE",
"getiterfunc", "SWIGPY_GETITERFUNC_CLOSURE",
"binaryfunc", "SWIGPY_BINARYFUNC_CLOSURE",
"ternaryfunc", "SWIGPY_TERNARYFUNC_CLOSURE",
"ternarycallfunc", "SWIGPY_TERNARYCALLFUNC_CLOSURE",
Expand All @@ -200,15 +202,15 @@ static String *getClosure(String *functype, String *wrapper, int funpack = 0) {
"objobjargproc", "SWIGPY_OBJOBJARGPROC_CLOSURE",
"reprfunc", "SWIGPY_REPRFUNC_CLOSURE",
"hashfunc", "SWIGPY_HASHFUNC_CLOSURE",
"iternextfunc", "SWIGPY_UNARYFUNC_CLOSURE",
"iternextfunc", "SWIGPY_ITERNEXTFUNC_CLOSURE",
NULL
};

static const char *funpack_functypes[] = {
"unaryfunc", "SWIGPY_UNARYFUNC_CLOSURE",
"destructor", "SWIGPY_DESTRUCTOR_CLOSURE",
"inquiry", "SWIGPY_INQUIRY_CLOSURE",
"getiterfunc", "SWIGPY_UNARYFUNC_CLOSURE",
"getiterfunc", "SWIGPY_GETITERFUNC_CLOSURE",
"ternaryfunc", "SWIGPY_TERNARYFUNC_CLOSURE",
"ternarycallfunc", "SWIGPY_TERNARYCALLFUNC_CLOSURE",
"lenfunc", "SWIGPY_LENFUNC_CLOSURE",
Expand All @@ -219,7 +221,7 @@ static String *getClosure(String *functype, String *wrapper, int funpack = 0) {
"objobjargproc", "SWIGPY_OBJOBJARGPROC_CLOSURE",
"reprfunc", "SWIGPY_REPRFUNC_CLOSURE",
"hashfunc", "SWIGPY_HASHFUNC_CLOSURE",
"iternextfunc", "SWIGPY_UNARYFUNC_CLOSURE",
"iternextfunc", "SWIGPY_ITERNEXTFUNC_CLOSURE",
NULL
};

Expand Down Expand Up @@ -626,6 +628,8 @@ class PYTHON:public Language {
f_directors_h = NewString("");
f_directors = NewString("");
builtin_getset = NewHash();
builtin_closures = NewHash();
builtin_closures_code = NewString("");
class_members = NewHash();
builtin_methods = NewString("");
builtin_default_unref = NewString("delete $self;");
Expand Down Expand Up @@ -3328,11 +3332,12 @@ class PYTHON:public Language {
String *func_type = Getattr(n, "feature:python:slot:functype");
String *closure_decl = getClosure(func_type, wrapper_name, overname ? 0 : funpack);
String *feature_name = NewStringf("feature:python:%s", slot);
String *closure_name = Copy(wrapper_name);
String *closure_name = 0;
if (closure_decl) {
Append(closure_name, "_closure");
if (!Getattr(n, "sym:overloaded") || !Getattr(n, "sym:nextSibling"))
Printf(f_wrappers, "%s /* defines %s */\n\n", closure_decl, closure_name);
closure_name = NewStringf("%s_%s_closure", wrapper_name, func_type);
if (!GetFlag(builtin_closures, closure_name))
Printf(builtin_closures_code, "%s /* defines %s */\n\n", closure_decl, closure_name);
SetFlag(builtin_closures, closure_name);
Delete(closure_decl);
}
if (func_type) {
Expand Down Expand Up @@ -4511,7 +4516,12 @@ class PYTHON:public Language {
SwigType *realct = Copy(real_classname);
SwigType_add_pointer(realct);
SwigType_remember(realct);
if (!builtin) {
if (builtin) {
Printv(f_wrappers, builtin_closures_code, NIL);
Delete(builtin_closures_code);
builtin_closures_code = NewString("");
Clear(builtin_closures);
} else {
Printv(f_wrappers, "SWIGINTERN PyObject *", class_name, "_swigregister(PyObject *SWIGUNUSEDPARM(self), PyObject *args) {\n", NIL);
Printv(f_wrappers, " PyObject *obj;\n", NIL);
if (modernargs) {
Expand Down

0 comments on commit 848628a

Please sign in to comment.