I would like to call my C functions within a shared library from Python scripts. Problem arrises when passing pointers, the 64bit addresses seem to be truncated to 32bit addresses within the called function. Both Python and my library are 64bit.
The example codes below demonstrate the problem. The Python script prints the address of the data being passed to the C function. Then, the address received is printed from within the called C function. Additionally, the C function proves that it is 64bit by printing the size and address of locally creating memory. If the pointer is used in any other way, the result is a segfault.
CMakeLists.txt
cmake_minimum_required (VERSION 2.6)
add_library(plate MODULE plate.c)
plate.c
#include <stdio.h>
#include <stdlib.h>
void plate(float *in, float *out, int cnt)
{
void *ptr = malloc(1024);
fprintf(stderr, "passed address: %p\n", in);
fprintf(stderr, "local pointer size: %lu\n local pointer address: %p\n", sizeof(void *), ptr);
free(ptr);
}
test_plate.py
import numpy
import scipy
import ctypes
N = 3
x = numpy.ones(N, dtype=numpy.float32)
y = numpy.ones(N, dtype=numpy.float32)
plate = ctypes.cdll.LoadLibrary('libplate.so')
print 'passing address: %0x' % x.ctypes.data
plate.plate(x.ctypes.data, y.ctypes.data, ctypes.c_int(N))
Output from python-2.7
In [1]: run ../test_plate.py
passing address: 7f9a09b02320
passed address: 0x9b02320
local pointer size: 8
local pointer address: 0x7f9a0949a400
If you don't tell ctypes what type the parameters are, it attempts to infer it from the values that you pass to the function. And this inference will not always work as you need.
The recommended way to deal with this is to set the
argtypes
attribute of the function and so explicitly tellctypes
what the parameter types are.Then you can call the function like this:
The problem is that the
ctypes
module doesn't check the function signature of the function you're trying to call. Instead, it bases the C types on the Python types, so the line......is passing the the first two params as integers. See eryksun's answer for the reason why they're being truncated to 32 bits.
To avoid the truncation, you'll need to tell
ctypes
that those params are actually pointers with something like......although what they're actually pointers to is another matter - they may not be pointers to
float
as your C code assumes.Update
eryksun has since posted a much more complete answer for the
numpy
-specific example in this question, but I'll leave this here, since it might be useful in the general case of pointer truncation for programmers using something other thannumpy
.Actually, You should set
plate.argstype = [ctypes.c_void_p, ctypes.c_void_p, ctypes.c_int]
, and then it will be ok to accept the address in c func from python.I met the problem and I solved it as what I say.
Python's
PyIntObject
uses a Clong
internally, which is 64-bit on most 64-bit platforms (excluding 64-bit Windows). However, ctypes assigns the converted result topa->value.i
, wherevalue
is a union and thei
field is a 32-bitint
. For the details, seeConvParam
in Modules/_ctypes/callproc.c, lines 588-607 and 645-664. ctypes was developed on Windows, where along
is always 32-bit, but I don't know why this hasn't been changed to use thelong
field instead, i.e.pa->value.l
. Probably, it's just more convenient most of the time to default to creating a Cint
instead of using the full range of thelong
.Anyway, this means you can't simply pass a Python
int
to create a 64-bit pointer. You have to explicitly create a ctypes pointer. You have a number of options for this. If you're not concerned about type safety, the simplest option for a NumPy array is to use itsctypes
attribute. This defines the hook_as_parameter_
that lets Python objects set how they're converted in ctypes function calls (see lines 707-719 in the previous link). In this case it creates avoid *
. For example, you'd callplate
like this:However, this doesn't offer any type safety to prevent the function from being called with an array of the wrong type, which will result in either nonsense, bugs, or a segmentation fault.
np.ctypeslib.ndpointer
solves this problem. This creates a custom type that you can use in setting theargtypes
andrestype
of a ctypes function pointer. This type can verify the array's data type, number of dimensions, shape, and flags. For example: