P_get for the 'P' pointer type uses PyLong_FromVoidPtr. If the address fits in a platform long, it returns a Python int; otherwise it returns a Python long, which has variable precision. That's fine, but when passing this integer value as an argument, the default behavior is to convert to a C int, which is 32-bit on all supported platforms.

I think the best solution is to set argtypes to properly convert an argument to a pointer type. Another option is to set restype to a subclass of c_void_p. Using a subclass disables the conversion to a Python integer. GetResult checks this by calling _ctypes_simple_instance, which actually returns the opposite of what its name and the source comment suggest. (In 2.5 this function was named IsSimpleSubType, and the source comment was wrong back then too. The "simple" in question was never the metaclass PyCSimpleType, but the base type _SimpleCData.)

POSIX:

# Configure the interpreter to load visible extension-
# module symbols, such as _ctypes_simple_instance, 
# into the global symbol table.
import sys, DLFCN
sys.setdlopenflags((sys.getdlopenflags() & ~DLFCN.RTLD_LOCAL) |
                   DLFCN.RTLD_GLOBAL)
from ctypes import *

_ctypes_simple_instance = PyDLL(None)._ctypes_simple_instance
_ctypes_simple_instance.argtypes = py_object,
malloc = CDLL(None).malloc

class my_void_p(c_void_p): 
    pass
>>> _ctypes_simple_instance(c_void_p)
0
>>> _ctypes_simple_instance(my_void_p)
1

>>> malloc.restype = c_void_p
>>> type(malloc(100))
<type 'int'>
>>> malloc.restype = my_void_p
>>> type(malloc(100))
<class '__main__.my_void_p'>

Windows:

_ctypes_simple_instance isn't exported by _ctypes.pyd.

from ctypes import *

malloc = cdll.msvcrt.malloc

class my_void_p(c_void_p): 
    pass
>>> malloc.restype = c_void_p
>>> type(malloc(100))          
<class 'int'>
>>> malloc.restype = my_void_p
>>> type(malloc(100))         
<class '__main__.my_void_p'>
Answer from Eryk Sun on Stack Overflow
🌐
Python
docs.python.org › 3 › library › ctypes.html
ctypes — A foreign function library for Python
It is possible to assign a callable Python object that is not a ctypes type, in this case the function is assumed to return a C int, and the callable will be called with this integer, allowing further processing or error checking. Using this is deprecated, for more flexible post processing or error checking use a ctypes data type as restype and assign a callable to the errcheck attribute.
Top answer
1 of 1
9

P_get for the 'P' pointer type uses PyLong_FromVoidPtr. If the address fits in a platform long, it returns a Python int; otherwise it returns a Python long, which has variable precision. That's fine, but when passing this integer value as an argument, the default behavior is to convert to a C int, which is 32-bit on all supported platforms.

I think the best solution is to set argtypes to properly convert an argument to a pointer type. Another option is to set restype to a subclass of c_void_p. Using a subclass disables the conversion to a Python integer. GetResult checks this by calling _ctypes_simple_instance, which actually returns the opposite of what its name and the source comment suggest. (In 2.5 this function was named IsSimpleSubType, and the source comment was wrong back then too. The "simple" in question was never the metaclass PyCSimpleType, but the base type _SimpleCData.)

POSIX:

# Configure the interpreter to load visible extension-
# module symbols, such as _ctypes_simple_instance, 
# into the global symbol table.
import sys, DLFCN
sys.setdlopenflags((sys.getdlopenflags() & ~DLFCN.RTLD_LOCAL) |
                   DLFCN.RTLD_GLOBAL)
from ctypes import *

_ctypes_simple_instance = PyDLL(None)._ctypes_simple_instance
_ctypes_simple_instance.argtypes = py_object,
malloc = CDLL(None).malloc

class my_void_p(c_void_p): 
    pass
>>> _ctypes_simple_instance(c_void_p)
0
>>> _ctypes_simple_instance(my_void_p)
1

>>> malloc.restype = c_void_p
>>> type(malloc(100))
<type 'int'>
>>> malloc.restype = my_void_p
>>> type(malloc(100))
<class '__main__.my_void_p'>

Windows:

_ctypes_simple_instance isn't exported by _ctypes.pyd.

from ctypes import *

malloc = cdll.msvcrt.malloc

class my_void_p(c_void_p): 
    pass
