Search code examples
pythonstd-functioncppyy

cppyy with std::function<double(std::vector<double>) callback


I am using cppyy. I want to pass a function to my C++. The C++ expects a function of signature std::function<double(std::vector<double>)>. I don't know how to do it. Here is a minimal example of the error:

import cppyy

# To be passed to C
def callback(x):
  return 10.

cppyy.cppdef("double callback_vector(const std::function<double(std::vector<double>)>& callback, std::vector<double> x) { return callback(x); }")
cppyy.cppdef("double callback(const std::function<double(double)>& callback, std::vector<double> x) { return callback(x[0]); }")

cppyy.gbl.callback(callback, [1]) # works
cppyy.gbl.callback_vector(callback, [1]) # fails

but I find

Traceback (most recent call last):
  File "test.py", line 11, in <module>
    cppyy.gbl.callback_vector(callback, [1])
TypeError: double ::callback_vector(function<double(std::vector<double> >& callback, vector<double> x) =>
    TypeError: could not convert argument 1

In other words, with std::function<double(std::vector<double>)> it fails, but with the simpler std::function<double(double)> signature it works.


Solution

  • As commented, the problem is deep inside the backend (a parsing bug), which mangles the std::function name into something unrecognizable. It does this to for a range of workarounds that I have tried.

    The closest solution I can come up with is this:

    cppyy.cppdef("""
        struct VecDArg : public std::vector<double> {
            VecDArg(const std::vector<double>& v) : std::vector<double>(v) {}
        };
        double wrap_callback_vector(const std::function<double(VecDArg)>& wrap, const std::vector<double>& x) {
            return callback_vector(wrap, x);
        }
    """)
    
    cppyy.gbl.callback_vector = cppyy.gbl.wrap_callback_vector
    cppyy.gbl.callback_vector(callback, [1])
    

    The reason why the above works, is that it hides the type name from the backend and thus prevents the problem with parsing. By deriving from std::vector, the Python class is mostly none the wiser.

    EDIT: Now fixed in repo. Will be part of release 1.7.0. Thanks for reporting!