Reputation: 559
Given a simple C file:
#include <stdio.h>
typedef struct point {
int x;
int y;
} POINT;
POINT get_point()
{
POINT p = {1, 2};
return p;
}
And I have a simple python file:
from ctypes import *
import os
lib_name = '/testlib.so'
test_lib = CDLL(os.getcwd() + lib_name)
class POINT(Structure):
_fields_ = [('x', c_int),
('y', c_int)]
# Sets p1 to the integer 1
p1 = test_lib.get_point()
# Sets p2 to the struct POINT with values {1, 0}
p2 = POINT(test_lib.get_point())
How can I set my returned value to the struct POINT
with values {1, 2}
?
Upvotes: 7
Views: 7322
Reputation: 2180
Python 3 docs say:
By default functions are assumed to return the C int type. Other return types can be specified by setting the restype attribute of the function object.
So you just need to set the restype of the function to the type you need, like this:
test_lib.get_point.restype = POINT
p1 = test_lib.get_point()
Python 2 docs say the same thing, but I haven't tested it.
Upvotes: 0
Reputation: 110686
What you ar asking is nto the sole problem in your example. Just to answer just what you asked first: you have to annotate the C function return type, so that ctypes know it is a memory address - otherwise it is a (4 byte) integer by default (while in any 64 bit OS, pointers are 8 bytes long).
Then you can create Python side POINT structures by using the (hidden) "from_address" method in your POINT class:
test_lib.get_point.restype = c_void_p
p = POINT.from_address(test_lib.get_point())
print(p.x, p.y)
Before that works, however, you have a more fundamental issue on the C side:
the POINT structure you declare on your example only exists while get_point
is running, and is deallocated afterwards. The code above would lead to a segmentation fault.
Your C code have to allocate memory properly. And also, you should take provisions to deallocate data structures you allocate in C - otherwise you will have memory leaks as each call to the function in C allocates more memory and you don't free that. (Notice that this memory won't be freed by itself when the Python POINT object goes out of scope).
Your C code could be like this:
#include <stdlib.h>
#include <stdio.h>
typedef struct point {
int x;
int y;
} POINT;
POINT *get_point()
{
POINT *p;
POINT initial = {1, 2};
p = malloc(sizeof(POINT));
*p = initial;
return p;
}
void free_point(POINT *p)
{
free(p);
}
And with this Python part:
from ctypes import *
import os
lib_name = '/testlib.so'
test_lib = CDLL(os.getcwd() + lib_name)
class POINT(Structure):
_fields_ = [('x', c_int),
('y', c_int)]
test_lib.get_point.restype = c_void_p
p1 = POINT.from_address( test_lib.get_point())
print (p1.x, p1.y)
test_lib.free_point(byref(p1))
del p1
everything should just work.
(just so that this answer is a complete ctypes example, I will add the GCC commands to build the testlib file:
gcc -c -fPIC test.c -o test.o
gcc test.o -shared -o testlib.so
)
Upvotes: 6