>>> malloc.restype = c_void_p
>>> type(malloc(100))          
<class 'int'>
>>> malloc.restype = my_void_p
>>> type(malloc(100))         
<class '__main__.my_void_p'>
🌐
Dalkescientific
dalkescientific.com › writings › NBN › ctypes.html
ctypes
To fix the problem, add the "restype" attribute to the function. This annotates the function (Python functions can have attribute just like classes can have attributes) so ctypes knows how to convert the response into a Python object.
🌐
Terryoy
terryoy.github.io › 2014 › 03 › using-ctypes-to-wrap-c-library.html
Use Ctypes to Wrap C Libraries in Python - Hello, World!
Ctypes assumes that all methods accept “int” or “char*” as parameters and return “int”, in other cases it doesn't work properly. So we'll need to define some attributes for the functions to fix that. >>> libc.atof("12345.123") 531599 >>> libc.atof.restype=ctypes.c_double >>> libc.atof("12345.123") 12345.123
🌐
Turing
turing.com › kb › ctypes-modules-and-their-implementation-in-python
Ctypes Modules and their implementation in Python.
Argtypes take multiple values and restype takes a single value. When dealing with strings in Python or C, there will be problems as strings are immutable. They can only be overwritten altogether in C and C++ strings. Below is a simple C function that will print out the message passed through it: import ctypes clibrary = ctypes.cdll(‘clibrary.so’) clibrary.display(b”Good World”)
🌐
SageMath
doc.sagemath.org › html › en › thematic_tutorials › numerical_sage › ctypes.html
Ctypes - Thematic Tutorials
Note the line laplace.timestep.restype=c double. By default ctypes assumes the return values are ints. If they are not you need to tell it by setting restype to the correct return type. If you execute the above code, then solve(u) will solve the system. It is comparable to the fortran solution ...
🌐
CodersLegacy
coderslegacy.com › home › python › python ctypes tutorial
Python ctypes Tutorial - CodersLegacy
October 27, 2022 - On the other hand, restype takes a single value, as there can only be one return type or none. ... import ctypes clibrary = ctypes.CDLL('clibrary.so') addTwoNumbers = clibrary.add addTwoNumbers.argtypes = [ctypes.c_int, ctypes.c_int] addTwoNumbers.restype = ctypes.c_int print("Sum of two numbers is :", addTwoNumbers(20, 10))
🌐
Beautiful Soup
tedboy.github.io › python_stdlib › generated › generated › ctypes.CFUNCTYPE.html
ctypes.CFUNCTYPE() — Python Standard Library
ctypes.CFUNCTYPE(restype, *argtypes, **kw)[source]¶ · CFUNCTYPE(restype, *argtypes, use_errno=False, use_last_error=False) -> function prototype. restype: the result type argtypes: a sequence specifying the argument types ·
Find elsewhere
🌐
DaniWeb
daniweb.com › programming › software-development › threads › 162440 › ctypes-restype-error
python - ctypes restype error! | DaniWeb
December 16, 2008 - @Murtan was right to flag the "list" problem — restype must be a single ctypes type (or a callable/None), and if you pass a list into CFUNCTYPE without unpacking it you end up handing an unhashable object to an API that expects types.
🌐
Samuelstevens
samuelstevens.me › writing › optimizing-python-code-with-ctypes
Optimizing Python Code with ctypes
lcsmodule = ctypes.cdll.LoadLibrary('lcsmodule/lcs.so') lcsmodule.lcs.argtypes = [ ctypes.POINTER(SEQUENCE), ctypes.POINTER(SEQUENCE), ] lcsmodule.lcs.restype = ctypes.POINTER(SEQUENCE) Finally, you can use your new C function to speed up your Python code.
🌐
SageMath
doc.sagemath.org › html › en › thematic_tutorials › numerical_sage › ctypes_examples.html
More complicated ctypes example - Thematic Tutorials
from ctypes import * class double_row_element(Structure): pass double_row_element._fields_=[("value",c_double),("col_index",c_int),("next_element",POINTER(double_row_element) )] class double_sparse_matrix(Structure): _fields_=[("nrows",c_int),("ncols",c_int),("nnz",c_int),("rows",POINTER(POINTER(double_row_element)))] double_sparse_pointer=POINTER(double_sparse_matrix) sparse_library=CDLL("/home/jkantor/linked_list_sparse.so") initialize_matrix=sparse_library.initialize_matrix initialize_matrix.restype=double_sparse_pointer set_value=sparse_library.set_value get_value=sparse_library.get_value get_value.restype=c_double free_matrix=sparse_library.free_matrix · Let’s discuss the above code. The original C code stored a sparse matrix as a linked list. The python code uses the ctypes Structure class to create structures mirroring the structs in the C code.
🌐
GitHub
github.com › alonho › course › blob › master › doc › ctypes.md
course/doc/ctypes.md at master · alonho/course
!python >>> strchr = libc.strchr >>> strchr.restype = c_char_p >>> strchr.argtypes = [c_char_p, c_char] >>> strchr("abcdef", "d") 'def' >>> strchr("abcdef", "def") Traceback (most recent call last): File "<stdin>", line 1, in ? ArgumentError: argument 2: exceptions.TypeError: \ one character string expected >>> print strchr("abcdef", "x") None >>> strchr("abcdef", "d") 'def' >>> This can be automated by the ctypeslib tool, by generating "wrapper" Python code from specified C header files.
Author: alonho
🌐
Python
svn.python.org › projects › ctypes › branches › ctypes-1.1 › docs › manual › reference.html
ctypes reference
The same is true for all other ctypes object instances. Fundamental data types, when returned as foreign function call results, or, for example, by retrieving structure field members or array items, are transparently converted to native Python types. In other words, if a foreign function has a restype of c_char_p, you will always receive a Python string, not a c_char_p instance.
🌐
Medium
medium.com › @nimratahir1212 › python-interface-that-uses-ctypes-to-load-and-call-ebc5874254c3
Python Interface that uses Ctypes to load and Call | by Nimra Tahir | Medium
April 16, 2023 - import ctypes # Load the shared library mylib = ctypes.cdll.LoadLibrary('/path/to/mylib.so') # Define the necessary data structures class Input(ctypes.Structure): _fields_ = [('x', ctypes.c_double), ('y', ctypes.c_double)] class Output(ctypes.Structure): _fields_ = [('z', ctypes.c_double)] # Define the function prototype myfunc = mylib.myfunc myfunc.argtypes = [ctypes.POINTER(Input), ctypes.POINTER(Output)] myfunc.restype = None # Call the function input_data = Input(1.0, 2.0) output_data = Output(0.0) myfunc(ctypes.byref(input_data), ctypes.byref(output_data)) # Print the result print(output_data.z)
🌐
NVIDIA
docs.nvidia.com › deeplearning › tensorrt › 10.x.x › _static › polygraphy › _modules › ctypes.html
ctypes - Polygraphy0.49.24
restype: the result type argtypes: a sequence specifying the argument types The function prototype can be called in different ways to create a callable object: prototype(integer address) -> foreign function prototype(callable) -> create and return a C callable function from callable ...
🌐
dbader.org
dbader.org › blog › python-ctypes-tutorial
Extending Python With C Libraries and the “ctypes” Module – dbader.org
September 5, 2017 - Because each of the functions in the loaded library is actually a Python object which has its own properties, specifying the return value is quite simple. To specify the return type of a function, you get the function object and set the restype property like this:
🌐
Python.org
discuss.python.org › python help
[ctypes] return type is different with what I setted up - Python Help - Discussions on Python.org
July 2, 2024 - Hello! while I use .dll file made from c++ for python, I got a problem. I setted ctypes function restype as such as c_bool and c_char_p, but what I got was random integer such as 363303008 and -829978160. Of course in c++, I used extern “C” and __declspec(dllexport) as well.
🌐
Python
svn.python.org › projects › ctypes › trunk › ctypes › docs › manual › tutorial.html
ctypes tutorial
You can also use a callable Python object (a function or a class for example) as the restype attribute, if the foreign function returns an integer. The callable will be called with the integer the C function returns, and the result of this call will be used as the result of your function call.
🌐
Readthedocs
scipy-cookbook.readthedocs.io › items › Ctypes.html
Ctypes — SciPy Cookbook documentation - Read the Docs
May 5, 2006 - import numpy as N import ctypes as C _foo = N.ctypeslib.load_library('libfoo', '.') _foo.bar.restype = C.c_int _foo.bar.argtypes = [C.POINTER(C.c_double), C.c_int] def bar(x): return _foo.bar(x.ctypes.data_as(C.POINTER(C.c_double)), len(x)) x = N.random.randn(10) n = bar(x) A ctypes property was recently added to NumPy arrays: In [ ]: In [18]: x = N.random.randn(2,3,4) In [19]: x.ctypes.data Out[19]: c_void_p(14394256) In [21]: x.ctypes.data_as(ctypes.POINTER(c_double)) In [24]: x.ctypes.shape Out[24]: <ctypes._endian.c_long_Array_3 object at 0x00DEF2B0> In [25]: x.ctypes.shape[:3] Out[25]: [2