Skip to content

feat: squash-merge smart_holder branch into master #5542

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

Merged

Conversation

rwgk
Copy link
Collaborator

@rwgk rwgk commented Feb 22, 2025

Description

EDIT: The smart_holder branch was archived here after this PR was merged.

Notes:


The following is for this PR at commit a4cfd38:

git diff counts under include/:

  • 26 lines removed (git diff master include/ | grep '^-' | grep -v '^---' | wc -l)
  • 1543 lines added (git diff master include/ | grep '^+' | grep -v '^+++' | wc -l)

I.e., the existing production code is barely touched.

Similarly for tests/:

  • 2 lines removed
  • 3714 lines added

For more detail, results produced by cloc --diff for include/ between the master branch and this PR:

-------------------------------------------------------------------------------
Language                     files          blank        comment           code
-------------------------------------------------------------------------------
C/C++ Header
 same                           30           1416           3171          15142
 modified                       10              0              1             18
 added                           4            182            159           1182
 removed                         0              0              2              4
-------------------------------------------------------------------------------

I.e., this PR increases the production code size by 7.8%.


The squash-commit cd1c9d4 includes these smart_holder PRs not authored by @rwgk, sorted chronologically:

Suggested changelog entry:

Merge the smart-holder branch.

To be covered in the upgrade guide:

@rwgk rwgk changed the title [PREVIEW/WIP] squash-merge smart_holder branch into master squash-merge smart_holder branch into master Feb 23, 2025
@rwgk rwgk marked this pull request as ready for review February 23, 2025 21:47
@rwgk rwgk requested a review from henryiii as a code owner February 23, 2025 21:47
Copy link
Contributor

@virtuald virtuald left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Only reviewed documentation.

I note that the documentation doesn't mention PYBIND11_USE_SMART_HOLDER_AS_DEFAULT at all. In my usage of pybind11, I enable that macro because (a) I'm lazy and (b) I assumed that because smart holder is awesome that eventually when it got merged it would become the default and then we could get rid of the macro.

I don't understand the backwards compatibility argument for not making smart holder the default, but if there really are known backwards compatibility issues and it's not going to be the default I think I lean towards getting rid of the macro? When reading pybind11 code it would be good to know "this is definitely using smart holder" and "this is definitely not" without having to know which compile flags are set.

Edit: lol clearly I didn't read the PR description carefully enough

docs/classes.rst Outdated

.. note::

Starting with pybind11v3, it is recommended to use `py::classh` in most
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe it's another PR, but since it's the recommended default, we probably should change all of the documentation to use it, since users often just copy/paste from the documentation without thinking about it.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was on the fence, thanks for the feedback! I'll change it around.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in commit ac9d31e.

(I made a fast pass, followed by a careful slow pass reviewing the generated html.)

@rwgk
Copy link
Collaborator Author

rwgk commented Feb 24, 2025

@virtuald As someone who's used the smart_holder branch for a long time, what's you opinion about this question?

Commit 4d8973e removes pybind11/smart_holder.h. Would it be better to keep it?

It only makes a difference to people who have been using the smart_holder branch before (probably a relative small number?). When they switch to pbyind11v3, they'll be forced to adjust their code (remove pybind11/smart_holder.h includes). Is that a hardship, or just a minor inconvenience?

.. seealso::

The file :file:`tests/test_smart_ptr.cpp` contains a complete example
that demonstrates how to work with custom reference-counting holder types
in more detail.


Be careful not to accidentally undermine automatic lifetime management
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe I missed it, but is this a new section that has nothing to do with smart holder? Maybe move it to a separate PR.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, it's definitely odd here, but it's based on this existing part:

One potential stumbling block when using holder types is that they need to be
applied consistently. Can you guess what's broken about the following binding
code?
.. code-block:: cpp
class Child { };
class Parent {
public:
Parent() : child(std::make_shared<Child>()) { }
Child *get_child() { return child.get(); } /* Hint: ** DON'T DO THIS ** */
private:
std::shared_ptr<Child> child;
};
PYBIND11_MODULE(example, m) {
py::class_<Child, std::shared_ptr<Child>>(m, "Child");
py::class_<Parent, std::shared_ptr<Parent>>(m, "Parent")
.def(py::init<>())
.def("get_child", &Parent::get_child);
}
The following Python code will cause undefined behavior (and likely a
segmentation fault).
.. code-block:: python
from example import Parent
print(Parent().get_child())
The problem is that ``Parent::get_child()`` returns a pointer to an instance of
``Child``, but the fact that this instance is already managed by
``std::shared_ptr<...>`` is lost when passing raw pointers. In this case,
pybind11 will create a second independent ``std::shared_ptr<...>`` that also
claims ownership of the pointer. In the end, the object will be freed **twice**
since these shared pointers have no way of knowing about each other.
There are two ways to resolve this issue:
1. For types that are managed by a smart pointer class, never use raw pointers
in function arguments or return values. In other words: always consistently
wrap pointers into their designated holder types (such as
``std::shared_ptr<...>``). In this case, the signature of ``get_child()``
should be modified as follows:
.. code-block:: cpp
std::shared_ptr<Child> get_child() { return child; }
2. Adjust the definition of ``Child`` by specifying
``std::enable_shared_from_this<T>`` (see cppreference_ for details) as a
base class. This adds a small bit of information to ``Child`` that allows
pybind11 to realize that there is already an existing
``std::shared_ptr<...>`` and communicate with it. In this case, the
declaration of ``Child`` should look as follows:
.. _cppreference: http://en.cppreference.com/w/cpp/memory/enable_shared_from_this
.. code-block:: cpp
class Child : public std::enable_shared_from_this<Child> { };

