diff --git a/include/pybind11/attr.h b/include/pybind11/attr.h index 651949bfc1..786133afe1 100644 --- a/include/pybind11/attr.h +++ b/include/pybind11/attr.h @@ -272,6 +272,12 @@ struct function_record { /// Pointer to next overload function_record *next = nullptr; }; +// The main purpose of this macro is to make it easy to pin-point the critically related code +// sections. +#define PYBIND11_ENSURE_PRECONDITION_FOR_FUNCTIONAL_H_PERFORMANCE_OPTIMIZATIONS(...) \ + static_assert( \ + __VA_ARGS__, \ + "Violation of precondition for pybind11/functional.h performance optimizations!") /// Special data structure which (temporarily) holds metadata about a bound class struct type_record { diff --git a/include/pybind11/detail/common.h b/include/pybind11/detail/common.h index bacc5a60db..23a9edf258 100644 --- a/include/pybind11/detail/common.h +++ b/include/pybind11/detail/common.h @@ -123,6 +123,14 @@ # endif #endif +#if defined(__cpp_lib_launder) && !(defined(_MSC_VER) && (_MSC_VER < 1914)) +# define PYBIND11_STD_LAUNDER std::launder +# define PYBIND11_HAS_STD_LAUNDER 1 +#else +# define PYBIND11_STD_LAUNDER +# define PYBIND11_HAS_STD_LAUNDER 0 +#endif + #if defined(PYBIND11_CPP20) # define PYBIND11_CONSTINIT constinit # define PYBIND11_DTOR_CONSTEXPR constexpr diff --git a/include/pybind11/functional.h b/include/pybind11/functional.h index 58e5b3645e..77ddf290ef 100644 --- a/include/pybind11/functional.h +++ b/include/pybind11/functional.h @@ -101,8 +101,14 @@ struct type_caster> { *reinterpret_cast(rec->data[1]))) { struct capture { function_type f; + + static capture *from_data(void **data) { + return PYBIND11_STD_LAUNDER(reinterpret_cast(data)); + } }; - value = ((capture *) &rec->data)->f; + PYBIND11_ENSURE_PRECONDITION_FOR_FUNCTIONAL_H_PERFORMANCE_OPTIMIZATIONS( + std::is_standard_layout::value); + value = capture::from_data(rec->data)->f; return true; } rec = rec->next; diff --git a/include/pybind11/pybind11.h b/include/pybind11/pybind11.h index 6a8e57ad34..75a0b49695 100644 --- a/include/pybind11/pybind11.h +++ b/include/pybind11/pybind11.h @@ -38,13 +38,6 @@ PYBIND11_WARNING_DISABLE_CLANG("-Wgnu-zero-variadic-macro-arguments") #endif -#if defined(__cpp_lib_launder) && !(defined(_MSC_VER) && (_MSC_VER < 1914)) -# define PYBIND11_STD_LAUNDER std::launder -# define PYBIND11_HAS_STD_LAUNDER 1 -#else -# define PYBIND11_STD_LAUNDER -# define PYBIND11_HAS_STD_LAUNDER 0 -#endif #if defined(__GNUG__) && !defined(__clang__) # include #endif @@ -345,6 +338,10 @@ class cpp_function : public function { using namespace detail; struct capture { remove_reference_t f; + + static capture *from_data(void **data) { + return PYBIND11_STD_LAUNDER(reinterpret_cast(data)); + } }; /* Store the function including any extra state it might have (e.g. a lambda capture @@ -364,7 +361,7 @@ class cpp_function : public function { PYBIND11_WARNING_DISABLE_GCC("-Wplacement-new") #endif - new ((capture *) &rec->data) capture{std::forward(f)}; + new (capture::from_data(rec->data)) capture{std::forward(f)}; #if !PYBIND11_HAS_STD_LAUNDER PYBIND11_WARNING_DISABLE_GCC("-Wstrict-aliasing") @@ -374,8 +371,8 @@ class cpp_function : public function { // a significant refactoring it's "impossible" to solve. if (!std::is_trivially_destructible::value) { rec->free_data = [](function_record *r) { - auto data = PYBIND11_STD_LAUNDER((capture *) &r->data); - (void) data; + auto data = capture::from_data(r->data); + (void) data; // suppress "unused variable" warnings data->~capture(); }; } @@ -492,6 +489,8 @@ class cpp_function : public function { using FunctionType = Return (*)(Args...); constexpr bool is_function_ptr = std::is_convertible::value && sizeof(capture) == sizeof(void *); + PYBIND11_ENSURE_PRECONDITION_FOR_FUNCTIONAL_H_PERFORMANCE_OPTIMIZATIONS( + !is_function_ptr || std::is_standard_layout::value); if (is_function_ptr) { rec->is_stateless = true; rec->data[1]