[pypy-commit] cffi default: Change the hack: instead of passing None for NULL pointers, we pass 0.

arigo noreply at buildbot.pypy.org
Thu Feb 28 17:32:11 CET 2013


Author: Armin Rigo <arigo at tunes.org>
Branch: 
Changeset: r1176:d37c1ddac94c
Date: 2013-02-28 17:31 +0100
http://bitbucket.org/cffi/cffi/changeset/d37c1ddac94c/

Log:	Change the hack: instead of passing None for NULL pointers, we pass
	0. This is more compatible with C. Also accept 0 at other places
	that expect a pointer.

diff --git a/c/_cffi_backend.c b/c/_cffi_backend.c
--- a/c/_cffi_backend.c
+++ b/c/_cffi_backend.c
@@ -1120,6 +1120,11 @@
         CTypeDescrObject *ctinit;
 
         if (!CData_Check(init)) {
+            if (PyIntOrLong_Check(init) && !PyObject_IsTrue(init)) {
+                /* convert 0 to NULL */
+                *(char **)data = NULL;
+                return 0;
+            }
             expected = "cdata pointer";
             goto cannot_convert;
         }
@@ -1604,16 +1609,26 @@
     char *v_cdata, *w_cdata;
 
     assert(CData_Check(v));
-    if (!CData_Check(w))
-        goto Unimplemented;
-
     v_cdata = ((CDataObject *)v)->c_data;
-    w_cdata = ((CDataObject *)w)->c_data;
+
+    if (!CData_Check(w)) {
+        if (PyIntOrLong_Check(w) && !PyObject_IsTrue(w) &&
+            !(((CDataObject *)v)->c_type->ct_flags & CT_PRIMITIVE_ANY)) {
+            /* comparing a non-primitive with 0 */
+            w_cdata = NULL;
+            goto compare;
+        }
+        pyres = Py_NotImplemented;
+        goto done;
+    }
+
     if ((op != Py_EQ && op != Py_NE) &&
         ((((CDataObject *)v)->c_type->ct_flags & CT_PRIMITIVE_ANY) ||
          (((CDataObject *)w)->c_type->ct_flags & CT_PRIMITIVE_ANY)))
         goto Error;
 
