tots_o_tater
tots_o_tater

Reputation: 559

ctypes struct returned from library

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

Answers (2)

Adham Zahran
Adham Zahran

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

jsbueno
jsbueno

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

Related Questions