I think it'll be best to move it only after this PR is merged, to not make this PR even more difficult to review.

@virtuald
Copy link
Contributor

Commit 4d8973e removes pybind11/smart_holder.h. Would it be better to keep it?

No promises were really made about smart_holder and if one is using (what is effectively) a fork of a project then you just accept whatever comes with it, so I think whatever makes sense would be good. Since everything in that file is marked as deprecated, it seems good to get rid of it.

Looking at my own usage, I used -DPYBIND11_USE_SMART_HOLDER_AS_DEFAULT and never explicitly included that header.

@rwgk
Copy link
Collaborator Author

rwgk commented Feb 24, 2025

Commit 4d8973e removes pybind11/smart_holder.h. Would it be better to keep it?

No promises were really made about smart_holder and if one is using (what is effectively) a fork of a project then you just accept whatever comes with it, so I think whatever makes sense would be good. Since everything in that file is marked as deprecated, it seems good to get rid of it.

Thanks! I'm glad to get rid of it completely. I'll remove my note from the PR description.

Looking at my own usage, I used -DPYBIND11_USE_SMART_HOLDER_AS_DEFAULT and never explicitly included that header.

I want to nudge people towards not ever using that macro in production situations, with this in mind:

  • Using it in one translation unit but not another could lead to ODR violations.

  • When bindings code is moved from one repo or project to another, and one of them uses the macro but not the other, people might get very surprised by the subtle behavior differences.

One idea is to rename the macro, .e.g.: PYBIND11_RUN_TESTING_WITH_SMART_HOLDER_AS_DEFAULT_BUT_NEVER_USE_IN_PRODUCTION_PLEASE

So in your case the old name would become a no-op, some tests would break, which you'd then fix by changing your class_ to classh.

I'm trying to think ahead, please chime in if I'm going off the tracks:

You wouldn't be able to go back to an older pybind11 version easily. But then again, if your code requires the smart_holder feature, that wouldn't work anyway. So all good?

But what will people do if I'm overlooking something and they have a reason to use py::classh when available, but need to stay compatible with py::class_ for some period of time?

They could go through their code to change py::class_ to e.g. PY_CLASSH_ (same number of characters as py::class_, but also hints that it might be one or the other), then define that from the command line or a project-specific config-kind-of header as needed. — Is that a good situation, or should we do something to support them more?

@rwgk
Copy link
Collaborator Author

rwgk commented Feb 24, 2025

@virtuald I also made these changes after you reviewed:

  • commit 7cae21f — Change macro name to PYBIND11_RUN_TESTING_WITH_SMART_HOLDER_AS_DEFAULT_BUT_NEVER_USE_IN_PRODUCTION_PLEASE
  • commit 388fa99 — Add a note pointing to the holder reinterpret_cast.

This should have been part of commit eb550d0.
@rwgk
Copy link
Collaborator Author

rwgk commented Feb 24, 2025

@timohl, could you help with a second set of eyes reviewing the documentation changes and the PR description?

@rwgk
Copy link
Collaborator Author

rwgk commented Feb 24, 2025

@henryiii, can you think of anything that I might be overlooking?

@rwgk
Copy link
Collaborator Author

rwgk commented Feb 24, 2025

@lanctot, could you help reviewing the documentation changes and the PR description? Will this work for https://github.com/google-deepmind/open_spiel?

@rwgk
Copy link
Collaborator Author

rwgk commented Feb 24, 2025

@laramiel for awareness mainly. — But it'd be awesome if you get a chance to glance through the PR description and documentation updates.

For context: Laramie was a/the main reviewer of the smart_holder code, in particular #5257.

@rwgk
Copy link
Collaborator Author

rwgk commented Feb 24, 2025

