|
24 | 24 | #include <cstring>
|
25 | 25 | #include <memory>
|
26 | 26 | #include <new>
|
| 27 | +#include <stack> |
27 | 28 | #include <string>
|
28 | 29 | #include <utility>
|
29 | 30 | #include <vector>
|
@@ -338,8 +339,8 @@ class cpp_function : public function {
|
338 | 339 |
|
339 | 340 | /* Generate a readable signature describing the function's arguments and return
|
340 | 341 | value types */
|
341 |
| - static constexpr auto signature = const_name("(") + cast_in::arg_names |
342 |
| - + const_name(") -> ") + as_return_type<cast_out>::name; |
| 342 | + static constexpr auto signature |
| 343 | + = const_name("(") + cast_in::arg_names + const_name(") -> ") + cast_out::name; |
343 | 344 | PYBIND11_DESCR_CONSTEXPR auto types = decltype(signature)::types();
|
344 | 345 |
|
345 | 346 | /* Register the function with Python from generic (non-templated) code */
|
@@ -442,6 +443,13 @@ class cpp_function : public function {
|
442 | 443 | std::string signature;
|
443 | 444 | size_t type_index = 0, arg_index = 0;
|
444 | 445 | bool is_starred = false;
|
| 446 | + // `is_return_value.top()` is true if we are currently inside the return type of the |
| 447 | + // signature. Using `@^`/`@$` we can force types to be arg/return types while `@!` pops |
| 448 | + // back to the previous state. |
| 449 | + std::stack<bool> is_return_value({false}); |
| 450 | + // The following characters have special meaning in the signature parsing. Literals |
| 451 | + // containing these are escaped with `!`. |
| 452 | + std::string special_chars("!@%{}-"); |
445 | 453 | for (const auto *pc = text; *pc != '\0'; ++pc) {
|
446 | 454 | const auto c = *pc;
|
447 | 455 |
|
@@ -495,7 +503,57 @@ class cpp_function : public function {
|
495 | 503 | } else {
|
496 | 504 | signature += detail::quote_cpp_type_name(detail::clean_type_id(t->name()));
|
497 | 505 | }
|
| 506 | + } else if (c == '!' && special_chars.find(*(pc + 1)) != std::string::npos) { |
| 507 | + // typing::Literal escapes special characters with ! |
| 508 | + signature += *++pc; |
| 509 | + } else if (c == '@') { |
| 510 | + // `@^ ... @!` and `@$ ... @!` are used to force arg/return value type (see |
| 511 | + // typing::Callable/detail::arg_descr/detail::return_descr) |
| 512 | + if (*(pc + 1) == '^') { |
| 513 | + is_return_value.emplace(false); |
| 514 | + ++pc; |
| 515 | + continue; |
| 516 | + } |
| 517 | + if (*(pc + 1) == '$') { |
| 518 | + is_return_value.emplace(true); |
| 519 | + ++pc; |
| 520 | + continue; |
| 521 | + } |
| 522 | + if (*(pc + 1) == '!') { |
| 523 | + is_return_value.pop(); |
| 524 | + ++pc; |
| 525 | + continue; |
| 526 | + } |
| 527 | + // Handle types that differ depending on whether they appear |
| 528 | + // in an argument or a return value position (see io_name<text1, text2>). |
| 529 | + // For named arguments (py::arg()) with noconvert set, return value type is used. |
| 530 | + ++pc; |
| 531 | + if (!is_return_value.top() |
| 532 | + && !(arg_index < rec->args.size() && !rec->args[arg_index].convert)) { |
| 533 | + while (*pc != '\0' && *pc != '@') { |
| 534 | + signature += *pc++; |
| 535 | + } |
| 536 | + if (*pc == '@') { |
| 537 | + ++pc; |
| 538 | + } |
| 539 | + while (*pc != '\0' && *pc != '@') { |
| 540 | + ++pc; |
| 541 | + } |
| 542 | + } else { |
| 543 | + while (*pc != '\0' && *pc != '@') { |
| 544 | + ++pc; |
| 545 | + } |
| 546 | + if (*pc == '@') { |
| 547 | + ++pc; |
| 548 | + } |
| 549 | + while (*pc != '\0' && *pc != '@') { |
| 550 | + signature += *pc++; |
| 551 | + } |
| 552 | + } |
498 | 553 | } else {
|
| 554 | + if (c == '-' && *(pc + 1) == '>') { |
| 555 | + is_return_value.emplace(true); |
| 556 | + } |
499 | 557 | signature += c;
|
500 | 558 | }
|
501 | 559 | }
|
|
0 commit comments