101010
101010

Reputation: 15756

How to instantiate a struct in python cffi?

I'm trying to instantiate a struct using the Python cffi library. I'd like to instantiate a struct from my own .h file as well as ones from the standard library.

import datetime
import os
from cffi import FFI

clib = None

script_path = os.path.dirname(os.path.realpath(__file__))

ffi = FFI()
with open(os.path.join(script_path, 'myheader.h'), 'r') as myfile:
    source = myfile.read()
    ffi.cdef(source)
clib = ffi.dlopen('mylib')

# these all fail
ffi.new("struct tm")
ffi.new("struct tm[]", 1)
ffi.new("struct tm *")
ffi.new("struct mystruct")

Upvotes: 1

Views: 4368

Answers (2)

Zim
Zim

Reputation: 515

TL;DR Armin is probably right (and is definitely the expert here). If tm is the struct from ctime, you need to define it. If it's your own struct, but it's not defined in myheader.h you need to add the definition or read in the pertinent header to your source string. If all else fails, you may need to typedef it.

This won't work:

ffi.new("struct tm")

This should work:

ffi.new("struct tm[]", 1)

But this one looks better:

ffi.new("struct tm *")

However, this one doesn't make sense to me:

ffi.new("struct mystruct")

If tm is being defined as a typedef, then you don't need struct. If you redefined ctime tm as mystruct, you need a "*" here. If it is a typedef, you can't specify struct, but this would work instead:

ffi.new("mystruct *")

I've not been able to get structs to work that aren't a typedef. If Armin hadn't implied otherwise, I'd have declared they aren't supported. Maybe you've hit the same problem.

If you have any control or say over your header files and struct names, I highly recommend typedef'ing your structs, anyway. This is a general practice for structs in C.

typedef struct tm {
/* struct internals here */
} tm; // not recommended using this short of a name though

Unless tm is completely core to your code, so much that it's meaning is always obvious, a more descriptive name is highly recommended when using typedef. This becomes a global definition, so it also helps avoid any collisions, e.g. with the ctime struct tm. This would be better:

typedef struct {
/* struct internals here */
} taskmaster; // or something similarly descriptive, having nothing else to go on I assume 'tm' is referring to my favorite UK panel show and not ctime

You can also drop tm entirely as I did here. Only the final type name is required when using typedef.

Anyway, the point is with typedef you don't use struct in your declaration:

mytm = ffi.new("tm *") # again, this level of abbreviation is not recommended

or

mytm = ffi.new("taskmaster *")

Also remember CFFI doesn't understand directives, like #include. So if myheader.h doesn't define struct tm but pulls it from another file (such as ctime), you either need to define it specially or (read and) add all the header files of interest to your source string for the cdef call. Reading in standard lib headers is not suggested.

Upvotes: 1

Armin Rigo
Armin Rigo

Reputation: 13000

ffi.new("struct mystruct") is incorrect, you probably mean ffi.new("struct mystruct *").

struct tm is most probably not defined in the cdef(), i.e. in your case, it is not mentioned inside myheader.h. You need to define it in the cdef() before you can use it, even if it is in one of the common standard headers.

You're probably better off using set_source() for that (the API mode), because you can then use an approximate definition of struct tm, e.g. something like:

struct tm {
    int tm_sec;
    int tm_min;
    int tm_hour;
    int tm_mday;
    int tm_mon;
    int tm_year;
    ...;       /* literally a "..." here */
};

If you use dlopen() (the ABI mode), then you must instead use exactly the same declaration as found in your platform's headers. The result is less portable.

Upvotes: 2

Related Questions