0

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

3
  • I found the problem: this does not work data_ptr = (ctypes.c_void_p).from_buffer_copy(data) thread can be marked as solved :) Commented Nov 2, 2021 at 16:30
  • You can answer your own question and mark it as the answer, or just delete it. Commented Nov 2, 2021 at 17:23
  • @MarkTolonen When I end working on this I will surely post solution to make it a worth stack overflow post Commented Nov 3, 2021 at 9:24

1 Answer 1

2

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 data\0nulls 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'
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.