You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
ctypes should return composite types from callbacks #49960
Note: these values reflect the state of the issue at the time it was migrated and might not reflect the current state.
Show more details
GitHub fields:
assignee=Noneclosed_at=Nonecreated_at=<Date2009-04-06.17:28:44.019>labels= ['ctypes', 'type-feature']
title='ctypes should return composite types from callbacks'updated_at=<Date2017-11-23.02:04:38.940>user='https://bugs.python.org/gholling'
We have an application that calls a 3rd party library that returns a
structure (by value) from a callback. I'm including some sample code
that duplicates the behavior. The problem is that return types from
callbacks cannot be anything other than simple datatypes (c_int,
c_float, ..., c_void_p). For other datatypes (STRUCTURE, POINTER, ...),
ctypes returns the following error:
"invalid result type for callback function"
The error message comes from callback.c, in function AllocFunctionCallback.
I think this may be a duplicate of issue bpo-1574584.
I've tested this on Windows and linux; I'm including a Makefile and
source code that works on Windows/cygwin.
There is a problem returning arbitrary complicated ctypes types from
callbacks. Consider that a callback function returns a structure; the
structure itself may contain 'char *' field for example. The callback
function creates the structure and fills in the fields. The 'char *'
field contains a pointer to a nul-terminated string; the ctypes
structure object must keep the Python string alive as long as the
structure itself. It does this via the private '_objects' instance
variable. If the callback function now returns the structure, the
Python ctypes object usually will go out of scope and will be garbage
collected, together with its '_objects'. So, the pointer(s) contained
in the structure will become invalid now, and even simple structure
fields will become invalid.
However, the callback function result will be used by some other code
and will surely crash.
In principle it should be possible to make code like this work; one
would have to maintain the returned objects elsewhere and clean them up
when they are no longer used anywhere; but I think this gets too
complicated.
For example, if I want to use a function such as QBuffer::setBuffer in
the Qt library: http://doc.trolltech.com/4.4/qbuffer.html#setBuffer
I must keep a reference to the buffer as long as the QBuffer is alive,
or expect a crash.
Returning a pointer from a function is always tough, even in C:
everybody has already tried to return the address of a local variable...
the pointer must belong to some container that outlives the function
call.
ctypes is not different in this aspect. The same precautions as in C
apply. And with a warning note in the documentation, there seems to be
no reason to limit the return type of a callback.
I agree with Amaury that this is purely a user side concern. While I think it's important to note the behaviour of ctypes in the case that Thomas describes, I believe it's more important to fully support the range of behaviours allowed by C function callbacks.
I see the use cases for complex return types that don't fall under the concerns raised by Thomas as the following:
Returning a pointer to already existing memory.
Returning complex value types (structs of ints, doubles, chars, ect). This is especially important from a compatibility standpoint for C libraries which expect such return types.
Because I need this for my current project I will work on writing a patch.
albertz commented on Nov 29, 2015
albertzmannequin
Mannequin
More actions
Any update here?
PamMcANulty commented on Nov 22, 2017
PamMcANultymannequin
Mannequin
More actions
In the example code, the 'result' variable is declared on the stack in the callback() function. So it will be effectively when callback() returns.
Also the "result" variable in main() is never referenced. A pointer main()'s "result" variable should be passed to callback() as an argument ("&result") and callback(struct helloStruct *result) should populate it (via result->i = 10; etc)
oops -
"In the example code, the 'result' variable is declared on the stack in the callback() function. So it will be effectively when callback() returns." should be
"In the example code, the 'result' variable is declared on the stack in the callback() function. So it will be effectively DESTROYED when callback() returns."
I hate waking up at 4:15a.
freakboy3742 commented on Nov 23, 2017
freakboy3742mannequin
Mannequin
More actions
For those interested, we developed a workaround for this in Rubicon:
The fix involves using ctypes to access ctypes own internals, and build a modified version of the Structure data type that is able to perform a copy when used as returned value.
Hopefully we'll be able to get this into the form of a patch for ctypes that is acceptable to Python core.
A follow up - the Rubicon patch has been updated to account for the changes to ctypes introduced in Python 3.13.0a6, and to account for the resolution of #81061.
Activity
gholling commentedon Apr 6, 2009
We have an application that calls a 3rd party library that returns a
structure (by value) from a callback. I'm including some sample code
that duplicates the behavior. The problem is that return types from
callbacks cannot be anything other than simple datatypes (c_int,
c_float, ..., c_void_p). For other datatypes (STRUCTURE, POINTER, ...),
ctypes returns the following error:
"invalid result type for callback function"
The error message comes from callback.c, in function AllocFunctionCallback.
I think this may be a duplicate of issue bpo-1574584.
I've tested this on Windows and linux; I'm including a Makefile and
source code that works on Windows/cygwin.
------ file: Makefile ----
all: hello.dll hello.exe hello_dll.exe
clean:
rm *.exe *.dll
hello.exe: hello.h hello.c hello_main.c
gcc hello.c hello_main.c -o hello.exe --save-temps
hello.dll: hello.h hello.c
gcc -mno-cygwin -shared hello.c -o hello.dll --save-temps
hello_dll.exe: hello.h hello.c
gcc hello_main.c -L. -lhello -o hello_main.exe
------ file: hello.h ----
------ file: hello.c ----
printf ("i: %d\n", result.i);
printf ("f: %f\n", result.f);
return result.f * result.i;
}
------ file: hello_main.c ----
------ file: hello.py ----
# 2.5
#print "result: ", result
# 3.0
print ("result: ", result)
theller commentedon Apr 7, 2009
There is a problem returning arbitrary complicated ctypes types from
callbacks. Consider that a callback function returns a structure; the
structure itself may contain 'char *' field for example. The callback
function creates the structure and fills in the fields. The 'char *'
field contains a pointer to a nul-terminated string; the ctypes
structure object must keep the Python string alive as long as the
structure itself. It does this via the private '_objects' instance
variable. If the callback function now returns the structure, the
Python ctypes object usually will go out of scope and will be garbage
collected, together with its '_objects'. So, the pointer(s) contained
in the structure will become invalid now, and even simple structure
fields will become invalid.
However, the callback function result will be used by some other code
and will surely crash.
In principle it should be possible to make code like this work; one
would have to maintain the returned objects elsewhere and clean them up
when they are no longer used anywhere; but I think this gets too
complicated.
amauryfa commentedon Apr 7, 2009
But isn't this purely a user-side concern?
For example, if I want to use a function such as QBuffer::setBuffer in
the Qt library:
http://doc.trolltech.com/4.4/qbuffer.html#setBuffer
I must keep a reference to the buffer as long as the QBuffer is alive,
or expect a crash.
Returning a pointer from a function is always tough, even in C:
everybody has already tried to return the address of a local variable...
the pointer must belong to some container that outlives the function
call.
ctypes is not different in this aspect. The same precautions as in C
apply. And with a warning note in the documentation, there seems to be
no reason to limit the return type of a callback.
MasonBially commentedon Sep 27, 2013
I agree with Amaury that this is purely a user side concern. While I think it's important to note the behaviour of ctypes in the case that Thomas describes, I believe it's more important to fully support the range of behaviours allowed by C function callbacks.
I see the use cases for complex return types that don't fall under the concerns raised by Thomas as the following:
Because I need this for my current project I will work on writing a patch.
albertz commentedon Nov 29, 2015
Any update here?
PamMcANulty commentedon Nov 22, 2017
In the example code, the 'result' variable is declared on the stack in the callback() function. So it will be effectively when callback() returns.
Also the "result" variable in main() is never referenced. A pointer main()'s "result" variable should be passed to callback() as an argument ("&result") and
callback(struct helloStruct *result)should populate it (viaresult->i = 10;etc)PamMcANulty commentedon Nov 22, 2017
oops -
"In the example code, the 'result' variable is declared on the stack in the callback() function. So it will be effectively when callback() returns." should be
"In the example code, the 'result' variable is declared on the stack in the callback() function. So it will be effectively DESTROYED when callback() returns."
I hate waking up at 4:15a.
freakboy3742 commentedon Nov 23, 2017
For those interested, we developed a workaround for this in Rubicon:
https://github.com/pybee/rubicon-objc/pull/85/files
The fix involves using ctypes to access ctypes own internals, and build a modified version of the Structure data type that is able to perform a copy when used as returned value.
Hopefully we'll be able to get this into the form of a patch for ctypes that is acceptable to Python core.
freakboy3742 commentedon Sep 25, 2024
A follow up - the Rubicon patch has been updated to account for the changes to ctypes introduced in Python 3.13.0a6, and to account for the resolution of #81061.
https://github.com/beeware/rubicon-objc/blob/main/src/rubicon/objc/ctypes_patch.py
Work around `ctypes`' inability to have callbacks return pointers bes…