@@ -1514,6 +1514,154 @@ using default_holder_type = smart_holder;
15141514
15151515#endif
15161516
1517+ // Helper for the property_cpp_function static member functions below.
1518+ // The only purpose of these functions is to support .def_readonly & .def_readwrite.
1519+ // In this context, the PM template parameter is certain to be a Pointer to a Member.
1520+ // The main purpose of must_be_member_function_pointer is to make this obvious, and to guard
1521+ // against accidents. As a side-effect, it also explains why the syntactical overhead for
1522+ // perfect forwarding is not needed.
1523+ template <typename PM>
1524+ using must_be_member_function_pointer
1525+ = detail::enable_if_t <std::is_member_pointer<PM>::value, int >;
1526+
1527+ // Note that property_cpp_function is intentionally in the main pybind11 namespace,
1528+ // because user-defined specializations could be useful.
1529+
1530+ // Classic (non-smart_holder) implementations for .def_readonly and .def_readwrite
1531+ // getter and setter functions.
1532+ // WARNING: This classic implementation can lead to dangling pointers for raw pointer members.
1533+ // See test_ptr() in tests/test_class_sh_property.py
1534+ // This implementation works as-is (and safely) for smart_holder std::shared_ptr members.
1535+ template <typename T, typename D, typename SFINAE = void >
1536+ struct property_cpp_function {
1537+ template <typename PM, must_be_member_function_pointer<PM> = 0 >
1538+ static cpp_function readonly (PM pm, const handle &hdl) {
1539+ return cpp_function ([pm](const T &c) -> const D & { return c.*pm; }, is_method (hdl));
1540+ }
1541+
1542+ template <typename PM, must_be_member_function_pointer<PM> = 0 >
1543+ static cpp_function read (PM pm, const handle &hdl) {
1544+ return readonly (pm, hdl);
1545+ }
1546+
1547+ template <typename PM, must_be_member_function_pointer<PM> = 0 >
1548+ static cpp_function write (PM pm, const handle &hdl) {
1549+ return cpp_function ([pm](T &c, const D &value) { c.*pm = value; }, is_method (hdl));
1550+ }
1551+ };
1552+
1553+ // smart_holder specializations for raw pointer members.
1554+ // WARNING: Like the classic implementation, this implementation can lead to dangling pointers.
1555+ // See test_ptr() in tests/test_class_sh_property.py
1556+ // However, the read functions return a shared_ptr to the member, emulating the PyCLIF approach:
1557+ // https://github.com/google/clif/blob/c371a6d4b28d25d53a16e6d2a6d97305fb1be25a/clif/python/instance.h#L233
1558+ // This prevents disowning of the Python object owning the raw pointer member.
1559+ template <typename T, typename D>
1560+ struct property_cpp_function <
1561+ T,
1562+ D,
1563+ detail::enable_if_t <detail::all_of<detail::type_uses_smart_holder_type_caster<T>,
1564+ detail::type_uses_smart_holder_type_caster<D>,
1565+ std::is_pointer<D>>::value>> {
1566+
1567+ using drp = typename std::remove_pointer<D>::type;
1568+
1569+ template <typename PM, must_be_member_function_pointer<PM> = 0 >
1570+ static cpp_function readonly (PM pm, const handle &hdl) {
1571+ return cpp_function (
1572+ [pm](const std::shared_ptr<T> &c_sp) -> std::shared_ptr<drp> {
1573+ D ptr = (*c_sp).*pm;
1574+ return std::shared_ptr<drp>(c_sp, ptr);
1575+ },
1576+ is_method (hdl));
1577+ }
1578+
1579+ template <typename PM, must_be_member_function_pointer<PM> = 0 >
1580+ static cpp_function read (PM pm, const handle &hdl) {
1581+ return readonly (pm, hdl);
1582+ }
1583+
1584+ template <typename PM, must_be_member_function_pointer<PM> = 0 >
1585+ static cpp_function write (PM pm, const handle &hdl) {
1586+ return cpp_function ([pm](T &c, D value) { c.*pm = std::forward<D>(value); },
1587+ is_method (hdl));
1588+ }
1589+ };
1590+
1591+ // smart_holder specializations for members held by-value.
1592+ // The read functions return a shared_ptr to the member, emulating the PyCLIF approach:
1593+ // https://github.com/google/clif/blob/c371a6d4b28d25d53a16e6d2a6d97305fb1be25a/clif/python/instance.h#L233
1594+ // This prevents disowning of the Python object owning the member.
1595+ template <typename T, typename D>
1596+ struct property_cpp_function <
1597+ T,
1598+ D,
1599+ detail::enable_if_t <detail::all_of<detail::type_uses_smart_holder_type_caster<T>,
1600+ detail::type_uses_smart_holder_type_caster<D>,
1601+ detail::none_of<std::is_pointer<D>,
1602+ detail::is_std_unique_ptr<D>,
1603+ detail::is_std_shared_ptr<D>>>::value>> {
1604+
1605+ template <typename PM, must_be_member_function_pointer<PM> = 0 >
1606+ static cpp_function readonly (PM pm, const handle &hdl) {
1607+ return cpp_function (
1608+ [pm](const std::shared_ptr<T> &c_sp)
1609+ -> std::shared_ptr<typename std::add_const<D>::type> {
1610+ return std::shared_ptr<typename std::add_const<D>::type>(c_sp, &(c_sp.get ()->*pm));
1611+ },
1612+ is_method (hdl));
1613+ }
1614+
1615+ template <typename PM, must_be_member_function_pointer<PM> = 0 >
1616+ static cpp_function read (PM pm, const handle &hdl) {
1617+ return cpp_function (
1618+ [pm](const std::shared_ptr<T> &c_sp) -> std::shared_ptr<D> {
1619+ return std::shared_ptr<D>(c_sp, &(c_sp.get ()->*pm));
1620+ },
1621+ is_method (hdl));
1622+ }
1623+
1624+ template <typename PM, must_be_member_function_pointer<PM> = 0 >
1625+ static cpp_function write (PM pm, const handle &hdl) {
1626+ return cpp_function ([pm](T &c, const D &value) { c.*pm = value; }, is_method (hdl));
1627+ }
1628+ };
1629+
1630+ // smart_holder specializations for std::unique_ptr members.
1631+ // read disowns the member unique_ptr.
1632+ // write disowns the passed Python object.
1633+ // readonly is disabled (static_assert) because there is no safe & intuitive way to make the member
1634+ // accessible as a Python object without disowning the member unique_ptr. A .def_readonly disowning
1635+ // the unique_ptr member is deemed highly prone to misunderstandings.
1636+ template <typename T, typename D>
1637+ struct property_cpp_function <
1638+ T,
1639+ D,
1640+ detail::enable_if_t <detail::all_of<
1641+ detail::type_uses_smart_holder_type_caster<T>,
1642+ detail::is_std_unique_ptr<D>,
1643+ detail::type_uses_smart_holder_type_caster<typename D::element_type>>::value>> {
1644+
1645+ template <typename PM, must_be_member_function_pointer<PM> = 0 >
1646+ static cpp_function readonly (PM, const handle &) {
1647+ static_assert (!detail::is_std_unique_ptr<D>::value,
1648+ " def_readonly cannot be used for std::unique_ptr members." );
1649+ return cpp_function{}; // Unreachable.
1650+ }
1651+
1652+ template <typename PM, must_be_member_function_pointer<PM> = 0 >
1653+ static cpp_function read (PM pm, const handle &hdl) {
1654+ return cpp_function (
1655+ [pm](const std::shared_ptr<T> &c_sp) -> D { return D{std::move (c_sp.get ()->*pm)}; },
1656+ is_method (hdl));
1657+ }
1658+
1659+ template <typename PM, must_be_member_function_pointer<PM> = 0 >
1660+ static cpp_function write (PM pm, const handle &hdl) {
1661+ return cpp_function ([pm](T &c, D &&value) { c.*pm = std::move (value); }, is_method (hdl));
1662+ }
1663+ };
1664+
15171665template <typename type_, typename ... options>
15181666class class_ : public detail ::generic_type {
15191667 template <typename T>
@@ -1727,18 +1875,22 @@ class class_ : public detail::generic_type {
17271875 class_ &def_readwrite (const char *name, D C::*pm, const Extra &...extra) {
17281876 static_assert (std::is_same<C, type>::value || std::is_base_of<C, type>::value,
17291877 " def_readwrite() requires a class member (or base class member)" );
1730- cpp_function fget ([pm](const type &c) -> const D & { return c.*pm; }, is_method (*this )),
1731- fset ([pm](type &c, const D &value) { c.*pm = value; }, is_method (*this ));
1732- def_property (name, fget, fset, return_value_policy::reference_internal, extra...);
1878+ def_property (name,
1879+ property_cpp_function<type, D>::read (pm, *this ),
1880+ property_cpp_function<type, D>::write (pm, *this ),
1881+ return_value_policy::reference_internal,
1882+ extra...);
17331883 return *this ;
17341884 }
17351885
17361886 template <typename C, typename D, typename ... Extra>
17371887 class_ &def_readonly (const char *name, const D C::*pm, const Extra &...extra) {
17381888 static_assert (std::is_same<C, type>::value || std::is_base_of<C, type>::value,
17391889 " def_readonly() requires a class member (or base class member)" );
1740- cpp_function fget ([pm](const type &c) -> const D & { return c.*pm; }, is_method (*this ));
1741- def_property_readonly (name, fget, return_value_policy::reference_internal, extra...);
1890+ def_property_readonly (name,
1891+ property_cpp_function<type, D>::readonly (pm, *this ),
1892+ return_value_policy::reference_internal,
1893+ extra...);
17421894 return *this ;
17431895 }
17441896
0 commit comments