Pass Mutable String Array From Python to C - python

I am new to both C and ctypes, but I cannot seem to find an answer on how to do this, particularly with a numpy array.
C Code
// Import/Export Macros
#define DllImport __declspec( dllimport )
#define DllExport __declspec( dllexport )
// Test function for receiving and transmitting arrays
extern "C"
DllExport void c_fun(char **string_array)
{
string_array[0] = "foo";
string_array[1] = "bar";
string_array[2] = "baz";
}
Python Code
import numpy as np
import ctypes
# Load the DLL library...
# Define function argtypes
lib.c_fun.argtypes = [np.ctypeslib.ndpointer(ctypes.c_char, ndim = 2, flags="C_CONTIGUOUS")]
# Initialize, call, and print
string_array = np.empty((3,10),dtype=ctypes.c_char)
lib.c_fun(string_array)
print(string_array)
I am sure there is some encoding/decoding that needs to happen as well, but I am not sure how/which. Thanks!

Addressing the C code part of the question only...
As noted in comments, C does not allow assignments in this way if the three variables shown are defined as char arrays:
string_array[0] = "foo";
string_array[1] = "bar";
string_array[2] = "baz";
Use the following:
strcpy(string_array[0], "foo");
strcpy(string_array[1], "bar");
strcpy(string_array[2], "baz");
And as long as the caller to this function is pre-allocating and freeing memory for the buffers, this part of the solution is now at least syntactically correct.
But if the strings do indeed need to be immutable to be compatible with Python, then in the caller function allocate memory to create char **string_array such that you can pass an array of 3 pointers as the argument. For example:
char **string_array = malloc(3*sizeof(*string_array));//creates array of 3 pointers.
Then call it as:
c_fun(string_array);
This allows use of the DLL call just as shown in in your original post.:
DllExport void c_fun(char **string_array)
{
//array of pointers being assigned to addresses of 3 string literals
string_array[0] = "foo";//these will now be immutable strings
string_array[1] = "bar";
string_array[2] = "baz";
}

Related

Why do I get a calling convention mismatch when passing a pointer to a slice with a cdecl Rust function and Python's Ctypes?