+    w_cdata = ((CDataObject *)w)->c_data;
+ compare:
     switch (op) {
     case Py_EQ: res = (v_cdata == w_cdata); break;
     case Py_NE: res = (v_cdata != w_cdata); break;
@@ -1628,10 +1643,6 @@
     Py_INCREF(pyres);
     return pyres;
 
- Unimplemented:
-    pyres = Py_NotImplemented;
-    goto done;
-
  Error:
     PyErr_SetString(PyExc_TypeError,
                     "cannot do comparison on a primitive cdata");
@@ -2067,7 +2078,12 @@
 
     ctitem = ctptr->ct_itemdescr;
     /* XXX some code duplication, how to avoid it? */
-    if (init == Py_None) {
+    if (PyIntOrLong_Check(init) && !PyObject_IsTrue(init)) {
+        /* Convert 0 to NULL.  Note that passing 0 is not ambigous,
+           despite the potential confusion: as a 'T*' argument, 0 means
+           NULL, but as a 'T[]' argument it would mean "array of size 0"
+           --- except that we specifically refuse to interpret numbers
+           as the array size when passing arguments. */
         *output_data = NULL;
         return 0;
     }
diff --git a/c/test_c.py b/c/test_c.py
--- a/c/test_c.py
+++ b/c/test_c.py
@@ -399,6 +399,19 @@
     assert (x == ["hello"]) is False
     assert (x != ["hello"]) is True
 
+def test_cmp_pointer_with_0():
+    p = new_pointer_type(new_primitive_type("int"))
+    x = cast(p, 0)
+    assert (x == 0) is True
+    assert (x != 0) is False
+    assert (0 == x) is True
+    assert (0 != x) is False
+    y = cast(p, 42)
+    assert (y == 0) is False
+    assert (y != 0) is True
+    assert (0 == y) is False
+    assert (0 != y) is True
+
 def test_invalid_indexing():
     p = new_primitive_type("int")
     x = cast(p, 42)
@@ -777,6 +790,7 @@
     assert s.a2 == 456
     assert s.a3 == 0
     assert s.p4 == cast(BVoidP, 0)
+    assert s.p4 == 0
     #
     s = newp(BStructPtr, {'a2': 41122, 'a3': -123})
     assert s.a1 == 0
@@ -791,6 +805,9 @@
     assert s.p4 == p
     #
     s = newp(BStructPtr, [12, 34, 56, cast(BVoidP, 0)])
+    assert s.p4 == 0
+    #
+    s = newp(BStructPtr, [12, 34, 56, 0])
     assert s.p4 == cast(BVoidP, 0)
     #
     py.test.raises(TypeError, newp, BStructPtr, [12, 34, 56, None])
@@ -1009,8 +1026,12 @@
     f = cast(BFunc23, _testfunc(23))
     res = f(b"foo")
     assert res == 1000 * ord(b'f')
-    res = f(None)
+    res = f(0)          # NULL
     assert res == -42
+    res = f(long(0))    # NULL
+    assert res == -42
+    py.test.raises(TypeError, f, None)
+    py.test.raises(TypeError, f, 0.0)
 
 def test_call_function_23_bis():
     # declaring the function as int(unsigned char*)
diff --git a/doc/source/index.rst b/doc/source/index.rst
--- a/doc/source/index.rst
+++ b/doc/source/index.rst
@@ -1304,13 +1304,14 @@
 |               | a compatible type (i.e.|                  |``+``, ``-``,   |
 |               | same type or ``char*`` |                  |bool()          |
 |               | or ``void*``, or as an |                  |                |
-|               | array instead) `(*)`   |                  |                |
+|               | array instead) `(*)`;  |                  |                |
+|               | or ``0`` `(******)`    |                  |                |
 +---------------+------------------------+                  |                |
 |  ``void *``,  | another <cdata> with   |                  |                |
 |  ``char *``   | any pointer or array   |                  |                |
-|               | type                   |                  |                |
+|               | type; or ``0``         |                  |                |
 +---------------+------------------------+                  +----------------+
-|  pointers to  | same as pointers `(*)` |                  | ``[]``, ``+``, |
+|  pointers to  | same as pointers       |                  | ``[]``, ``+``, |
 |  structure or |                        |                  | ``-``, bool(), |
 |  union        |                        |                  | and read/write |
 |               |                        |                  | struct fields  |
@@ -1350,9 +1351,6 @@
    you need to specify it in a list of length 1; for example, a ``struct
    foo *`` argument might be passed as ``[[field1, field2...]]``.
 
-.. versionadded:: 0.6
-   You can also pass None to ``item *`` arguments (meaning NULL).
-
 As an optimization, the CPython version of CFFI assumes that a function
 with a ``char *`` argument to which you pass a Python string will not
 actually modify the array of characters passed in, and so passes directly
@@ -1391,6 +1389,11 @@
    If you really want to get their value as a string, use
    ``ffi.string(ffi.cast("the_enum_type", x.field))``.
 
+.. versionadded:: 0.6
+   `(******)` ``0`` is interpreted like ``ffi.NULL`` in most places.
+   It is the way both gcc and MSVC work.  (Of course non-null integers
+   are not transparently interpreted as pointers; only ``0`` is.)
+
 
 Reference: verifier
 -------------------
diff --git a/testing/test_verify.py b/testing/test_verify.py
--- a/testing/test_verify.py
+++ b/testing/test_verify.py
@@ -1552,7 +1552,7 @@
     p = ffi.new("char[]", b'\x10\x20\x30')
     assert lib.sum3chars(p) == b'\x60'
 
-def test_passing_None():
+def test_passing_0_for_NULL():
     ffi = FFI()
     ffi.cdef("int seeme1(char *); int seeme2(int *);")
     lib = ffi.verify("""
@@ -1564,6 +1564,12 @@
         }
     """)
     assert lib.seeme1(b"foo") == 0
-    assert lib.seeme1(None) == 1
+    assert lib.seeme1(0) == 1
+    assert lib.seeme1(long(0)) == 1
     assert lib.seeme2([42, 43]) == 0
-    assert lib.seeme2(None) == 1
+    assert lib.seeme2(0) == 1
+    assert lib.seeme2(long(0)) == 1
+    py.test.raises(TypeError, lib.seeme1, None)
+    py.test.raises(TypeError, lib.seeme2, None)
+    py.test.raises(TypeError, lib.seeme1, 0.0)
+    py.test.raises(TypeError, lib.seeme2, 0.0)


More information about the pypy-commit mailing list