Skip to content

Commit b517942

Browse files
committed
Per Cursor, move all versions to Vectorcall since it has been supported since 3.8.
This means getting rid of simple_collector, we can do the same with a constexpr if in the unpacking_collector.
1 parent 849df0b commit b517942

1 file changed

Lines changed: 55 additions & 221 deletions

File tree

include/pybind11/cast.h

Lines changed: 55 additions & 221 deletions
Original file line numberDiff line numberDiff line change
@@ -2191,216 +2191,56 @@ class argument_loader {
21912191
std::tuple<make_caster<Args>...> argcasters;
21922192
};
21932193

2194-
/// Helper class which collects only positional arguments for a Python function call.
2195-
/// A fancier version below can collect any argument, but this one is optimal for simple calls.
2196-
template <return_value_policy policy>
2197-
class simple_collector {
2198-
public:
2199-
template <typename... Ts>
2200-
explicit simple_collector(Ts &&...values)
2201-
: m_args(pybind11::make_tuple<policy>(std::forward<Ts>(values)...)) {}
2202-
2203-
const tuple &args() const & { return m_args; }
2204-
dict kwargs() const { return {}; }
2205-
2206-
tuple args() && { return std::move(m_args); }
2207-
2208-
/// Call a Python function and pass the collected arguments
2209-
object call(PyObject *ptr) const {
2210-
PyObject *result = PyObject_CallObject(ptr, m_args.ptr());
2211-
if (!result) {
2212-
throw error_already_set();
2213-
}
2214-
return reinterpret_steal<object>(result);
2215-
}
2216-
2217-
private:
2218-
tuple m_args;
2219-
};
2220-
2221-
/// Helper class which collects positional, keyword, * and ** arguments for a Python function call
2222-
template <return_value_policy policy>
2223-
class unpacking_collector {
2224-
public:
2225-
template <typename... Ts>
2226-
explicit unpacking_collector(Ts &&...values) {
2227-
// Tuples aren't (easily) resizable so a list is needed for collection,
2228-
// but the actual function call strictly requires a tuple.
2229-
auto args_list = list();
2230-
using expander = int[];
2231-
(void) expander{0, (process(args_list, std::forward<Ts>(values)), 0)...};
2232-
2233-
m_args = std::move(args_list);
2234-
}
2235-
2236-
const tuple &args() const & { return m_args; }
2237-
const dict &kwargs() const & { return m_kwargs; }
2238-
2239-
tuple args() && { return std::move(m_args); }
2240-
dict kwargs() && { return std::move(m_kwargs); }
2241-
2242-
/// Call a Python function and pass the collected arguments
2243-
object call(PyObject *ptr) const {
2244-
PyObject *result = PyObject_Call(ptr, m_args.ptr(), m_kwargs.ptr());
2245-
if (!result) {
2246-
throw error_already_set();
2247-
}
2248-
return reinterpret_steal<object>(result);
2249-
}
2250-
2251-
private:
2252-
template <typename T>
2253-
void process(list &args_list, T &&x) {
2254-
auto o = reinterpret_steal<object>(
2255-
detail::make_caster<T>::cast(std::forward<T>(x), policy, {}));
2256-
if (!o) {
2257-
#if !defined(PYBIND11_DETAILED_ERROR_MESSAGES)
2258-
throw cast_error_unable_to_convert_call_arg(std::to_string(args_list.size()));
2259-
#else
2260-
throw cast_error_unable_to_convert_call_arg(std::to_string(args_list.size()),
2261-
type_id<T>());
2262-
#endif
2263-
}
2264-
args_list.append(std::move(o));
2265-
}
2266-
2267-
void process(list &args_list, detail::args_proxy ap) {
2268-
for (auto a : ap) {
2269-
args_list.append(a);
2270-
}
2271-
}
2272-
2273-
void process(list & /*args_list*/, arg_v a) {
2274-
if (!a.name) {
2275-
#if !defined(PYBIND11_DETAILED_ERROR_MESSAGES)
2276-
nameless_argument_error();
2277-
#else
2278-
nameless_argument_error(a.type);
2279-
#endif
2280-
}
2281-
if (m_kwargs.contains(a.name)) {
2282-
#if !defined(PYBIND11_DETAILED_ERROR_MESSAGES)
2283-
multiple_values_error();
2284-
#else
2285-
multiple_values_error(a.name);
2286-
#endif
2287-
}
2288-
if (!a.value) {
2289-
#if !defined(PYBIND11_DETAILED_ERROR_MESSAGES)
2290-
throw cast_error_unable_to_convert_call_arg(a.name);
2291-
#else
2292-
throw cast_error_unable_to_convert_call_arg(a.name, a.type);
2293-
#endif
2294-
}
2295-
m_kwargs[a.name] = std::move(a.value);
2296-
}
2297-
2298-
void process(list & /*args_list*/, detail::kwargs_proxy kp) {
2299-
if (!kp) {
2300-
return;
2301-
}
2302-
for (auto k : reinterpret_borrow<dict>(kp)) {
2303-
if (m_kwargs.contains(k.first)) {
2304-
#if !defined(PYBIND11_DETAILED_ERROR_MESSAGES)
2305-
multiple_values_error();
2306-
#else
2307-
multiple_values_error(str(k.first));
2308-
#endif
2309-
}
2310-
m_kwargs[k.first] = k.second;
2311-
}
2312-
}
2313-
2314-
[[noreturn]] static void nameless_argument_error() {
2315-
throw type_error(
2316-
"Got kwargs without a name; only named arguments "
2317-
"may be passed via py::arg() to a python function call. "
2318-
"(#define PYBIND11_DETAILED_ERROR_MESSAGES or compile in debug mode for details)");
2319-
}
2320-
[[noreturn]] static void nameless_argument_error(const std::string &type) {
2321-
throw type_error("Got kwargs without a name of type '" + type
2322-
+ "'; only named "
2323-
"arguments may be passed via py::arg() to a python function call. ");
2324-
}
2325-
[[noreturn]] static void multiple_values_error() {
2326-
throw type_error(
2327-
"Got multiple values for keyword argument "
2328-
"(#define PYBIND11_DETAILED_ERROR_MESSAGES or compile in debug mode for details)");
2329-
}
2330-
2331-
[[noreturn]] static void multiple_values_error(const std::string &name) {
2332-
throw type_error("Got multiple values for keyword argument '" + name + "'");
2333-
}
2334-
2335-
private:
2336-
tuple m_args;
2337-
dict m_kwargs;
2338-
};
2339-
23402194
// [workaround(intel)] Separate function required here
23412195
// We need to put this into a separate function because the Intel compiler
23422196
// fails to compile enable_if_t<!all_of<is_positional<Args>...>::value>
23432197
// (tested with ICC 2021.1 Beta 20200827).
23442198
template <typename... Args>
2345-
constexpr bool args_are_all_positional() {
2346-
return all_of<is_positional<Args>...>::value;
2347-
}
2348-
2349-
#if PY_VERSION_HEX < 0x030C0000
2350-
/// Collect only positional arguments for a Python function call
2351-
template <return_value_policy policy,
2352-
typename... Args,
2353-
typename = enable_if_t<args_are_all_positional<Args...>()>>
2354-
simple_collector<policy> collect_arguments(Args &&...args) {
2355-
return simple_collector<policy>(std::forward<Args>(args)...);
2199+
constexpr bool args_has_keyword_or_ds() {
2200+
return any_of<is_keyword_or_ds<Args>...>::value;
23562201
}
23572202

2358-
/// Collect all arguments, including keywords and unpacking (only instantiated when needed)
2359-
template <return_value_policy policy,
2360-
typename... Args,
2361-
typename = enable_if_t<!args_are_all_positional<Args...>()>>
2362-
unpacking_collector<policy> collect_arguments(Args &&...args) {
2363-
// Following argument order rules for generalized unpacking according to PEP 448
2364-
static_assert(constexpr_last<is_positional, Args...>()
2365-
< constexpr_first<is_keyword_or_ds, Args...>()
2366-
&& constexpr_last<is_s_unpacking, Args...>()
2367-
< constexpr_first<is_ds_unpacking, Args...>(),
2368-
"Invalid function call: positional args must precede keywords and ** unpacking; "
2369-
"* unpacking must precede ** unpacking");
2370-
return unpacking_collector<policy>(std::forward<Args>(args)...);
2371-
}
2372-
#else
23732203
/// Helper class which collects positional, keyword, * and ** arguments for a Python function call
23742204
template <return_value_policy policy>
2375-
class unpacking_vectorcall_collector {
2205+
class unpacking_collector {
23762206
public:
23772207
template <typename... Ts>
2378-
explicit unpacking_vectorcall_collector(Ts &&...values) {
2208+
explicit unpacking_collector(Ts &&...values) {
2209+
/*
2210+
Python can sometimes utilize an extra space before the arguments to add on `self`.
2211+
This is important enough that there is a special flag for it:
2212+
PY_VECTORCALL_ARGUMENTS_OFFSET.
2213+
All we have to do it allocate an extra space at the beginning of this array, and set the
2214+
flag. Note that the extra space is not passed directly in to vectorcall.
2215+
*/
23792216
m_args.reserve(sizeof...(values) + 1);
2380-
m_args.push_back(
2381-
nullptr); // dummy first argument so we can use PY_VECTORCALL_ARGUMENTS_OFFSET
2217+
m_args.push_back(nullptr);
23822218

2383-
object names_list; // null or a list of names
2219+
if (args_has_keyword_or_ds<Ts...>()) {
2220+
object names_list = list();
23842221

2385-
// collect_arguments guarantees this can't be constructed with kwargs before the last
2386-
// positional so we don't need to worry about Ts... being in anything but normal python
2387-
// order.
2388-
using expander = int[];
2389-
(void) expander{0, (process(names_list, std::forward<Ts>(values)), 0)...};
2222+
// collect_arguments guarantees this can't be constructed with kwargs before the last
2223+
// positional so we don't need to worry about Ts... being in anything but normal python
2224+
// order.
2225+
using expander = int[];
2226+
(void) expander{0, (process(names_list, std::forward<Ts>(values)), 0)...};
23902227

2391-
if (names_list) {
23922228
m_names = reinterpret_steal<tuple>(PyList_AsTuple(names_list.ptr()));
2229+
} else {
2230+
object not_used;
2231+
2232+
using expander = int[];
2233+
(void) expander{0, (process(not_used, std::forward<Ts>(values)), 0)...};
23932234
}
23942235
}
23952236

23962237
/// Call a Python function and pass the collected arguments
23972238
object call(PyObject *ptr) const {
2398-
// -1 to account for PY_VECTORCALL_ARGUMENTS_OFFSET
2399-
size_t nargs = m_args.size() - 1;
2239+
size_t nargs = m_args.size() - 1; // -1 for PY_VECTORCALL_ARGUMENTS_OFFSET (see ctor)
24002240
if (m_names) {
24012241
nargs -= static_cast<size_t>(PyTuple_GET_SIZE(m_names.ptr()));
24022242
}
2403-
PyObject *result = PyObject_Vectorcall(
2243+
PyObject *result = _PyObject_Vectorcall(
24042244
ptr, m_args.data() + 1, nargs | PY_VECTORCALL_ARGUMENTS_OFFSET, m_names.ptr());
24052245
if (!result) {
24062246
throw error_already_set();
@@ -2409,15 +2249,14 @@ class unpacking_vectorcall_collector {
24092249
}
24102250

24112251
tuple args() const {
2412-
// -1 to account for PY_VECTORCALL_ARGUMENTS_OFFSET
2413-
size_t nargs = m_args.size() - 1;
2252+
size_t nargs = m_args.size() - 1; // -1 for PY_VECTORCALL_ARGUMENTS_OFFSET (see ctor)
24142253
if (m_names) {
24152254
nargs -= static_cast<size_t>(PyTuple_GET_SIZE(m_names.ptr()));
24162255
}
24172256
tuple val(nargs);
24182257
for (size_t i = 0; i < nargs; ++i) {
2419-
// +1 to account for PY_VECTORCALL_ARGUMENTS_OFFSET
2420-
val[i] = reinterpret_borrow<object>(m_args[i + 1]);
2258+
val[i] = reinterpret_borrow<object>(
2259+
m_args[i + 1]); // +1 for PY_VECTORCALL_ARGUMENTS_OFFSET (see ctor)
24212260
}
24222261
return val;
24232262
}
@@ -2441,12 +2280,12 @@ class unpacking_vectorcall_collector {
24412280
auto o = reinterpret_steal<object>(
24422281
detail::make_caster<T>::cast(std::forward<T>(x), policy, {}));
24432282
if (!o) {
2444-
# if !defined(PYBIND11_DETAILED_ERROR_MESSAGES)
2283+
#if !defined(PYBIND11_DETAILED_ERROR_MESSAGES)
24452284
throw cast_error_unable_to_convert_call_arg(std::to_string(m_args.size() - 1));
2446-
# else
2285+
#else
24472286
throw cast_error_unable_to_convert_call_arg(std::to_string(m_args.size() - 1),
24482287
type_id<T>());
2449-
# endif
2288+
#endif
24502289
}
24512290
m_args.push_back(o.ptr());
24522291
m_temp.push_back(std::move(o));
@@ -2465,30 +2304,28 @@ class unpacking_vectorcall_collector {
24652304

24662305
// named argument
24672306
void process(object &names_list, arg_v a) {
2468-
if (!names_list) {
2469-
names_list = list();
2470-
}
2307+
assert(names_list);
24712308
if (!a.name) {
2472-
# if !defined(PYBIND11_DETAILED_ERROR_MESSAGES)
2309+
#if !defined(PYBIND11_DETAILED_ERROR_MESSAGES)
24732310
nameless_argument_error();
2474-
# else
2311+
#else
24752312
nameless_argument_error(a.type);
2476-
# endif
2313+
#endif
24772314
}
24782315
auto name = str(a.name);
24792316
if (names_list.contains(name)) {
2480-
# if !defined(PYBIND11_DETAILED_ERROR_MESSAGES)
2317+
#if !defined(PYBIND11_DETAILED_ERROR_MESSAGES)
24812318
multiple_values_error();
2482-
# else
2319+
#else
24832320
multiple_values_error(a.name);
2484-
# endif
2321+
#endif
24852322
}
24862323
if (!a.value) {
2487-
# if !defined(PYBIND11_DETAILED_ERROR_MESSAGES)
2324+
#if !defined(PYBIND11_DETAILED_ERROR_MESSAGES)
24882325
throw cast_error_unable_to_convert_call_arg(a.name);
2489-
# else
2326+
#else
24902327
throw cast_error_unable_to_convert_call_arg(a.name, a.type);
2491-
# endif
2328+
#endif
24922329
}
24932330
if (PyList_Append(names_list.ptr(), name.release().ptr()) < 0) {
24942331
throw error_already_set();
@@ -2502,17 +2339,15 @@ class unpacking_vectorcall_collector {
25022339
if (!kp) {
25032340
return;
25042341
}
2505-
if (!names_list) {
2506-
names_list = list();
2507-
}
2342+
assert(names_list);
25082343
for (auto &&k : reinterpret_borrow<dict>(kp)) {
25092344
auto name = str(k.first);
25102345
if (names_list.contains(name)) {
2511-
# if !defined(PYBIND11_DETAILED_ERROR_MESSAGES)
2346+
#if !defined(PYBIND11_DETAILED_ERROR_MESSAGES)
25122347
multiple_values_error();
2513-
# else
2348+
#else
25142349
multiple_values_error(name);
2515-
# endif
2350+
#endif
25162351
}
25172352
if (PyList_Append(names_list.ptr(), name.release().ptr()) < 0) {
25182353
throw error_already_set();
@@ -2549,19 +2384,18 @@ class unpacking_vectorcall_collector {
25492384
small_vector<object, arg_vector_small_size> m_temp;
25502385
};
25512386

2552-
/// Collect all arguments, including keywords and unpacking for vectorcall
2387+
/// Collect all arguments, including keywords and unpacking
25532388
template <return_value_policy policy, typename... Args>
2554-
unpacking_vectorcall_collector<policy> collect_arguments(Args &&...args) {
2389+
unpacking_collector<policy> collect_arguments(Args &&...args) {
25552390
// Following argument order rules for generalized unpacking according to PEP 448
2556-
static_assert(constexpr_last<is_positional, Args...>()
2557-
< constexpr_first<is_keyword_or_ds, Args...>()
2558-
&& constexpr_last<is_s_unpacking, Args...>()
2559-
< constexpr_first<is_ds_unpacking, Args...>(),
2560-
"Invalid function call: positional args must precede keywords and ** unpacking; "
2561-
"* unpacking must precede ** unpacking");
2562-
return unpacking_vectorcall_collector<policy>(std::forward<Args>(args)...);
2391+
static_assert(
2392+
constexpr_last<is_positional, Args...>() < constexpr_first<is_keyword_or_ds, Args...>(),
2393+
"Invalid function call: positional args must precede keywords and */** unpacking;");
2394+
static_assert(constexpr_last<is_s_unpacking, Args...>()
2395+
< constexpr_first<is_ds_unpacking, Args...>(),
2396+
"Invalid function call: * unpacking must precede ** unpacking");
2397+
return unpacking_collector<policy>(std::forward<Args>(args)...);
25632398
}
2564-
#endif
25652399

25662400
template <typename Derived>
25672401
template <return_value_policy policy, typename... Args>

0 commit comments

Comments
 (0)