Reputation: 302758
I'm trying to learn SWIG, trying to compare it with other wrappers for C++ in python. One of the functions I want to write is something like this:
obj = MyObj()
obj.add([(1,2,), (3,4.0,), (5,"six","string",)])
add
needs to take a list of tuples, that are either size 2 or 3 and not all uniform type (because, you know, Python). What is the easiest way to do this in SWIG (ultimately I want this code to call obj.add_int(1, 2)
, obj.add_double(3, 4.0)
, on the actual C++ object).
Upvotes: 1
Views: 700
Reputation: 88711
If it were me doing this in SWIG I'd try to write the least amount of weird extra code possible. So my initial approach would be to write some Python code to handle the heterogeneous list and figure out which add_TYPE
to call for each entry in the list.
As an example:
%module test
%{
#include <iostream>
#include <limits>
%}
%include <std_string.i>
%inline %{
struct MyObj {
void add_double(unsigned a, const double& b, const double& c=std::numeric_limits<double>::quiet_NaN()) {
std::cout << "add_double(): " << a << ", " << b << ", " << c << "\n";
}
void add_int(unsigned a, int b, int c=~0) {
std::cout << "add_int(): " << a << ", " << b << ", " << c << "\n";
}
void add_string(unsigned a, const std::string& b, const std::string& c="") {
std::cout << "add_string(): " << a << ", " << b << ", " << c << "\n";
}
};
%}
%pythoncode %{
def obj_add(self, items):
tries = (self.add_int, self.add_double, self.add_string)
for args in items:
for method in tries:
good = False
try:
method(*args)
except NotImplementedError:
pass # Just pick a different version
else:
good = True
break
if not good: raise TypeError('No matches')
MyObj.add = obj_add
%}
For convenience of a demo I declared, defined and wrapped all of MyObj
in the one %inline
directive. You might have slight variations on this, e.g. overloads instead of default arguments to distinguish the two/three arg versions probably makes a lot of sense and would 'just work' with SWIG also.
The magic of add
is written entirely in Python using good old fashioned ducktyping. Being plain Python all the usual approaches to this that you might want to take are open - iterating over the list and using try
to handle failures until one works was my choice. (Worth noting that the order you try them in matters - if double comes before int then you'll never pick int over double).
This was sufficient to let me run:
from test import *
obj = MyObj()
obj.add([(1,2,), (3,4.0,), (5,"six","string",)])
print('Done')
Once I had compiled it with:
swig -py3 -c++ -python -Wall test.i
g++ -Wall -Wextra test_wrap.cxx -I/usr/include/python3.4/ -lpython3.4m -shared -o _test.so
And the result was as expected:
add_int(): 1, 2, -1
add_double(): 3, 4, nan
add_string(): 5, six, string
Done
You could have solved this in quite a few other ways, the most obvious other choice being to write add
directly using lots of Python C API calls (PyFloat_Check
, PyInt_Check
etc.) but that seems like a lot of effort to write harder to maintain code when we could just rely on existing SWIG behaviour.
Upvotes: 1