I'm writing framework in Python 2.7 which would provide functionality to test C written APIs. I have the DLL and the C source code itself. The problem is that some of the C API functions require as an input structure of a callback functions and I can't find the way how to create structure in Python which fields are the C function pointers. I know how to create callback function in Python using ctypes, but I need to pack those callbacks in C structure to pass to C API. All these in code looks like this:
The C function pointers and the C structure:
#define DRIVERCALLAPI
typedef void*( DRIVERCALLAPI *fn_DRV_COMM_Open )(void*);
typedef void(DRIVERCALLAPI *fn_DRV_COMM_Close)(void*);
typedef int(DRIVERCALLAPI *fn_DRV_COMM_Transfer)(void *, unsigned char *, int);
typedef struct DriverCallbacks_t
{
fn_DRV_COMM_Open drvcomm_Open;
fn_DRV_COMM_Close drvcomm_Close;
fn_DRV_COMM_Transfer drvcomm_Transfer;
} DriverCallbacks;
typedef struct InitDataEntry_t
{
char iface[64];
void* handle;
} InitDataEntry;
Where handle points to an object of DriverCallbacks.
typedef struct InitDataContainer_t
{
uint32_t size;
uint32_t id;
InitDataEntry* data;
} InitDataContainer;
The pointer of a InitDataContainer should be passed to the API function.
void* dev_Create( void* args )
The API initializes the callback functions with appropriate functions to use them later. I need to somehow create Python structures of DriverCallbacks InitDataEntry and InitDataContainer. Any hint on how it can be achieved ? Thanks in advance !
After many experiments I finally found how to create Python structures which correspond to C structures with function pointers as fields. The idea is to use void pointers instead i.e. c_void_p from ctypes. For the provided example the python code would be:
from ctypes import *
class DriverCallbacks(Structure):
_fields_ = [
("drvcomm_Open", c_void_p),
("drvcomm_Close", c_void_p),
("drvcomm_Transfer", c_void_p)
]
class InitDataEntry(Structure):
_fields_ = [
("iface", 64 * c_byte),
("handle", c_void_p)
]
class InitDataContainer(Structure):
_fields_ = [
("size", c_uint),
("id", c_uint),
("data", POINTER(InitDataEntry))
]
The creation of the objects and the library function call could be like this (it's tested and works for me):
lib = cdll.LoadLibrary(LIBNAME)
driverFuncList = DriverCallbacks()
driverFuncList.drvcomm_Open = cast(lib.ftdi_open, c_void_p)
driverFuncList.drvcomm_Close = cast(lib.ftdi_close, c_void_p)
driverFuncList.drvcomm_Transfer = cast(lib.ftdi_transfer, c_void_p)
initData = InitDataEntry()
libc = cdll.msvcrt
libc.strcpy(byref(initData.iface), c_char_p("DriverCallbacks"))
initData.handle = cast(pointer(driverFuncList), c_void_p)
initDataCont = InitDataContainer()
initDataCont.size = c_uint(3)
initDataCont.id = c_uint(0)
initDataCont.data = pointer(initData)
ret = lib.dev_Create(byref(initDataCont))
The driverFuncList object can be filled also from within the C library if there is a such function which sets the callback function pointers.
Related
I am writing a Python wrapper for cpp APIs in that for one API I am trying to pass a NULL structure pointer as a parameter. Not sure how we can achieve that in Python.
Below is my sample implementation:
cpp_header.hpp
typedef enum {
E_FLAG_ON = 0,
E_FLAG_OFF
} option;
typedef struct {
float *a;
float b;
char *file_path;
option flag;
} inputs;
// API
int op_init(const inputs*);
This is what happening inside the init API:
Implementation.cpp
int op_init(const inputs* usr_ptr) {
internal_opration_read_set(&local_struct) { // local struct variable
// read one input file and update the structure values
}
if (usr_prt != NULL) {
internal_opration_update_set(usr_ptr, &local_struct) {
// update the values only if i send NOT NULL structure
}
}
}
From cpp test application I'm passing NULL structure to initialize.
test.cpp
int main() {
inputs *usr_cfg = NULL;
op_init(usr_cfg);
}
ctypes_imple.py
From ctypes import *
class inputs(Structure):
_fields_ = [('a', POINTER(c_float)),
('b', c_float),
('file_path', c_char_p),
('flag', option)]
# loading so file
so_lib = CDLL('some so')
# how we can initialize NULL structure pointer here?
so_lib.op_init() # how to send structure pointer as argument?
NOTE: this API reads inputs from a file and updates values to a structure in C. I am clueless how we can achieve the same in a Python wrapper? I mean updating values from so file runtime to a ctypes Python class.
Use None to pass a null pointer:
so_lib.op_init(None)
To send the actual structure instantiate one and send it. Best to define .argtypes and restype as well so ctypes doesn't have to guess and can perform better error checking:
so_lib.op_init.argtypes = POINTER(inputs),
so_lib.op_init.restype = c_int
arg = inputs() # all initialized to zero/null by default.
so_lib.op_init(arg)
I wrote a python code embedded with C code by using ctypes.
the C code is as follows:
test.h
#include<Python.h>
PyObject *getFeature(wchar_t *text);
// where the unigram is a Set Object with type 'PySetObject'
test.c
#include<test.h>
PyObject *getFeature(wchar_t *text)
{
int ret = -1;
// the below three line is to initialize for the following statements.
// they are not being used because this func is a simple version.
const wchar_t *startFeature = L"[START]";
const wchar_t *endFeature = L"[END]";
const wchar_t *delim = L".";
PyObject *featureList = PyList_New(0);
PyObject *curString = PyUnicode_FromWideChar(text, 2);
ret = PyList_Append(featureList, curString);
Py_DECREF(curString);
return featureList;
}
the above C code is a simple version, and the whole C code is at thw whole C code and there exists about 20 const wchar_t * variables initialized in the beginning of the C function
and then I compiled it and get a shared lib called libtest.so. So I can import this C .so file into the python code with ctypes like below:
test.py
import ctypes
dir_path = 'path/to/the/libtest.so'
feature_extractor = ctypes.PyDLL(
os.path.join(dir_path, 'libtest.so'))
get_feature_c = feature_extractor.getFeature
get_feature_c.argtypes = [
ctypes.c_wchar_p, ctypes.py_object]
get_feature_c.restype = ctypes.py_object
def get_feature(text):
return [text[:2]]
times = 100000
for i in range(times):
res = get_feature_c('ncd') # this func will always initialized const wchar_t * variables first
and in this example, those three initialization statements const wchar_t *startFeature and etc. is being initialized every time in the loop when calling the C func.
the question is :
I wanna first initialize those const wchar_t* variables in the python code and then call the get_feature_c func, just like a c++ class, thus avoiding executing the initialization in every loop.
So, how to use ctypes to realize my needs? and I only find that ctypes wraps a C func, and can not wrap a C struct to form a class like c++.
Thank you very much, outperforming programming master~
I have two different C functions and I would like to use them with ctypes in Python.
One function is establishing a connection and returns a pointer to an truct. The pointer shall be used as an argument in the second function to reuse the established connection.
C Code:
customStruct * connect()
{
customStruct *obj = connection_helper();
return obj;
}
void foo(customStruct * obj)
{
foo_helper(obj);
}
Python code:
from ctypes import *
lib = CDLL("./test.dll")
obj = lib.connect()
lib.foo(obj)
Unfortunately, I retrieve access violation errors when I call lib.foo(). I could recreate the customStruct struct in Python using a class with the _fields_ attribute, but since the struct consists of many other structs and since I don't want to access the struct members in Python itself, I'm thinking about an alternative how to create an identifier that can be reused.
I can change the definition of connect() as well as foo() as I'd like. I could also create another "identifier" struct if that would allow me to not have to recreate the struct in python.
Update:
It looks like I have to use the function byref() to achieve what I want.
https://docs.python.org/3/library/ctypes.html#ctypes.byref
The documentation states "The returned object can only be used as a foreign function call parameter" but I am not sure what to return in connect() then.
If you have an opaque structure (you do not know its members, or do not want to know its members), you should still create a class to represent that struct in python. You can then use this class to properly type your functions. This will help prevent bugs where you accidentally pass the wrong object as a "CustomStruct" pointer.
For example:
from ctypes import cdll, c_int, c_void_p
mylib = cdll.LoadLibrary('mylib')
class CustomStructP(c_void_p):
# subclassing c_void_p creates an opaque pointer type that is distinct
# from c_void_p, and can only be instantiated as a pointer
pass
create = mylib.create
create.argtypes = [c_int]
create.restype = CustomStructP
display = mylib.display
display.argtypes = [CustomStructP]
display.restype = None
delete = mylib.delete
delete.argtypes = [CustomStructP]
delete.restype = None
obj = create(10)
display(obj)
delete(obj)
display(CustomStructP()) # passing a null pointer
Now, if you tried something like: display(c_void_p()), you would get:
Traceback (most recent call last):
File "C:\Users\User\Documents\python\src\main.py", line 31, in <module>
display(c_void_p())
ctypes.ArgumentError: argument 1: <class 'TypeError'>: wrong type
The C code I used was:
#include <stdio.h>
#include <stdlib.h>
struct customStruct {
int val;
};
struct customStruct *
create(int val) {
struct customStruct *obj = malloc(sizeof(struct customStruct));
obj->val = val;
return obj;
}
void
display(struct customStruct *obj) {
if (obj) {
printf("customStruct(%d) # %p\n", obj->val, obj);
}
else {
puts("customStruct is NULL");
}
}
void
delete(struct customStruct *obj) {
free(obj);
}
Like mentioned in comments already you need to set restype for the connect function and argtypes for the foo function on Python side.
In code it would look like this:
from ctypes import *
lib = cdll.LoadLibrary("some.dll")
lib.connect.restype = c_void_p
lib.foo.argtypes = c_void_p,
obj = lib.connect()
lib.foo(obj)
Test
A short test should verify that this gives the same pointer in your connection and foo function on the C side.
A slightly modified version of your code might look like this:
#include <stdlib.h>
#include <stdio.h>
typedef struct {
int x;
} customStruct;
static customStruct *connection_helper() {
return malloc(sizeof(customStruct));
}
customStruct *connect()
{
customStruct *obj = connection_helper();
printf("connect: %p\n", obj);
return obj;
}
void foo(customStruct * obj)
{
printf("foo: %p\n", obj);
//do something
}
If you run this you get something like:
connect: 0x7fa219e094a0
foo: 0x7fa219e094a0
I'm trying to learn how to use the Python ctypes library to write data to a file that can easily be read by C executables. In the little test case that I've put together, I'm running into some problems with reading/writing character arrays.
At the moment, I have three source files. write_struct.py creates a simple struct with two
entries, an integer value called git and a character array called command, then writes the struct to a file using ctypes.fwrite. read_struct.c and read_struct.h compile into an executable that internally defines an identical struct to the one in write_struct.py, then reads in the data written by the python script and prints it out.
At the moment, the following values are assigned in the python file (not literally in the manner shown below, scroll down to see the actual code):
git = 1
command = 'cp file1 file2'
And when run, the C executable prints the following:
git: 1
command:
I realize that the problem is almost certainly in how the command variable is being assigned in the python script. I have read that c_char_p() (the function I'm currently using to initialize the data in that variable) does not create a pointer to mutable memory, and create_string_buffer() should be used instead, however I'm not sure about how this works with either adding that data to a struct, or writing it to a file. I guess I'm also confused about how writing pointers/their data to a file works in the first place. What is the best way to go about doing this?
Thanks in advance to anyone that is able to help!!
The code of my three files is below for reference:
write_struct.py:
"""
write_struct.py
"""
from ctypes import *
libc = cdll.LoadLibrary("libc.so.6")
class DataStruct(Structure):
_fields_ = [("git", c_int),
("command", c_char_p)
]
def main():
pydata = DataStruct(1, c_char_p("cp file1 file2"))
libc.fopen.argtypes = c_char_p, c_char_p
libc.fopen.restype = c_void_p
libc.fwrite = libc.fwrite
libc.fwrite.argtypes = c_void_p, c_size_t, c_size_t, c_void_p
libc.fwrite.restype = c_size_t
libc.fclose = libc.fclose
libc.fclose.argtypes = c_void_p,
libc.fclose.restype = c_int
f = libc.fopen("stored_data", "wb")
libc.fwrite(byref(pydata), sizeof(pydata), 1, f)
libc.fclose(f)
return 0
main()
read_struct.c:
/*
* read_struct.c
*
*/
#include "read_struct.h"
int main()
{
data_struct cdata = malloc(DATASIZE);
FILE *fp;
if ((fp = fopen("stored_data", "r")) != NULL) {
fread(cdata, DATASIZE, 1, fp);
printf("git: %i\n", cdata->git);
printf("command:");
printf("%s\n", cdata->command);
fclose(fp);
} else {
printf("Could not open file\n");
exit(1);
}
return 0;
}
read_struct.h:
/*
* read_struct.h
*
*/
#include <stdio.h>
#include <stdlib.h>
typedef struct _data_struct *data_struct;
struct _data_struct {
int git;
char command[40];
};
#define DATASIZE sizeof(struct _data_struct)
You can write binary data directly with Python. ctypes can be used to create the structure and supports bit fields and unions, or for simple structures the struct module can be used.
from ctypes import *
class DataStruct(Structure):
_fields_ = [("git", c_int),
("command", c_char * 40)] # You want array here, not pointer
pydata = DataStruct(1,b'cp file1 file2') # byte string for initialization.
with open('stored_data','wb') as f: # write file in binary mode
f.write(pydata) # ctypes support conversion to bytes
import struct
# See struct docs for formatting codes
# i = int (native-endian. Use <i to force little-endian, >i for big-endian)
# 40s = char[40] (zero-padded if initializer is shorter)
pydata = struct.pack('i40s',1,b'cp file1 file2')
with open('stored_data2','wb') as f:
f.write(pydata)
Ref: https://docs.python.org/3/library/struct.html#format-strings
I have a C program containing a structure
struct S{
int x;
struct timeval t;
};
and a function
int func(struct S s1, struct S s2)
I need to call this function from my python program.
I am using ctypes.The parallel structure on Python
import ctypes
from ctypes import *
class S(Structure):
_fields_ = [("x",c_int),
("t", ?)]
Now, my question is what will I write in the ? place and any dependencies related to it.
Thanks in advance.
Find the definition of struct timeval in your platform's C include files (the Internet suggests sys/time.h), then transcode that into a ctypes structure.
On my platform a struct timeval is
struct timeval {
long tv_sec;
long tv_usec;
};
(and I suppose this is the standard anyway), so
class timeval(Structure):
_fields_ = [("tv_sec", c_long), ("tv_usec", c_long)]
class S(Structure):
_fields_ = [("x",c_int), ("t", timeval)]
would probably fit the bill.
without any further info, it will be the definition of timeval:
class timeval(Structure):
_fields_ = [("tv_sec",c_long),
("tv_usec", c_long)]
class S(Structure):
_fields_ = [("x",c_int),
("t", timeval)]