4

With the help of https://stackoverflow.com/a/22965961/353337, I was able to create a simple example of how to pass one function pointer into a function via Python. Specifically, with

double f(double x) {
  return x*x;
}

double myfun(double (*f)(double x)) {
  fprintf(stdout, "%g\n", f(2.0));
  return -1.0;
}
%module test

%{
#include "test.hpp"
%}

%pythoncallback;
double f(double);
%nopythoncallback;

%ignore f;
%include "test.hpp"

I can call

import test
test.f(13)
test.myfun(test.f)

and get the expected results.

Now, I would like to change the signature of myfun to allow for an array of function pointers (all with the same signature), e.g.,

double myfun(std::vector<double (*)(double)>)

How do I have adapt the .i file?

Ideally, the Python call would be via a list

test.myfun([test.f, test.g])
5
  • @πάντα ῥεῖ The basic working example is the same, but the question is different. Please unmark as dup. Commented Aug 19, 2016 at 21:56
  • Please improve your question 1st. Commented Aug 19, 2016 at 21:58
  • @NicoSchlömer You can use the basic code provided for this question, stackoverflow.com/questions/34445045/… What you could do to support multiple signatures is to keep an array of function pointers and whenever you assign a python function you need to specify the signature to be one of the supported signatures. Commented Aug 20, 2016 at 4:58
  • Thanks @JensMunk for the comment. I don't understand the multiple signatures hint -- In fact, all the functions that I'd like to pass in should have the same signature. I clarified the question in this regard. Commented Aug 20, 2016 at 8:09
  • 1
    Well I was expecting the answer to this to be "just use %template(PfnVec) std::vector<double(*)(double)>;, but that doesn't seem to work properly. Looking at a workaround. Commented Aug 20, 2016 at 14:41

1 Answer 1

1

I made the following test case to illustrate what you're trying to do. It has a real implementation of myfun(const std::vector<double(*)(double)>&) to make life a little more interesting:

#include <vector>

double g(double x) {
  return -x;
}

double f(double x) {
  return x*x;
}

typedef double(*pfn_t)(double);

std::vector<double> myfun(const std::vector<pfn_t>& funs, const double d) {
  std::vector<double> ret;
  ret.reserve(funs.size());
  for(auto && fn : funs)
    ret.emplace_back(fn(d));
  return ret;
}

I expected that all we'd need to do to make this work is use:

%include <std_vector.i>
%template(FunVec) std::vector<double(*)(double)>;
%template(DoubleVec) std::vector<double>;
%include "test.h"

However SWIG 3.0 (from Debian stable) doesn't handle this FunVec correctly and the resulting module doesn't compile. So I added a typemap as a workaround:

%module test

%{
#include "test.h"
%}

%pythoncallback;
double f(double);
double g(double);
%nopythoncallback;

%ignore f;
%ignore g;

%typemap(in) const std::vector<pfn_t>& (std::vector<pfn_t> tmp) {
    // Adapted from: https://docs.python.org/2/c-api/iter.html
    PyObject *iterator = PyObject_GetIter($input);
    PyObject *item;

    if (iterator == NULL) {
      assert(iterator);
      SWIG_fail; // Do this properly
    }

    while ((item = PyIter_Next(iterator))) {
        pfn_t f;
        const int res = SWIG_ConvertFunctionPtr(item, (void**)(&f), $descriptor(double(*)(double)));
        if (!SWIG_IsOK(res)) {
          assert(false);
          SWIG_exception_fail(SWIG_ArgError(res), "in method '" "foobar" "', argument " "1"" of type '" "pfn_t""'");
        }
        Py_DECREF(item);
        tmp.push_back(f);
    }

    Py_DECREF(iterator);
    $1 = &tmp;
}

%include <std_vector.i>
// Doesn't work:
//%template(FunVec) std::vector<double(*)(double)>;
%template(DoubleVec) std::vector<double>;
%include "test.h"

Basically all this does is add one 'in' typemap for the vector of function pointer types. That typemap just iterates over the input given from Python and builds a temporary std::vector from a Python iterable.

This is sufficient that the following Python works as expected:

import test

print test.g
print test.f
print test.g(666)
print test.f(666)

print test.myfun([test.g,test.f],123)
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.