6

I'm calling a C shared library from Python on a Linux system.

The problem I'm running into is the function inside the C library takes a pointer to a structure as an argument. It then mallocs the memory for an array of structures, populates the array with data and returns. So I've defined the function as

from ctypes import *
class myStruct(Structure):
    _fields_ = [("id", c_uint), "name", c_char*256)]

library.func.argtypes =  [POINTER(myStruct)]

Then I call it like so:

Myfoo = myStruct
Foo = pointer(Myfoo)
Bar = library.func(Foo)
for i in range(Bar):
    print("id = %u, name = %s" % (Foo[i].id, Foo[i].name))

Bar contains the number of structures that were allocated by func.

No matter what I do, I can't get any data out of Foo. I've tried multiple different variations on this for months. I can look at the logs from the C library and I know it's getting the data and returning it, but I can't seem to find a way to extract it from Python.

Any thoughts?

2
  • Could you clarify the function signature of func()? It takes a pointer to myStruct as a parameter, got that, but then it allocates memory for an array of what? More myStructs? And where is the pointer to the new memory supposed to go? Or is func() supposed to take a myStruct** argument, and it fills in the new pointer there? Commented May 20, 2012 at 19:08
  • Yes, func takes an unitialized pointer of type **myStruct, reads dynamic data and determines how many myStructs it needs, mallocs an array of type myStruct using the supplied pointer to the function, populates the array and returns the number of array elements that were allocated in Bar. Commented May 20, 2012 at 23:30

2 Answers 2

5

If I got the 'func' prototype right from your comment in the question, then this should do:

C Code

#include <stdlib.h>
#include <stdio.h>

struct Foo
{
    unsigned int id;
    char name[256];
};

__declspec(dllexport) int func(struct Foo **ppFoo)
{
    int i;
    struct Foo *foo = malloc(5 * sizeof(struct Foo));
    for(i=0;i<5;i++)
    {
        foo[i].id = i;
        sprintf_s(foo[i].name,_countof(foo[i].name),"Name#%d",i);
    }
    *ppFoo = foo;
    return 5;
}

Python Code

from ctypes import *
dll = CDLL('x')
class Foo(Structure):
    _fields_ = [
        ('id',c_uint),
        ('name',c_char*256)]

pfoo = POINTER(Foo)()
count = dll.func(byref(pfoo))
for i in xrange(count):
    print pfoo[i].id,pfoo[i].name

Output

0 Name#0
1 Name#1
2 Name#2
3 Name#3
4 Name#4

Note you will still need to free the allocated memory somehow...

Sign up to request clarification or add additional context in comments.

2 Comments

Damn, one minute too late? Ha.
Yeah, this one works also. Thanks, this has been driving me buggy. Now I can get back to work and finish this stupid project.
4

Ok, this just needs a few small updates, then.

First, your argument declaration is off, but that probably doesn't matter. A pointer is a pointer, as far as the python-c value translation machinery is concerned. Most accurate would be POINTER(POINTER(myStruct)), but to make things simpler, let's just use:

library.func.argtypes = [c_void_p]

Next, you don't need to bother creating a myStruct instance for your argument to point at; you just need a valid pointer, and a pointer to that. func will allocate the actual myStruct instances. So let's start with:

Foo = ctypes.POINTER(myStruct)()

Now we can call it. We have a myStruct*, but we'll pass a pointer to it so func can change it. We don't need to construct a whole other pointer object, so we'll use the lighter byref:

Bar = library.func(byref(Foo))

Now you can iterate through Foo[i].

for i in range(Bar):
    print("id = %u, name = %s" % (Foo[i].id, Foo[i].name))

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.