I use this in my code:
POINTER(POINTER(c_char))
ctypes is a foreign function library for Python. It provides C compatible data types, and allows calling functions in DLLs or shared libraries. It can be used to wrap these libraries in pure Python.,Instances of foreign functions are also C compatible data types; they represent C function pointers.,ctypes allows creating C callable function pointers from Python callables. These are sometimes called callback functions.,ctypes reference Finding shared libraries Loading shared libraries Foreign functions Function prototypes Utility functions Data types Fundamental data types Structured data types Arrays and pointers
>>> from ctypes import *
>>> print(windll.kernel32)
<WinDLL 'kernel32' , handle ... at ...>
>>> print(cdll.msvcrt)
<CDLL 'msvcrt' , handle ... at ...>
>>> libc = cdll.msvcrt
>>>
>>> cdll.LoadLibrary("libc.so.6")
<CDLL 'libc.so.6' , handle ... at ...>
>>> libc = CDLL("libc.so.6")
>>> libc
<CDLL 'libc.so.6' , handle ... at ...>
>>>
>>> from ctypes import *
>>> libc.printf
<_FuncPtr object at 0x...>
>>> print(windll.kernel32.GetModuleHandleA)
<_FuncPtr object at 0x...>
>>> print(windll.kernel32.MyOwnFunction)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "ctypes.py", line 239, in __getattr__
func = _StdcallFuncPtr(name, self)
AttributeError: function 'MyOwnFunction' not found
>>>
/* ANSI version */
HMODULE GetModuleHandleA(LPCSTR lpModuleName);
/* UNICODE version */
HMODULE GetModuleHandleW(LPCWSTR lpModuleName);
>>> getattr(cdll.msvcrt, "??2@YAPAXI@Z") <
_FuncPtr object at 0x... >
>>>
>>> cdll.kernel32[1]
<_FuncPtr object at 0x...>
>>> cdll.kernel32[0]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "ctypes.py", line 310, in __getitem__
func = _StdcallFuncPtr(name, self)
AttributeError: function ordinal 0 not found
>>>
An object to simplify the interaction of the array with the ctypes module.,If the ctypes module is not available, then the ctypes attribute of array objects still returns something useful, but ctypes objects are not returned and errors may be raised instead. In particular, the object will still have the as_parameter attribute which will return an integer equal to the data attribute.,A pointer to the memory area of the array as a Python integer. This memory area may contain data that is not aligned, or not in correct byte-order. The memory area may not even be writeable. The array flags and data-type of this array should be respected when passing this attribute to arbitrary C-code to avoid trouble that can include Python crashing. User Beware! The value of this attribute is exactly the same as self._array_interface_['data'][0].,(c_intp*self.ndim): A ctypes array of length self.ndim where the basetype is the same as for the shape attribute. This ctypes array contains the strides information from the underlying array. This strides information is important for showing how many bytes must be jumped to get to the next element in the array.
>>> import ctypes
>>> x = np.array([[0, 1], [2, 3]], dtype=np.int32)
>>> x
array([[0, 1],
[2, 3]], dtype=int32)
>>> x.ctypes.data
31962608 # may vary
>>> x.ctypes.data_as(ctypes.POINTER(ctypes.c_uint32))
<__main__.LP_c_uint object at 0x7ff2fc1fc200> # may vary
>>> x.ctypes.data_as(ctypes.POINTER(ctypes.c_uint32)).contents
c_uint(0)
>>> x.ctypes.data_as(ctypes.POINTER(ctypes.c_uint64)).contents
c_ulong(4294967296)
>>> x.ctypes.shape
<numpy.core._internal.c_long_Array_2 object at 0x7ff2fc1fce60> # may vary
>>> x.ctypes.strides
<numpy.core._internal.c_long_Array_2 object at 0x7ff2fc1ff320> # may vary
Instead of using a Python string, we use a char pointer (the string equivalent) from ctypes. Hence it worked! Every ctype datatype has a value attribute, which returns a native python object. Hence we were able to return a string from the char pointer (which is basically C’s version of a string).,C++ and C both make use of Pointers, but Pointers are not available as a datatype in Python. The ctypes library gives ctypes.POINTER, which we can use to create a pointer in Python. ,Do try and use ctypes datatypes as much as possible instead of Python types when writing code which is meant to interact with C. Very few Python types can directly be used with C (without any errors), among-st which two are integers and bytes (byte strings). ,There are alternate techniques that we can use to call functions that we will discuss in this Python ctypes tutorial.
Now let’s try to import a basic library with a single function over to our Python program using ctypes. You can’t link your regular .c
file though. You need to generate a shared library, which can be done with the following command.
gcc - fPIC - shared - o clibrary.so clibrary.c
#include <stdio.h>
void prompt()
{
printf("hello world\n");
}
import ctypes
libObject = ctypes.CDLL('clibrary.so')
hello world
#include <stdio.h>
void prompt(int num)
{
printf("The number %d was entered", num);
}
#include <stdio.h>
void prompt()
{
printf("hello world\n");
}
import ctypes
libObject = ctypes.CDLL('clibrary.so')
libObject = ctypes.CDLL('clibrary.so')
libObject.prompt()
#include <stdio.h>
void prompt(int num)
{
printf("The number %d was entered", num);
}
import ctypes
testlib = ctypes.CDLL('clibrary.so')
testlib.prompt(20)
The ctypes module handles type conversions between Python and C types. In this case, the module passes the underlying Python client as the first argument of the C function. The two Python strings are passed as NULL-terminated char * arrays.,Once you’ve compiled the library, you use the ctypes module to load the library. You then create an instance of the message handler wrapper, and pass that wrapper to the AMPS client methods, as shown below:,The AMPS Python client registers the pointer and user data you provide as a C++ message handler. Once the handler is registered, no Python code is called when providing messages to the handler.,The ctypes module also handles more complicated signatures and correctly passes arrays. For example, you could implement a method that publishes an array of Python values as follows:
extern "C"
void my_message_handler(
AMPS::Message & message,
void * userdata
);
import ctypes import AMPS ... # assumes that client is already created and connected # load the shared object dll = ctypes.CDLL("./libmymessagehandler.so") # create a handler that points to the underlying C function # and bind the user data to that handler. handler = AMPS.CMessageHandler(dll.my_message_hander, "user data") # handler can be used anywhere you would use a message handler client.subscribe(handler, "myTopic") client.set_last_chance_message_handler(handler) # and so it goes
extern "C"
void configure_client(AMPS::Client & client);
extern "C"
void publish_data(
AMPS::Client & client,
const char * topic,
const char * data
);
extern "C"
void install_server_chooser(AMPS::HAClient & client);
extern "C"
void publish_message(AMPS::Client & client,
const char * topic,
const char * data) {
try {
if ( & client && topic && data) {
client.publish(topic, data);
}
} catch (AMPS::Exception & e) {
/* Handle error reporting and recovery logic */
}
}