Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[smart_holder] Fix handling of const unique_ptr<T, D> & (do not disown). #5332

Merged
merged 15 commits into from
Aug 25, 2024

Conversation

rwgk
Copy link
Collaborator

@rwgk rwgk commented Aug 24, 2024

Description

Before this PR, passing a Python object as a C++ const unique_ptr<T, D> & involved building a temporary (non-const) unique_ptr<T, D>, which was then passed as a const unique_ptr<T, D> &. The Python object was disowned when the temporary unique_ptr<T, D> was built. This was unintentional and counter-intuitive (although bug-compatible with PyCLIF; probably also unintentional).

This PR introduces a different mechanism, specifically for const unique_ptr<T, D> &: a temporary (still non-const) unique_ptr<T, D> is built, but the Python object is not disowned, and the temporary unique_ptr is meant to never expire (this would only be possible if the passed-to C++ code applied a const_cast, to then disown the non-const unique_ptr). When the type_caster goes out of scope, the new implementation checks if the temporary unique_ptr did in fact not expire (otherwise a FATAL exception is thrown), then .release()s it before deleting the unique_ptr object. Conveniently, this can be achieved by managing the temporary unique_ptr with a shared_ptr and a custom deleter.

I (rwgk) strongly considered the alternative of using a static_assert() to make it impossible to pass a Python object as a const unique_ptr<T, D> &, but the new implementation is sufficiently small, it's less trouble than backing out all existing test code that would break. There could also be existing client code in the wild that may need workarounds. Having the revised feature is convenient and not very costly (amount of code; complexity).

@rhaschke FYI

Suggested changelog entry:

rwgk pushed a commit to rwgk/pybind11 that referenced this pull request Aug 25, 2024
PREPARATION for:

PR pybind#5332 — Fix handling of const unique_ptr<T, D> & (do not disown).

Splitting out so that the functional changes under PR pybind#5332 will be more obvious.
rwgk pushed a commit to rwgk/pybind11 that referenced this pull request Aug 25, 2024
PREPARATION for:

PR pybind#5332 — Fix handling of const unique_ptr<T, D> & (do not disown).

Splitting out so that the functional changes under PR pybind#5332 will be more obvious.

The only functional change under this PR is that

```
            assert(custom_deleter_ptr != nullptr);
```

is replaced with:

```
            if (custom_deleter_ptr == nullptr) {
                throw std::runtime_error(
                    std::string("smart_holder::extract_deleter() precondition failure (") + context
                    + ").");
            }
```
rwgk pushed a commit that referenced this pull request Aug 25, 2024
PREPARATION for:

PR #5332 — Fix handling of const unique_ptr<T, D> & (do not disown).

Splitting out so that the functional changes under PR #5332 will be more obvious.
rwgk pushed a commit to rwgk/pybind11 that referenced this pull request Aug 25, 2024
PREPARATION for:

PR pybind#5332 — Fix handling of const unique_ptr<T, D> & (do not disown).

Splitting out so that the functional changes under PR pybind#5332 will be more obvious.

The only functional change under this PR is that

```
            assert(custom_deleter_ptr != nullptr);
```

is replaced with:

```
            if (custom_deleter_ptr == nullptr) {
                throw std::runtime_error(
                    std::string("smart_holder::extract_deleter() precondition failure (") + context
                    + ").");
            }
```
rwgk pushed a commit that referenced this pull request Aug 25, 2024
PREPARATION for:

PR #5332 — Fix handling of const unique_ptr<T, D> & (do not disown).

Splitting out so that the functional changes under PR #5332 will be more obvious.

The only functional change under this PR is that

```
            assert(custom_deleter_ptr != nullptr);
```

is replaced with:

```
            if (custom_deleter_ptr == nullptr) {
                throw std::runtime_error(
                    std::string("smart_holder::extract_deleter() precondition failure (") + context
                    + ").");
            }
```
@rwgk
Copy link
Collaborator Author

rwgk commented Aug 25, 2024

@iwanders: be3bafe

I guess we could make that a requirement only when actually constructing a const unique_ptr<T, D> &, but maybe only when someone asks for it? (To keep the code simpler for now.)

@rwgk rwgk marked this pull request as ready for review August 25, 2024 17:57
@rwgk rwgk merged commit 04d9f84 into pybind:smart_holder Aug 25, 2024
79 checks passed
@rwgk rwgk deleted the unique_ptr_cref_sh branch August 25, 2024 17:57
@github-actions github-actions bot added the needs changelog Possibly needs a changelog entry label Aug 25, 2024
@rwgk rwgk removed the needs changelog Possibly needs a changelog entry label Aug 25, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants