diff --git a/include/pybind11/numpy.h b/include/pybind11/numpy.h index 3d34503c36..4e83dc799f 100644 --- a/include/pybind11/numpy.h +++ b/include/pybind11/numpy.h @@ -991,14 +991,15 @@ inline PYBIND11_NOINLINE void register_structured_dtype( [](const field_descriptor &a, const field_descriptor &b) { return a.offset < b.offset; }); size_t offset = 0; std::ostringstream oss; - oss << "T{"; + // mark the structure as unaligned with '^', because numpy and C++ don't + // always agree about alignment (particularly for complex), and we're + // explicitly listing all our padding. This depends on none of the fields + // overriding the endianness. Putting the ^ in front of individual fields + // isn't guaranteed to work due to https://github.com/numpy/numpy/issues/9049 + oss << "^T{"; for (auto& field : ordered_fields) { if (field.offset > offset) oss << (field.offset - offset) << 'x'; - // mark all fields with '^' (unaligned native type), because numpy - // and C++ don't always agree about alignment (particularly for - // complex, and we're explicitly listing all padding. - oss << '^'; oss << field.format << ':' << field.name << ':'; offset = field.offset + field.size; } diff --git a/tests/test_numpy_dtypes.py b/tests/test_numpy_dtypes.py index 415b5b7a06..91ac446a3e 100644 --- a/tests/test_numpy_dtypes.py +++ b/tests/test_numpy_dtypes.py @@ -72,22 +72,22 @@ def test_format_descriptors(): assert re.match('^NumPy type info missing for .*UnboundStruct.*$', str(excinfo.value)) ld = np.dtype('longdouble') - ldbl_fmt = ('4x' if ld.alignment > 4 else '') + '^' + ld.char - ss_fmt = "T{^?:bool_:3x^I:uint_:^f:float_:" + ldbl_fmt + ":ldbl_:}" + ldbl_fmt = ('4x' if ld.alignment > 4 else '') + ld.char + ss_fmt = "^T{?:bool_:3xI:uint_:f:float_:" + ldbl_fmt + ":ldbl_:}" dbl = np.dtype('double') - partial_fmt = ("T{^?:bool_:3x^I:uint_:^f:float_:" + + partial_fmt = ("^T{?:bool_:3xI:uint_:f:float_:" + str(4 * (dbl.alignment > 4) + dbl.itemsize + 8 * (ld.alignment > 8)) + - "x^g:ldbl_:}") + "xg:ldbl_:}") nested_extra = str(max(8, ld.alignment)) assert print_format_descriptors() == [ ss_fmt, - "T{^?:bool_:^I:uint_:^f:float_:^g:ldbl_:}", - "T{^" + ss_fmt + ":a:^T{^?:bool_:^I:uint_:^f:float_:^g:ldbl_:}:b:}", + "^T{?:bool_:I:uint_:f:float_:g:ldbl_:}", + "^T{" + ss_fmt + ":a:^T{?:bool_:I:uint_:f:float_:g:ldbl_:}:b:}", partial_fmt, - "T{" + nested_extra + "x^" + partial_fmt + ":a:" + nested_extra + "x}", - "T{^3s:a:^3s:b:}", - 'T{^q:e1:^B:e2:}', - 'T{^Zf:cflt:^Zd:cdbl:}' + "^T{" + nested_extra + "x" + partial_fmt + ":a:" + nested_extra + "x}", + "^T{3s:a:3s:b:}", + '^T{q:e1:B:e2:}', + '^T{Zf:cflt:Zd:cdbl:}' ]