#pragma once #include "cast.h" #include namespace pybind11 { template struct keep_alive {}; template struct call_guard { static_assert(std::is_default_constructible_v, "call_guard must be default constructible"); }; // append the overload to the beginning of the overload list struct prepend {}; template struct init {}; // TODO: support more customized tags // struct kw_only {}; // // struct pos_only {}; // // struct default_arg {}; // // struct arg { // const char* name; // const char* description; // }; // // struct default_arg { // const char* name; // const char* description; // const char* value; // }; template >, typename IndexSequence = std::make_index_sequence>> struct generator; class function_record { union { void* data; char buffer[16]; }; // TODO: optimize the function_record size to reduce memory usage const char* name; function_record* next; void (*destructor)(function_record*); return_value_policy policy = return_value_policy::automatic; handle (*wrapper)(function_record&, pkpy::ArgsView, bool convert, handle parent); template friend struct generator; public: template function_record(Fn&& f, const char* name, const Extras&... extras) : name(name), next(nullptr) { if constexpr(sizeof(f) <= sizeof(buffer)) { new (buffer) auto(std::forward(f)); destructor = [](function_record* self) { reinterpret_cast(self->buffer)->~Fn(); }; } else { data = new auto(std::forward(f)); destructor = [](function_record* self) { delete static_cast(self->data); }; } using Generator = generator, std::tuple>; Generator::initialize(*this, extras...); wrapper = Generator::generate(); } ~function_record() { destructor(this); } template auto& cast() { if constexpr(sizeof(Fn) <= sizeof(buffer)) { return *reinterpret_cast(buffer); } else { return *static_cast(data); } } void append(function_record* record) { function_record* p = this; while(p->next != nullptr) { p = p->next; } p->next = record; } handle operator() (pkpy::ArgsView view) { function_record* p = this; // foreach function record and call the function with not convert while(p != nullptr) { handle result = p->wrapper(*this, view, false, {}); if(result) { return result; } p = p->next; } p = this; // foreach function record and call the function with convert while(p != nullptr) { handle result = p->wrapper(*this, view, true, {}); if(result) { return result; } p = p->next; } vm->TypeError("no matching function found"); } }; template handle invoke(Fn&& fn, std::index_sequence, std::tuple...>& casters, return_value_policy policy, handle parent) { using underlying_type = std::decay_t; using ret = callable_return_t; // if the return type is void, return None if constexpr(std::is_void_v) { // resolve the member function pointer if constexpr(std::is_member_function_pointer_v) { [&](class_type_t& self, auto&... args) { (self.*fn)(args...); }(std::get(casters).value...); } else { fn(std::get(casters).value...); } return vm->None; } else { // resolve the member function pointer if constexpr(std::is_member_function_pointer_v>) { return type_caster::cast( [&](class_type_t& self, auto&... args) { return (self.*fn)(args...); }(std::get(casters).value...), policy, parent); } else { return type_caster::cast(fn(std::get(casters).value...), policy, parent); } } } template struct generator, std::tuple, std::index_sequence> { static void initialize(function_record& record, const Extras&... extras) {} static auto generate() { return +[](function_record& self, pkpy::ArgsView view, bool convert, handle parent) { // FIXME: // Temporarily, args and kwargs must be at the end of the arguments list // Named arguments are not supported yet constexpr bool has_args = types_count_v...> != 0; constexpr bool has_kwargs = types_count_v...> != 0; constexpr std::size_t count = sizeof...(Args) - has_args - has_kwargs; handle stack[sizeof...(Args)] = {}; // initialize the stack if(!has_args && (view.size() != count)) { return handle(); } if(has_args && (view.size() < count)) { return handle(); } for(std::size_t i = 0; i < count; ++i) { stack[i] = view[i]; } // pack the args and kwargs if constexpr(has_args) { const auto n = view.size() - count; pkpy::PyVar var = vm->new_object(vm->tp_tuple, n); auto& tuple = var.obj_get(); for(std::size_t i = 0; i < n; ++i) { tuple[i] = view[count + i]; } stack[count] = var; } if constexpr(has_kwargs) { const auto n = vm->s_data._sp - view.end(); pkpy::PyVar var = vm->new_object(vm->tp_dict); auto& dict = var.obj_get(); for(std::size_t i = 0; i < n; i += 2) { pkpy::i64 index = pkpy::_py_cast(vm, view[count + i]); pkpy::PyVar str = vm->new_object(vm->tp_str, pkpy::StrName(index).sv()); dict.set(vm, str, view[count + i + 1]); } stack[count + 1] = var; } // check if all the arguments are not valid for(std::size_t i = 0; i < sizeof...(Args); ++i) { if(!stack[i]) { return handle(); } } // ok, all the arguments are valid, call the function std::tuple...> casters; // check type compatibility if(((std::get(casters).load(stack[Is], convert)) && ...)) { return invoke(self.cast(), std::index_sequence{}, casters, self.policy, parent); } return handle(); }; } }; constexpr inline static auto _wrapper = +[](pkpy::VM*, pkpy::ArgsView view) { auto& record = pkpy::lambda_get_userdata(view.begin()); return record(view).ptr(); }; class cpp_function : public function { public: template cpp_function(Fn&& f, const Extras&... extras) { pkpy::any userdata = function_record(std::forward(f), "anonymous", extras...); m_ptr = vm->bind_func(nullptr, "", -1, _wrapper, std::move(userdata)); inc_ref(); } }; template handle bind_function(const handle& obj, const char* name, Fn&& fn, pkpy::BindType type, const Extras&... extras) { // do not use cpp_function directly to avoid unnecessary reference count change pkpy::PyVar var = obj.ptr(); pkpy::PyVar callable = var->attr().try_get(name); // if the function is not bound yet, bind it if(!callable) { pkpy::any userdata = function_record(std::forward(fn), name, extras...); callable = vm->bind_func(var, name, -1, _wrapper, std::move(userdata)); } else { auto& userdata = callable.obj_get()._userdata; function_record* record = new function_record(std::forward(fn), name, extras...); constexpr bool is_prepend = (types_count_v != 0); if constexpr(is_prepend) { // if prepend is specified, append the new record to the beginning of the list function_record* last = (function_record*)userdata.data; userdata.data = record; record->append(last); } else { // otherwise, append the new record to the end of the list function_record* last = (function_record*)userdata.data; last->append(record); } } return callable; } template handle bind_property(const handle& obj, const char* name, Getter_&& getter_, Setter_&& setter_, const Extras&... extras) { pkpy::PyVar var = obj.ptr(); pkpy::PyVar getter = vm->None; pkpy::PyVar setter = vm->None; using Getter = std::decay_t; using Setter = std::decay_t; getter = vm->new_object( vm->tp_native_func, [](pkpy::VM* vm, pkpy::ArgsView view) -> pkpy::PyVar { auto& getter = pkpy::lambda_get_userdata(view.begin()); if constexpr(std::is_member_pointer_v) { using Self = class_type_t; auto& self = _builtin_cast(view[0]).cast(); if constexpr(std::is_member_object_pointer_v) { return type_caster>::cast(self.*getter, return_value_policy::reference_internal, view[0]) .ptr(); } else { return type_caster>::cast((self.*getter)(), return_value_policy::reference_internal, view[0]) .ptr(); } } else { using Self = std::tuple_element_t<0, callable_args_t>; auto& self = _builtin_cast(view[0]).cast(); return type_caster>::cast(getter(self), return_value_policy::reference_internal, view[0]) .ptr(); } }, 1, std::forward(getter_)); if constexpr(!std::is_same_v) { setter = vm->new_object( vm->tp_native_func, [](pkpy::VM* vm, pkpy::ArgsView view) -> pkpy::PyVar { auto& setter = pkpy::lambda_get_userdata(view.begin()); if constexpr(std::is_member_pointer_v) { using Self = class_type_t; auto& self = _builtin_cast(view[0]).cast(); if constexpr(std::is_member_object_pointer_v) { type_caster> caster; if(caster.load(view[1], true)) { self.*setter = caster.value; return vm->None; } } else { type_caster>> caster; if(caster.load(view[1], true)) { (self.*setter)(caster.value); return vm->None; } } } else { using Self = std::tuple_element_t<0, callable_args_t>; auto& self = _builtin_cast(view[0]).cast(); type_caster>> caster; if(caster.load(view[1], true)) { setter(self, caster.value); return vm->None; } } vm->TypeError("invalid argument"); }, 2, std::forward(setter_)); } pkpy::PyVar property = vm->new_object(vm->tp_property, getter, setter); var->attr().set(name, property); return property; } } // namespace pybind11