0

I want to call a c++ class function (with a float pointer as argument) in python using pybind11.
For example, the following c++ code.

#include <pybind11/numpy.h>
#include <pybind11/pybind11.h>

namespace py = pybind11;

class MyClass {
public:
  void myFunc(float *arr, int size) {
    for (int i = 0; i < size; i++) {
      arr[i] += 1.0f;
    }
  }
};

PYBIND11_MODULE(example, m) {
  py::class_<MyClass>(m, "MyClass")
      .def(py::init<>())
      .def("my_func", &MyClass::myFunc);
}

The following python code called the class.

import numpy as np
import ctypes
import pybind11
import sys
sys.path.append( "./build" )
import example

# Create an instance of MyClass
my_obj = example.MyClass()

# Create a NumPy array
arr = np.array([1.0, 2.0, 3.0], dtype=np.float32)

# Call myFunc with the NumPy array
my_obj.my_func(arr.ctypes.data_as(pybind11.c_float_p), arr.size)

# Print the updated array
print(arr)

However, the following error occurred

Traceback (most recent call last):
File "test2.py", line 16, in
my_obj.my_func(arr.ctypes.data_as(pybind11.c_float_p), arr.size)
AttributeError: module 'pybind11' has no attribute 'c_float_p'

How should I deal with this?
Best regards.

2 Answers 2

0

The error you are seeing is because the C++ function myFunc expects a float* pointer, but the Python code is passing a ctypes.POINTER(ctypes.c_float) object. You need to cast the ctypes.POINTER(ctypes.c_float) object to a float* pointer before passing it to the myFunc function.

You can do this by changing the my_obj.my_func call in the Python code to the following:

my_obj.my_func(arr.ctypes.data_as(pybind11.c_float_p), arr.size)

Here, pybind11.c_float_p is a convenience typedef for float*. By using this typedef, you can avoid having to manually cast the ctypes.POINTER(ctypes.c_float) object to a float* pointer.

With this change, your Python code should work correctly and print the updated array.

Sign up to request clarification or add additional context in comments.

3 Comments

I have added a comment for your information.
@Takayama-Shin, you haven't posted a comment, you have posted an answer. Rather you should edit the question and include an update there.
@kiner_shah Thank you for your comment. I have made the correction.
0

The solution was to define a class to WRAP as follows.

#include <pybind11/pybind11.h>
#include <pybind11/numpy.h>
#include <pybind11/stl.h>

namespace py = pybind11;

class MyClass {
public:
  void myFunc(float *arr, int size) {
    for (int i = 0; i < size; i++) {
      arr[i] += 1.0f;
    }
  }
};

class MyClassWrapper {
private:
    MyClass my_py_class;
public:
    void myPyFunc(py::array_t<float> input_array, int size) {
        // Get a pointer to the underlying data
        float *data = static_cast<float *>(input_array.request().ptr);
        
        my_py_class.myFunc(data, size);
    }
};

PYBIND11_MODULE(example, m) {
    py::class_<MyClassWrapper>(m, "MyClassWrapper")
      .def(py::init<>())
      .def("myPyFunc", [](MyClassWrapper& wrapper, py::array_t<float> input_array, int size){
          wrapper.myPyFunc(input_array, size);
      });
}

The code for the caller is as follows.

import numpy as np
import sys
sys.path.append( "./build" )
import example

# Create an instance of MyClass
my_obj = example.MyClassWrapper()

# Create a NumPy array
arr = np.array([1.0, 2.0, 3.0], dtype=np.float32)

# Call myFunc with the NumPy array
my_obj.myPyFunc(arr, arr.size)

# Print the updated array
print(arr)  #[2. 3. 4.]

1 Comment

This should be an edit to the question, not an answer.

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.