Reputation: 15756
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
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
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