@isuruf for awareness mainly. — But please let me know if you believe this PR will cause issues for Conda. (I don't think so; I believe any adjustments that may be needed on the Conda side (for migrating to pybind11 v3.0.0) will be for changes that exist on pybind11 master already.)

@timohl
Copy link
Contributor

timohl commented Feb 25, 2025

@timohl, could you help with a second set of eyes reviewing the documentation changes and the PR description?

Sure. I can check tomorrow.

Apparently the mis-indentation was introduced when resolving merge conflicts for what became 1e646c9
@laramiel
Copy link
Contributor

laramiel commented Mar 3, 2025

FWIW, I think https://github.com/pybind/pybind11_protobuf/blob/main/pybind11_protobuf/tests/regression_wrappers_test.py failed before 4b05643

@rwgk
Copy link
Collaborator Author

rwgk commented Mar 4, 2025

Thanks @henryiii!

I'll merge this asap, but I want to do this first:

ChatGPT prompt:

I have a git feature branch that lived for a long time. Many contributions were made to the branch, and master was merged many times, too. I now want to squash-merge the feature branch into master. I'm looking for ways to give proper credit.

My mind is going to:

  • I need to identify all commits to the feature branch (that didn't come from the master branch).
  • Ideally I'd like all contributors to appear as as contributors of the squash-merge into master.

Do you have suggestions for how to achieve that?

Response: https://chatgpt.com/share/67c77bef-c554-8008-b23d-3ebab2b1572b

rwgk and others added 3 commits March 5, 2025 12:00
…ions (for completeness, unrelated to smart_holder work).
…major and/or influential contributors to smart_holder branch

* pybind#2904 by @rhaschke was merged on Mar 16, 2021
* pybind#3012 by @rhaschke was merged on May 28, 2021
* pybind#3039 by @jakobandersen was merged on Jun 29, 2021
* pybind#3048 by @Skylion007 was merged on Jun 18, 2021
* pybind#3588 by @virtuald was merged on Jan 3, 2022
* pybind#3633 by @wangxf123456 was merged on Jan 25, 2022
* pybind#3635 by @virtuald was merged on Jan 26, 2022
* pybind#3645 by @wangxf123456 was merged on Jan 25, 2022
* pybind#3796 by @wangxf123456 was merged on Mar 10, 2022
* pybind#3807 by @wangxf123456 was merged on Mar 18, 2022
* pybind#3838 by @wangxf123456 was merged on Apr 15, 2022
* pybind#3929 by @tomba was merged on May 7, 2022
* pybind#4031 by @wangxf123456 was merged on Jun 27, 2022
* pybind#4343 by @wangxf123456 was merged on Nov 18, 2022
* pybind#4381 by @wangxf123456 was merged on Dec 5, 2022
* pybind#4539 by @wangxf123456 was merged on Feb 28, 2023
* pybind#4609 by @wangxf123456 was merged on Apr 6, 2023
* pybind#4775 by @wangxf123456 was merged on Aug 3, 2023
* pybind#4921 by @iwanders was merged on Nov 7, 2023
* pybind#4924 by @iwanders was merged on Nov 6, 2023
* pybind#5401 by @msimacek was merged on Oct 8, 2024

Co-authored-by: Aaron Gokaslan <aaronGokaslan@gmail.com>
Co-authored-by: Dustin Spicuzza <dustin@virtualroadside.com>
Co-authored-by: Ivor Wanders <iwanders@users.noreply.github.com>
Co-authored-by: Jakob Lykke Andersen <Jakob@caput.dk>
Co-authored-by: Michael Šimáček <michael.simacek@oracle.com>
Co-authored-by: Robert Haschke <rhaschke@users.noreply.github.com>
Co-authored-by: Tomi Valkeinen <tomi.valkeinen@iki.fi>
Co-authored-by: Xiaofei Wang <6218006+wangxf123456@users.noreply.github.com>
@rwgk
Copy link
Collaborator Author

rwgk commented Mar 5, 2025

Not rerunning GitHub Actions before merging:

The last three commits (9bb8d06, b9bf98a, 8e432e9) only update the top-level README.rst

I added Co-authored-by: to the last commit (8e432e9), listing all contributors to the smart_holder branch. In retrospect, I wish I had done that for the squash-commit cd1c9d4, but changing that commit would mean invalidating all other existing commit hashes on this PR as well, which would break all references from the PR comments.

For visibility:
@EthanSteinberg
@iwanders
@jakobandersen
@msimacek
@rhaschke
@Skylion007
@tomba
@virtuald
@wangxf123456

@rwgk rwgk merged commit 2943a27 into pybind:master Mar 5, 2025
2 checks passed
@github-actions github-actions bot added the needs changelog Possibly needs a changelog entry label Mar 5, 2025
@rwgk rwgk deleted the squash_merge_smart_holder_into_master_previews branch March 5, 2025 21:18
@rwgk
Copy link
Collaborator Author

rwgk commented Mar 5, 2025

I want to say thank you very much to:

My main goal was to reunify the entire pybind11 ecosystem, and I hope this work brings lasting benefits to everyone.

@petersteneteg
Copy link

Huge thanks to everyone involved here, everyone in the pybind11 team and rwgk especially! This branch and the pybind11 project are providing huge value to us, making many things possible which would otherwise be far out of reach for us!

Huge thanks!!!!

@henryiii henryiii changed the title squash-merge smart_holder branch into master feat: squash-merge smart_holder branch into master Mar 31, 2025
@henryiii henryiii removed the needs changelog Possibly needs a changelog entry label Apr 1, 2025
@danielcjacobs
Copy link
Contributor

Is there a clear set of steps to update my code that was using the smart holder branch before?

The changes I had made to use the smart holder branch are:

  1. Add -DPYBIND11_USE_SMART_HOLDER_AS_DEFAULT. Sounds like I can just remove this now?
  2. Use PYBIND11_SH_DEF macro, e.g.:
py::class_<MyClass, PYBIND11_SH_DEF(MyClass)>(m, "MyClass");

Is the solution here to just change this to:

py::classh<MyClass>(m, "MyClass");

?

@rwgk
Copy link
Collaborator Author

rwgk commented Apr 3, 2025

Is there a clear set of steps to update my code that was using the smart holder branch before?

The changes I had made to use the smart holder branch are:

  1. Add -DPYBIND11_USE_SMART_HOLDER_AS_DEFAULT. Sounds like I can just remove this now?

Yes.

  1. Use PYBIND11_SH_DEF macro, e.g.:
py::class_<MyClass, PYBIND11_SH_DEF(MyClass)>(m, "MyClass");

Is the solution here to just change this to:

py::classh<MyClass>(m, "MyClass");

Yes.

See also: https://github.com/pybind/pybind11/pull/5589/files#diff-b174d30a2ffad5a473787c436139622f3672e033474f6cf22ad5d715c15f6087

It doesn't answer your exact questions, but I'm interested in feedback.

@petersteneteg
Copy link

One thing I ran into when removing PYBIND11_USE_SMART_HOLDER_AS_DEFAULT was that I had to explicitly add py::smart_holder to the helpers in std_bind like py::bind_vector.

@rwgk
Copy link
Collaborator Author

rwgk commented Apr 4, 2025

One thing I ran into when removing PYBIND11_USE_SMART_HOLDER_AS_DEFAULT was that I had to explicitly add py::smart_holder to the helpers in std_bind like py::bind_vector.

Like this, in your bindings code?

py::bind_vector<CppType, py::smart_holder>(m, "CppTypeVector");

We should probably mention that in the upgrade guide.

@petersteneteg
Copy link

Exactly, the class_ -> classh does not cover that one. So some debugging was needed to figure out that ones was still using the old one.

@danielcjacobs
Copy link
Contributor

We should probably mention that in the upgrade guide.

Does this guide exist already?

@henryiii
Copy link
Collaborator

henryiii commented Apr 4, 2025

In #5589. I think we should focus on recommending py::class_<MyClass, py::smart_holder>(m, "MyClass");, not py::classh<MyClass>(m, "MyClass);, then this would have been more obvious. py::classh only exists for backward compatibility and to keep the number of characters the same when converting.

@rwgk
Copy link
Collaborator Author

rwgk commented Apr 7, 2025

Exactly, the class_ -> classh does not cover that one. So some debugging was needed to figure out that ones was still using the old one.

I just added this commit for the Upgrade guide:

then this would have been more obvious

Without calling out that py::bind_vector and py::bind_map default to holder_type std::unique_ptr this will not be obvious at all.

I verified that this was actually the only situation of this kind:

$ git grep default_holder_type
include/pybind11/pybind11.h:using default_holder_type = smart_holder;
include/pybind11/pybind11.h:using default_holder_type = std::unique_ptr<T>;
include/pybind11/pybind11.h:    using holder_type = detail::exactly_one_t<is_holder, default_holder_type<type>, options...>;
include/pybind11/stl_bind.h:template <typename Vector, typename holder_type = default_holder_type<Vector>, typename... Args>
include/pybind11/stl_bind.h:template <typename Map, typename holder_type = default_holder_type<Map>, typename... Args>

@petersteneteg's situation is probably very unusual, because he is an early adopter and used to work with PYBIND11_USE_SMART_HOLDER_AS_DEFAULT (which was renamed to PYBIND11_RUN_TESTING_WITH_SMART_HOLDER_AS_DEFAULT_BUT_NEVER_USE_IN_PRODUCTION_PLEASE ). For most people, adding py::smart_holder to py::bind_vector or py::bind_map will just be an optional migration step.

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.

10 participants