简体   繁体   中英

access variable in so file and register callback function in ctypes

I'm trying to access variable declared in cpp header file from the compiled shared object. Below is my case

/ cpp_header.hpp /

#include <stdint.h>
#include <stdio.h>
#include <string.h>

//variables declaration
const uint8_t variable1 = 3;
const uint16_t variable2 = 4056;
const uint16_t variable3 = 3040;

typedef struct {
    void* a;
    uint32_t b
    uint16_t w;
    uint16_t h;
    size_t p;
} structure1 ;

typedef struct {
    uint32_t a;
    uint32_t b
} structure2 ;

//callback function declaration
typedef void (*one_callback) (const structure1 *);
typedef void (*output_callback) (const structure1 *);
typedef void (*inpout_callback) (const structure2 *);

//APIs using the callback function
int start_api(enum_type, output_callback, inpout_callback);

What i'm trying in ctypes

/ ctype_wrapper.py /

import ctypes
from ctypes import *

lib_instance = CDLL('test.so')

#accessing the variable declared in cpp header
variable1 = c_uint8.in_dll(lib_instance, 'variable1')
variable2 = c_uint16.in_dll(lib_instance, 'variable2')
variable3 = c_uint16.in_dll(lib_instance, 'variable2')

//registering callback function
ctype_start_api = lib_instance.start_api
ctype_start_api.argtypes = [enum_type, output_callback, inpout_callback] # register the callback
ctype_start_api.restype = c_int

Error output

#error for variable access
File "ctype_wrapper.py", line 6, in <module>
    variable1 = c_uint8.in_dll(lib_instance, 'variable1')
ValueError: ./test.so: undefined symbol: variable1

For callback register, i referred the ctypes document but no idea how to implement that for my scenario.

Is my variable declaration is correct in header.hpp file or i need to add anything to get export the variables in compiled so file?

In the header change

const uint8_t variable1 = 3;

to

extern const uint8_t variable1;

And add this

extern const uint8_t variable1 = 3;

to a .cpp file

By default const variables have internal linkage in C++, so won't be exported from a shared library.

Variables and function in must be exported for ctypes to find them. extern may be sufficient on Linux to export variables, but on Windows both variables and functions need an additional __declspec(dllexport) declaration.

ctypes also expects exported variables and functions to be C linkage, so C++ variables and functions need to be wrapped in extern "C" .

Here's a working example, tested on Windows, that also demonstrates callbacks:

test.hpp

#include <stdint.h>

#ifdef _WIN32
#   define API __declspec(dllexport)
#else
#   define API
#endif

typedef struct {
    void* a;
    uint32_t b;
    uint16_t w;
    uint16_t h;
    size_t p;
} structure1;

typedef struct {
    uint32_t a;
    uint32_t b;
} structure2;

typedef void (*output_callback) (const structure1 *);
typedef void (*inpout_callback) (const structure2 *);

extern "C" {

API extern const uint8_t variable1;
API extern const uint16_t variable2;
API extern const uint16_t variable3;

API int start_api(output_callback, inpout_callback);

}

test.cpp

#include "test.hpp"

extern "C" {

const uint8_t variable1 = 3;
const uint16_t variable2 = 4056;
const uint16_t variable3 = 3040;

int start_api(output_callback ocb, inpout_callback iocb) {
    structure1 s1 { nullptr, 1, 2, 3, 4 };
    structure2 s2 { 5, 6 };
    if(ocb)
        ocb(&s1);
    if(iocb)
        iocb(&s2);
    return 0;
}

}

test.py

import ctypes as ct

class Structure1(ct.Structure):
    _fields_ = (('a', ct.c_void_p),
                ('b', ct.c_uint32),
                ('w', ct.c_uint16),
                ('h', ct.c_uint16),
                ('p', ct.c_size_t))
    # Good habit: print representation of class so it can print itself.
    def __repr__(self):
        return f'Structure1(a={self.a}, b={self.b}, w={self.w}, h={self.h}, p={self.p})'

class Structure2(ct.Structure):
    _fields_ = (('a', ct.c_uint32),
                ('b', ct.c_uint32))
    def __repr__(self):
        return f'Structure2(a={self.a}, b={self.b})'

OCB = ct.CFUNCTYPE(None, ct.POINTER(Structure1))
IOCB = ct.CFUNCTYPE(None, ct.POINTER(Structure2))

# decorating a function with the callback signature makes it callable from C
@OCB
def output_callback(ps1):
    print(ps1.contents)

@IOCB
def inpout_callback(ps2):
    print(ps2.contents)

lib_instance = ct.CDLL('./test')
start_api = lib_instance.start_api
start_api.argtypes = OCB, IOCB
start_api.restype = ct.c_int

variable1 = ct.c_uint8.in_dll(lib_instance, 'variable1')
variable2 = ct.c_uint16.in_dll(lib_instance, 'variable2')
variable3 = ct.c_uint16.in_dll(lib_instance, 'variable3')

print(variable1.value, variable2.value, variable3.value)
start_api(output_callback, inpout_callback)

Output:

3 4056 3040
Structure1(a=None, b=1, w=2, h=3, p=4)
Structure2(a=5, b=6)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM