@@ -2078,8 +2078,7 @@ using is_pos_only = std::is_same<intrinsic_t<T>, pos_only>;
20782078// forward declaration (definition in attr.h)
20792079struct function_record ;
20802080
2081- // / (Inline size chosen mostly arbitrarily; 6 should pad function_call out to two cache lines
2082- // / (16 pointers) in size.)
2081+ // / Inline size chosen mostly arbitrarily.
20832082constexpr std::size_t arg_vector_small_size = 6 ;
20842083
20852084// / Internal data associated with a single function call
@@ -2191,94 +2190,130 @@ class argument_loader {
21912190 std::tuple<make_caster<Args>...> argcasters;
21922191};
21932192
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.
2193+ // [workaround(intel)] Separate function required here
2194+ // We need to put this into a separate function because the Intel compiler
2195+ // fails to compile enable_if_t<!all_of<is_positional<Args>...>::value>
2196+ // (tested with ICC 2021.1 Beta 20200827).
2197+ template <typename ... Args>
2198+ constexpr bool args_has_keyword_or_ds () {
2199+ return any_of<is_keyword_or_ds<Args>...>::value;
2200+ }
2201+
2202+ // / Helper class which collects positional, keyword, * and ** arguments for a Python function call
21962203template <return_value_policy policy>
2197- class simple_collector {
2204+ class unpacking_collector {
21982205public:
21992206 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 {}; }
2207+ explicit unpacking_collector (Ts &&...values)
2208+ : m_names(reinterpret_steal<tuple>(
2209+ handle ())) // initialize to null to avoid useless allocation of 0-length tuple
2210+ {
2211+ /*
2212+ Python can sometimes utilize an extra space before the arguments to prepend `self`.
2213+ This is important enough that there is a special flag for it:
2214+ PY_VECTORCALL_ARGUMENTS_OFFSET.
2215+ All we have to do is allocate an extra space at the beginning of this array, and set the
2216+ flag. Note that the extra space is not passed directly in to vectorcall.
2217+ */
2218+ m_args.reserve (sizeof ...(values) + 1 );
2219+ m_args.push_back_null ();
2220+
2221+ if (args_has_keyword_or_ds<Ts...>()) {
2222+ list names_list;
2223+
2224+ // collect_arguments guarantees this can't be constructed with kwargs before the last
2225+ // positional so we don't need to worry about Ts... being in anything but normal python
2226+ // order.
2227+ using expander = int [];
2228+ (void ) expander{0 , (process (names_list, std::forward<Ts>(values)), 0 )...};
2229+
2230+ m_names = reinterpret_steal<tuple>(PyList_AsTuple (names_list.ptr ()));
2231+ } else {
2232+ auto not_used
2233+ = reinterpret_steal<list>(handle ()); // initialize as null (to avoid an allocation)
22052234
2206- tuple args () && { return std::move (m_args); }
2235+ using expander = int [];
2236+ (void ) expander{0 , (process (not_used, std::forward<Ts>(values)), 0 )...};
2237+ }
2238+ }
22072239
22082240 // / Call a Python function and pass the collected arguments
22092241 object call (PyObject *ptr) const {
2210- PyObject *result = PyObject_CallObject (ptr, m_args.ptr ());
2242+ size_t nargs = m_args.size () - 1 ; // -1 for PY_VECTORCALL_ARGUMENTS_OFFSET (see ctor)
2243+ if (m_names) {
2244+ nargs -= m_names.size ();
2245+ }
2246+ PyObject *result = _PyObject_Vectorcall (
2247+ ptr, m_args.data () + 1 , nargs | PY_VECTORCALL_ARGUMENTS_OFFSET, m_names.ptr ());
22112248 if (!result) {
22122249 throw error_already_set ();
22132250 }
22142251 return reinterpret_steal<object>(result);
22152252 }
22162253
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);
2254+ tuple args () const {
2255+ size_t nargs = m_args.size () - 1 ; // -1 for PY_VECTORCALL_ARGUMENTS_OFFSET (see ctor)
2256+ if (m_names) {
2257+ nargs -= m_names.size ();
2258+ }
2259+ tuple val (nargs);
2260+ for (size_t i = 0 ; i < nargs; ++i) {
2261+ // +1 for PY_VECTORCALL_ARGUMENTS_OFFSET (see ctor)
2262+ val[i] = reinterpret_borrow<object>(m_args[i + 1 ]);
2263+ }
2264+ return val;
22342265 }
22352266
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 ();
2267+ dict kwargs () const {
2268+ dict val;
2269+ if (m_names) {
2270+ size_t offset = m_args.size () - m_names.size ();
2271+ for (size_t i = 0 ; i < m_names.size (); ++i, ++offset) {
2272+ val[m_names[i]] = reinterpret_borrow<object>(m_args[offset]);
2273+ }
22472274 }
2248- return reinterpret_steal<object>(result) ;
2275+ return val ;
22492276 }
22502277
22512278private:
2279+ // normal argument, possibly needing conversion
22522280 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) {
2281+ void process (list & /* names_list*/ , T &&x) {
2282+ handle h = detail::make_caster<T>::cast (std::forward<T>(x), policy, {});
2283+ if (!h) {
22572284#if !defined(PYBIND11_DETAILED_ERROR_MESSAGES)
2258- throw cast_error_unable_to_convert_call_arg (std::to_string (args_list .size ()));
2285+ throw cast_error_unable_to_convert_call_arg (std::to_string (m_args .size () - 1 ));
22592286#else
2260- throw cast_error_unable_to_convert_call_arg (std::to_string (args_list .size ()),
2287+ throw cast_error_unable_to_convert_call_arg (std::to_string (m_args .size () - 1 ),
22612288 type_id<T>());
22622289#endif
22632290 }
2264- args_list. append ( std::move (o ));
2291+ m_args. push_back_steal (h. ptr ( )); // cast returns a new reference
22652292 }
22662293
2267- void process (list &args_list, detail::args_proxy ap) {
2294+ // * unpacking
2295+ void process (list & /* names_list*/ , detail::args_proxy ap) {
2296+ if (!ap) {
2297+ return ;
2298+ }
22682299 for (auto a : ap) {
2269- args_list. append (a );
2300+ m_args. push_back_borrow (a. ptr () );
22702301 }
22712302 }
22722303
2273- void process (list & /* args_list*/ , arg_v a) {
2304+ // named argument
2305+ // NOLINTNEXTLINE(performance-unnecessary-value-param)
2306+ void process (list &names_list, arg_v a) {
2307+ assert (names_list);
22742308 if (!a.name ) {
22752309#if !defined(PYBIND11_DETAILED_ERROR_MESSAGES)
22762310 nameless_argument_error ();
22772311#else
22782312 nameless_argument_error (a.type );
22792313#endif
22802314 }
2281- if (m_kwargs.contains (a.name )) {
2315+ auto name = str (a.name );
2316+ if (names_list.contains (name)) {
22822317#if !defined(PYBIND11_DETAILED_ERROR_MESSAGES)
22832318 multiple_values_error ();
22842319#else
@@ -2292,22 +2327,27 @@ class unpacking_collector {
22922327 throw cast_error_unable_to_convert_call_arg (a.name , a.type );
22932328#endif
22942329 }
2295- m_kwargs[a.name ] = std::move (a.value );
2330+ names_list.append (std::move (name));
2331+ m_args.push_back_borrow (a.value .ptr ());
22962332 }
22972333
2298- void process (list & /* args_list*/ , detail::kwargs_proxy kp) {
2334+ // ** unpacking
2335+ void process (list &names_list, detail::kwargs_proxy kp) {
22992336 if (!kp) {
23002337 return ;
23012338 }
2302- for (auto k : reinterpret_borrow<dict>(kp)) {
2303- if (m_kwargs.contains (k.first )) {
2339+ assert (names_list);
2340+ for (auto &&k : reinterpret_borrow<dict>(kp)) {
2341+ auto name = str (k.first );
2342+ if (names_list.contains (name)) {
23042343#if !defined(PYBIND11_DETAILED_ERROR_MESSAGES)
23052344 multiple_values_error ();
23062345#else
2307- multiple_values_error (str (k. first ) );
2346+ multiple_values_error (name );
23082347#endif
23092348 }
2310- m_kwargs[k.first ] = k.second ;
2349+ names_list.append (std::move (name));
2350+ m_args.push_back_borrow (k.second .ptr ());
23112351 }
23122352 }
23132353
@@ -2333,39 +2373,20 @@ class unpacking_collector {
23332373 }
23342374
23352375private:
2336- tuple m_args;
2337- dict m_kwargs ;
2376+ ref_small_vector<arg_vector_small_size> m_args;
2377+ tuple m_names ;
23382378};
23392379
2340- // [workaround(intel)] Separate function required here
2341- // We need to put this into a separate function because the Intel compiler
2342- // fails to compile enable_if_t<!all_of<is_positional<Args>...>::value>
2343- // (tested with ICC 2021.1 Beta 20200827).
2344- template <typename ... Args>
2345- constexpr bool args_are_all_positional () {
2346- return all_of<is_positional<Args>...>::value;
2347- }
2348-
2349- // / Collect only positional arguments for a Python function call
2350- template <return_value_policy policy,
2351- typename ... Args,
2352- typename = enable_if_t <args_are_all_positional<Args...>()>>
2353- simple_collector<policy> collect_arguments (Args &&...args) {
2354- return simple_collector<policy>(std::forward<Args>(args)...);
2355- }
2356-
2357- // / Collect all arguments, including keywords and unpacking (only instantiated when needed)
2358- template <return_value_policy policy,
2359- typename ... Args,
2360- typename = enable_if_t <!args_are_all_positional<Args...>()>>
2380+ // / Collect all arguments, including keywords and unpacking
2381+ template <return_value_policy policy, typename ... Args>
23612382unpacking_collector<policy> collect_arguments (Args &&...args) {
23622383 // Following argument order rules for generalized unpacking according to PEP 448
2363- static_assert (constexpr_last<is_positional, Args...>()
2364- < constexpr_first<is_keyword_or_ds, Args...>()
2365- && constexpr_last<is_s_unpacking, Args...>()
2366- < constexpr_first<is_ds_unpacking , Args...>(),
2367- " Invalid function call: positional args must precede keywords and ** unpacking; "
2368- " * unpacking must precede ** unpacking" );
2384+ static_assert (
2385+ constexpr_last<is_positional, Args...>() < constexpr_first<is_keyword_or_ds, Args...>(),
2386+ " Invalid function call: positional args must precede keywords and */** unpacking; " );
2387+ static_assert (constexpr_last<is_s_unpacking , Args...>()
2388+ < constexpr_first<is_ds_unpacking, Args...>(),
2389+ " Invalid function call: * unpacking must precede ** unpacking" );
23692390 return unpacking_collector<policy>(std::forward<Args>(args)...);
23702391}
23712392
0 commit comments