I have following C library
struct algo_hdl_str_t {
size_t size;
void* data;
};
typedef algo_hdl_str_t* algo_hdl_t;
/**
* @brief Encode data
*
* @param type[in] type of data
* @param data[in] pointer to data
* @param size[in] size of data
* @param hdl[out] handler to allocated encoded data
*
* @return status
*/
algo_status_t algo_encode(algo_type_t const type, void const* const data, size_t const size, algo_hdl_t* const hdl);
/**
* @brief Free message memory
*
* @param hdl[in] handler to message
*/
void algo_free(algo_hdl_t const hdl);
For more understanding what this library do here is unit test:
algo_hdl_t hdl_enc;
char *in_str = (char *)"some_weird_string";
CHECK(ALGO_SUCCESS == algo_encode(ALGO_GENERAL, in_str, strlen(in_str), &hdl_enc));
CHECK(0U == memcmp(algo_data(&hdl_enc), expected_data, algo_data_size(&hdl_enc)));
algo_free(hdl_enc)
I want to make python bind of this memory, but I am failing to pass pointer to handler in Python bind code
import ctypes, os
from enum import Enum
class MessType(Enum):
UNKNOWN = 0
EXAMPLE = 1
def encode(message_type, data):
path = os.path.abspath('../../../build/libalgo.dylib')
lib = ctypes.CDLL(path)
lib.algo_encode.argtypes = ctypes.c_int, ctypes.c_void_p, ctypes.c_size_t, ctypes.c_void_p
lib.algo_encode.restype = ctypes.c_int
handler_mem = ctypes.create_string_buffer(8)
handler = ctypes.cast(handler_mem, ctypes.c_void_p)
data_ptr = (ctypes.c_void_p).from_buffer_copy(data)
result = lib.algo_encode(message_type.value, data_ptr, len(data), handler)
print(result)
if __name__ == "__main__":
data = b'some_weird_string' + b'\x00'
encode(MessType.EXAMPLE, data)
How to pass last argument of algo_encode to make it work? Currently I have 'segmentation fault` error since - I guess - I am passing pointer to immutable data... I did tried more ways "to pass this handler", but every one failed.
handler = ctypes.c_void_p() # fails with C library assertion telling we are passing nullptr
handler = ctypes.c_void_p(1) # failes with segmentation fault
Please give me a little guidence since I am new in python :) Thank you
1 Answer 1
For an issue like this, making a simple working example of the C code helps clarify how it works. Here's my best guess. It allocates the return structure and fills it out with the provided data and length:
test.c
#include <stdlib.h>
#include <string.h>
#define API __declspec(dllexport)
struct algo_hdl_str_t {
size_t size;
void* data;
};
typedef struct algo_hdl_str_t* algo_hdl_t;
typedef int algo_status_t;
typedef int algo_type_t;
API algo_status_t algo_encode(algo_type_t const type, void const* const data, size_t const size, algo_hdl_t* const hdl) {
*hdl = malloc(sizeof(struct algo_hdl_str_t));
(*hdl)->data = malloc(size);
memcpy((*hdl)->data, data, size);
(*hdl)->size = size;
return 0;
}
API void algo_free(algo_hdl_t const hdl) {
free(hdl->data);
free(hdl);
}
Here is the ctypes Python code to read it:
import ctypes as ct
# Matching structure definition
class algo_hdl_str_t(ct.Structure):
_fields_ = (('size',ct.c_size_t),
('data',ct.c_void_p))
# Equivalent typedef
algo_hdl_t = ct.POINTER(algo_hdl_str_t)
dll = ct.CDLL('./test')
dll.algo_encode.argtypes = ct.c_int,ct.c_void_p,ct.c_size_t,ct.POINTER(algo_hdl_t)
dll.algo_encode.restype = ct.c_int
dll.algo_free.argtypes = algo_hdl_t,
dll.algo_free.restype = None
hdl = algo_hdl_t()
data = b'some byte data0円nulls ok'
dll.algo_encode(1,data,len(data),ct.byref(hdl))
print(hdl.contents.size)
# hdl.contents obtains the structure.
# Cast hdl.contents.data from void* to char* and slice the pointer to the
# desired length to view the raw data.
print(ct.cast(hdl.contents.data,ct.POINTER(ct.c_char))[:hdl.contents.size])
dll.algo_free(hdl)
Output:
23
b'some byte data\x00nulls ok'
data_ptr = (ctypes.c_void_p).from_buffer_copy(data)thread can be marked as solved :)