wlav / cppyy

Other
391 stars 40 forks source link

python lambda type conversion to void * in c++ #143

Closed NiKeYiGuN closed 1 year ago

NiKeYiGuN commented 1 year ago

in C++ void cpp_func(void *d);

python_lambda = a: a*a

cppyy.gbl.cpp_func(python_lambda)

Can give me any advice on how I should do this? Thanks!

wlav commented 1 year ago

There's nothing that can be done automatically in the above as-is. Even looking at it, I have to guess what is intended here, which is a standard problem with void* (or any low-level code for that matter).

Two rather different things I can think off on the top of my head of what that code possibly intended ...

C++ function pointer, so an example would be:

import cppyy

cppyy.cppdef(r"""\
double cpp_func(double (*l)(double)) {
  return l(2.);
}""")

python_lambda = lambda a: a*a
print(cppyy.gbl.cpp_func(python_lambda))

(Note that in the above example, Python remains responsible for the python_lambda lifetime: C++ does not take ownership.)

Or perhaps the lambda should be passed as-is:

import cppdef

cppyy.cppdef(r"""\
#include "Python.h"

PyObject* py_func(PyObject* l) {
  PyObject* py2 = PyFloat_FromDouble(2.);
  PyObject* res = PyObject_CallFunctionObjArgs(l, py2, NULL);
  Py_DECREF(py2);
  return res;
}""")

python_lambda = lambda a: a*a
print(cppyy.gbl.py_func(python_lambda))

Or??

If you're dealing with 3rd party code, you can always wrap the C++ function to make the typing clear, with the wrapper passing subsequently passing the result through as a void*.

NiKeYiGuN commented 1 year ago

Thanks!

NiKeYiGuN commented 1 year ago

I think I know what I'm going to do. void cpp_func(void *d);

python_lambda = a: a*a

cppyy.gbl.cpp_func(python_lambda) cpp_func is not modifiable

But lambada expressions may be of a different form, for example, there may also be : python_lambda = a,b: a*a+b or others

NiKeYiGuN commented 1 year ago

I don't really want to modify the C++ code unless I have to, and I'm wondering if there's a way to accept any expressions。 Thank you very much for your patient answer, I will use cppyy for a long time, and I hope it will be of some help to cppyy in the future

wlav commented 1 year ago

I'm wondering if there's a way to accept any expressions

With templates and proper annotation, yes, otherwise PyObject* (as per above) is as generic as it gets. The problem you'll have with void*though, is that you can't do anything with it on the C++ side (unless you e.g. pre-compile a range of possible options).

Template example:

import cppyy

cppyy.cppdef(r"""\
void cpp_func2(void* l) {
  std::cerr << "received: " << l << std::endl;  // what to do with `l` here?
}""")

python_lambda = lambda a: a*a

# use a std::function template to construct a typed signature
f = cppyy.gbl.std.function['double(double)'](python_lambda)

print(f(2.)) # fine to use directly

cppyy.gbl.cpp_func2(f)  # works, but there's nothing to be done with `f` on the C++ side ...
NiKeYiGuN commented 1 year ago

with 'l' that to do:

`import cppyy

cppyy.cppdef(r""" typedef double (cpp_func)(double u, void f);

class B{ private: cpp_func f; void *d

B(cpp_func f, void *d): f(f),d(d){}

void a(){ double c=1; f(a, d) } } double func(double u, void f) { PyObject func = (PyObject )f; PyObject d = PyFloat_FromDouble(u); PyObject *pyret = PyObject_CallFunctionObjArgs(func, d, NULL); return pyret }""")

python_lambda = lambda a: a*a cppyy.gbl.B(cppyy.gbl.func, python_lambda )`

wlav commented 1 year ago

Still not 100% sure what you're after, but to make the above code work, would be probably take something like this:

import cppyy

cppyy.cppdef(r"""\
#include "Python.h"

void* castpy(PyObject* pyobject) { return (void*)pyobject; }

typedef double (*cpp_func)(double u, void* f);

class B {
  cpp_func f;
  void* d;

public:
  B(cpp_func ff, void* d) : f(ff), d(d) {}

  double a(double u) {
    return f(u, d);
  }
};

double func(double u, void* f) {
  PyObject* func = (PyObject*)f;
  PyObject* d = PyFloat_FromDouble(u);
  PyObject* pyret = PyObject_CallFunctionObjArgs(func, d, NULL);
  Py_DECREF(d);
  double ret = PyFloat_AsDouble(pyret);
  Py_DECREF(pyret);
  return ret;
}""")

python_lambda = lambda a: a*a
b = cppyy.gbl.B(cppyy.gbl.func, cppyy.gbl.castpy(python_lambda))
print(b.a(2.))
NiKeYiGuN commented 1 year ago

OK,Thanks, that's exactly the answer I was looking for.