pybind11/tests/test_methods_and_attributes.cpp
Ralf W. Grosse-Kunstleve 1bafd5db5f
Adding py::smart_holder (for smart-pointer interoperability). (#2672)
* Adding test_unique_ptr_member (for desired PyCLIF behavior).

See also: https://github.com/pybind/pybind11/issues/2583

Does not build with upstream master or
https://github.com/pybind/pybind11/pull/2047, but builds with
https://github.com/RobotLocomotion/pybind11 and almost runs:

```
Running tests in directory "/usr/local/google/home/rwgk/forked/EricCousineau-TRI/pybind11/tests":
================================================================================= test session starts =================================================================================
platform linux -- Python 3.8.5, pytest-5.4.3, py-1.9.0, pluggy-0.13.1
rootdir: /usr/local/google/home/rwgk/forked/EricCousineau-TRI/pybind11/tests, inifile: pytest.ini
collected 2 items

test_unique_ptr_member.py .F                                                                                                                                                    [100%]

====================================================================================== FAILURES =======================================================================================
_____________________________________________________________________________ test_pointee_and_ptr_owner ______________________________________________________________________________

    def test_pointee_and_ptr_owner():
        obj = m.pointee()
        assert obj.get_int() == 213
        m.ptr_owner(obj)
        with pytest.raises(ValueError) as exc_info:
>           obj.get_int()
E           Failed: DID NOT RAISE <class 'ValueError'>

test_unique_ptr_member.py:17: Failed
============================================================================= 1 failed, 1 passed in 0.06s =============================================================================
```

* unique_ptr or shared_ptr return

* new test_variant_unique_shared with vptr_holder prototype

* moving prototype code to pybind11/vptr_holder.h, adding type_caster specialization to make the bindings involving unique_ptr passing compile, but load and cast implementations are missing

* disabling GitHub Actions on pull_request (for this PR)

* disabling AppVeyor (for this PR)

* TRIGGER_SEGSEV macro, annotations for GET_STACK (vptr::get), GET_INT_STACK (pointee)

* adding test_promotion_of_disowned_to_shared

* Copying tests as-is from xxx_value_ptr_xxx_holder branch.

https://github.com/rwgk/pybind11/tree/xxx_value_ptr_xxx_holder

Systematically exercising returning and passing unique_ptr<T>, shared_ptr<T>
with unique_ptr, shared_ptr holder.

Observations:

test_holder_unique_ptr:
  make_unique_pointee  OK
  pass_unique_pointee  BUILD_FAIL (as documented)
  make_shared_pointee  Abort free(): double free detected
  pass_shared_pointee  RuntimeError: Unable to load a custom holder type from a default-holder instance

test_holder_shared_ptr:
  make_unique_pointee  Segmentation fault (#1138)
  pass_unique_pointee  BUILD_FAIL (as documented)
  make_shared_pointee  OK
  pass_shared_pointee  OK

* Copying tests as-is from xxx_value_ptr_xxx_holder branch.

https://github.com/rwgk/pybind11/tree/xxx_value_ptr_xxx_holder

Systematically exercising casting between shared_ptr<base>, shared_ptr<derived>.

* Demonstration of Undefined Behavior in handling of shared_ptr holder.

Based on https://godbolt.org/z/4fdjaW by jorgbrown@ (thanks Jorg!).

* Additional demonstration of Undefined Behavior in handling of shared_ptr holder.

* fixing up-down mixup in comment

* Demonstration of Undefined Behavior in handling of polymorphic pointers.

(This demo does NOT involve smart pointers at all, unlike the otherwise similar test_smart_ptr_private_first_base.)

* minor test_private_first_base.cpp simplification (after discovering that this can be wrapped with Boost.Python, using boost::noncopyable)

* pybind11 equivalent of Boost.Python test similar to reproducer under #1333

* Snapshot of WIP, TODO: shared_ptr deleter with on/off switch

* Adding vptr_deleter.

* Adding from/as unique_ptr<T> and unique_ptr<T, D>.

* Adding from_shared_ptr. Some polishing.

* New tests/core/smart_holder_poc_test.cpp, using Catch2.

* Adding in vptr_deleter_guard_flag.

* Improved labeling of TEST_CASEs.

* Shuffling existing TEST_CASEs into systematic matrix.

* Implementing all [S]uccess tests.

* Implementing all [E]xception tests.

* Testing of exceptions not covered by the from-as matrix.

* Adding top-level comment.

* Converting from methods to factory functions (no functional change).

* Removing obsolete and very incomplete test (replaced by Catch2-based test).

* Removing stray file.

* Adding type_caster_bare_interface_demo.

* Adding shared_ptr<mpty>, shared_ptr<mpty const> casters.

* Adding unique_ptr<mpty>, unique_ptr<mpty const> casters.

* Pure copy of `class class_` implementation in pybind11.h (master commit 98f1bbb800).

* classh.h: renaming of class_ to classh + namespace; forking test_classh_wip from test_type_caster_bare_interface_demo.

* Hard-coding smart_holder into classh.

* Adding mpty::mtxt string member.

* Adding isinstance<mpty> in type_caster::load functions.

* Adding rvalue_ref, renaming const_value_ref to lvalue_ref & removing const.

* Retrieving smart_holder pointer in type_caster<mpty>::load, and using it cast_op operators.

* Factoring out smart_holder_type_caster_load.

* Retrieving smart_holder pointer in type_caster<std::shared_ptr<mpty[ const]>>::load, and using it cast_op operators.

* Improved error messaging: Cannot disown nullptr (as_unique_ptr).

* Retrieving smart_holder pointer in type_caster<std::unique_ptr<mpty[ const]>>::load, and using it cast_op operators.

* Pure `clang-format --style=file -i` change.

* Pure `clang-format --style=file -i` change, with two `clang-format off` directives.

* Fixing oversight (discovered by flake8).

* flake8 cleanup

* Systematically setting mtxt for all rtrn_mpty_* functions (preparation, the values are not actually used yet).

* static cast handle for rtrn_cptr works by simply dropping in code from type_caster_base (marked with comments).

* static cast handle for rtrn_cref works by simply dropping in code from type_caster_base (marked with comments). rtrn_mref and rtrn_mptr work via const_cast (to add const).

* static cast handle for rtrn_valu works by simply dropping in code from type_caster_base (marked with comments). rtrn_rref raises a RuntimeError, to be investigated.

* Copying type_caster_generic::cast into type_caster<mpty> as-is (preparation for handling smart pointers).

* Pure clang-format change (applied to original type_caster_generic::cast).

* Adding comment re potential use_count data race.

* static handle cast implementations for rtrn_shmp, rtrn_shcp.

* Adding MISSING comments in operator std::unique_ptr<mpty[ const]>.

* static handle cast implementations for rtrn_uqmp, rtrn_uqcp.

* Bug fix: vptr_deleter_armed_flag_ptr has to live on the heap.

See new bullet point in comment section near the top.

The variable was also renamed to reflect its function more accurately.

* Fixing bugs discovered by ASAN. The code is now ASAN, MSAN, UBSAN clean.

* Making test_type_caster_bare_interface_demo.cpp slightly more realistic, ASAN, MSAN, UBSAN clean.

* Calling deregister_instance after disowning via unique_ptr.

* Removing enable_shared_from_this stub, simplifying existing code, clang-format.

Open question, with respect to the original code:
76a160070b/include/pybind11/pybind11.h (L1510)
To me it looks like the exact situation marked as `std::shared_ptr<Good> gp1 = not_so_good.getptr();` here: https://en.cppreference.com/w/cpp/memory/enable_shared_from_this
The comment there is: `// undefined behavior (until C++17) and std::bad_weak_ptr thrown (since C++17)`
Does the existing code have UB pre C++17?

I'll leave handling of enable_shared_from_this for later, as the need arises.

* Cosmetical change around helper functions.

* Using type_caster_base<mpty>::src_and_type directly, removing copy. Also renaming one cast to cast_const_raw_ptr, for clarity.

* Fixing clang-format oversight.

* Using factored-out make_constructor (PR #2798), removing duplicate code.

* Inserting additional assert to ensure a returned unique_ptr is always a new Python instance.

* Adding minor comment (change to internals needed to distinguish uninitialized/disowned in error message).

* Factoring out find_existing_python_instance().

* Moving factored-out make_constructor to test_classh_wip.cpp, restoring previous version of cast.h. This is currently the most practical approach. See PR #2798 for background.

* Copying classh type_casters from test_classh_wip.cpp UNMODIFIED, as a baseline for generalizing the code.

* Using pybind11/detail/classh_type_casters.h from test_classh_wip.cpp.

* Adding & using PYBIND11_CLASSH_TYPE_CASTERS define.

* Adding test_classh_inheritance, currently failing (passes with class_).

* Removing .clang-format before git rebase master (where the file was added).

* Bringing back .clang-format, the previous rm was a bad idea.

* Folding in modified_type_caster_generic_load_impl, just enough to pass test_class_wip. test_classh_inheritance is still failing, but with a different error: [RuntimeError: Incompatible type (as_raw_ptr_unowned).]

* Minimal changes needed to pass test_classh_inheritance.

* First pass adjusting try_implicit_casts and try_load_foreign_module_local to capture loaded_v_h, but untested and guarded with pybind11_failure("Untested"). This was done mainly to determine general feasibility. Note the TODO in pybind11.h, where type_caster_generic::local_load is currently hard-coded. test_classh_wip and test_classh_inheritance still pass, as before.

* Decoupling generic_type from type_caster_generic.

* Changes and tests covering classh_type_casters try_implicit_casts.

* Minimal test covering classh_type_casters load_impl Case 2b.

* Removing stray isinstance<T>(src): it interferes with the py::module_local feature. Adding missing #includes.

* Tests for classh py::module_local() feature.

* Pure renaming of function names in test_classh_inheritance, similar to the systematic approach used in test_class_wip. NO functional changes.

* Pure renaming of function and variable names, for better generalization when convoluting with inheritance. NO functional changes.

* Adopting systematic naming scheme from test_classh_wip. NO functional changes.

* Moving const after type name, for functions that cover a systematic scheme. NO functional changes.

* Adding smart_holder_type_caster_load::loaded_as_shared_ptr, currently bypassing smart_holder shared_ptr tracking completely, but the tests pass and are sanitizer clean.

* Removing rtti_held from smart_holder. See updated comment.

* Cleaning up loaded_as_raw_ptr_unowned, loaded_as_shared_ptr.

* Factoring out convert_type and folding into loaded_as_unique_ptr.

* Folding convert_type into lvalue_ref and rvalue_ref paths. Some smart_holder_type_caster_load cleanup.

* Using unique_ptr in local_load to replace static variable. Also adding local_load_safety_guard.

* Converting test_unique_ptr_member to using classh: fully working, ASAN, MSAN, UBSAN clean.

* Removing debugging comments (GET_STACK, GET_INT_STACK). cast.h is identical to current master again, pybind11.h only has the generic_type::initialize(..., &type_caster_generic::local_load) change.

* Purging obsolete pybind11/vptr_holder.h and associated test.

* Moving several tests to github.com/rwgk/rwgk_tbx/tree/main/pybind11_tests

a2c2f88174

These tests are from experimenting, and for demonstrating UB in pybind11 multiple inheritance handling ("first_base"), to be fixed later.

* Adding py::smart_holder support to py::class_, purging py::classh completely.

* Renaming files in include directory, creating pybind11/smart_holder.h.

* Renaming all "classh" to "smart_holder" in pybind11/detail/smart_holder_type_casters.h.

The user-facing macro is now PYBIND11_SMART_HOLDER_TYPE_CASTERS.

* Systematically renaming tests to use "class_sh" in the name.

* Renaming test_type_caster_bare_interface_demo to test_type_caster_bare_interface.

* Renaming new tests/core subdirectory to tests/pure_cpp.

* Adding new tests to CMake config, resetting CI config.

* Changing CMake file so that test_class_sh_module_local.py actually runs.

* clang-tidy fixes.

* 32-bit compatibility.

* Reusing type_caster_base make_copy_constructor, make_move_constructor with a trick.

* CMake COMPARE NATURAL is not available with older versions.

* Adding copyright notices to new header files.

* Explicitly define copy/move constructors/assignments.

* Adding new header files to tests/extra_python_package/test_files.py.

* Adding tests/pure_cpp/CMakeLists.txt.

* Making use of the new find_existing_python_instance() function factored out with PR #2822.

* Moving define PYBIND11_SMART_HOLDER_TYPE_CASTERS(T) down in the file. NO functional changes. Preparation for follow-up work (to keep that diff smaller).

* Reintroducing py::classh, this time as a simple alias for py::class_<U, py::smart_holder>.

* Replacing detail::is_smart_holder<H> in cast.h with detail::is_smart_holder_type_caster<T>.
Moving get_local_load_function_ptr, init_instance_for_type to smart_holder_type_caster_class_hooks.
Expanding static_assert in py::type::handle_of<> to accommodate smart_holder_type_casters.

* Fixing oversight.

* Adding classu alias for class_<U, std::unique_ptr<U>>.

* Giving up on idea to use legacy init_instance only if is_base_of<type_caster_generic, type_caster<T>. There are use cases in the wild that define both a custom type_caster and class_.

* Removing test_type_caster_bare_interface, which was moved to the separate PR #2834.

* Moving up is_smart_holder_type_caster, to also use in cast_is_temporary_value_reference.

* Adding smart_holder_type_casters for unique_ptr with custom deleter. SEVERE CODE DUPLICATION. This commit is to establish a baseline for consolidating the unique_ptr code.

* Unification of unique_ptr, unique_ptr_with_deleter code in smart_holder_poc.h. Leads to more fitting error messages. Enables use of unique_ptr<T, D> smart_holder_type_casters also for unique_ptr<T>.

* Copying files as-is from branch test_unique_ptr_member (PR #2672).

* Adding comment, simplifying naming, cmake addition.

* Introducing PYBIND11_USE_SMART_HOLDER_AS_DEFAULT macro (tested only undefined; there are many errors with the macro defined).

* Removing test_type_caster_bare_interface, which was moved to the separate PR #2834.

* Fixing oversight introduced with commit 95425f13d6.

* Setting record.default_holder correctly for PYBIND11_USE_SMART_HOLDER_AS_DEFAULT.

With this test_class.cpp builds and even mostly runs, except
`test_multiple_instances_with_same_pointer`, which segfaults because it is
using a `unique_ptr` holder but `smart_holder` `type_caster`.

Also adding `static_assert`s to generate build errors for such situations,
but guarding with `#if 0` to first pivot to test_factory_constructors.cpp.

* Fixing up cast.h and smart_holder.h after rebase.

* Removing detail/smart_holder_type_casters.h in separate commit.

* Commenting out const in def_buffer(... const). With this, test_buffers builds and runs with PYBIND11_USE_SMART_HOLDER_AS_DEFAULT. Explanation why the const needs to be removed, or fix elsewhere, is still needed, but left for later.

* Adding test_class_sh_factory_constructors, reproducing test_factory_constructors failure. Using py::class_ in this commit, to be changed to py::classh for debugging.

* Removing include/pybind11/detail/smart_holder_type_casters.h from CMakeLists.txt, test_files.py (since it does not exist in this branch).

* Adding // DANGER ZONE reminders.

* Converting as many py::class_ to py::classh as possible, not breaking tests.

* Adding initimpl::construct() overloads, resulting in test_class_sh_factory_constructors feature parity for py::class_ and py::classh.

* Adding enable_if !is_smart_holder_type_caster to existing initimpl::construct(). With this test_factory_constructors.cpp builds with PYBIND11_USE_SMART_HOLDER_AS_DEFAULT.

* Disabling shared_ptr&, shared_ptr* tests when building with PYBIND11_USE_SMART_HOLDER_AS_DEFAULT for now, pending work on smart_holder_type_caster<shared_ptr>.

* Factoring out struct and class definitions into anonymous namespace. Preparation for building with PYBIND11_USE_SMART_HOLDER_AS_DEFAULT.

* Simplifying from_unique_ptr(): typename D = std::default_delete<T> is not needed. Factoring out is_std_default_delete<T>() for consistentcy between ensure_compatible_rtti_uqp_del() and from_unique_ptr().

* Introducing PYBIND11_SMART_POINTER_HOLDER_TYPE_CASTERS. Using it in test_smart_ptr.cpp. With this test_smart_ptr builds with PYBIND11_USE_SMART_HOLDER_AS_DEFAULT and all but one test run successfully.

* Introducing 1. type_caster_for_class_, used in PYBIND11_MAKE_OPAQUE, and 2. default_holder_type, used in stl_bind.h.

* Using __VA_ARGS__ in PYBIND11_SMART_POINTER_HOLDER_TYPE_CASTERS.

* Replacing condense_for_macro with much simpler approach.

* Softening static_assert, to only check specifically that smart_holder is not mixed with type_caster_base, and unique_ptr/shared_ptr holders are not mixed with smart_holder_type_casters.

* Adding PYBIND11_SMART_POINTER_HOLDER_TYPE_CASTERS in test_class.cpp (with this all but one test succeed with PYBIND11_USE_SMART_HOLDER_AS_DEFAULT).

* Adding remaining PYBIND11_SMART_POINTER_HOLDER_TYPE_CASTERS. static_assert for "necessary conditions" for both types of default holder, static_assert for "strict conditions" guarded by new PYBIND11_STRICT_ASSERTS_CLASS_HOLDER_VS_TYPE_CASTER_MIX. All tests build & run as before with unique_ptr as the default holder, all tests build for smart_holder as the default holder, even with the strict static_assert.

* Introducing check_is_smart_holder_type_caster() function for runtime check, and reinterpreting record.default_holder as "uses_unique_ptr_holder". With this test_smart_ptr succeeds. (All 42 tests build, 35 tests succeed, 5 run but have some failures, 2 segfault.)

* Bug fix: Adding have_value() to smart_holder_type_caster_load. With this test_builtin_casters succeeds. (All 42 tests build, 36 tests succeed, 5 run but have some failures, 1 segfault.)

* Adding unowned_void_ptr_from_direct_conversion to modified_type_caster_generic_load_impl. This fixes the last remaining segfault (test_numpy_dtypes). New stats for all tests combined: 12 failed, 458 passed.

* Adding "Lazy allocation for unallocated values" (for old-style __init__) into load_value_and_holder. Deferring destruction of disowned holder until clear_instance, to remain inspectable for "uninitialized" or "disowned" detection. New stats for all tests combined: 5 failed, 465 passed.

* Changing std::shared_ptr pointer/reference to const pointer/reference. New stats for all tests combined: 4 failed, 466 passed.

* Adding return_value_policy::move to permissible policies for unique_ptr returns. New stats for all tests combined: 3 failed, 467 passed.

* Overlooked flake8 fixes.

* Manipulating failing ConstructorStats test to pass, to be able to run all tests with ASAN.

This version of the code is ASAN clean with unique_ptr or smart_holder as the default.

This change needs to be reverted after adopting the existing move-only-if-refcount-is-1
logic used by type_caster_base.

* Adding copy constructor and move constructor tracking to atyp. Preparation for a follow-up change in smart_holder_type_caster, to make this test sensitive to the changing behavior.

[skip ci]

* Removing `operator T&&() &&` from smart_holder_type_caster, for compatibility with the behavior of type_caster_base. Enables reverting 2 of 3 test manipulations applied under commit 249df7cbdb. The manipulation in test_factory_constructors.py is NOT reverted in this commit.

[skip ci]

* Fixing unfortunate editing mishap. This reverts the last remaining test manipulation in commit 249df7cbdb and makes all existing unit tests pass with smart_holder as default holder.

* GitHub CI clang-tidy fixes.

* Adding messages to terse `static_assert`s, for pre-C++17 compatibility.

* Using @pytest.mark.parametrize to run each assert separately (to see all errors, not just the first).

* Systematically removing _atyp from function names, to make the test code simpler.

* Using re.match to accommodate variable number of intermediate MvCtor.

* Also removing `operator T()` from smart_holder_type_caster, to fix gcc compilation errors. The only loss is pass_rref in test_class_sh_basic.

* Systematically replacing `detail::enable_if_t<...smart_holder...>` with `typename std::enable_if<...smart_holder...>::type`. Attempt to work around MSVC 2015 issues, to be tested via GitHub CI. The idea for this change originates from this comment: https://github.com/pybind/pybind11/issues/1616#issuecomment-444536813

* Importing re before pytest after observing a PyPy CI flake when importing pytest first.

* Copying MSVC 2015 compatibility change from branch pr2672_use_smart_holder_as_default.

* Introducing is_smart_holder_type_caster_base_tag, to keep smart_holder code more disconnected.

* Working around MSVC 2015 bug.

* Expanding comment for MSVC 2015 workaround.

* Systematically changing std::enable_if back to detail::enable_if_t, effectively reverting commit 5d4b6890a3.

* Removing unused smart_holder_type_caster_load::loaded_as_rvalue_ref (it was an oversight that it was not removed with commit 23036a45eb).

* Removing py::classu, because it does not seem useful enough.

* Reverting commit 6349531306 by un-commenting `const` in `def_buffer(...)`. To make this possible, `operator T const&` and `operator T const*` in `smart_holder_type_caster` need to be marked as `const` member functions.

* Adding construct() overloads for constructing smart_holder from alias unique_ptr, shared_ptr returns.

* Adding test_class_sh_factory_constructors.cpp to tests/CMakeLists.txt (fixes oversight, this should have been added long before).

* Compatibility with old clang versions (clang 3.6, 3.7 C++11).

* Cleaning up changes to existing unit tests.

* Systematically adding SMART_HOLDER_WIP tag. Removing minor UNTESTED tags (only the throw are not actually exercised, investing time there has a high cost but very little benefit).

* Splitting out smart_holder_type_casters again, into new detail/smart_holder_type_casters_inline_include.h.

* Splitting out smart_holder_init_inline_include.h.

* Adding additional new include files to CMakeLists.txt, tests/extra_python_package/test_files.py.

* clang-format cleanup of most smart_holder code.

* Adding source code comments in response to review.

* Simple micro-benchmark ("ubench") comparing runtime performance for several holders.

Tested using github.com/rwgk/pybind11_scons and Google-internal build system.
Sorry, no cmake support at the moment.

First results: https://docs.google.com/spreadsheets/d/1InapCYws2Gt-stmFf_Bwl33eOMo3aLE_gc9adveY7RU/edit#gid=0

* Breaking out number_bucket.h, adding hook for also collecting performance data for PyCLIF.

* Accounting for ubench in MANIFEST.in (simply prune, for now).

* Smarter determination of call_repetitions.

[skip ci]

* Also scaling performance data to PyCLIF.

[skip ci]

* Adding ubench/python/number_bucket.clif here for general visibility.

* Fix after rebase

* Merging detail/smart_holder_init_inline_include.h into detail/init.h.

* Renaming detail/is_smart_holder_type_caster.h -> detail/smart_holder_sfinae_hooks_only.h.

* Renaming is_smart_holder_type_caster -> type_uses_smart_holder_type_caster for clarity.

* Renaming type_caster_type_is_smart_holder_type_caster -> wrapped_type_uses_smart_holder_type_caster for clarity.

* Renaming is_smart_holder_type_caster_base_tag -> smart_holder_type_caster_base_tag for simplicity.

* Adding copyright notices and minor colateral cleanup.

* iwyu cleanup (comprehensive only for cast.h and smart_holder*.h files).

* Fixing `git rebase master` accident.

* Moving large `pragma warning` block from pybind11.h to detail/common.h.

* Fixing another `git rebase master` accident.
2021-02-23 21:50:42 -08:00

386 lines
19 KiB
C++

/*
tests/test_methods_and_attributes.cpp -- constructors, deconstructors, attribute access,
__str__, argument and return value conventions
Copyright (c) 2016 Wenzel Jakob <wenzel.jakob@epfl.ch>
All rights reserved. Use of this source code is governed by a
BSD-style license that can be found in the LICENSE file.
*/
#include "pybind11_tests.h"
#include "constructor_stats.h"
#if !defined(PYBIND11_OVERLOAD_CAST)
template <typename... Args>
using overload_cast_ = pybind11::detail::overload_cast_impl<Args...>;
#endif
class ExampleMandA {
public:
ExampleMandA() { print_default_created(this); }
ExampleMandA(int value) : value(value) { print_created(this, value); }
ExampleMandA(const ExampleMandA &e) : value(e.value) { print_copy_created(this); }
ExampleMandA(std::string&&) {}
ExampleMandA(ExampleMandA &&e) : value(e.value) { print_move_created(this); }
~ExampleMandA() { print_destroyed(this); }
std::string toString() {
return "ExampleMandA[value=" + std::to_string(value) + "]";
}
void operator=(const ExampleMandA &e) { print_copy_assigned(this); value = e.value; }
void operator=(ExampleMandA &&e) { print_move_assigned(this); value = e.value; }
void add1(ExampleMandA other) { value += other.value; } // passing by value
void add2(ExampleMandA &other) { value += other.value; } // passing by reference
void add3(const ExampleMandA &other) { value += other.value; } // passing by const reference
void add4(ExampleMandA *other) { value += other->value; } // passing by pointer
void add5(const ExampleMandA *other) { value += other->value; } // passing by const pointer
void add6(int other) { value += other; } // passing by value
void add7(int &other) { value += other; } // passing by reference
void add8(const int &other) { value += other; } // passing by const reference
void add9(int *other) { value += *other; } // passing by pointer
void add10(const int *other) { value += *other; } // passing by const pointer
void consume_str(std::string&&) {}
ExampleMandA self1() { return *this; } // return by value
ExampleMandA &self2() { return *this; } // return by reference
const ExampleMandA &self3() { return *this; } // return by const reference
ExampleMandA *self4() { return this; } // return by pointer
const ExampleMandA *self5() { return this; } // return by const pointer
int internal1() { return value; } // return by value
int &internal2() { return value; } // return by reference
const int &internal3() { return value; } // return by const reference
int *internal4() { return &value; } // return by pointer
const int *internal5() { return &value; } // return by const pointer
py::str overloaded() { return "()"; }
py::str overloaded(int) { return "(int)"; }
py::str overloaded(int, float) { return "(int, float)"; }
py::str overloaded(float, int) { return "(float, int)"; }
py::str overloaded(int, int) { return "(int, int)"; }
py::str overloaded(float, float) { return "(float, float)"; }
py::str overloaded(int) const { return "(int) const"; }
py::str overloaded(int, float) const { return "(int, float) const"; }
py::str overloaded(float, int) const { return "(float, int) const"; }
py::str overloaded(int, int) const { return "(int, int) const"; }
py::str overloaded(float, float) const { return "(float, float) const"; }
static py::str overloaded(float) { return "static float"; }
int value = 0;
};
struct TestProperties {
int value = 1;
static int static_value;
int get() const { return value; }
void set(int v) { value = v; }
static int static_get() { return static_value; }
static void static_set(int v) { static_value = v; }
};
int TestProperties::static_value = 1;
struct TestPropertiesOverride : TestProperties {
int value = 99;
static int static_value;
};
int TestPropertiesOverride::static_value = 99;
struct TestPropRVP {
UserType v1{1};
UserType v2{1};
static UserType sv1;
static UserType sv2;
const UserType &get1() const { return v1; }
const UserType &get2() const { return v2; }
UserType get_rvalue() const { return v2; }
void set1(int v) { v1.set(v); }
void set2(int v) { v2.set(v); }
};
UserType TestPropRVP::sv1(1);
UserType TestPropRVP::sv2(1);
// Test None-allowed py::arg argument policy
class NoneTester { public: int answer = 42; };
int none1(const NoneTester &obj) { return obj.answer; }
int none2(NoneTester *obj) { return obj ? obj->answer : -1; }
int none3(const std::shared_ptr<NoneTester> &obj) { return obj ? obj->answer : -1; }
int none4(const std::shared_ptr<NoneTester> *obj) { return obj && *obj ? (*obj)->answer : -1; }
int none5(std::shared_ptr<NoneTester> obj) { return obj ? obj->answer : -1; }
struct StrIssue {
int val = -1;
StrIssue() = default;
StrIssue(int i) : val{i} {}
};
// Issues #854, #910: incompatible function args when member function/pointer is in unregistered base class
class UnregisteredBase {
public:
void do_nothing() const {}
void increase_value() { rw_value++; ro_value += 0.25; }
void set_int(int v) { rw_value = v; }
int get_int() const { return rw_value; }
double get_double() const { return ro_value; }
int rw_value = 42;
double ro_value = 1.25;
};
class RegisteredDerived : public UnregisteredBase {
public:
using UnregisteredBase::UnregisteredBase;
double sum() const { return rw_value + ro_value; }
};
// Test explicit lvalue ref-qualification
struct RefQualified {
int value = 0;
void refQualified(int other) & { value += other; }
int constRefQualified(int other) const & { return value + other; }
};
PYBIND11_SMART_POINTER_HOLDER_TYPE_CASTERS(NoneTester, std::shared_ptr<NoneTester>)
TEST_SUBMODULE(methods_and_attributes, m) {
// test_methods_and_attributes
py::class_<ExampleMandA> emna(m, "ExampleMandA");
emna.def(py::init<>())
.def(py::init<int>())
.def(py::init<std::string&&>())
.def(py::init<const ExampleMandA&>())
.def("add1", &ExampleMandA::add1)
.def("add2", &ExampleMandA::add2)
.def("add3", &ExampleMandA::add3)
.def("add4", &ExampleMandA::add4)
.def("add5", &ExampleMandA::add5)
.def("add6", &ExampleMandA::add6)
.def("add7", &ExampleMandA::add7)
.def("add8", &ExampleMandA::add8)
.def("add9", &ExampleMandA::add9)
.def("add10", &ExampleMandA::add10)
.def("consume_str", &ExampleMandA::consume_str)
.def("self1", &ExampleMandA::self1)
.def("self2", &ExampleMandA::self2)
.def("self3", &ExampleMandA::self3)
.def("self4", &ExampleMandA::self4)
.def("self5", &ExampleMandA::self5)
.def("internal1", &ExampleMandA::internal1)
.def("internal2", &ExampleMandA::internal2)
.def("internal3", &ExampleMandA::internal3)
.def("internal4", &ExampleMandA::internal4)
.def("internal5", &ExampleMandA::internal5)
#if defined(PYBIND11_OVERLOAD_CAST)
.def("overloaded", py::overload_cast<>(&ExampleMandA::overloaded))
.def("overloaded", py::overload_cast<int>(&ExampleMandA::overloaded))
.def("overloaded", py::overload_cast<int, float>(&ExampleMandA::overloaded))
.def("overloaded", py::overload_cast<float, int>(&ExampleMandA::overloaded))
.def("overloaded", py::overload_cast<int, int>(&ExampleMandA::overloaded))
.def("overloaded", py::overload_cast<float, float>(&ExampleMandA::overloaded))
.def("overloaded_float", py::overload_cast<float, float>(&ExampleMandA::overloaded))
.def("overloaded_const", py::overload_cast<int >(&ExampleMandA::overloaded, py::const_))
.def("overloaded_const", py::overload_cast<int, float>(&ExampleMandA::overloaded, py::const_))
.def("overloaded_const", py::overload_cast<float, int>(&ExampleMandA::overloaded, py::const_))
.def("overloaded_const", py::overload_cast<int, int>(&ExampleMandA::overloaded, py::const_))
.def("overloaded_const", py::overload_cast<float, float>(&ExampleMandA::overloaded, py::const_))
#else
// Use both the traditional static_cast method and the C++11 compatible overload_cast_
.def("overloaded", overload_cast_<>()(&ExampleMandA::overloaded))
.def("overloaded", overload_cast_<int>()(&ExampleMandA::overloaded))
.def("overloaded", overload_cast_<int, float>()(&ExampleMandA::overloaded))
.def("overloaded", static_cast<py::str (ExampleMandA::*)(float, int)>(&ExampleMandA::overloaded))
.def("overloaded", static_cast<py::str (ExampleMandA::*)(int, int)>(&ExampleMandA::overloaded))
.def("overloaded", static_cast<py::str (ExampleMandA::*)(float, float)>(&ExampleMandA::overloaded))
.def("overloaded_float", overload_cast_<float, float>()(&ExampleMandA::overloaded))
.def("overloaded_const", overload_cast_<int >()(&ExampleMandA::overloaded, py::const_))
.def("overloaded_const", overload_cast_<int, float>()(&ExampleMandA::overloaded, py::const_))
.def("overloaded_const", static_cast<py::str (ExampleMandA::*)(float, int) const>(&ExampleMandA::overloaded))
.def("overloaded_const", static_cast<py::str (ExampleMandA::*)(int, int) const>(&ExampleMandA::overloaded))
.def("overloaded_const", static_cast<py::str (ExampleMandA::*)(float, float) const>(&ExampleMandA::overloaded))
#endif
// test_no_mixed_overloads
// Raise error if trying to mix static/non-static overloads on the same name:
.def_static("add_mixed_overloads1", []() {
auto emna = py::reinterpret_borrow<py::class_<ExampleMandA>>(py::module_::import("pybind11_tests.methods_and_attributes").attr("ExampleMandA"));
emna.def ("overload_mixed1", static_cast<py::str (ExampleMandA::*)(int, int)>(&ExampleMandA::overloaded))
.def_static("overload_mixed1", static_cast<py::str ( *)(float )>(&ExampleMandA::overloaded));
})
.def_static("add_mixed_overloads2", []() {
auto emna = py::reinterpret_borrow<py::class_<ExampleMandA>>(py::module_::import("pybind11_tests.methods_and_attributes").attr("ExampleMandA"));
emna.def_static("overload_mixed2", static_cast<py::str ( *)(float )>(&ExampleMandA::overloaded))
.def ("overload_mixed2", static_cast<py::str (ExampleMandA::*)(int, int)>(&ExampleMandA::overloaded));
})
.def("__str__", &ExampleMandA::toString)
.def_readwrite("value", &ExampleMandA::value);
// test_copy_method
// Issue #443: can't call copied methods in Python 3
emna.attr("add2b") = emna.attr("add2");
// test_properties, test_static_properties, test_static_cls
py::class_<TestProperties>(m, "TestProperties")
.def(py::init<>())
.def_readonly("def_readonly", &TestProperties::value)
.def_readwrite("def_readwrite", &TestProperties::value)
.def_property("def_writeonly", nullptr,
[](TestProperties& s,int v) { s.value = v; } )
.def_property("def_property_writeonly", nullptr, &TestProperties::set)
.def_property_readonly("def_property_readonly", &TestProperties::get)
.def_property("def_property", &TestProperties::get, &TestProperties::set)
.def_property("def_property_impossible", nullptr, nullptr)
.def_readonly_static("def_readonly_static", &TestProperties::static_value)
.def_readwrite_static("def_readwrite_static", &TestProperties::static_value)
.def_property_static("def_writeonly_static", nullptr,
[](py::object, int v) { TestProperties::static_value = v; })
.def_property_readonly_static("def_property_readonly_static",
[](py::object) { return TestProperties::static_get(); })
.def_property_static("def_property_writeonly_static", nullptr,
[](py::object, int v) { return TestProperties::static_set(v); })
.def_property_static("def_property_static",
[](py::object) { return TestProperties::static_get(); },
[](py::object, int v) { TestProperties::static_set(v); })
.def_property_static("static_cls",
[](py::object cls) { return cls; },
[](py::object cls, py::function f) { f(cls); });
py::class_<TestPropertiesOverride, TestProperties>(m, "TestPropertiesOverride")
.def(py::init<>())
.def_readonly("def_readonly", &TestPropertiesOverride::value)
.def_readonly_static("def_readonly_static", &TestPropertiesOverride::static_value);
auto static_get1 = [](py::object) -> const UserType & { return TestPropRVP::sv1; };
auto static_get2 = [](py::object) -> const UserType & { return TestPropRVP::sv2; };
auto static_set1 = [](py::object, int v) { TestPropRVP::sv1.set(v); };
auto static_set2 = [](py::object, int v) { TestPropRVP::sv2.set(v); };
auto rvp_copy = py::return_value_policy::copy;
// test_property_return_value_policies
py::class_<TestPropRVP>(m, "TestPropRVP")
.def(py::init<>())
.def_property_readonly("ro_ref", &TestPropRVP::get1)
.def_property_readonly("ro_copy", &TestPropRVP::get2, rvp_copy)
.def_property_readonly("ro_func", py::cpp_function(&TestPropRVP::get2, rvp_copy))
.def_property("rw_ref", &TestPropRVP::get1, &TestPropRVP::set1)
.def_property("rw_copy", &TestPropRVP::get2, &TestPropRVP::set2, rvp_copy)
.def_property("rw_func", py::cpp_function(&TestPropRVP::get2, rvp_copy), &TestPropRVP::set2)
.def_property_readonly_static("static_ro_ref", static_get1)
.def_property_readonly_static("static_ro_copy", static_get2, rvp_copy)
.def_property_readonly_static("static_ro_func", py::cpp_function(static_get2, rvp_copy))
.def_property_static("static_rw_ref", static_get1, static_set1)
.def_property_static("static_rw_copy", static_get2, static_set2, rvp_copy)
.def_property_static("static_rw_func", py::cpp_function(static_get2, rvp_copy), static_set2)
// test_property_rvalue_policy
.def_property_readonly("rvalue", &TestPropRVP::get_rvalue)
.def_property_readonly_static("static_rvalue", [](py::object) { return UserType(1); });
// test_metaclass_override
struct MetaclassOverride { };
py::class_<MetaclassOverride>(m, "MetaclassOverride", py::metaclass((PyObject *) &PyType_Type))
.def_property_readonly_static("readonly", [](py::object) { return 1; });
// test_overload_ordering
m.def("overload_order", [](std::string) { return 1; });
m.def("overload_order", [](std::string) { return 2; });
m.def("overload_order", [](int) { return 3; });
m.def("overload_order", [](int) { return 4; }, py::prepend{});
#if !defined(PYPY_VERSION)
// test_dynamic_attributes
class DynamicClass {
public:
DynamicClass() { print_default_created(this); }
DynamicClass(const DynamicClass&) = delete;
~DynamicClass() { print_destroyed(this); }
};
py::class_<DynamicClass>(m, "DynamicClass", py::dynamic_attr())
.def(py::init());
class CppDerivedDynamicClass : public DynamicClass { };
py::class_<CppDerivedDynamicClass, DynamicClass>(m, "CppDerivedDynamicClass")
.def(py::init());
#endif
// test_bad_arg_default
// Issue/PR #648: bad arg default debugging output
#if !defined(NDEBUG)
m.attr("debug_enabled") = true;
#else
m.attr("debug_enabled") = false;
#endif
m.def("bad_arg_def_named", []{
auto m = py::module_::import("pybind11_tests");
m.def("should_fail", [](int, UnregisteredType) {}, py::arg(), py::arg("a") = UnregisteredType());
});
m.def("bad_arg_def_unnamed", []{
auto m = py::module_::import("pybind11_tests");
m.def("should_fail", [](int, UnregisteredType) {}, py::arg(), py::arg() = UnregisteredType());
});
// [workaround(intel)] ICC 20/21 breaks with py::arg().stuff, using py::arg{}.stuff works.
// test_accepts_none
py::class_<NoneTester, std::shared_ptr<NoneTester>>(m, "NoneTester")
.def(py::init<>());
m.def("no_none1", &none1, py::arg{}.none(false));
m.def("no_none2", &none2, py::arg{}.none(false));
m.def("no_none3", &none3, py::arg{}.none(false));
m.def("no_none4", &none4, py::arg{}.none(false));
m.def("no_none5", &none5, py::arg{}.none(false));
m.def("ok_none1", &none1);
m.def("ok_none2", &none2, py::arg{}.none(true));
m.def("ok_none3", &none3);
m.def("ok_none4", &none4, py::arg{}.none(true));
m.def("ok_none5", &none5);
m.def("no_none_kwarg", &none2, "a"_a.none(false));
m.def("no_none_kwarg_kw_only", &none2, py::kw_only(), "a"_a.none(false));
// test_str_issue
// Issue #283: __str__ called on uninitialized instance when constructor arguments invalid
py::class_<StrIssue>(m, "StrIssue")
.def(py::init<int>())
.def(py::init<>())
.def("__str__", [](const StrIssue &si) {
return "StrIssue[" + std::to_string(si.val) + "]"; }
);
// test_unregistered_base_implementations
//
// Issues #854/910: incompatible function args when member function/pointer is in unregistered
// base class The methods and member pointers below actually resolve to members/pointers in
// UnregisteredBase; before this test/fix they would be registered via lambda with a first
// argument of an unregistered type, and thus uncallable.
py::class_<RegisteredDerived>(m, "RegisteredDerived")
.def(py::init<>())
.def("do_nothing", &RegisteredDerived::do_nothing)
.def("increase_value", &RegisteredDerived::increase_value)
.def_readwrite("rw_value", &RegisteredDerived::rw_value)
.def_readonly("ro_value", &RegisteredDerived::ro_value)
// These should trigger a static_assert if uncommented
//.def_readwrite("fails", &UserType::value) // should trigger a static_assert if uncommented
//.def_readonly("fails", &UserType::value) // should trigger a static_assert if uncommented
.def_property("rw_value_prop", &RegisteredDerived::get_int, &RegisteredDerived::set_int)
.def_property_readonly("ro_value_prop", &RegisteredDerived::get_double)
// This one is in the registered class:
.def("sum", &RegisteredDerived::sum)
;
using Adapted = decltype(py::method_adaptor<RegisteredDerived>(&RegisteredDerived::do_nothing));
static_assert(std::is_same<Adapted, void (RegisteredDerived::*)() const>::value, "");
// test_methods_and_attributes
py::class_<RefQualified>(m, "RefQualified")
.def(py::init<>())
.def_readonly("value", &RefQualified::value)
.def("refQualified", &RefQualified::refQualified)
.def("constRefQualified", &RefQualified::constRefQualified);
}