Commits

Armin Rigo  committed ee60367

More tests for MSVC's struct return type. Add a workaround similar
to the one present in ctypes.

  • Participants
  • Parent commits c2e7a85

Comments (0)

Files changed (4)

File c/_cffi_backend.c

     }
 }
 
-static ffi_type *fb_fill_type(struct funcbuilder_s *fb, CTypeDescrObject *ct)
+static ffi_type *fb_fill_type(struct funcbuilder_s *fb, CTypeDescrObject *ct,
+                              int is_result_type)
 {
     if (ct->ct_flags & CT_PRIMITIVE_ANY) {
         return (ffi_type *)ct->ct_extra;
             return NULL;
         }
 
+#ifdef USE_C_LIBFFI_MSVC
+        /* MSVC returns small structures in registers.  Pretend int32 or
+           int64 return type.  This is needed as a workaround for what
+           is really a bug of libffi_msvc seen as an independent library
+           (ctypes has a similar workaround). */
+        if (is_result_type) {
+            if (ct->ct_size <= 4)
+                return &ffi_type_sint32;
+            if (ct->ct_size <= 8)
+                return &ffi_type_sint64;
+        }
+#endif
+
         n = PyDict_Size(ct->ct_stuff);
         elements = fb_alloc(fb, (n + 1) * sizeof(ffi_type*));
         cf = (CFieldObject *)ct->ct_extra;
                     "cannot pass as argument a struct with bit fields");
                 return NULL;
             }
-            ffifield = fb_fill_type(fb, cf->cf_type);
+            ffifield = fb_fill_type(fb, cf->cf_type, 0);
             if (elements != NULL)
                 elements[i] = ffifield;
             cf = cf->cf_next;
     fb->nargs = nargs;
 
     /* ffi buffer: next comes the result type */
-    fb->rtype = fb_fill_type(fb, fresult);
+    fb->rtype = fb_fill_type(fb, fresult, 1);
     if (PyErr_Occurred())
         return -1;
     if (cif_descr != NULL) {
 
         /* ffi buffer: fill in the ffi for the i'th argument */
         assert(farg != NULL);
-        atype = fb_fill_type(fb, farg);
+        atype = fb_fill_type(fb, farg, 0);
         if (PyErr_Occurred())
             return -1;
 
     return result;
 }
 
+struct _testfunc14_s { float a1; };
+static struct _testfunc14_s _testfunc14(int n)
+{
+    struct _testfunc14_s result;
+    result.a1 = (float)n;
+    return result;
+}
+
+struct _testfunc15_s { float a1; int a2; };
+static struct _testfunc15_s _testfunc15(int n)
+{
+    struct _testfunc15_s result;
+    result.a1 = (float)n;
+    result.a2 = n * n;
+    return result;
+}
+
+struct _testfunc16_s { float a1, a2; };
+static struct _testfunc16_s _testfunc16(int n)
+{
+    struct _testfunc16_s result;
+    result.a1 = (float)n;
+    result.a2 = -(float)n;
+    return result;
+}
+
+struct _testfunc17_s { int a1; float a2; };
+static struct _testfunc17_s _testfunc17(int n)
+{
+    struct _testfunc17_s result;
+    result.a1 = n;
+    result.a2 = (float)n * (float)n;
+    return result;
+}
+
 static PyObject *b__testfunc(PyObject *self, PyObject *args)
 {
     /* for testing only */
     case 11: f = &_testfunc11; break;
     case 12: f = &_testfunc12; break;
     case 13: f = &_testfunc13; break;
+    case 14: f = &_testfunc14; break;
+    case 15: f = &_testfunc15; break;
+    case 16: f = &_testfunc16; break;
+    case 17: f = &_testfunc17; break;
     default:
         PyErr_SetNone(PyExc_ValueError);
         return NULL;
 def test_struct_return_in_func():
     BChar = new_primitive_type("char")
     BShort = new_primitive_type("short")
+    BFloat = new_primitive_type("float")
     BDouble = new_primitive_type("double")
     BInt = new_primitive_type("int")
     BStruct = new_struct_type("foo_s")
     assert s.a1 == 40
     assert s.a2 == 40 * 40
     assert s.a3 == 40 * 40 * 40
+    #
+    BStruct14 = new_struct_type("test14")
+    complete_struct_or_union(BStruct14, [('a1', BFloat, -1),
+                                         ])
+    BFunc14 = new_function_type((BInt,), BStruct14)
+    f = cast(BFunc14, _testfunc(14))
+    s = f(40)
+    assert repr(s) == "<cdata 'struct test14' owning 4 bytes>"
+    assert s.a1 == 40.0
+    #
+    BStruct15 = new_struct_type("test15")
+    complete_struct_or_union(BStruct15, [('a1', BFloat, -1),
+                                         ('a2', BInt, -1)])
+    BFunc15 = new_function_type((BInt,), BStruct15)
+    f = cast(BFunc15, _testfunc(15))
+    s = f(40)
+    assert repr(s) == "<cdata 'struct test15' owning 8 bytes>"
+    assert s.a1 == 40.0
+    assert s.a2 == 40 * 40
+    #
+    BStruct16 = new_struct_type("test16")
+    complete_struct_or_union(BStruct16, [('a1', BFloat, -1),
+                                         ('a2', BFloat, -1)])
+    BFunc16 = new_function_type((BInt,), BStruct16)
+    f = cast(BFunc16, _testfunc(16))
+    s = f(40)
+    assert repr(s) == "<cdata 'struct test16' owning 8 bytes>"
+    assert s.a1 == 40.0
+    assert s.a2 == -40.0
+    #
+    BStruct17 = new_struct_type("test17")
+    complete_struct_or_union(BStruct17, [('a1', BInt, -1),
+                                         ('a2', BFloat, -1)])
+    BFunc17 = new_function_type((BInt,), BStruct17)
+    f = cast(BFunc17, _testfunc(17))
+    s = f(40)
+    assert repr(s) == "<cdata 'struct test17' owning 8 bytes>"
+    assert s.a1 == 40
+    assert s.a2 == 40.0 * 40.0
 
 def test_cast_with_functionptr():
     BFunc = new_function_type((), new_void_type())
 sources = ['c/_cffi_backend.c']
 libraries = ['ffi']
 include_dirs = []
+define_macros = []
 
 
 if sys.platform == 'win32':
         _filenames.remove('win32.c')
     sources.extend(os.path.join(COMPILE_LIBFFI, filename)
                    for filename in _filenames)
+    define_macros.append(('USE_C_LIBFFI_MSVC', '1'))
 else:
     try:
         p = subprocess.Popen(['pkg-config', '--cflags-only-I', 'libffi'],
                 Extension(name='_cffi_backend',
                           include_dirs=include_dirs,
                           sources=sources,
-                          libraries=libraries),
+                          libraries=libraries,
+                          define_macros=define_macros),
             ],
         ),
     },

File setup_base.py

 import sys, os
 
 
-from setup import include_dirs, sources, libraries
+from setup import include_dirs, sources, libraries, define_macros
 
 
 if __name__ == '__main__':
                                  include_dirs=include_dirs,
                                  sources=sources,
                                  libraries=libraries,
+                                 define_macros=define_macros,
                                  )])