@@ -82,7 +82,9 @@ class cpp_function : public function {
82
82
// / Special internal constructor for functors, lambda functions, etc.
83
83
template <typename Func, typename Return, typename ... Args, typename ... Extra /* ,*/ PYBIND11_NOEXCEPT_TPL_ARG>
84
84
void initialize (Func &&f, Return (*)(Args...) PYBIND11_NOEXCEPT_SPECIFIER, const Extra&... extra) {
85
- static_assert (detail::expected_num_args<Extra...>(sizeof ...(Args)),
85
+ constexpr bool have_args = detail::any_of<std::is_same<args, detail::intrinsic_t <Args>>...>::value,
86
+ have_kwargs = detail::any_of<std::is_same<kwargs, detail::intrinsic_t <Args>>...>::value;
87
+ static_assert (detail::expected_num_args<Extra...>(sizeof ...(Args), have_args, have_kwargs),
86
88
" The number of named arguments does not match the function signature" );
87
89
88
90
struct capture { typename std::remove_reference<Func>::type f; };
@@ -117,11 +119,11 @@ class cpp_function : public function {
117
119
>;
118
120
119
121
/* Dispatch code which converts function arguments and performs the actual function call */
120
- rec->impl = [](detail::function_record *rec, handle args, handle kwargs, handle parent) -> handle {
122
+ rec->impl = [](detail::function_record *rec, handle args, handle parent) -> handle {
121
123
cast_in args_converter;
122
124
123
125
/* Try to cast the function arguments into the C++ domain */
124
- if (!args_converter.load_args (args, kwargs ))
126
+ if (!args_converter.load_args (args))
125
127
return PYBIND11_TRY_NEXT_OVERLOAD;
126
128
127
129
/* Invoke call policy pre-call hook */
@@ -379,66 +381,144 @@ class cpp_function : public function {
379
381
}
380
382
381
383
// / Main dispatch logic for calls to functions bound using pybind11
382
- static PyObject *dispatcher (PyObject *self, PyObject *args , PyObject *kwargs ) {
384
+ static PyObject *dispatcher (PyObject *self, PyObject *args_in , PyObject *kwargs_in ) {
383
385
/* Iterator over the list of potentially admissible overloads */
384
386
detail::function_record *overloads = (detail::function_record *) PyCapsule_GetPointer (self, nullptr ),
385
387
*it = overloads;
386
388
387
389
/* Need to know how many arguments + keyword arguments there are to pick the right overload */
388
- size_t nargs = (size_t ) PyTuple_GET_SIZE (args),
389
- nkwargs = kwargs ? (size_t ) PyDict_Size (kwargs) : 0 ;
390
+ const size_t n_args_in = (size_t ) PyTuple_GET_SIZE (args_in);
390
391
391
- handle parent = nargs > 0 ? PyTuple_GET_ITEM (args , 0 ) : nullptr ,
392
+ handle parent = n_args_in > 0 ? PyTuple_GET_ITEM (args_in , 0 ) : nullptr ,
392
393
result = PYBIND11_TRY_NEXT_OVERLOAD;
393
394
try {
394
395
for (; it != nullptr ; it = it->next ) {
395
- auto args_ = reinterpret_borrow<tuple>(args);
396
- size_t kwargs_consumed = 0 ;
397
-
398
396
/* For each overload:
399
- 1. If the required list of arguments is longer than the
400
- actually provided amount, create a copy of the argument
401
- list and fill in any available keyword/default arguments.
402
- 2. Ensure that all keyword arguments were "consumed"
403
- 3. Call the function call dispatcher (function_record::impl)
397
+ 1. Copy all positional arguments we were given, also checking to make sure that
398
+ named positional arguments weren't *also* specified via kwarg.
399
+ 2. If we weren't given enough, try to make up the ommitted ones by checking
400
+ whether they were provided by a kwarg matching the `py::arg("name")` name. If
401
+ so, use it (and remove it from kwargs; if not, see if the function binding
402
+ provided a default that we can use.
403
+ 3. Ensure that either all keyword arguments were "consumed", or that the function
404
+ takes a kwargs argument to accept unconsumed kwargs.
405
+ 4. Any positional arguments still left get put into a tuple (for args), and any
406
+ leftover kwargs get put into a dict.
407
+ 5. Pack everything into a tuple; if we have py::args or py::kwargs, they are an
408
+ extra tuple or dict at the end of the positional arguments.
409
+ 6. Call the function call dispatcher (function_record::impl)
410
+
411
+ If one of these fail, move on to the next overload and keep trying until we get a
412
+ result other than PYBIND11_TRY_NEXT_OVERLOAD.
404
413
*/
405
- size_t nargs_ = nargs;
406
- if (nargs < it->args .size ()) {
407
- nargs_ = it->args .size ();
408
- args_ = tuple (nargs_);
409
- for (size_t i = 0 ; i < nargs; ++i) {
410
- handle item = PyTuple_GET_ITEM (args, i);
411
- PyTuple_SET_ITEM (args_.ptr (), i, item.inc_ref ().ptr ());
412
- }
413
414
414
- int arg_ctr = 0 ;
415
- for (auto const &it2 : it->args ) {
416
- int index = arg_ctr++;
417
- if (PyTuple_GET_ITEM (args_.ptr (), index ))
418
- continue ;
415
+ size_t pos_args = it->nargs ; // Number of positional arguments that we need
416
+ if (it->has_args ) --pos_args; // (but don't count py::args
417
+ if (it->has_kwargs ) --pos_args; // or py::kwargs)
419
418
420
- handle value;
421
- if (kwargs)
422
- value = PyDict_GetItemString (kwargs, it2.name );
419
+ if (!it->has_args && n_args_in > pos_args)
420
+ continue ; // Too many arguments for this overload
421
+
422
+ if (n_args_in < pos_args && it->args .size () < pos_args)
423
+ continue ; // Not enough arguments given, and not enough defaults to fill in the blanks
423
424
425
+ tuple pass_args (it->nargs );
426
+
427
+ size_t args_to_copy = std::min (pos_args, n_args_in);
428
+ size_t args_copied = 0 ;
429
+
430
+ // 1. Copy any position arguments given.
431
+ for (; args_copied < args_to_copy; ++args_copied) {
432
+ // If we find a given positional argument that also has a named kwargs argument,
433
+ // raise a TypeError like Python does. (We could also continue with the next
434
+ // overload, but this seems highly likely to be a caller mistake rather than a
435
+ // legitimate overload).
436
+ if (kwargs_in && args_copied < it->args .size ()) {
437
+ handle value = PyDict_GetItemString (kwargs_in, it->args [args_copied].name );
424
438
if (value)
425
- kwargs_consumed++;
426
- else if (it2.value )
427
- value = it2.value ;
439
+ throw type_error (std::string (it->name ) + " (): got multiple values for argument '" +
440
+ std::string (it->args [args_copied].name ) + " '" );
441
+ }
442
+
443
+ handle item = PyTuple_GET_ITEM (args_in, args_copied);
444
+ PyTuple_SET_ITEM (pass_args.ptr (), args_copied, item.inc_ref ().ptr ());
445
+ }
446
+
447
+ // We'll need to copy this if we steal some kwargs for defaults
448
+ dict kwargs = reinterpret_borrow<dict>(kwargs_in);
449
+
450
+ // 2. Check kwargs and, failing that, defaults that may help complete the list
451
+ if (args_copied < pos_args) {
452
+ bool copied_kwargs = false ;
453
+
454
+ for (; args_copied < pos_args; ++args_copied) {
455
+ const auto &arg = it->args [args_copied];
456
+
457
+ handle value;
458
+ if (kwargs_in)
459
+ value = PyDict_GetItemString (kwargs.ptr (), arg.name );
428
460
429
461
if (value) {
430
- PyTuple_SET_ITEM (args_.ptr (), index , value.inc_ref ().ptr ());
431
- } else {
432
- kwargs_consumed = (size_t ) -1 ; /* definite failure */
462
+ // Consume a kwargs value
463
+ if (!copied_kwargs) {
464
+ kwargs = reinterpret_steal<dict>(PyDict_Copy (kwargs.ptr ()));
465
+ copied_kwargs = true ;
466
+ }
467
+ PyDict_DelItemString (kwargs.ptr (), arg.name );
468
+ }
469
+ else if (arg.value ) {
470
+ value = arg.value ;
471
+ }
472
+
473
+ if (value)
474
+ PyTuple_SET_ITEM (pass_args.ptr (), args_copied, value.inc_ref ().ptr ());
475
+ else
433
476
break ;
477
+ }
478
+
479
+ if (args_copied < pos_args)
480
+ continue ; // Not enough arguments, defaults, or kwargs to fill the positional arguments
481
+ }
482
+
483
+ // 3. Check everything was consumed (unless we have a kwargs arg)
484
+ if (kwargs && kwargs.size () > 0 && !it->has_kwargs )
485
+ continue ; // Unconsumed kwargs, but no py::kwargs argument to accept them
486
+
487
+ // 4a. If we have a py::args argument, create a new tuple with leftovers
488
+ if (it->has_args ) {
489
+ tuple extra_args;
490
+ if (args_to_copy == 0 ) {
491
+ // We didn't copy out any position arguments from the args_in tuple, so we
492
+ // can use it directly:
493
+ extra_args = reinterpret_borrow<tuple>(args_in);
494
+ }
495
+ else if (args_copied >= n_args_in) {
496
+ extra_args = tuple (0 );
497
+ }
498
+ else {
499
+ size_t args_size = n_args_in - args_copied;
500
+ extra_args = tuple (args_size);
501
+ for (size_t i = 0 ; i < args_size; ++i) {
502
+ handle item = PyTuple_GET_ITEM (args_in, args_copied + i);
503
+ extra_args[i] = item.inc_ref ().ptr ();
434
504
}
435
505
}
506
+ PyTuple_SET_ITEM (pass_args.ptr (), args_copied++, extra_args.release ().ptr ());
436
507
}
437
508
509
+ // 4b. If we have a py::kwargs, pass on any remaining kwargs
510
+ if (it->has_kwargs ) {
511
+ if (!kwargs.ptr ())
512
+ kwargs = dict (); // If we didn't get one, send an empty one
513
+ PyTuple_SET_ITEM (pass_args.ptr (), args_copied++, kwargs.inc_ref ().ptr ());
514
+ }
515
+
516
+ // 5. Put everything in a big tuple. Not technically step 5, we've been building it
517
+ // in `pass_args` all along.
518
+
519
+ // 6. Call the function.
438
520
try {
439
- if ((kwargs_consumed == nkwargs || it->has_kwargs ) &&
440
- (nargs_ == it->nargs || it->has_args ))
441
- result = it->impl (it, args_, kwargs, parent);
521
+ result = it->impl (it, pass_args, parent);
442
522
} catch (reference_cast_error &) {
443
523
result = PYBIND11_TRY_NEXT_OVERLOAD;
444
524
}
@@ -512,7 +592,7 @@ class cpp_function : public function {
512
592
msg += " \n " ;
513
593
}
514
594
msg += " \n Invoked with: " ;
515
- auto args_ = reinterpret_borrow<tuple>(args );
595
+ auto args_ = reinterpret_borrow<tuple>(args_in );
516
596
for (size_t ti = overloads->is_constructor ? 1 : 0 ; ti < args_.size (); ++ti) {
517
597
msg += pybind11::repr (args_[ti]);
518
598
if ((ti + 1 ) != args_.size () )
@@ -530,9 +610,8 @@ class cpp_function : public function {
530
610
if (overloads->is_constructor ) {
531
611
/* When a constructor ran successfully, the corresponding
532
612
holder type (e.g. std::unique_ptr) must still be initialized. */
533
- PyObject *inst = PyTuple_GET_ITEM (args, 0 );
534
- auto tinfo = detail::get_type_info (Py_TYPE (inst));
535
- tinfo->init_holder (inst, nullptr );
613
+ auto tinfo = detail::get_type_info (Py_TYPE (parent.ptr ()));
614
+ tinfo->init_holder (parent.ptr (), nullptr );
536
615
}
537
616
return result.ptr ();
538
617
}
0 commit comments