2

I have this struct:

struct result {
    int position;
    int *taken;
};

struct result get_result(int pos, int take[]){
    struct result res;
    res.position = pos;
    res.taken = take;
    return res;
}

Which I call in a function:

struct result get_taken_values(){
    //some code
    int position = 0;
    int* chosen = calloc(9, sizeof(int));     
    //some more code
    struct result res;
    res.position = position;
    res.taken = chosen;
    return res;
}

If I were to use this in main, I would simply call free(res.taken) in the end.

But I'm making this into a shared library using:

gcc -fPIC -c get_taken_values.c
ld -shared -soname libtest.so.1 -o my_library.so -lc get_taken_values.o

I'll be using this library in Python using ctypes.

Do I need to free the calloc, if so how do I do it?

f.rodrigues
  • 3,499
  • 6
  • 26
  • 62
  • 3
    Since the array is fixed size, why not simply define `int taken[9]` in the struct? Then you don't need `calloc` and `free`. ctypes handles `ctypes.Structure` subclasses set in `restype`, but you could also allocate the struct in Python and pass it in by reference for the function to modify. – Eryk Sun Dec 29 '14 at 08:02
  • 3
    you could define a function `free_taken_values()` that does the necessary cleanup. – jfs Dec 29 '14 at 08:04

1 Answers1

1

Assuming you need a variable sized array for taken, then you can create a ctypes.Structure, give it a __del__ method that will call free. You can call free by loading libc. __del__ is called after the last reference to an object falls out of scope. Using __del__ can cause problems if your objects contain circular references as python will not know which objects to call __del__ on first (so it doesn't, it just keeps the objects hanging around in memory).

from ctypes import Structure, c_int, POINTER, CDLL
from ctypes.util import find_library

__all__ = ["get_taken_values", "Result"]

libc = CDLL(find_library("c"))
libmy_library = CDLL("./my_library.so")

class Result(Structure):
    _fields_ = [
        ("position", c_int),
        ("taken", POINTER(c_int)),
        ("size", c_int)
    ]

    def __str__(self):
        return "result(position={}, taken={})".format(
            self.position, self.taken[:self.size])

    def __del__(self):
        libc.free(self.taken)

get_taken_values = libmy_library.get_taken_values
get_taken_values.argtypes = ()
get_taken_values.restype = Result

if __name__ == "__main__":
    result = get_taken_values()
    print("position is {} and value at position is {}".format(
        result.position, result.taken[result.position]))
    print(result)

This makes the assumption that the instance of the python Result is the owner of the memory. Also, if taken really is variable sized then you will need to include the size member in your struct that Result references. If taken is a fixed size then you can just declare taken as an array in the struct, forget about using free, and use c_int * 9 rather than POINTER(c_int) when declaring the type of taken in python.

Dunes
  • 37,291
  • 7
  • 81
  • 97
  • @eryksun I only really have experience with glibc and linux. It might be better to add the edits relevant to a microsoft runtime to the answer yourself. – Dunes Dec 29 '14 at 14:36
  • @eryksun Ah, I see now. – Dunes Dec 29 '14 at 19:44
  • Tried your approach, it works 'kinda', in C it gives me this: `position: 69, taken[0]: 3, taken[1]: 9`, while python gives me result(position=69, moves=[5, -1]). The POINTER(c_int) is turning into a c_long, instead it should be an array of ints. – f.rodrigues Dec 31 '14 at 01:31
  • Without seeing more of your code I couldn't possibly comment on what's gone wrong. – Dunes Dec 31 '14 at 10:56
  • 1
    If `c_long` is 32-bit on a platform, it gets used for `c_int` as well. On Windows a `long` is always 32-bit, even in a 64-bit process. Most other 64-bit platforms use a 64-bit `long`. But I don't know why it has the wrong value. Compare the actual pointer address in C vs Python. Use `addressof(result.taken.contents)`. – Eryk Sun Dec 31 '14 at 19:43