function.h 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682
  1. #pragma once
  2. #include "cast.h"
  3. namespace pkbind {
  4. namespace impl {
  5. template <typename... Args>
  6. struct constructor {};
  7. template <typename Fn, typename Args = callable_args_t<Fn>>
  8. struct factory;
  9. template <typename Fn, typename... Args>
  10. struct factory<Fn, std::tuple<Args...>> {
  11. Fn fn;
  12. auto make() {
  13. using Self = callable_return_t<Fn>;
  14. return [fn = std::move(fn)](Self* self, Args... args) {
  15. new (self) Self(fn(args...));
  16. };
  17. }
  18. };
  19. } // namespace impl
  20. template <typename... Args>
  21. impl::constructor<Args...> init() {
  22. return {};
  23. }
  24. template <typename Fn>
  25. impl::factory<Fn> init(Fn&& fn) {
  26. return {std::forward<Fn>(fn)};
  27. }
  28. struct arg_with_default {
  29. const char* name;
  30. object value;
  31. };
  32. struct arg {
  33. const char* name;
  34. arg(const char* name) : name(name) {}
  35. template <typename T>
  36. arg_with_default operator= (T&& value) {
  37. return arg_with_default{name, cast(std::forward<T>(value))};
  38. }
  39. };
  40. struct kwargs_proxy {
  41. handle value;
  42. };
  43. struct args_proxy {
  44. handle value;
  45. kwargs_proxy operator* () { return kwargs_proxy{value}; }
  46. };
  47. template <typename Derived>
  48. args_proxy interface<Derived>::operator* () const {
  49. return args_proxy{handle(this->ptr())};
  50. }
  51. template <typename Derived>
  52. template <return_value_policy policy, typename... Args>
  53. object interface<Derived>::operator() (Args&&... args) const {
  54. py_StackRef p0 = py_peek(0); // checkpoint before pushing so py_clearexc can safely rewind
  55. py_push(ptr());
  56. py_pushnil();
  57. int argc = 0;
  58. int kwargsc = 0;
  59. auto foreach = [&](auto&& argument) {
  60. using type = std::decay_t<decltype(argument)>;
  61. if constexpr(std::is_constructible_v<handle, type>) {
  62. argc += 1;
  63. py_push(handle(argument).ptr());
  64. } else if constexpr(std::is_same_v<type, arg_with_default>) {
  65. kwargsc += 1;
  66. arg_with_default& default_ = argument;
  67. py_pushname(name(default_.name).index());
  68. py_push(default_.value.ptr());
  69. } else if constexpr(std::is_same_v<type, args_proxy>) {
  70. tuple args = argument.value.template cast<tuple>();
  71. for(auto arg: args) {
  72. argc += 1;
  73. py_push(arg.ptr());
  74. }
  75. } else if constexpr(std::is_same_v<type, kwargs_proxy>) {
  76. dict kwargs = argument.value.template cast<dict>();
  77. kwargs.apply([&](handle key, handle value) {
  78. kwargsc += 1;
  79. name name = key.cast<std::string_view>();
  80. py_pushname(name.index());
  81. py_push(value.ptr());
  82. });
  83. } else {
  84. argc += 1;
  85. py_push(pkbind::cast(std::forward<decltype(argument)>(argument), policy).ptr());
  86. }
  87. };
  88. (foreach(std::forward<Args>(args)), ...);
  89. if(!py_vectorcall(argc, kwargsc)) {
  90. py_matchexc(tp_Exception);
  91. object e = object::from_ret();
  92. auto what = py_formatexc();
  93. py_clearexc(p0);
  94. throw python_error(what, std::move(e));
  95. }
  96. return object::from_ret();
  97. }
  98. class function : public object {
  99. PKBIND_TYPE_IMPL(object, function, tp_function);
  100. };
  101. namespace impl {
  102. template <typename Callable,
  103. typename Extra,
  104. typename Args = callable_args_t<Callable>,
  105. typename IndexSequence = std::make_index_sequence<std::tuple_size_v<Args>>>
  106. struct template_parser;
  107. class function_record {
  108. template <typename C, typename E, typename A, typename I>
  109. friend struct template_parser;
  110. using destructor_t = void (*)(function_record*);
  111. using wrapper_t = bool (*)(function_record&,
  112. std::vector<handle>&,
  113. std::vector<std::pair<handle, handle>>&,
  114. bool convert,
  115. handle parent);
  116. struct arguments_t {
  117. std::vector<std::string> names;
  118. std::vector<object> defaults;
  119. };
  120. public:
  121. template <typename Fn, typename... Extras>
  122. function_record(Fn&& f, const Extras&... extras) {
  123. using Callable = std::decay_t<Fn>;
  124. if constexpr(std::is_trivially_copyable_v<Callable> && sizeof(Callable) <= sizeof(buffer)) {
  125. // if the callable object is trivially copyable and the size is less than 16 bytes,
  126. // store it in the buffer
  127. new (buffer) auto(std::forward<Fn>(f));
  128. destructor = [](function_record* self) {
  129. reinterpret_cast<Callable*>(self->buffer)->~Callable();
  130. };
  131. } else {
  132. // otherwise, store it in the heap
  133. data = new auto(std::forward<Fn>(f));
  134. destructor = [](function_record* self) {
  135. delete static_cast<Callable*>(self->data);
  136. };
  137. }
  138. using Parser = template_parser<Callable, std::tuple<Extras...>>;
  139. Parser::initialize(*this, extras...);
  140. wrapper = Parser::call;
  141. }
  142. function_record(const function_record&) = delete;
  143. function_record& operator= (const function_record&) = delete;
  144. function_record(function_record&& other) noexcept {
  145. std::memcpy(this, &other, sizeof(function_record));
  146. std::memset(&other, 0, sizeof(function_record));
  147. }
  148. function_record& operator= (function_record&&) = delete;
  149. ~function_record() {
  150. if(destructor) { destructor(this); }
  151. if(arguments) { delete arguments; }
  152. if(next) { delete next; }
  153. if(signature) { delete[] signature; }
  154. }
  155. void append(function_record* record) {
  156. function_record* p = this;
  157. while(p->next) {
  158. p = p->next;
  159. }
  160. p->next = record;
  161. }
  162. template <typename T>
  163. T& as() {
  164. if constexpr(std::is_trivially_copyable_v<T> && sizeof(T) <= sizeof(buffer)) {
  165. return *reinterpret_cast<T*>(buffer);
  166. } else {
  167. return *static_cast<T*>(data);
  168. }
  169. }
  170. static function_record& from(handle h) {
  171. auto slot = py_getslot(h.ptr(), 0);
  172. return *static_cast<function_record*>(py_touserdata(slot));
  173. }
  174. void operator() (int argc, handle stack) {
  175. function_record* p = this;
  176. bool has_self = argc == 3;
  177. std::vector<handle> args;
  178. handle self = py_offset(stack.ptr(), 0);
  179. if(has_self) { args.push_back(self); }
  180. auto tuple = py_offset(stack.ptr(), 0 + has_self);
  181. for(int i = 0; i < py_tuple_len(tuple); ++i) {
  182. args.push_back(py_tuple_getitem(tuple, i));
  183. }
  184. auto dict = steal<pkbind::dict>(py_offset(stack.ptr(), 1 + has_self));
  185. std::vector<std::pair<handle, handle>> kwargs;
  186. dict.apply([&](handle key, handle value) {
  187. kwargs.emplace_back(key, value);
  188. });
  189. // foreach function record and call the function with not convert
  190. while(p != nullptr) {
  191. auto result = p->wrapper(*p, args, kwargs, false, self);
  192. if(result) { return; }
  193. p = p->next;
  194. }
  195. p = this;
  196. // foreach function record and call the function with convert
  197. while(p != nullptr) {
  198. auto result = p->wrapper(*p, args, kwargs, true, self);
  199. if(result) { return; }
  200. p = p->next;
  201. }
  202. std::string msg = "no matching function found, function signature:\n";
  203. p = this;
  204. while(p != nullptr) {
  205. msg += " ";
  206. msg += p->signature;
  207. msg += "\n";
  208. p = p->next;
  209. }
  210. throw std::runtime_error(msg);
  211. }
  212. private:
  213. union {
  214. void* data;
  215. char buffer[16];
  216. };
  217. wrapper_t wrapper = nullptr;
  218. function_record* next = nullptr;
  219. arguments_t* arguments = nullptr;
  220. destructor_t destructor = nullptr;
  221. const char* signature = nullptr;
  222. return_value_policy policy = return_value_policy::automatic;
  223. };
  224. template <typename Fn, std::size_t... Is, typename... Args>
  225. void invoke(Fn&& fn,
  226. std::index_sequence<Is...>,
  227. std::tuple<type_caster<Args>...>& casters,
  228. return_value_policy policy,
  229. handle parent) {
  230. using underlying_type = std::decay_t<Fn>;
  231. using return_type = callable_return_t<underlying_type>;
  232. constexpr bool is_void = std::is_void_v<return_type>;
  233. constexpr bool is_member_function_pointer = std::is_member_function_pointer_v<underlying_type>;
  234. if constexpr(is_member_function_pointer) {
  235. // helper function to unpack the arguments to call the member pointer
  236. auto unpack = [&](class_type_t<underlying_type>& self, auto&&... args) {
  237. return (self.*fn)(args...);
  238. };
  239. if constexpr(!is_void) {
  240. py_assign(py_retval(),
  241. pkbind::cast(unpack(std::get<Is>(casters).value()...), policy, parent).ptr());
  242. } else {
  243. unpack(std::get<Is>(casters).value()...);
  244. py_newnone(py_retval());
  245. }
  246. } else {
  247. if constexpr(!is_void) {
  248. py_assign(py_retval(),
  249. pkbind::cast(fn(std::get<Is>(casters).value()...), policy, parent).ptr());
  250. } else {
  251. fn(std::get<Is>(casters).value()...);
  252. py_newnone(py_retval());
  253. }
  254. }
  255. }
  256. template <typename Callable, typename... Extras, typename... Args, std::size_t... Is>
  257. struct template_parser<Callable,
  258. std::tuple<Extras...>,
  259. std::tuple<Args...>,
  260. std::index_sequence<Is...>> {
  261. using types = type_list<Args...>;
  262. /// count of the Callable parameters.
  263. constexpr inline static auto argc = types::size;
  264. // count the number of py::args and py::kwargs
  265. constexpr inline static auto args_count = types::template count<pkbind::args>;
  266. constexpr inline static auto kwargs_count = types::template count<pkbind::kwargs>;
  267. static_assert(args_count <= 1, "py::args can occur at most once");
  268. static_assert(kwargs_count <= 1, "py::kwargs can occur at most once");
  269. /// find the position of py::args and py::kwargs
  270. constexpr inline static auto args_pos = types::template find<pkbind::args>;
  271. constexpr inline static auto kwargs_pos = types::template find<pkbind::kwargs>;
  272. // FIXME: temporarily, args and kwargs must be at the end of the arguments list
  273. /// if have py::kwargs, it must be at the end of the arguments list.
  274. static_assert(kwargs_count == 0 || kwargs_pos == argc - 1,
  275. "py::kwargs must be the last parameter");
  276. /// if have py::args, it must be before py::kwargs or at the end of the arguments list.
  277. static_assert(args_count == 0 || args_pos == kwargs_pos - 1 || args_pos == argc - 1,
  278. "py::args must be before py::kwargs or at the end of the parameter list");
  279. using extras = type_list<Extras...>;
  280. // count the number of py::doc and py::return_value_policy
  281. constexpr inline static auto doc_count = extras::template count<const char*>;
  282. constexpr inline static auto policy_count = extras::template count<pkbind::return_value_policy>;
  283. static_assert(doc_count <= 1, "doc can occur at most once");
  284. static_assert(policy_count <= 1, "return_value_policy can occur at most once");
  285. constexpr inline static auto policy_pos = extras::template find<pkbind::return_value_policy>;
  286. constexpr inline static auto last_arg_without_default_pos =
  287. types::template find_last<pkbind::arg>;
  288. constexpr inline static auto first_arg_with_default_pos =
  289. types::template find<pkbind::arg_with_default>;
  290. static_assert(last_arg_without_default_pos < first_arg_with_default_pos ||
  291. first_arg_with_default_pos == -1,
  292. "parameter with default value must be after parameter without default value");
  293. /// count of named parameters(explicit with name).
  294. constexpr inline static auto named_only_argc = extras::template count<pkbind::arg>;
  295. constexpr inline static auto named_default_argc =
  296. extras::template count<pkbind::arg_with_default>;
  297. constexpr inline static auto named_argc = named_only_argc + named_default_argc;
  298. /// count of normal parameters(which are not py::args or py::kwargs).
  299. constexpr inline static auto normal_argc = argc - (args_pos != -1) - (kwargs_pos != -1);
  300. /// all parameters must either have no names or all must have names.
  301. static_assert(named_argc == 0 || named_argc == normal_argc,
  302. "all parameters must either have no names or all must have names.");
  303. static void initialize(function_record& record, const Extras&... extras) {
  304. auto extras_tuple = std::make_tuple(extras...);
  305. constexpr static bool has_named_args = (named_argc > 0);
  306. if constexpr(policy_pos != -1) { record.policy = std::get<policy_pos>(extras_tuple); }
  307. // TODO: set others
  308. // set default arguments
  309. if constexpr(has_named_args) {
  310. record.arguments = new function_record::arguments_t();
  311. auto add_arguments = [&](const auto& default_) {
  312. using type = remove_cvref_t<decltype(default_)>;
  313. if constexpr(std::is_same_v<arg, type>) {
  314. auto& arguments = *record.arguments;
  315. arguments.names.emplace_back(default_.name);
  316. arguments.defaults.emplace_back();
  317. } else if constexpr(std::is_same_v<arg_with_default, type>) {
  318. auto& arguments = *record.arguments;
  319. arguments.names.emplace_back(default_.name);
  320. arguments.defaults.emplace_back(std::move(default_.value));
  321. }
  322. };
  323. (add_arguments(extras), ...);
  324. }
  325. // set signature
  326. {
  327. std::string sig = "(";
  328. std::size_t index = 0;
  329. auto append = [&](auto _t) {
  330. using T = remove_cvref_t<typename decltype(_t)::type>;
  331. if constexpr(std::is_same_v<T, args>) {
  332. sig += "*args";
  333. } else if constexpr(std::is_same_v<T, kwargs>) {
  334. sig += "**kwargs";
  335. } else if constexpr(has_named_args) {
  336. sig += record.arguments->names[index].c_str();
  337. sig += ": ";
  338. sig += type_info::of<T>().name;
  339. if(!record.arguments->defaults[index].empty()) {
  340. sig += " = ";
  341. sig += record.arguments->defaults[index]
  342. .attr("__repr__")()
  343. .cast<std::string_view>();
  344. }
  345. } else {
  346. sig += "_: ";
  347. sig += type_info::of<T>().name;
  348. }
  349. if(index + 1 < argc) { sig += ", "; }
  350. index++;
  351. };
  352. (append(type_identity<Args>{}), ...);
  353. sig += ")";
  354. char* buffer = new char[sig.size() + 1];
  355. std::memcpy(buffer, sig.data(), sig.size());
  356. buffer[sig.size()] = '\0';
  357. record.signature = buffer;
  358. }
  359. }
  360. /// try to call a C++ function(store in function_record) with the arguments which are from
  361. /// Python. if success, return true, otherwise return false.
  362. static bool call(function_record& record,
  363. std::vector<handle>& args,
  364. std::vector<std::pair<handle, handle>>& kwargs,
  365. bool convert,
  366. handle parent) {
  367. // first, we try to load arguments into the stack.
  368. // use argc + 1 to avoid compile error when argc is 0.
  369. handle stack[argc + 1] = {};
  370. // if have default arguments, load them
  371. if constexpr(named_default_argc > 0) {
  372. auto& defaults = record.arguments->defaults;
  373. for(std::size_t i = named_only_argc; i < named_argc; ++i) {
  374. stack[i] = defaults[i];
  375. }
  376. }
  377. // load arguments from call arguments
  378. if(args.size() > normal_argc) {
  379. if constexpr(args_pos == -1) { return false; }
  380. }
  381. for(std::size_t i = 0; i < std::min(normal_argc, (int)args.size()); ++i) {
  382. stack[i] = args[i];
  383. }
  384. object repack_args;
  385. // pack the args
  386. if constexpr(args_pos != -1) {
  387. const auto n =
  388. static_cast<int>(args.size() > normal_argc ? args.size() - normal_argc : 0);
  389. auto pack = tuple(n);
  390. for(int i = 0; i < n; ++i) {
  391. pack[i] = args[normal_argc + i];
  392. }
  393. repack_args = std::move(pack);
  394. stack[args_pos] = repack_args;
  395. }
  396. // pack the kwargs
  397. int index = 0;
  398. if constexpr(named_argc != 0) {
  399. int arg_index = 0;
  400. while(arg_index < named_argc && index < kwargs.size()) {
  401. const auto name = kwargs[index].first;
  402. const auto value = kwargs[index].second;
  403. if(name.cast<std::string_view>() == record.arguments->names[arg_index]) {
  404. stack[arg_index] = value;
  405. index += 1;
  406. }
  407. arg_index += 1;
  408. }
  409. }
  410. object repacked_kwargs;
  411. if constexpr(kwargs_pos != -1) {
  412. auto pack = dict();
  413. while(index < kwargs.size()) {
  414. pack[kwargs[index].first] = kwargs[index].second;
  415. index += 1;
  416. }
  417. repacked_kwargs = std::move(pack);
  418. stack[kwargs_pos] = repacked_kwargs;
  419. }
  420. // check if all the arguments are valid
  421. for(std::size_t i = 0; i < argc; ++i) {
  422. if(!stack[i]) { return false; }
  423. }
  424. // ok, all the arguments are valid, call the function
  425. std::tuple<type_caster<Args>...> casters;
  426. if(((std::get<Is>(casters).load(stack[Is], convert)) && ...)) {
  427. invoke(record.as<Callable>(),
  428. std::index_sequence<Is...>{},
  429. casters,
  430. record.policy,
  431. parent);
  432. return true;
  433. }
  434. return false;
  435. }
  436. };
  437. } // namespace impl
  438. class cpp_function : public function {
  439. PKBIND_TYPE_IMPL(function, cpp_function, tp_function);
  440. inline static lazy<py_Type> tp_function_record = +[](py_Type& type) {
  441. type = py_newtype("function_record", tp_object, nullptr, [](void* data) {
  442. static_cast<impl::function_record*>(data)->~function_record();
  443. });
  444. };
  445. static bool is_function_record(handle h) {
  446. if(isinstance<function>(h)) {
  447. auto slot = py_getslot(h.ptr(), 0);
  448. if(slot) { return py_typeof(slot) == tp_function_record; }
  449. }
  450. return false;
  451. }
  452. template <typename Fn, typename... Extras>
  453. cpp_function(bool is_method, const char* name, Fn&& fn, const Extras&... extras) :
  454. function(alloc_t{}) {
  455. // bind the function
  456. std::string sig = name;
  457. sig += is_method ? "(self, *args, **kwargs)" : "(*args, **kwargs)";
  458. py_newfunction(m_ptr, sig.c_str(), call, nullptr, 1);
  459. auto slot = py_getslot(m_ptr, 0);
  460. void* data = py_newobject(slot, tp_function_record, 0, sizeof(impl::function_record));
  461. new (data) impl::function_record(std::forward<Fn>(fn), extras...);
  462. }
  463. private:
  464. static bool call(int argc, py_Ref stack) {
  465. handle func = py_inspect_currentfunction();
  466. auto data = py_touserdata(py_getslot(func.ptr(), 0));
  467. auto& record = *static_cast<impl::function_record*>(data);
  468. try {
  469. record(argc, stack);
  470. return true;
  471. } catch(std::domain_error& e) {
  472. py_exception(tp_ValueError, e.what());
  473. } catch(std::invalid_argument& e) {
  474. py_exception(tp_ValueError, e.what());
  475. } catch(std::length_error& e) {
  476. py_exception(tp_ValueError, e.what());
  477. } catch(std::out_of_range& e) {
  478. py_exception(tp_IndexError, e.what());
  479. } catch(std::range_error& e) {
  480. py_exception(tp_ValueError, e.what());
  481. } catch(stop_iteration& e) {
  482. if(auto value_ptr = e.value().ptr()) {
  483. bool ok = py_tpcall(tp_StopIteration, 1, value_ptr);
  484. if(ok) { py_raise(py_retval()); }
  485. } else {
  486. StopIteration();
  487. }
  488. } catch(index_error& e) {
  489. py_exception(tp_IndexError, e.what());
  490. } catch(key_error& e) { py_exception(tp_KeyError, e.what()); } catch(value_error& e) {
  491. py_exception(tp_ValueError, e.what());
  492. } catch(type_error& e) { py_exception(tp_TypeError, e.what()); } catch(import_error& e) {
  493. py_exception(tp_ImportError, e.what());
  494. } catch(error_already_set&) {
  495. // exception already set, do nothing
  496. } catch(attribute_error& e) {
  497. py_exception(tp_AttributeError, e.what());
  498. } catch(std::exception& e) { py_exception(tp_RuntimeError, e.what()); }
  499. return false;
  500. };
  501. };
  502. class property : public object {
  503. PKBIND_TYPE_IMPL(object, property, tp_property);
  504. property(handle getter, handle setter = none()) :
  505. object(type::of<property>()(getter, setter)) {}
  506. };
  507. class staticmethod : public cpp_function {
  508. PKBIND_TYPE_IMPL(cpp_function, staticmethod, tp_staticmethod);
  509. };
  510. namespace impl {
  511. template <bool is_method, bool is_static, typename Fn, typename... Extras>
  512. void bind_function(handle obj, const char* name_, Fn&& fn, const Extras&... extras) {
  513. constexpr bool has_named_args =
  514. ((std::is_same_v<Extras, arg> || std::is_same_v<Extras, arg_with_default>) || ...);
  515. auto name = py_name(name_);
  516. auto func = py_getdict(obj.ptr(), name);
  517. if(func && cpp_function::is_function_record(func)) {
  518. auto slot = py_getslot(func, 0);
  519. auto& record = *static_cast<function_record*>(py_touserdata(slot));
  520. if constexpr(has_named_args && is_method) {
  521. record.append(new function_record(std::forward<Fn>(fn), arg("self"), extras...));
  522. } else {
  523. record.append(new function_record(std::forward<Fn>(fn), extras...));
  524. }
  525. } else {
  526. if constexpr(is_static) {
  527. py_setdict(
  528. obj.ptr(),
  529. name,
  530. staticmethod(is_method, name_, std::forward<Fn>(fn), extras...).ptr());
  531. } else {
  532. if constexpr(has_named_args && is_method) {
  533. py_setdict(
  534. obj.ptr(),
  535. name,
  536. cpp_function(is_method, name_, std::forward<Fn>(fn), arg("self"), extras...)
  537. .ptr());
  538. } else {
  539. py_setdict(obj.ptr(),
  540. name,
  541. cpp_function(is_method, name_, std::forward<Fn>(fn), extras...).ptr());
  542. }
  543. }
  544. }
  545. }
  546. template <typename Getter, typename Setter, typename... Extras>
  547. void bind_property(handle obj,
  548. const char* name,
  549. Getter&& getter_,
  550. Setter&& setter_,
  551. const Extras&... extras) {
  552. if constexpr(std::is_same_v<std::decay_t<Setter>, std::nullptr_t>) {
  553. cpp_function getter(true,
  554. name,
  555. std::forward<Getter>(getter_),
  556. return_value_policy::reference_internal,
  557. extras...);
  558. property prop(getter.ptr());
  559. setattr(obj, name, prop);
  560. } else {
  561. cpp_function getter(true,
  562. name,
  563. std::forward<Getter>(getter_),
  564. return_value_policy::reference_internal,
  565. extras...);
  566. cpp_function setter(true,
  567. name,
  568. std::forward<Setter>(setter_),
  569. return_value_policy::reference_internal,
  570. extras...);
  571. property prop(getter.ptr(), setter.ptr());
  572. setattr(obj, name, prop);
  573. }
  574. }
  575. } // namespace impl
  576. inline dict::dict(std::initializer_list<arg_with_default> args) : dict() {
  577. for(auto& arg: args) {
  578. this->operator[] (arg.name) = arg.value;
  579. }
  580. }
  581. } // namespace pkbind