0

I'm trying to use a c++ function, that takes a pointer as an argument, in python. For the facade I'm using pybind11 and ctypes in python in order to create pointers.

However the adress I'm getting in python isn't equal to the one in c++. I do need the adress of a variable later in the project and i cant get it by return, since the function is already returning something else.

c++ function

  void myFunc(double* ptr, double value)
  {
      *ptr = value;

      std::cout << "ptr value:\t\t" << *ptr << std::endl;
      std::cout << "ptr adress:\t\t" << ptr << std::endl;
  };

pybind code

m.def("myFunc",
    [](double* ptr,
        double value) {
            myFunc(ptr, value);
    }, "funtion to test stuff out");

python code

ptr_value = ctypes.c_double()
ptr_addressof = ctypes.addressof(ptr_value)
ptr_object_pointer = pointer(ptr_value)
ptr_pointer = ctypes.cast(ptr_object_pointer, ctypes.c_void_p).value
print(f'python ptr using addressof(ptr_value):\t\t{ptr_addressof}')
print(f'python adress using ctypes.cast:\t\t{ptr_pointer}')
print(f'python ptr object using pointer(ptr_value):\t{ptr_object_pointer}')

value = 14.0
myFunc(ptr_addressof, value)

output

python ptr using addressof(ptr_value):          2784493684616
python adress using ctypes.cast:                2784493684616
python ptr object using pointer(ptr_value):     <__main__.LP_c_double object at 0x0000028850C1C8C0>
ptr value:              14
ptr adress:             000000CC2D3EE490

How do I get the same adress in c++ and python?

Nova
  • 50
  • 8
  • Why are you using pubind11 and ctypes at the same time? – n. m. could be an AI Jul 06 '22 at 14:41
  • I tried to pass in a pointer to a variable in python with ctypes. However I didn't get that to work. I've seen stuff about smart pointers but dont quite get that. The final goal is to call a function that got a pointer pointer **ptr as an argument. The function should save the adress of a c++ object to the value the given pointer is linking to. --> **pptr links to *ptr links to c++ object – Nova Jul 07 '22 at 06:54
  • You cannot even export a function that takes a `**` with pybind11. You need to wrap pointers to hide them from Python. – n. m. could be an AI Jul 07 '22 at 09:40

3 Answers3

1

A ctypes-only solutiom:

test.cpp

#include <stdio.h>

#ifdef _WIN32
#   define API __declspec(dllexport)
#else
#   define API
#endif

extern "C"
API void myFunc(double* ptr, double value) {
    *ptr = value;
};

test.py

import ctypes as ct

dll = ct.CDLL('./test')
dll.myFunc.argtypes = ct.POINTER(ct.c_double), ct.c_double
dll.myFunc.restype = None

val = ct.c_double()
dll.myFunc(val, 14)
print(val.value)

Output:

14.0
Mark Tolonen
  • 166,664
  • 26
  • 169
  • 251
0

Pointer function parameters in pybind11 don't work this way.

All "pointer-to-T" and "reference-to-T" function parameters are transformed such that Python only sees plain T. So if you call

x = 0.123
myFunc(x, 42)

Python sees a function that accepts two float arguments, and the C++ implementation sees *ptr == 0.123 before *ptr = val assignment.

void myFunc(double* ptr, double value)
{
    std::cout << "Before:\t\t" << *ptr << std::endl;
    *ptr = value;
    std::cout << "After:\t\t" << *ptr << std::endl;
};

Before:   0.123
After:    42

The pointer in the C++ function points not to the Python x object (it would not be generally possible, because Python's float is not necessarily the same thing as C++'s double), but to its C++ representation held by pybind11 machinery for the duration of the call. Modifications to that C++ object are not propagated back to Python.

In order to pass pointers between C++ and Python, you need to wrap them in some sort of class that hides them from the pybind11 machinery.

n. m. could be an AI
  • 112,515
  • 14
  • 128
  • 243
  • This doesn't work for me. The value of x doesn't change. Also i need x to store the adress of an object. – Nova Jul 07 '22 at 06:57
  • @Nova Hmm it is not working for me now either (on a different computer). I will check again later. But inside `myFunc`, *before* `*ptr = value;`, `*ptr` is `0.123`. This means whatever you are passing as the first argument to `myFunc` is *not* a pointer on the Python side but a number. The takeout is the same: wrap pointers in a class and pass it around. See e.g. [here](https://stackoverflow.com/questions/48982143/returning-and-passing-around-raw-pod-pointers-arrays-with-python-c-and-pyb). – n. m. could be an AI Jul 07 '22 at 09:16
  • I checked it and it doesn't indeed change. I updated the answer accordingly. – n. m. could be an AI Jul 07 '22 at 10:58
0

I was able to fix the Issue. It was just an error in my thinking process. Pybind now looks like this

m.def("myFunc",
    [](double value) {
            void *pointer;
            otherValue = myFunc(&pointer, value);
        return std::make_tuple(pointer, otherValue);
    }, "funtion to test stuff out");

Note that this is kinda similar to something i was trying before. I was confused because the output when printing the pointer variable in python was object at adress xxx. The adress however didn't represent the c++ adress but the adress of the variable in python.

Now i can call other cpp functions that are taking a pointer as an input with the returned pointer as an argument and it works just fine.

The c++ function takes a pointer pointer as an argument and then changes the value of the adress, that the input pointer is refering to, to an object that will be used later in the program.

  void myFunc(void **ptr, double value)
  {
      *ptr = &value;
  }; 
Nova
  • 50
  • 8