diff --git a/docs/pybind11.md b/docs/pybind11.md index 4884a7c0..904d2815 100644 --- a/docs/pybind11.md +++ b/docs/pybind11.md @@ -17,7 +17,7 @@ Or explicitly call `py::interpreter::initialize()` and `py::interpreter::finaliz #include namespace py = pybind11; -PYBIND11_MODULE(example, m) { +PYBIND11_EMBEDDED_MODULE(example, m) { m.def("add", [](int a, int b) { return a + b; }); diff --git a/include/pybind11/internal/class.h b/include/pybind11/internal/class.h index dd4e01e8..7fe0e0f9 100644 --- a/include/pybind11/internal/class.h +++ b/include/pybind11/internal/class.h @@ -47,11 +47,11 @@ public: /// bind constructor template - class_& def(init, const Extra&... extra) { + class_& def(impl::constructor, const Extra&... extra) { if constexpr(!std::is_constructible_v) { static_assert(std::is_constructible_v, "Invalid constructor arguments"); } else { - impl::bind_function( + impl::bind_function( *this, "__init__", [](T* self, Args... args) { @@ -63,17 +63,29 @@ public: } } + template + class_& def(impl::factory factory, const Extra&... extra) { + using ret = callable_return_t; + + if constexpr(!std::is_same_v) { + static_assert(std::is_same_v, "Factory function must return the class type"); + } else { + impl::bind_function(*this, "__init__", factory.make(), pkpy::BindType::DEFAULT, extra...); + return *this; + } + } + /// bind member function template class_& def(const char* name, Fn&& f, const Extra&... extra) { - using first = std::tuple_element_t<0, callable_args_t>>; - constexpr bool is_first_base_of_v = std::is_base_of_v, T>; + using first = remove_cvref_t>>>; + constexpr bool is_first_base_of_v = std::is_base_of_v || std::is_same_v; if constexpr(!is_first_base_of_v) { static_assert(is_first_base_of_v, "If you want to bind member function, the first argument must be the base class"); } else { - impl::bind_function(*this, name, std::forward(f), pkpy::BindType::DEFAULT, extra...); + impl::bind_function(*this, name, std::forward(f), pkpy::BindType::DEFAULT, extra...); } return *this; @@ -91,7 +103,7 @@ public: /// bind static function template class_& def_static(const char* name, Fn&& f, const Extra&... extra) { - impl::bind_function(*this, name, std::forward(f), pkpy::BindType::STATICMETHOD, extra...); + impl::bind_function(*this, name, std::forward(f), pkpy::BindType::STATICMETHOD, extra...); return *this; } @@ -163,6 +175,15 @@ public: template enum_(const handle& scope, const char* name, Args&&... args) : class_(scope, name, std::forward(args)...) { + + Base::def(init([](int value) { + return static_cast(value); + })); + + Base::def("__eq__", [](T& self, T& other) { + return self == other; + }); + Base::def_property_readonly("value", [](T& self) { return int_(static_cast>(self)); }); diff --git a/include/pybind11/internal/cpp_function.h b/include/pybind11/internal/cpp_function.h index f12e5b02..1c4ebd17 100644 --- a/include/pybind11/internal/cpp_function.h +++ b/include/pybind11/internal/cpp_function.h @@ -7,8 +7,37 @@ namespace pybind11 { // append the overload to the beginning of the overload list struct prepend {}; +namespace impl { + template -struct init {}; +struct constructor {}; + +template > +struct factory; + +template +struct factory> { + Fn fn; + + auto make() { + using Self = callable_return_t; + return [fn = std::move(fn)](Self* self, Args... args) { + new (self) Self(fn(args...)); + }; + } +}; + +} // namespace impl + +template +impl::constructor init() { + return {}; +} + +template +impl::factory init(Fn&& fn) { + return {std::forward(fn)}; +} // TODO: support more customized tags // @@ -256,6 +285,7 @@ struct template_parser, std::tuple, std constexpr auto named_argc = types_count_v; constexpr auto normal_argc = sizeof...(Args) - (arguments_info.args_pos != -1) - (arguments_info.kwargs_pos != -1); + static_assert(named_argc == 0 || named_argc == normal_argc, "named arguments must be the same as the number of function arguments"); @@ -419,24 +449,32 @@ inline auto _wrapper(pkpy::VM* vm, pkpy::ArgsView view) { return record(view).ptr(); } -template +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(); cpp_function callable = var->attr().try_get(name); + function_record* record = nullptr; - // if the function is not bound yet, bind it - if(!callable) { - auto record = function_record(std::forward(fn), extras...); - void* data = interpreter::take_ownership(std::move(record)); - callable = interpreter::bind_func(var, name, -1, _wrapper, data); + if constexpr(is_method && types_count_v > 0) { + // if the function is a method and has named arguments + // prepend self to the arguments list + record = new function_record(std::forward(fn), arg("self"), extras...); } else { - function_record* record = new function_record(std::forward(fn), extras...); + record = new function_record(std::forward(fn), extras...); + } + + if(!callable) { + // if the function is not bound yet, bind it + void* data = interpreter::take_ownership(std::move(*record)); + callable = interpreter::bind_func(var, name, -1, _wrapper, data, type); + } else { + // if the function is already bound, append the new record to the function function_record* last = callable.get_userdata_as(); if constexpr((types_count_v != 0)) { // if prepend is specified, append the new record to the beginning of the list - fn.set_userdata(record); + callable.set_userdata(record); record->append(last); } else { // otherwise, append the new record to the end of the list diff --git a/include/pybind11/internal/module.h b/include/pybind11/internal/module.h index 6eef55e2..d3cff054 100644 --- a/include/pybind11/internal/module.h +++ b/include/pybind11/internal/module.h @@ -29,7 +29,7 @@ public: template module_& def(const char* name, Fn&& fn, const Extras... extras) { - impl::bind_function(*this, name, std::forward(fn), pkpy::BindType::DEFAULT, extras...); + impl::bind_function(*this, name, std::forward(fn), pkpy::BindType::DEFAULT, extras...); return *this; } };