function.h 24 KB

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