From 5825203966351d0f4ec1ea9f30f254d2f2f94712 Mon Sep 17 00:00:00 2001 From: Wenzel Jakob Date: Tue, 30 Oct 2018 16:19:22 +0100 Subject: [PATCH] Support C++17 aligned new statement This patch makes pybind11 aware of nonstandard alignment requirements in bound types and passes on this information to C++17 aligned 'new' operator. Pre-C++17, the behavior is unchanged. --- include/pybind11/attr.h | 5 ++++- include/pybind11/cast.h | 11 ++++++++++- include/pybind11/detail/internals.h | 4 ++-- include/pybind11/pybind11.h | 2 ++ tests/test_class.cpp | 9 +++++++++ tests/test_class.py | 6 ++++++ 6 files changed, 33 insertions(+), 4 deletions(-) diff --git a/include/pybind11/attr.h b/include/pybind11/attr.h index 985bc5794..8732cfe10 100644 --- a/include/pybind11/attr.h +++ b/include/pybind11/attr.h @@ -214,11 +214,14 @@ struct type_record { /// How large is the underlying C++ type? size_t type_size = 0; + /// What is the alignment of the underlying C++ type? + size_t type_align = 0; + /// How large is the type's holder? size_t holder_size = 0; /// The global operator new can be overridden with a class-specific variant - void *(*operator_new)(size_t) = ::operator new; + void *(*operator_new)(size_t) = nullptr; /// Function pointer to class_<..>::init_instance void (*init_instance)(instance *, const void *) = nullptr; diff --git a/include/pybind11/cast.h b/include/pybind11/cast.h index 5529768b3..ff15a414f 100644 --- a/include/pybind11/cast.h +++ b/include/pybind11/cast.h @@ -571,7 +571,16 @@ public: // Lazy allocation for unallocated values: if (vptr == nullptr) { auto *type = v_h.type ? v_h.type : typeinfo; - vptr = type->operator_new(type->type_size); + if (type->operator_new) { + vptr = type->operator_new(type->type_size); + } else { + #if !defined(PYBIND11_CPP17) + vptr = ::operator new(type->type_size); + #else + vptr = ::operator new(type->type_size, + (std::align_val_t) type->type_align); + #endif + } } value = vptr; } diff --git a/include/pybind11/detail/internals.h b/include/pybind11/detail/internals.h index 78d4afed0..ad3441576 100644 --- a/include/pybind11/detail/internals.h +++ b/include/pybind11/detail/internals.h @@ -116,7 +116,7 @@ struct internals { struct type_info { PyTypeObject *type; const std::type_info *cpptype; - size_t type_size, holder_size_in_ptrs; + size_t type_size, type_align, holder_size_in_ptrs; void *(*operator_new)(size_t); void (*init_instance)(instance *, const void *); void (*dealloc)(value_and_holder &v_h); @@ -138,7 +138,7 @@ struct type_info { }; /// Tracks the `internals` and `type_info` ABI version independent of the main library version -#define PYBIND11_INTERNALS_VERSION 2 +#define PYBIND11_INTERNALS_VERSION 3 #if defined(WITH_THREAD) # define PYBIND11_INTERNALS_KIND "" diff --git a/include/pybind11/pybind11.h b/include/pybind11/pybind11.h index f326bd727..3fb122191 100644 --- a/include/pybind11/pybind11.h +++ b/include/pybind11/pybind11.h @@ -896,6 +896,7 @@ protected: tinfo->type = (PyTypeObject *) m_ptr; tinfo->cpptype = rec.type; tinfo->type_size = rec.type_size; + tinfo->type_align = rec.type_align; tinfo->operator_new = rec.operator_new; tinfo->holder_size_in_ptrs = size_in_ptrs(rec.holder_size); tinfo->init_instance = rec.init_instance; @@ -1054,6 +1055,7 @@ public: record.name = name; record.type = &typeid(type); record.type_size = sizeof(conditional_t); + record.type_align = alignof(conditional_t); record.holder_size = sizeof(holder_type); record.init_instance = init_instance; record.dealloc = dealloc; diff --git a/tests/test_class.cpp b/tests/test_class.cpp index 9ed1f50bb..bdd6836f6 100644 --- a/tests/test_class.cpp +++ b/tests/test_class.cpp @@ -354,6 +354,15 @@ TEST_SUBMODULE(class_, m) { [](StringWrapper) -> NotRegistered { return {}; }); py::class_(m, "StringWrapper").def(py::init()); py::implicitly_convertible(); + + #if defined(PYBIND11_CPP17) + struct alignas(1024) Aligned { + std::uintptr_t ptr() const { return (uintptr_t) this; } + }; + py::class_(m, "Aligned") + .def(py::init<>()) + .def("ptr", &Aligned::ptr); + #endif } template class BreaksBase { public: virtual ~BreaksBase() = default; }; diff --git a/tests/test_class.py b/tests/test_class.py index 4a488ab65..ed63ca853 100644 --- a/tests/test_class.py +++ b/tests/test_class.py @@ -273,3 +273,9 @@ def test_error_after_conversions(): m.test_error_after_conversions("hello") assert str(exc_info.value).startswith( "Unable to convert function return value to a Python type!") + + +def test_aligned(): + if hasattr(m, "Aligned"): + p = m.Aligned().ptr() + assert p % 1024 == 0