From c60181c5a14fb11b715a4c631ab465dc18aed3df Mon Sep 17 00:00:00 2001 From: Ethan Steinberg Date: Mon, 21 Nov 2022 04:43:00 +0000 Subject: [PATCH 01/18] Squashed commit of the following: commit 61c7699a0896decc50ee74429a8473606718c9aa Author: Ethan Steinberg Date: Mon Nov 21 04:30:15 2022 +0000 test it commit 1e6b024b2a5f43bd3367afe190560a18a20feaaf Author: Ethan Steinberg Date: Mon Oct 24 07:25:13 2022 +0000 Shoot, forgot to disable test commit e97060adcbcd0dc4a02643b0dceceee631ea139e Author: Ethan Steinberg Date: Mon Oct 24 07:14:19 2022 +0000 Final run before I break out the VMs commit 1c0fb6dcaec0710a9e4932b898e21aa52680fdfd Author: Ethan Steinberg Date: Mon Oct 24 06:46:54 2022 +0000 Fix a bunch commit 738fc7c1c027f3366f15a1cd18f8fe06e04beffc Author: Ethan Steinberg Date: Mon Oct 24 06:14:27 2022 +0000 Closer commit 3c3b47698bec088b2c59c3286e5cdf5c6b4fa7ba Author: Ethan Steinberg Date: Mon Oct 24 05:54:25 2022 +0000 Silence commit 794bcf4681b653794c6d619afb63c1899efdb1f6 Author: Ethan Steinberg Date: Mon Oct 24 05:35:41 2022 +0000 Fix regex commit 759e6a6d6e2fecb38b33a2a050a424b813acd8e9 Author: Ethan Steinberg Date: Mon Oct 24 05:26:33 2022 +0000 OOps commit e9d7f8ae5588c281c1433a80bada10b379298957 Author: Ethan Steinberg Date: Mon Oct 24 05:20:31 2022 +0000 Next commit 0cef232652d55d12aba8dfa9965e35c31073b675 Author: Ethan Steinberg Date: Mon Oct 24 04:58:47 2022 +0000 Fix more warnings commit 1d208766ef2f532599d69ca56f163fcbf678e783 Author: Ethan Steinberg Date: Mon Oct 24 04:48:28 2022 +0000 Fix warnings commit 81a6f821b0167e3af936e1fc239fa94ea4e4ba65 Author: Ethan Steinberg Date: Mon Oct 24 04:38:56 2022 +0000 Fix issues commit 6d44ec9af20633f52d7f1c9374011f6d20067ad1 Author: Ethan Steinberg Date: Mon Oct 24 01:23:42 2022 +0000 Force it to work commit 359742c2155e8219b4f2cfe58f4773acb7eb82c9 Author: Ethan Steinberg Date: Mon Oct 24 01:18:09 2022 +0000 Fix scope issue commit 958441cd4fbab134654ef65f8309f8a084fda1d7 Author: Ethan Steinberg Date: Mon Oct 24 01:10:48 2022 +0000 Oops, more commit 42000d9046ce1504e0387ce535f9f79d2870934d Author: Ethan Steinberg Date: Mon Oct 24 01:01:55 2022 +0000 Fix more warnings commit 5357d35cfbd0426134ec2c74597d8a8a438e2f16 Author: Ethan Steinberg Date: Mon Oct 24 00:40:04 2022 +0000 Continue fixing ci commit e38960dc51bba940f03060b8eda4250dbfad3606 Author: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon Oct 24 00:22:37 2022 +0000 style: pre-commit fixes commit e5e84e50a5c7fd3a49d5f36d2eba259fff520f67 Author: Ethan Steinberg Date: Mon Oct 24 00:21:59 2022 +0000 Switch to other optional commit 553fd85806c58d3b1e7b8d45c1c516840092c54b Author: Ethan Steinberg Date: Mon Oct 24 00:03:48 2022 +0000 Temp commit 1ee063fcfae7ac2bcc28e0ff78d843e8a7a58d0d Author: Ethan Steinberg Date: Sun Oct 23 22:47:08 2022 +0000 Next commit c62200cb8fb74a928df1f6a444563516e458c354 Author: Ethan Steinberg Date: Sun Oct 23 17:28:29 2022 +0000 Temp commit 7cd12aad3f3461186a9766fe3a6461249c3d2162 Author: Ethan Steinberg Date: Sun Oct 23 13:04:28 2022 +0000 Store commit e65c573de8793e26b654e8f4d795ed2d9bafd922 Author: Ethan Steinberg Date: Sun Oct 23 12:27:28 2022 +0000 Temp commit b2fe2f0ea80b0910342fcb5d2a3e2b8ff7b128bf Author: Ethan Steinberg Date: Sun Oct 23 01:25:03 2022 +0000 Reset commit 86e6b88efc58e4c2b06af7fb7c44cd69209bbac5 Author: Ethan Steinberg Date: Sat Oct 22 23:58:44 2022 +0000 Next commit ee8010be80c871f994780e43d57d39ad9fb2a47e Author: Ethan Steinberg Date: Sat Oct 22 21:11:09 2022 +0000 Temp commit a2caf9fdcc68a565136834ee8dd48fb1abf83965 Author: Ethan Steinberg Date: Sat Oct 22 04:25:54 2022 +0000 Next commit feeecc728cf78077cb5cc6097f087ddc91f08b77 Author: Ethan Steinberg Date: Sat Oct 22 03:06:48 2022 +0000 First commit Add benchmark Temp Next What CSontinue Next Temp style: pre-commit fixes --- .clang-tidy | 2 +- CMakeLists.txt | 12 +- include/pybind11/attr.h | 439 +++-- include/pybind11/cast.h | 56 +- include/pybind11/detail/class.h | 16 - include/pybind11/detail/common.h | 81 +- include/pybind11/detail/function_record.h | 639 ++++++ include/pybind11/detail/internal_pytypes.h | 377 ++++ include/pybind11/detail/internals.h | 33 +- include/pybind11/detail/python_compat.h | 99 + include/pybind11/detail/vendor/optional.h | 2031 ++++++++++++++++++++ include/pybind11/functional.h | 3 + include/pybind11/pybind11.h | 1405 +++----------- include/pybind11/pytypes.h | 18 - pybench/Makefile | 12 + pybench/bench.py | 21 + pybench/test_module.cc | 10 + pybench/test_module_raw.cc | 27 + tests/constructor_stats.h | 9 +- tests/test_call_policies.cpp | 2 - tests/test_call_policies.py | 4 - tests/test_callbacks.py | 7 +- tests/test_class.cpp | 5 - tests/test_class.py | 17 +- tests/test_factory_constructors.cpp | 25 +- tests/test_factory_constructors.py | 9 - tests/test_kwargs_and_defaults.py | 2 +- tests/test_methods_and_attributes.py | 27 - tests/test_multiple_inheritance.py | 2 - tests/test_pickling.py | 16 +- 30 files changed, 3805 insertions(+), 1601 deletions(-) create mode 100644 include/pybind11/detail/function_record.h create mode 100644 include/pybind11/detail/internal_pytypes.h create mode 100644 include/pybind11/detail/python_compat.h create mode 100644 include/pybind11/detail/vendor/optional.h create mode 100644 pybench/Makefile create mode 100644 pybench/bench.py create mode 100644 pybench/test_module.cc create mode 100644 pybench/test_module_raw.cc diff --git a/.clang-tidy b/.clang-tidy index 23018386c1..6a51acc8ba 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -74,4 +74,4 @@ CheckOptions: - key: readability-implicit-bool-conversion.AllowPointerConditions value: true -HeaderFilterRegex: 'pybind11/.*h' +HeaderFilterRegex: 'pybind11\/(?!.*vendor\/).*h' diff --git a/CMakeLists.txt b/CMakeLists.txt index 0d93203881..de555bc522 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -34,6 +34,7 @@ endforeach() if(PYBIND11_VERSION_PATCH MATCHES [[\.([a-zA-Z0-9]+)$]]) set(pybind11_VERSION_TYPE "${CMAKE_MATCH_1}") endif() + string(REGEX MATCH "^[0-9]+" PYBIND11_VERSION_PATCH "${PYBIND11_VERSION_PATCH}") project( @@ -52,7 +53,7 @@ endif() # Check if pybind11 is being used directly or via add_subdirectory if(CMAKE_SOURCE_DIR STREQUAL PROJECT_SOURCE_DIR) - ### Warn if not an out-of-source builds + # Warn if not an out-of-source builds if(CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_CURRENT_BINARY_DIR) set(lines "You are building in-place. If that is not what you intended to " @@ -129,6 +130,10 @@ set(PYBIND11_HEADERS include/pybind11/eigen/matrix.h include/pybind11/eigen/tensor.h include/pybind11/embed.h + include/pybind11/detail/function_record.h + include/pybind11/detail/internal_pytypes.h + include/pybind11/detail/vendor/optional.h + include/pybind11/detail/python_compat.h include/pybind11/eval.h include/pybind11/gil.h include/pybind11/iostream.h @@ -152,9 +157,11 @@ if(PYBIND11_MASTER_PROJECT AND NOT CMAKE_VERSION VERSION_LESS 3.12) set(_pybind11_disk_only ${_pybind11_header_check}) list(REMOVE_ITEM _pybind11_here_only ${_pybind11_header_check}) list(REMOVE_ITEM _pybind11_disk_only ${PYBIND11_HEADERS}) + if(_pybind11_here_only) message(AUTHOR_WARNING "PYBIND11_HEADERS has extra files:" ${_pybind11_here_only}) endif() + if(_pybind11_disk_only) message(AUTHOR_WARNING "PYBIND11_HEADERS is missing files:" ${_pybind11_disk_only}) endif() @@ -196,6 +203,7 @@ if(NOT TARGET pybind11_headers) target_compile_features(pybind11_headers INTERFACE cxx_inheriting_constructors cxx_user_literals cxx_right_angle_brackets) + if(NOT "${PYBIND11_INTERNALS_VERSION}" STREQUAL "") target_compile_definitions( pybind11_headers INTERFACE "PYBIND11_INTERNALS_VERSION=${PYBIND11_INTERNALS_VERSION}") @@ -206,6 +214,7 @@ else() endif() include("${CMAKE_CURRENT_SOURCE_DIR}/tools/pybind11Common.cmake") + # https://github.com/jtojnar/cmake-snips/#concatenating-paths-when-building-pkg-config-files # TODO: cmake 3.20 adds the cmake_path() function, which obsoletes this snippet include("${CMAKE_CURRENT_SOURCE_DIR}/tools/JoinPaths.cmake") @@ -277,6 +286,7 @@ if(PYBIND11_INSTALL) if(NOT prefix_for_pc_file) set(prefix_for_pc_file "${CMAKE_INSTALL_PREFIX}") endif() + join_paths(includedir_for_pc_file "\${prefix}" "${CMAKE_INSTALL_INCLUDEDIR}") configure_file("${CMAKE_CURRENT_SOURCE_DIR}/tools/pybind11.pc.in" "${CMAKE_CURRENT_BINARY_DIR}/pybind11.pc" @ONLY) diff --git a/include/pybind11/attr.h b/include/pybind11/attr.h index db7cd8efff..f1077cf0ae 100644 --- a/include/pybind11/attr.h +++ b/include/pybind11/attr.h @@ -14,6 +14,7 @@ #include "cast.h" #include +#include PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) @@ -65,6 +66,8 @@ struct base { base() = default; }; +struct has_no_temporary_casts {}; + /// Keep patient alive while nurse lives template struct keep_alive {}; @@ -163,104 +166,16 @@ struct call_guard { /// @} annotations PYBIND11_NAMESPACE_BEGIN(detail) + +template +using is_sibling = std::is_same, sibling>; + /* Forward declarations */ enum op_id : int; enum op_type : int; struct undefined_t; template struct op_; -void keep_alive_impl(size_t Nurse, size_t Patient, function_call &call, handle ret); - -/// Internal data structure which holds metadata about a keyword argument -struct argument_record { - const char *name; ///< Argument name - const char *descr; ///< Human-readable version of the argument value - handle value; ///< Associated Python object - bool convert : 1; ///< True if the argument is allowed to convert when loading - bool none : 1; ///< True if None is allowed when loading - - argument_record(const char *name, const char *descr, handle value, bool convert, bool none) - : name(name), descr(descr), value(value), convert(convert), none(none) {} -}; - -/// Internal data structure which holds metadata about a bound function (signature, overloads, -/// etc.) -struct function_record { - function_record() - : is_constructor(false), is_new_style_constructor(false), is_stateless(false), - is_operator(false), is_method(false), has_args(false), has_kwargs(false), - prepend(false) {} - - /// Function name - char *name = nullptr; /* why no C++ strings? They generate heavier code.. */ - - // User-specified documentation string - char *doc = nullptr; - - /// Human-readable version of the function signature - char *signature = nullptr; - - /// List of registered keyword arguments - std::vector args; - - /// Pointer to lambda function which converts arguments and performs the actual call - handle (*impl)(function_call &) = nullptr; - - /// Storage for the wrapped function pointer and captured data, if any - void *data[3] = {}; - - /// Pointer to custom destructor for 'data' (if needed) - void (*free_data)(function_record *ptr) = nullptr; - - /// Return value policy associated with this function - return_value_policy policy = return_value_policy::automatic; - - /// True if name == '__init__' - bool is_constructor : 1; - - /// True if this is a new-style `__init__` defined in `detail/init.h` - bool is_new_style_constructor : 1; - - /// True if this is a stateless function pointer - bool is_stateless : 1; - - /// True if this is an operator (__add__), etc. - bool is_operator : 1; - - /// True if this is a method - bool is_method : 1; - - /// True if the function has a '*args' argument - bool has_args : 1; - - /// True if the function has a '**kwargs' argument - bool has_kwargs : 1; - - /// True if this function is to be inserted at the beginning of the overload resolution chain - bool prepend : 1; - - /// Number of arguments (including py::args and/or py::kwargs, if present) - std::uint16_t nargs; - - /// Number of leading positional arguments, which are terminated by a py::args or py::kwargs - /// argument or by a py::kw_only annotation. - std::uint16_t nargs_pos = 0; - - /// Number of leading arguments (counted in `nargs`) that are positional-only - std::uint16_t nargs_pos_only = 0; - - /// Python method object - PyMethodDef *def = nullptr; - - /// Python handle to the parent scope (a class or a module) - handle scope; - - /// Python handle to the sibling function representing an overload chain - handle sibling; - - /// Pointer to next overload - function_record *next = nullptr; -}; /// Special data structure which (temporarily) holds metadata about a bound class struct type_record { @@ -357,11 +272,6 @@ struct type_record { } }; -inline function_call::function_call(const function_record &f, handle p) : func(f), parent(p) { - args.reserve(f.nargs); - args_convert.reserve(f.nargs); -} - /// Tag for a new-style `__init__` defined in `detail/init.h` struct is_new_style_constructor {}; @@ -377,29 +287,41 @@ struct process_attribute; template struct process_attribute_default { /// Default implementation: do nothing - static void init(const T &, function_record *) {} - static void init(const T &, type_record *) {} - static void precall(function_call &) {} - static void postcall(function_call &, handle) {} + template + static void init(const T &, RecordType &) {} + + template + static void precall(CallArgs &, handle) {} + + template + static void postcall(CallArgs &, handle, handle) {} }; /// Process an attribute specifying the function's name template <> struct process_attribute : process_attribute_default { - static void init(const name &n, function_record *r) { r->name = const_cast(n.value); } + template + static void init(const name &n, RecordType &r) { + r.name = n.value; + } }; /// Process an attribute specifying the function's docstring template <> struct process_attribute : process_attribute_default { - static void init(const doc &n, function_record *r) { r->doc = const_cast(n.value); } + template + static void init(const doc &n, RecordType &r) { + r.doc = n.value; + } }; /// Process an attribute specifying the function's docstring (provided as a C-style string) template <> struct process_attribute : process_attribute_default { - static void init(const char *d, function_record *r) { r->doc = const_cast(d); } - static void init(const char *d, type_record *r) { r->doc = const_cast(d); } + template + static void init(const char *d, RecordType &r) { + r.doc = d; + } }; template <> struct process_attribute : process_attribute {}; @@ -407,78 +329,93 @@ struct process_attribute : process_attribute {}; /// Process an attribute indicating the function's return value policy template <> struct process_attribute : process_attribute_default { - static void init(const return_value_policy &p, function_record *r) { r->policy = p; } + template + static void init(const return_value_policy &p, RecordType &r) { + r.policy = p; + } }; /// Process an attribute which indicates that this is an overloaded function associated with a /// given sibling template <> -struct process_attribute : process_attribute_default { - static void init(const sibling &s, function_record *r) { r->sibling = s.value; } -}; +struct process_attribute : process_attribute_default {}; /// Process an attribute which indicates that this function is a method template <> struct process_attribute : process_attribute_default { - static void init(const is_method &s, function_record *r) { - r->is_method = true; - r->scope = s.class_; + template + static void init(const is_method &s, RecordType &r) { + if (PYBIND11_SILENCE_MSVC_C4127(RecordType::nargs > 0)) { + if (r.arginfo_index != 0) { + pybind11_fail("is_method must come before any arguments when mapping"); + } + r.argument_info[0].name = "self"; + r.argument_info[0].convert = false; + r.arginfo_index++; + r.current_scope = s.class_; + r.argument_index["self"] = -1; + } } }; /// Process an attribute which indicates the parent scope of a method template <> struct process_attribute : process_attribute_default { - static void init(const scope &s, function_record *r) { r->scope = s.value; } + template + static void init(const scope &s, RecordType &r) { + r.current_scope = s.value; + } }; /// Process an attribute which indicates that this function is an operator template <> -struct process_attribute : process_attribute_default { - static void init(const is_operator &, function_record *r) { r->is_operator = true; } -}; +struct process_attribute : process_attribute_default {}; template <> -struct process_attribute - : process_attribute_default { - static void init(const is_new_style_constructor &, function_record *r) { - r->is_new_style_constructor = true; - } -}; - -inline void check_kw_only_arg(const arg &a, function_record *r) { - if (r->args.size() > r->nargs_pos && (!a.name || a.name[0] == '\0')) { - pybind11_fail("arg(): cannot specify an unnamed argument after a kw_only() annotation or " - "args() argument"); - } -} +struct process_attribute + : process_attribute_default {}; -inline void append_self_arg_if_needed(function_record *r) { - if (r->is_method && r->args.empty()) { - r->args.emplace_back("self", nullptr, handle(), /*convert=*/true, /*none=*/false); - } -} +template <> +struct process_attribute + : process_attribute_default {}; /// Process a keyword argument attribute (*without* a default value) template <> struct process_attribute : process_attribute_default { - static void init(const arg &a, function_record *r) { - append_self_arg_if_needed(r); - r->args.emplace_back(a.name, nullptr, handle(), !a.flag_noconvert, a.flag_none); + template + static void init(const arg &a, RecordType &r) { + if (r.arginfo_index >= RecordType::nargs_pos && (!a.name || a.name[0] == '\0')) { + pybind11_fail( + "arg(): cannot specify an unnamed argument after a kw_only() annotation or " + "args() argument"); + } + + if (r.arginfo_index == RecordType::nargs_pos && RecordType::has_args) { + r.arginfo_index++; + } + + if (a.name != nullptr) { + r.argument_info[r.arginfo_index].name = a.name; + if (r.arginfo_index < RecordType::nargs_pos_only) { + r.argument_index[a.name] = -1; + } else { + r.argument_index[a.name] = static_cast(r.arginfo_index); + } + } + + r.argument_info[r.arginfo_index].convert = !a.flag_noconvert; + r.argument_info[r.arginfo_index].none = a.flag_none; - check_kw_only_arg(a, r); + r.arginfo_index++; } }; /// Process a keyword argument attribute (*with* a default value) template <> struct process_attribute : process_attribute_default { - static void init(const arg_v &a, function_record *r) { - if (r->is_method && r->args.empty()) { - r->args.emplace_back( - "self", /*descr=*/nullptr, /*parent=*/handle(), /*convert=*/true, /*none=*/false); - } - + template + static void init(const arg_v &a, RecordType &r) { + process_attribute::init(a, r); if (!a.value) { #if defined(PYBIND11_DETAILED_ERROR_MESSAGES) std::string descr("'"); @@ -486,15 +423,15 @@ struct process_attribute : process_attribute_default { descr += std::string(a.name) + ": "; } descr += a.type + "'"; - if (r->is_method) { - if (r->name) { - descr += " in method '" + (std::string) str(r->scope) + "." - + (std::string) r->name + "'"; + if (RecordType::has_self) { + if (!r.name.empty()) { + descr += " in method '" + (std::string) str(r.current_scope) + "." + + (std::string) r.name + "'"; } else { - descr += " in method of '" + (std::string) str(r->scope) + "'"; + descr += " in method of '" + (std::string) str(r.current_scope) + "'"; } - } else if (r->name) { - descr += " in function '" + (std::string) r->name + "'"; + } else if (!r.name.empty()) { + descr += " in function '" + (std::string) r.name + "'"; } pybind11_fail("arg(): could not convert default argument " + descr + " into a Python object (type not registered yet?)"); @@ -505,97 +442,128 @@ struct process_attribute : process_attribute_default { "more information."); #endif } - r->args.emplace_back(a.name, a.descr, a.value.inc_ref(), !a.flag_noconvert, a.flag_none); - - check_kw_only_arg(a, r); + if (a.descr != nullptr) { + r.argument_info[r.arginfo_index - 1].desc = a.descr; + } else { + r.argument_info[r.arginfo_index - 1].desc = repr(a.value).cast(); + } + r.argument_info[r.arginfo_index - 1].value = reinterpret_borrow(a.value); } }; /// Process a keyword-only-arguments-follow pseudo argument template <> struct process_attribute : process_attribute_default { - static void init(const kw_only &, function_record *r) { - append_self_arg_if_needed(r); - if (r->has_args && r->nargs_pos != static_cast(r->args.size())) { - pybind11_fail("Mismatched args() and kw_only(): they must occur at the same relative " - "argument location (or omit kw_only() entirely)"); + template + static void init(const kw_only &, RecordType &r) { + if (RecordType::nargs_pos != r.arginfo_index) { + throw std::runtime_error("Should never happen as defined at compile time " + + std::to_string(RecordType::nargs_pos) + " " + + std::to_string(r.arginfo_index) + " " + r.name + " " + + std::to_string(RecordType::has_kw_only_args) + " " + + std::to_string(RecordType::kw_only_pos) + " " + + std::to_string(RecordType::has_args)); + + // static constexpr size_t nargs_pos = has_kw_only_args ? kw_only_pos : (has_args ? + // args_pos : + // (has_kwargs ? nargs - 1 : nargs) } - r->nargs_pos = static_cast(r->args.size()); } }; /// Process a positional-only-argument maker template <> struct process_attribute : process_attribute_default { - static void init(const pos_only &, function_record *r) { - append_self_arg_if_needed(r); - r->nargs_pos_only = static_cast(r->args.size()); - if (r->nargs_pos_only > r->nargs_pos) { - pybind11_fail("pos_only(): cannot follow a py::args() argument"); + template + static void init(const pos_only &, RecordType &r) { + if (RecordType::nargs_pos_only != r.arginfo_index) { + throw std::runtime_error("Should never happen as defined at compile time pos only " + + std::to_string(r.arginfo_index) + " " + + std::to_string(RecordType::nargs_pos_only)); } - // It also can't follow a kw_only, but a static_assert in pybind11.h checks that } }; -/// Process a parent class attribute. Single inheritance only (class_ itself already guarantees -/// that) +/// Process a parent class attribute. Single inheritance only (class_ itself already +/// guarantees that) template struct process_attribute::value>> : process_attribute_default { - static void init(const handle &h, type_record *r) { r->bases.append(h); } + template + static void init(const handle &h, RecordType &r) { + r.bases.append(h); + } }; /// Process a parent class attribute (deprecated, does not support multiple inheritance) template struct process_attribute> : process_attribute_default> { - static void init(const base &, type_record *r) { r->add_base(typeid(T), nullptr); } + template + static void init(const base &, RecordType &r) { + r.add_base(typeid(T), nullptr); + } }; /// Process a multiple inheritance attribute template <> struct process_attribute : process_attribute_default { - static void init(const multiple_inheritance &, type_record *r) { - r->multiple_inheritance = true; + template + static void init(const multiple_inheritance &, RecordType &r) { + r.multiple_inheritance = true; } }; template <> struct process_attribute : process_attribute_default { - static void init(const dynamic_attr &, type_record *r) { r->dynamic_attr = true; } + template + static void init(const dynamic_attr &, RecordType &r) { + r.dynamic_attr = true; + } }; template <> struct process_attribute { - static void init(const custom_type_setup &value, type_record *r) { - r->custom_type_setup_callback = value.value; + template + static void init(const custom_type_setup &value, RecordType &r) { + r.custom_type_setup_callback = value.value; } }; template <> struct process_attribute : process_attribute_default { - static void init(const is_final &, type_record *r) { r->is_final = true; } + template + static void init(const is_final &, RecordType &r) { + r.is_final = true; + } }; template <> struct process_attribute : process_attribute_default { - static void init(const buffer_protocol &, type_record *r) { r->buffer_protocol = true; } + template + static void init(const buffer_protocol &, RecordType &r) { + r.buffer_protocol = true; + } }; template <> struct process_attribute : process_attribute_default { - static void init(const metaclass &m, type_record *r) { r->metaclass = m.value; } + template + static void init(const metaclass &m, RecordType &r) { + r.metaclass = m.value; + } }; template <> struct process_attribute : process_attribute_default { - static void init(const module_local &l, type_record *r) { r->module_local = l.value; } + template + static void init(const module_local &l, RecordType &r) { + r.module_local = l.value; + } }; /// Process a 'prepend' attribute, putting this at the beginning of the overload chain template <> -struct process_attribute : process_attribute_default { - static void init(const prepend &, function_record *r) { r->prepend = true; } -}; +struct process_attribute : process_attribute_default {}; /// Process an 'arithmetic' attribute for enums (does nothing here) template <> @@ -604,6 +572,28 @@ struct process_attribute : process_attribute_default {}; template struct process_attribute> : process_attribute_default> {}; +template +void keep_alive_impl_for_call(const CallArgs &call_args, handle parent, handle ret) { + static_assert(Nurse <= std::tuple_size::value, + "Nurse must be within the range of call arguments"); + static_assert(Patient <= std::tuple_size::value, + "Patient must be within the range of call arguments"); + auto get_arg = [&](size_t n) { + if (n == 0) { + return ret; + } + if (n == 1) { + return parent; + } + if (n <= call_args.size()) { + return call_args[n - 1]; + } + pybind11_fail("This should never happen, internal pybind11 error"); + }; + + keep_alive_impl(get_arg(Nurse), get_arg(Patient)); +} + /** * Process a keep_alive call policy -- invokes keep_alive_impl during the * pre-call handler if both Nurse, Patient != 0 and use the post-call handler @@ -612,67 +602,68 @@ struct process_attribute> : process_attribute_default struct process_attribute> : public process_attribute_default> { - template = 0> - static void precall(function_call &call) { - keep_alive_impl(Nurse, Patient, call, handle()); + + template = 0> + static void precall(CallArgs &call_args, handle parent) { + keep_alive_impl_for_call(call_args, parent, handle()); } - template = 0> - static void postcall(function_call &, handle) {} - template = 0> - static void precall(function_call &) {} - template = 0> - static void postcall(function_call &call, handle ret) { - keep_alive_impl(Nurse, Patient, call, ret); + template = 0> + static void postcall(CallArgs &, handle, handle) {} + + template = 0> + static void precall(CallArgs &, handle) {} + + template = 0> + static void postcall(CallArgs &call_args, handle parent, handle ret) { + keep_alive_impl_for_call(call_args, parent, ret); } }; /// Recursively iterate over variadic template arguments template struct process_attributes { - static void init(const Args &...args, function_record *r) { + template + static void init(const Args &...args, RecordType &r) { PYBIND11_WORKAROUND_INCORRECT_MSVC_C4100(r); PYBIND11_WORKAROUND_INCORRECT_GCC_UNUSED_BUT_SET_PARAMETER(r); using expander = int[]; (void) expander{ 0, ((void) process_attribute::type>::init(args, r), 0)...}; } - static void init(const Args &...args, type_record *r) { - PYBIND11_WORKAROUND_INCORRECT_MSVC_C4100(r); - PYBIND11_WORKAROUND_INCORRECT_GCC_UNUSED_BUT_SET_PARAMETER(r); - using expander = int[]; - (void) expander{0, - (process_attribute::type>::init(args, r), 0)...}; - } - static void precall(function_call &call) { - PYBIND11_WORKAROUND_INCORRECT_MSVC_C4100(call); + template + static void precall(CallArgs &call_args, handle parent) { + PYBIND11_WORKAROUND_INCORRECT_MSVC_C4100(call_args); + PYBIND11_WORKAROUND_INCORRECT_GCC_UNUSED_BUT_SET_PARAMETER(parent); using expander = int[]; - (void) expander{0, - (process_attribute::type>::precall(call), 0)...}; + (void) expander{ + 0, + (process_attribute::type>::precall(call_args, parent), + 0)...}; } - static void postcall(function_call &call, handle fn_ret) { - PYBIND11_WORKAROUND_INCORRECT_MSVC_C4100(call, fn_ret); + template + static void postcall(CallArgs &call_args, handle parent, handle fn_ret) { + PYBIND11_WORKAROUND_INCORRECT_MSVC_C4100(call_args, fn_ret); + PYBIND11_WORKAROUND_INCORRECT_GCC_UNUSED_BUT_SET_PARAMETER(parent); PYBIND11_WORKAROUND_INCORRECT_GCC_UNUSED_BUT_SET_PARAMETER(fn_ret); using expander = int[]; - (void) expander{ - 0, (process_attribute::type>::postcall(call, fn_ret), 0)...}; + (void) expander{0, + (process_attribute::type>::postcall( + call_args, parent, fn_ret), + 0)...}; } }; -template -using is_call_guard = is_instantiation; - -/// Extract the ``type`` from the first `call_guard` in `Extras...` (or `void_type` if none found) -template -using extract_guard_t = typename exactly_one_t, Extra...>::type; - -/// Check the number of named arguments at compile time -template ::value...), - size_t self = constexpr_sum(std::is_same::value...)> -constexpr bool expected_num_args(size_t nargs, bool has_args, bool has_kwargs) { - PYBIND11_WORKAROUND_INCORRECT_MSVC_C4100(nargs, has_args, has_kwargs); - return named == 0 || (self + named + size_t(has_args) + size_t(has_kwargs)) == nargs; -} - PYBIND11_NAMESPACE_END(detail) PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE) diff --git a/include/pybind11/cast.h b/include/pybind11/cast.h index 3a40460276..2a095ddf34 100644 --- a/include/pybind11/cast.h +++ b/include/pybind11/cast.h @@ -34,6 +34,8 @@ PYBIND11_WARNING_DISABLE_MSVC(4127) PYBIND11_NAMESPACE_BEGIN(detail) +struct argument_record; + template class type_caster : public type_caster_base {}; template @@ -1350,33 +1352,6 @@ using is_kw_only = std::is_same, kw_only>; template using is_pos_only = std::is_same, pos_only>; -// forward declaration (definition in attr.h) -struct function_record; - -/// Internal data associated with a single function call -struct function_call { - function_call(const function_record &f, handle p); // Implementation in attr.h - - /// The function data: - const function_record &func; - - /// Arguments passed to the function: - std::vector args; - - /// The `convert` value the arguments should be loaded with - std::vector args_convert; - - /// Extra references for the optional `py::args` and/or `py::kwargs` arguments (which, if - /// present, are also in `args` but without a reference). - object args_ref, kwargs_ref; - - /// The parent, if any - handle parent; - - /// If this is a call to an initializer, this argument contains `self` - handle init_self; -}; - /// Helper class which loads arguments for C++ functions called from Python template class argument_loader { @@ -1397,13 +1372,18 @@ class argument_loader { // py::args argument position; -1 if not present. static constexpr int args_pos = constexpr_last(); + static constexpr bool has_args = args_pos != -1; static_assert(args_pos == -1 || args_pos == constexpr_first(), "py::args cannot be specified more than once"); static constexpr auto arg_names = concat(type_descr(make_caster::name)...); - bool load_args(function_call &call) { return load_impl_sequence(call, indices{}); } + bool load_args(const std::array &args, + const std::array &infos, + bool force_noconvert) { + return load_impl_sequence(args, infos, force_noconvert, indices{}); + } template // NOLINTNEXTLINE(readability-const-return-type) @@ -1420,16 +1400,26 @@ class argument_loader { } private: - static bool load_impl_sequence(function_call &, index_sequence<>) { return true; } + static bool load_impl_sequence(const std::array &, + const std::array &, + bool, + index_sequence<>) { + return true; + } template - bool load_impl_sequence(function_call &call, index_sequence) { -#ifdef __cpp_fold_expressions - if ((... || !std::get(argcasters).load(call.args[Is], call.args_convert[Is]))) { + bool load_impl_sequence(const std::array &args, + const std::array &infos, + bool force_noconvert, + index_sequence) { +#ifdef __cpp_fold_expression + if ((... + || !std::get(argcasters).load(args[Is], infos[Is].convert && !force_noconvert))) { return false; } #else - for (bool r : {std::get(argcasters).load(call.args[Is], call.args_convert[Is])...}) { + for (bool r : + {std::get(argcasters).load(args[Is], infos[Is].convert && !force_noconvert)...}) { if (!r) { return false; } diff --git a/include/pybind11/detail/class.h b/include/pybind11/detail/class.h index 528e716f78..550e98b2f7 100644 --- a/include/pybind11/detail/class.h +++ b/include/pybind11/detail/class.h @@ -165,21 +165,6 @@ extern "C" inline int pybind11_meta_setattro(PyObject *obj, PyObject *name, PyOb } } -/** - * Python 3's PyInstanceMethod_Type hides itself via its tp_descr_get, which prevents aliasing - * methods via cls.attr("m2") = cls.attr("m1"): instead the tp_descr_get returns a plain function, - * when called on a class, or a PyMethod, when called on an instance. Override that behaviour here - * to do a special case bypass for PyInstanceMethod_Types. - */ -extern "C" inline PyObject *pybind11_meta_getattro(PyObject *obj, PyObject *name) { - PyObject *descr = _PyType_Lookup((PyTypeObject *) obj, name); - if (descr && PyInstanceMethod_Check(descr)) { - Py_INCREF(descr); - return descr; - } - return PyType_Type.tp_getattro(obj, name); -} - /// metaclass `__call__` function that is used to create all pybind11 objects. extern "C" inline PyObject *pybind11_meta_call(PyObject *type, PyObject *args, PyObject *kwargs) { @@ -274,7 +259,6 @@ inline PyTypeObject *make_default_metaclass() { type->tp_call = pybind11_meta_call; type->tp_setattro = pybind11_meta_setattro; - type->tp_getattro = pybind11_meta_getattro; type->tp_dealloc = pybind11_meta_dealloc; diff --git a/include/pybind11/detail/common.h b/include/pybind11/detail/common.h index 9acf104668..e87aa7ef4b 100644 --- a/include/pybind11/detail/common.h +++ b/include/pybind11/detail/common.h @@ -307,6 +307,7 @@ PYBIND11_WARNING_POP #include #include #include +#include #include #include #include @@ -346,9 +347,6 @@ PYBIND11_WARNING_POP // behavior. /// Compatibility macros for Python 2 / Python 3 versions TODO: remove -#define PYBIND11_INSTANCE_METHOD_NEW(ptr, class_) PyInstanceMethod_New(ptr) -#define PYBIND11_INSTANCE_METHOD_CHECK PyInstanceMethod_Check -#define PYBIND11_INSTANCE_METHOD_GET_FUNCTION PyInstanceMethod_GET_FUNCTION #define PYBIND11_BYTES_CHECK PyBytes_Check #define PYBIND11_BYTES_FROM_STRING PyBytes_FromString #define PYBIND11_BYTES_FROM_STRING_AND_SIZE PyBytes_FromStringAndSize @@ -830,12 +828,70 @@ constexpr int constexpr_first() { return constexpr_impl::first(0, Predicate::value...); } +template