[smart_holder] Bug fix: std::unique_ptr deleter needs to be copied. (#4850)

* Store `std::function<void (void *)>` del_fun; in `guarded_delete`

* Specialize the simple common case.

Using a `union` is complicated: https://en.cppreference.com/w/cpp/language/union

> If members of a union are classes with user-defined constructors and destructors, to switch the active member, explicit destructor and placement new are generally needed:

Using `std::variant` increases compile-time overhead.

It is currently unclear how much these effects matter in practice: optimization left for later.

* Add one test case (more later).

* Add `const` to resolve clang-tidy error.

```
-- The CXX compiler identification is Clang 15.0.7

/usr/bin/cmake -E __run_co_compile --tidy="/usr/bin/clang-tidy;--use-color;--warnings-as-errors=*;--extra-arg-before=--driver-mode=g++" --source=/__w/pybind11/pybind11/tests/test_class_sh_inheritance.cpp -- /usr/bin/c++ -DPYBIND11_ENABLE_TYPE_CASTER_ODR_GUARD_IF_AVAILABLE -DPYBIND11_TEST_EIGEN -Dpybind11_tests_EXPORTS -I/__w/pybind11/pybind11/include -isystem /usr/include/python3.9 -isystem /__w/pybind11/pybind11/build/_deps/eigen-src -Os -DNDEBUG -fPIC -fvisibility=hidden -Wall -Wextra -Wconversion -Wcast-qual -Wdeprecated -Wundef -Wnon-virtual-dtor -flto=thin -std=c++17 -o CMakeFiles/pybind11_tests.dir/test_class_sh_inheritance.cpp.o -c /__w/pybind11/pybind11/tests/test_class_sh_inheritance.cpp
/__w/pybind11/pybind11/tests/pure_cpp/smart_holder_poc_test.cpp:264:30: error: pointer parameter 'raw_ptr' can be pointer to const [readability-non-const-parameter,-warnings-as-errors]
        new int(19), [](int *raw_ptr) { delete raw_ptr; });
                             ^
                        const
```

* Introduce `struct custom_deleter` to ensure the deleter is moved as desired (the lambda function only captures a reference, which can become dangling).

* Resolve helpful clang-tidy errors.

```
/usr/bin/cmake -E __run_co_compile --tidy="/usr/bin/clang-tidy;--use-color;--warnings-as-errors=*;--extra-arg-before=--driver-mode=g++" --source=/__w/pybind11/pybind11/tests/test_class.cpp -- /usr/bin/c++ -DPYBIND11_ENABLE_TYPE_CASTER_ODR_GUARD_IF_AVAILABLE -DPYBIND11_TEST_EIGEN -Dpybind11_tests_EXPORTS -I/__w/pybind11/pybind11/include -isystem /usr/include/python3.9 -isystem /__w/pybind11/pybind11/build/_deps/eigen-src -Os -DNDEBUG -fPIC -fvisibility=hidden -Wall -Wextra -Wconversion -Wcast-qual -Wdeprecated -Wundef -Wnon-virtual-dtor -flto=thin -std=c++17 -o CMakeFiles/pybind11_tests.dir/test_class.cpp.o -c /__w/pybind11/pybind11/tests/test_class.cpp
/__w/pybind11/pybind11/include/pybind11/detail/smart_holder_poc.h:114:5: error: single-argument constructors must be marked explicit to avoid unintentional implicit conversions [google-explicit-constructor,-warnings-as-errors]
    custom_deleter(D &&deleter) : deleter{std::move(deleter)} {}
    ^
    explicit
/__w/pybind11/pybind11/include/pybind11/detail/smart_holder_poc.h:120:76: error: forwarding reference passed to std::move(), which may unexpectedly cause lvalues to be moved; use std::forward() instead [bugprone-move-forwarding-reference,-warnings-as-errors]
    return guarded_delete(std::function<void(void *)>(custom_deleter<T, D>(std::move(uqp_del))),
                                                                           ^~~~~~~~~
                                                                           std::forward<D>
```

* Workaround for gcc 4.8.5, clang 3.6

* Transfer reduced test here.

Reduced from a PyCLIF use case in the wild by @wangxf123456 (internal change cl/565476030).

* Add missing include (clangd Include Cleaner)

* Change `std::move` to `std::forward` as suggested by @iwanders.

* Add missing includes (clangd Include Cleaner)

* Use new `PYBIND11_TESTS_PURE_CPP_SMART_HOLDER_POC_TEST_CPP` to exclude `smart_holder::as_unique_ptr` method from production code.

* Systematically add `PYBIND11_TESTS_PURE_CPP_SMART_HOLDER_POC_TEST_CPP` to mark code that is not used from production code. Add comment to explain.

* Very simple experiment related to https://github.com/pybind/pybind11/pull/4850#issuecomment-1789780676

Does the `PYBIND11_TESTS_PURE_CPP_SMART_HOLDER_POC_TEST_CPP` define have anything to do with it?

* Revert "Very simple experiment related to https://github.com/pybind/pybind11/pull/4850#issuecomment-1789780676"

This reverts commit fe59369f40.
This commit is contained in:
Ralf W. Grosse-Kunstleve
2023-11-02 08:21:13 -07:00
committed by GitHub
parent e955753c1b
commit 55105fbeaf
5 changed files with 114 additions and 10 deletions

View File

@@ -1,5 +1,12 @@
#define PYBIND11_TESTS_PURE_CPP_SMART_HOLDER_POC_TEST_CPP
#include "pybind11/detail/smart_holder_poc.h"
#include <functional>
#include <memory>
#include <type_traits>
#include <utility>
// Catch uses _ internally, which breaks gettext style defines
#ifdef _
# undef _
@@ -21,6 +28,15 @@ struct movable_int {
template <typename T>
struct functor_builtin_delete {
void operator()(T *ptr) { delete ptr; }
#if (defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ == 8) \
|| (defined(__clang_major__) && __clang_major__ == 3 && __clang_minor__ == 6)
// Workaround for these errors:
// gcc 4.8.5: too many initializers for 'helpers::functor_builtin_delete<int>'
// clang 3.6: excess elements in struct initializer
functor_builtin_delete() = default;
functor_builtin_delete(const functor_builtin_delete &) {}
functor_builtin_delete(functor_builtin_delete &&) {}
#endif
};
template <typename T>
@@ -259,6 +275,14 @@ TEST_CASE("from_unique_ptr_with_deleter+as_lvalue_ref", "[S]") {
REQUIRE(hld.as_lvalue_ref<int>() == 19);
}
TEST_CASE("from_unique_ptr_with_std_function_deleter+as_lvalue_ref", "[S]") {
std::unique_ptr<int, std::function<void(const int *)>> orig_owner(
new int(19), [](const int *raw_ptr) { delete raw_ptr; });
auto hld = smart_holder::from_unique_ptr(std::move(orig_owner));
REQUIRE(orig_owner.get() == nullptr);
REQUIRE(hld.as_lvalue_ref<int>() == 19);
}
TEST_CASE("from_unique_ptr_with_deleter+as_raw_ptr_release_ownership", "[E]") {
std::unique_ptr<int, helpers::functor_builtin_delete<int>> orig_owner(new int(19));
auto hld = smart_holder::from_unique_ptr(std::move(orig_owner));