New issue 26 by l...@segv.dk: POINTER/None work around for 64 bit platforms
breaks pointer argument parsing
http://code.google.com/p/ctypesgen/issues/detail?id=26
The following work around in preamble.py seems to break pointer argument
parsing:
def POINTER(obj):
p = ctypes.POINTER(obj)
# Convert None to a real NULL pointer to work around bugs
# in how ctypes handles None on 64-bit platforms
if not isinstance(p.from_param, classmethod):
def from_param(cls, x):
if x is None:
return cls()
else:
return x
p.from_param = classmethod(from_param)
return p
To reproduce the problem:
------ library libptr.so -----------
#include <stdio.h>
#include "ptr.h"
/*
gcc -m64 -fPIC -shared -o libptr.so ptr.c
/usr/local/bin/ctypesgen.py -o ptr.py ptr.h -l ptr
*/
struct private_s {
int dummy;
};
void go(private_t *p) {
if(p == 0)
fprintf(stderr, "p is supposed to contain an address\n");
}
----- interface ptr.h -----------
#ifndef _PTR_H_
#define _PTR_H_
typedef struct private_s* private_t;
void go(private_t *p);
#endif
-------- test program ptrtest.py ----------
#!/usr/bin/env python
from ptr import *
if __name__ == "__main__":
p = private_t()
go(p)
Now running ptrtest.py I get: "p is supposed to contain an address"
but if I change the POINTER function to:
POINTER=ctypes.POINTER
everything works as expected.
This is tested on Ubuntu 11.10 x86_64 with Python 2.7.2+
The example you have there is a typo. The function "go" accepts a pointer
to private_t object, but you instead passed in a private_t object (which
is, itself, a NULL pointer to a private_s object). If you want to pass in a
reference to a private_t object, the example should work. See
http://docs.python.org/library/ctypes.html#ctypes.byref
Here's a corrected example of your program:
-------- test program ptrtest.py ----------
#!/usr/bin/env python
from ptr import *
if __name__ == "__main__":
p = private_t()
go(byref(p))
I'm not sure how your program is working when you use the standard
ctypes.POINTER function.
It's perfectly legal to omit the byref in this case. calling go(p) and
go(byref(p)) gives the same pointer value in the go function. I looked in
the PyCPointerType_from_param function in the python 2.7.2 sources and it
has this comment:
/* If we expect POINTER(<type>), but receive a <type> instance, accept
it by calling byref(<type>).
*/
So, IMHO ctypesgen needs a fix for this.
Comment #3 on issue 26 by jame...@gmail.com: POINTER/None work around for
64 bit platforms breaks pointer argument parsing
http://code.google.com/p/ctypesgen/issues/detail?id=26
Thanks for pointing this out, lars! So what we need to do here is teach
ctypesgen to call byref(...) automatically if we receive an object of the
right <type>.
Maybe it would be an idea to also add "if sys.version_info == (2, 5)"
around the POINTER function definition. There is no need for the extra
overhead on working versions of python.