An apparent calling convention mismatch exists where the position and contents of arguments are incorrect when loading a small function using Python's Ctypes module.
In the example I built up while trying to get something working, one positional argument gets another's value while the other gets garbage.
The Ctypes docs state that cdll.LoadLibrary expects the cdecl convention. Resulting standard boilerplate:
# Tell Rustc to output a dynamically linked library
crate-type = ["cdylib"]
// Specify clean symbol and cdecl calling convention
#[no_mangle]
pub extern "cdecl" fn boring_function(
n: *mut size_t,
in_data: *mut [c_ulong],
out_data: *mut [c_double],
garbage: *mut [c_double],
) -> c_int {
//...
Loading our library after build...
lib = ctypes.CDLL("nothing/lib/playtoys.so")
lib.boring_function.restype = ctypes.c_int
Load the result into Python and call it with some initialized data
data_len = 8
in_array_t = ctypes.c_ulong * data_len
out_array_t = ctypes.c_double * data_len
in_array = in_array_t(7, 7, 7, 7, 7, 8, 7, 7)
out_array = out_array_t(10000.2, 1.3, 1.4, 1.5, 1.6, 1.7, 1.8, 1.9)
val = ctypes.c_size_t(data_len)
in_array_p = ctypes.byref(in_array)
out_array_p = ctypes.byref(out_array)
n_p = ctypes.byref(val)
garbage = n_p
res = boring_function(n_p,
in_array_p,
# garbage cannot be observed in any callee arg
ctypes.cast(garbage, ctypes.POINTER(out_array_t)),
out_array_p)
Notice the garbage parameter. It is so-named because it winds up containing a garbage address. Note that its position is swapped with out_array_p in the Python call and the Rust declaration.
[src/hello.rs:29] n = 0x00007f56dbce5bc0
[src/hello.rs:30] in_data = 0x00007f56f81e3270
[src/hello.rs:31] out_data = 0x00007f56f81e3230
[src/hello.rs:32] garbage = 0x000000000000000a
in_data, out_data, and n print the correct values in this configuration. The positional swap between garbage and out_data makes this possible.
Other examples using more or less arguments reveal similar patterns of intermediate ordered variables containing odd values that resemble addresses earlier in the program or unrelated garbage.
Either I'm missing something in how I set up the calling convention or some special magic in argtypes must be missing. So far I had no luck with changing the declared calling conventions or explicit argtypes. Are there any other knobs I should try turning?
in_data: *mut [c_ulong],
A slice is not a FFI-safe data type. Namely, Rust's slices use fat pointers, which take up two pointer-sized values.
You need to pass the data pointer and length as two separate arguments.
See also:
Why can comparing two seemingly equal pointers with == return false?
Rust functions with slice arguments in The Rust FFI Omnibus
The complete example from the Omnibus:
extern crate libc;
use libc::{uint32_t, size_t};
use std::slice;
#[no_mangle]
pub extern fn sum_of_even(n: *const uint32_t, len: size_t) -> uint32_t {
let numbers = unsafe {
assert!(!n.is_null());
slice::from_raw_parts(n, len as usize)
};
let sum =
numbers.iter()
.filter(|&v| v % 2 == 0)
.fold(0, |acc, v| acc + v);
sum as uint32_t
}
#!/usr/bin/env python3
import sys, ctypes
from ctypes import POINTER, c_uint32, c_size_t
prefix = {'win32': ''}.get(sys.platform, 'lib')
extension = {'darwin': '.dylib', 'win32': '.dll'}.get(sys.platform, '.so')
lib = ctypes.cdll.LoadLibrary(prefix + "slice_arguments" + extension)
lib.sum_of_even.argtypes = (POINTER(c_uint32), c_size_t)
lib.sum_of_even.restype = ctypes.c_uint32
def sum_of_even(numbers):
buf_type = c_uint32 * len(numbers)
buf = buf_type(*numbers)
return lib.sum_of_even(buf, len(numbers))
print(sum_of_even([1,2,3,4,5,6]))
Disclaimer: I am the primary author of the Omnibus

Python Ctypes passing a structure pointer defined in .h file.

I'm new to both ctypes and C and having trouble passing a struct pointer variable into a c-function called in python via ctypes. Please bear with me if it is too basic and obvious.
Below is how my c-code looks like.
#include "mylib.h" (inside this mylib.h file MYSTRUCT is defined)
struct MYSTRUCT* modifystruct(a,b,c,d,e)
{
MYSTRUCT *mystpointer;
.....
.....
return mystpointer;
}
int mycfunction(mystpointer)
MYSTRUCT *mystpointer;
{
.........
.........
.........
}
Like above, modifystruct function update *mystpointer which is a pointer to MYSTRUCT and returns it.
And mycfunction is to pass the returned mystpointer. In C, this works fine inside main function.
But when I try to load the ".so" file using ctypes into python, it fails and I think I'm not correctly defining the argtype for mystpointer.
Below is the brief python code I wrote. Let's say the above c-code is compiled to "mycmodule.so".
mylib=cdll.LoadLibrary("mycmodule.so")
mycfunction=mylib.mycfunction
mycfunction.restype=c_int
mycfunction.argtypes=[c_void_p]
mystpointer=c_void_p()
In the C code, I have to define mystpointer type as "MYSTRUCT *mystpointer;"
However, I do not know how to do so in ctypes... instead, I defined the types as c_void_p but this triggers the failure. Thanks in advance!
I think what you may be missing is knowing exactly where you want the struct memory to be allocated. The c code below provides a function that allocates memory for the struct and returns a pointer to it (new_struct()).
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct {
int a;
int b;
} my_struct;
my_struct *new_struct()
{
my_struct *struct_instance = (my_struct *)malloc(sizeof(my_struct));
memset(struct_instance, 0, sizeof(my_struct));
return struct_instance;
}
int modify_struct(my_struct *ms) {
ms->a = 1;
ms->b = 2;
return 0;
}
void print_struct_c(my_struct *ms) {
printf("my_struct {\n"
" a = %d\n"
" b = %d\n"
"}\n", ms->a, ms->b);
}
From Python, to get the pointer, call the C function that does the allocation, then you can pass that to the other C functions that take it as a parameter.
import ctypes
lib_file_path = <<< path to lib file >>>
# Very simple example of how to declare a ctypes structure to twin the
# C library's declaration. This doesn't need to be declared if the Python
# code isn't going to need access to the struct's data members.
class MyStruct(ctypes.Structure):
_fields_ = [('a', ctypes.c_int),
('b', ctypes.c_int)]
def print_struct(s):
# Print struct that was allocated via Python ctypes.
print("my_struct.a = %d, my_struct.b = %d" % (s.a, s.b))
def print_struct_ptr(sptr):
# Print pointer to struct. Note the data members of the pointer are
# accessed via 'contents'.
print("my_struct_ptr.contents.a = %d, my_struct_ptr.contents.b = %d"
% (sptr.contents.a, sptr.contents.b))
my_c_lib = ctypes.cdll.LoadLibrary(lib_file_path)
# If you don't need to access the struct's data members from Python, then
# it's not necessary to declare MyStruct above. Also, in that case,
# 'restype' and 'argtypes' (below) can be set to ctypes.c_void_p instead.
my_c_lib.new_struct.restype = ctypes.POINTER(MyStruct)
my_c_lib.modify_struct.argtypes = [ctypes.POINTER(MyStruct)]
# Call C function to create struct instance.
my_struct_c_ptr = my_c_lib.new_struct()
print_struct_ptr(my_struct_c_ptr)
my_c_lib.modify_struct(my_struct_c_ptr)
print_struct_ptr(my_struct_c_ptr)
# Allocating struct instance from Python, then passing to C function.
my_struct_py = MyStruct(0, 0)
print_struct(my_struct_py)
my_c_lib.modify_struct(ctypes.byref(my_struct_py))
print_struct(my_struct_py)
# Data members of Python allocated struct can be acessed directly.
my_struct_py.a = 555
my_c_lib.print_struct_c(ctypes.byref(my_struct_py)) # Note use of 'byref()'
# to invoke c function.
The code above has been updated to include an example of how to allocate a struct instance via Python, and how to access the data members of either the C allocated or Python allocated struct (note differences in the print functions).

Pass Python list to embedded Rust function

I am learning how to embed Rust functions in Python, and everything works fine if my inputs are ints, but not list.
If my lib.rs file is:
#[no_mangle]
pub extern fn my_func(x: i32, y: i32) -> i32 {
return x + y;
}
I can use this as follows:
In [1]: from ctypes import cdll
In [2]: lib = cdll.LoadLibrary("/home/user/RustStuff/embed/target/release/libembed.so")
In [3]: lib.my_func(5,6)
Out[3]: 11
However if I change my lib.rs to:
#[no_mangle]
pub extern fn my_func(my_vec: Vec<i32>) -> i32 {
let mut my_sum = 0;
for i in my_vec {
my_sum += i;
}
return my_sum;
}
I can no longer use it in Python (this compiled fine):
In [1]: from ctypes import cdll
In [2]: lib = cdll.LoadLibrary("/home/user/RustStuff/embed/target/release/libembed.so")
In [3]: lib.my_func([2,3,4])
---------------------------------------------------------------------------
ArgumentError Traceback (most recent call last)
<ipython-input-3-454ffc5ba9dd> in <module>()
----> 1 lib.my_func([2,3,4])
ArgumentError: argument 1: <type 'exceptions.TypeError'>: Don't know how to convert parameter 1
The reason, I though this could work is that Python's list and Rust's Vec are the both dynamic arrays, but apparently I am missing something here...
Why does my attempt not work? What should I do to fix it?
Don't do this:
#[no_mangle]
pub extern fn my_func(my_vec: Vec<i32>) -> i32 { ... }
You basically never want to accept or return an arbitrary Rust object in an extern function, only ones that are Repr. Instead, you should accept something that is representable by C. As 6502 says, the best idea for this particular case would be to accept a pointer and a length.
Rust's Vec is conceptually a pointer to data, a count, and a capacity. You are able to modify a Vec by adding or removing objects, which can cause reallocation to happen. This is doubly bad because it is likely that Python and Rust use different allocators that are not compatible with each other. Segfaults lie this way! You really want a slice.
Instead, do something like this on the Rust side:
extern crate libc;
use libc::{size_t,int32_t};
use std::slice;
#[no_mangle]
pub extern fn my_func(data: *const int32_t, length: size_t) -> int32_t {
let nums = unsafe { slice::from_raw_parts(data, length as usize) };
nums.iter().fold(0, |acc, i| acc + i)
}
Namely, you are using the guaranteed-to-match C types, and then converting the pointer and length to something Rust knows how to deal with.
I'm no Pythonista, but this cobbled-together code (with help from How do I convert a Python list into a C array by using ctypes?) seems to work with the Rust I have above:
import ctypes
lib = ctypes.cdll.LoadLibrary("./target/debug/libpython.dylib")
lib.my_func.argtypes = (ctypes.POINTER(ctypes.c_int32), ctypes.c_size_t)
list_to_sum = [1,2,3,4]
c_array = (ctypes.c_int32 * len(list_to_sum))(*list_to_sum)
print lib.my_func(c_array, len(list_to_sum))
Of course, you probably want to wrap that to make it nicer for the caller of your code.
ctypes is about C bindings and in C there's no such a thing as a dynamic array.
The closest object you can pass to a C function is a pointer to integer that however is not a dynamic array because
It doesn't carry the size information
You cannot grow or shrink the area, just access existing elements
A simple alternative to passing pointers (and being very careful about not getting past the size) you could use instead is a function-based API.
For example:
getNumberOfThings() -> number
getThing(index) -> thing
but the Python code would then become like
def func():
n = getNumberOfThings()
return [getThing(i) for i in range(n)]
The counterpart (passing a variable number of elements) would be
def func2(L):
setNumberOfThings(len(L))
for i, x in enumerate(L):
setThing(i, x)

How to pass a list/array of structs from python to C

I have a C function that must be callable from C and Python.
I'm having trouble figuring out how to pass a python list of c-type structs,
each of which contains several nested structs, to the c function.
A single one of these structs looks like this in python:
class STATION_MM_NODE(ctypes.Structure):
_fields_ = [
("signal", MM_STRUCT),
("noise", MM_STRUCT),
("signalWindowLen", ctypes.c_double),
("metadata", SAC_PZ)
]
And like this in C:
typedef struct stationMMnode {
struct mantleMagStruct *signal;
struct mantleMagStruct *noise;
double signalWindowLen;
SAC_PZ metadata;
} stationMMnode_t;
The c function that takes an array of stationMMnode structs is callable as:
double magnitudeCompute_Mw_Mm_Event(stationMMnode_t **stationMMarray, int numStations);
For instance, I can call it purely from C as in:
int testfunc() {
stationMMnode_t *node1 = malloc(sizeof(struct stationMMnode));
node1->signalWindowLen = 500;
stationMMnode_t *node2 = malloc(sizeof(struct stationMMnode));
node2->signalWindowLen = 100;
struct stationMMnode *nodes[2];
nodes[0] = node1;
nodes[1] = node2;
magnitudeCompute_Mw_Mm_Event(nodes, 2); // Works!
}
In python, I can create a list of nodes that looks similar to the c array of structs:
stationMMnodes = []
...
node = get_stationMMnode() # Returns a STATION_MM_NODE
node.signal = mm_signal
node.noise = mm_noise
node.metadata = sacPoleZero
stationMMnodes.append(node)
...
wrap_lib.magnitudeCompute_Mw_Mm_Event(stationMMnodes, numStations) # Does NOT work
where I've defined the argtypes as:
wrap_lib.magnitudeCompute_Mw_Mm_Event.argtypes =
[ctypes.POINTER(STATION_MM_NODE), ctypes.c_int ]
The model I'm using above (passing a ctype pointer to a c-style struct to a c function that takes a pointer to struct) seems to work fine when I am passing in a pointer to a single struct, however, for a pointer to an array of structs, it seems to break down. In addition, I am uncertain of what the python memory layout is for a list of structs versus an array of pointers to struct (as the C function is expecting).
Any help would be greatly appreciated!
Update: I found the following link very helpful:
python ctypes array of structs
I solved my problem by:
1. Declaring an array of pointers to my struct:
nodeArrayType = ctypes.POINTER(STATION_MM_NODE) * 1024
nodeArray = nodeArrayType()
nstn = 0
2. Writing a C function to join the member structs into a larger struct (=a node) and return a pointer to that struct - which is stored in nodeArray[].
nodeArray[nstn] = wrap_libmth.libmth.makeNode(node.signal, node.noise, node.metadata)
nstn += 1
3. Fixing the argtype of the C function that receives the pointer to struct array:
wrap_libmth.libmth.magnitudeCompute_Mw_Mm_Event.argtypes = [ctypes.POINTER(ctypes.POINTER(STATION_MM_NODE)), ctypes.c_int]
So ... I have it working, but like most thing with Python, I feel like I'm holding the tiger by the tail as I don't fully understand exactly why it works and what (better) alternatives would be (e.g., the C hack makeNode() to return a pointer to a STATION_MM_NODE struct is less than satisfactory - it would be better to generate this struct fully in python).

Python ctype - How to pass data between C functions

I have a self-made C library that I want to access using python. The problem is that the code consists essentially of two parts, an initialization to read in data from a number of files and a few calculations that need to be done only once. The other part is called in a loop and uses the data generated before repeatedly. To this function I want to pass parameters from python.
My idea was to write two C wrapper functions, "init" and "loop" - "init" reads the data and returns a void pointer to a structure that "loop" can use together with additional parameters that I can pass on from python. Something like
void *init() {
struct *mystruct ret = (mystruct *)malloc(sizeof(mystruct));
/* Fill ret with data */
return ret;
}
float loop(void *data, float par1, float par2) {
/* do stuff with data, par1, par2, return result */
}
I tried calling "init" from python as a c_void_p, but since "loop" changes some of the contents of "data" and ctypes' void pointers are immutable, this did not work.
Other solutions to similar problems I saw seem to require knowledge of how much memory "init" would use, and I do not know that.
Is there a way to pass data from one C function to another through python without telling python exactly what or how much it is? Or is there another way to solve my problem?
I tried (and failed) to write a minimum crashing example, and after some debugging it turned out there was a bug in my C code. Thanks to everyone who replied!
Hoping that this might help other people, here is a sort-of-minimal working version (still without separate 'free' - sorry):
pybug.c:
#include <stdio.h>
#include <stdlib.h>
typedef struct inner_struct_s {
int length;
float *array;
} inner_struct_t;
typedef struct mystruct_S {
int id;
float start;
float end;
inner_struct_t *inner;
} mystruct_t;
void init(void **data) {
int i;
mystruct_t *mystruct = (mystruct_t *)malloc(sizeof(mystruct_t));
inner_struct_t *inner = (inner_struct_t *)malloc(sizeof(inner_struct_t));
inner->length = 10;
inner->array = calloc(inner->length, sizeof(float));
for (i=0; i<inner->length; i++)
inner->array[i] = 2*i;
mystruct->id = 0;
mystruct->start = 0;
mystruct->end = inner->length;
mystruct->inner = inner;
*data = mystruct;
}
float loop(void *data, float par1, float par2, int newsize) {
mystruct_t *str = data;
inner_struct_t *inner = str->inner;
int i;
inner->length = newsize;
inner->array = realloc(inner->array, newsize * sizeof(float));
for (i=0; i<inner->length; i++)
inner->array[i] = par1 + i * par2;
return inner->array[inner->length-1];
}
compile as
cc -c -fPIC pybug.c
cc -shared -o libbug.so pybug.o
Run in python:
from ctypes import *
sl = CDLL('libbug.so')
# What arguments do functions take / return?
sl.init.argtype = c_void_p
sl.loop.restype = c_float
sl.loop.argtypes = [c_void_p, c_float, c_float, c_int]
# Init takes a pointer to a pointer
px = c_void_p()
sl.init(byref(px))
# Call the loop a couple of times
for i in range(10):
print sl.loop(px, i, 5, 10*i+5)
You should have a corresponding function to free the data buffer when the caller is done. Otherwise I don't see the issue. Just pass the pointer to loop that you get from init.
init.restype = c_void_p
loop.argtypes = [c_void_p, c_float, c_float]
loop.restype = c_float
I'm not sure what you mean by "ctypes' void pointers are immutable", unless you're talking about c_char_p and c_wchar_p. The issue there is if you pass a Python string as an argument it uses Python's private pointer to the string buffer. If a function can change the string, you should first copy it to a c_char or c_wchar array.
Here's a simple example showing the problem of passing a Python string (2.x byte string) as an argument to a function that modifies it. In this case it changes index 0 to '\x00':
>>> import os
>>> from ctypes import *
>>> open('tmp.c', 'w').write("void f(char *s) {s[0] = 0;}")
>>> os.system('gcc -shared -fPIC -o tmp.so tmp.c')
0
>>> tmp = CDLL('./tmp.so')
>>> tmp.f.argtypes = [c_void_p]
>>> tmp.f.restype = None
>>> tmp.f('a')
>>> 'a'
'\x00'
>>> s = 'abc'
>>> tmp.f(s)
>>> s
'\x00bc'
This is specific to passing Python strings as arguments. It isn't a problem to pass pointers to data structures that are intended to be mutable, either ctypes data objects such as a Structure, or pointers returned by libraries.
Is your C code in a DLL? If so can might consider creating a global pointer in there. init() will do any initialization required and set the pointer equal to newly allocated memory and loop() will operate on that memory. Also don't forget to free it up with a close() function

Categories

Resources