diff --git a/include/pybind11/embed.h b/include/pybind11/embed.h new file mode 100644 index 00000000..be8234ee --- /dev/null +++ b/include/pybind11/embed.h @@ -0,0 +1,2 @@ +#pragma once +#include "pybind11.h" diff --git a/include/pybind11/functional.h b/include/pybind11/functional.h new file mode 100644 index 00000000..bdfc5342 --- /dev/null +++ b/include/pybind11/functional.h @@ -0,0 +1,2 @@ +#pragma once +#include "pybind11.h" \ No newline at end of file diff --git a/include/pybind11/internal/accessor.h b/include/pybind11/internal/accessor.h new file mode 100644 index 00000000..6d100557 --- /dev/null +++ b/include/pybind11/internal/accessor.h @@ -0,0 +1,173 @@ +#pragma once +#include "builtins.h" + +namespace pybind11 { + +// implement iterator methods for interface +template +inline iterator interface::begin() const { + return handle(vm->py_iter(this->ptr())); +} + +template +inline iterator interface::end() const { + return iterator::sentinel(); +} + +template +inline str interface::package() const { + return handle(this->attr(pkpy::__package__)); +} + +template +inline str interface::name() const { + return handle(this->attr(pkpy::__name__)); +} + +template +inline str interface::repr() const { + return handle(str(vm->py_repr(this->ptr()))); +} + +template +class accessor : public interface> { + + using key_type = typename policy::key_type; + + handle m_obj; + mutable handle m_value; + key_type m_key; + + friend interface; + friend interface>; + friend tuple; + friend list; + friend dict; + + accessor(const handle& obj, key_type key) : m_obj(obj), m_value(), m_key(key) {} + +public: + pkpy::PyVar ptr() const { + if(!m_value) { m_value = policy::get(m_obj, m_key); } + return m_value.ptr(); + } + + template + accessor& operator= (Value&& value) && { + policy::set(m_obj, m_key, std::forward(value)); + return *this; + } + + template + accessor& operator= (Value&& value) & { + m_value = std::forward(value); + return *this; + } + + template + T cast() const { + return operator handle ().template cast(); + } + + operator handle () const { return ptr(); } +}; + +namespace policy { +struct attr { + using key_type = pkpy::StrName; + + static handle get(const handle& obj, pkpy::StrName key) { return vm->getattr(obj.ptr(), key); } + + static void set(const handle& obj, pkpy::StrName key, const handle& value) { + vm->setattr(obj.ptr(), key, value.ptr()); + } +}; + +struct item { + using key_type = handle; + + static handle get(const handle& obj, const handle& key) { + return vm->call(vm->py_op("getitem"), obj.ptr(), key.ptr()); + } + + static void set(const handle& obj, const handle& key, const handle& value) { + vm->call(vm->py_op("setitem"), obj.ptr(), key.ptr(), value.ptr()); + } +}; + +struct tuple { + using key_type = int; + + static handle get(const handle& obj, int key) { return obj._as()[key]; } + + static void set(const handle& obj, size_t key, const handle& value) { obj._as()[key] = value.ptr(); } +}; + +struct list { + using key_type = int; + + static handle get(const handle& obj, size_t key) { return obj._as()[key]; } + + static void set(const handle& obj, size_t key, const handle& value) { obj._as()[key] = value.ptr(); } +}; + +struct dict { + using key_type = handle; + + static handle get(const handle& obj, const handle& key) { return obj.cast().getitem(key); } + + static void set(const handle& obj, const handle& key, const handle& value) { + obj.cast().setitem(key, value); + } +}; + +} // namespace policy + +// implement other methods of interface + +template +inline attr_accessor interface::attr(pkpy::StrName key) const { + return attr_accessor(this->ptr(), key); +} + +template +inline attr_accessor interface::attr(const char* key) const { + return attr_accessor(this->ptr(), pkpy::StrName(key)); +} + +template +inline attr_accessor interface::attr(const handle& key) const { + return attr_accessor(this->ptr(), pkpy::StrName(key._as())); +} + +template +inline attr_accessor interface::doc() const { + return attr_accessor(this->ptr(), pkpy::StrName("__doc__")); +} + +template +inline item_accessor interface::operator[] (int index) const { + return item_accessor(this->ptr(), int_(index)); +} + +template +inline item_accessor interface::operator[] (const char* key) const { + return item_accessor(this->ptr(), str(key)); +} + +template +inline item_accessor interface::operator[] (const handle& key) const { + return item_accessor(this->ptr(), key); +} + +inline tuple_accessor tuple::operator[] (int i) const { return tuple_accessor(this->ptr(), i); } + +inline list_accessor list::operator[] (int i) const { return list_accessor(this->ptr(), i); } + +inline dict_accessor dict::operator[] (int index) const { return dict_accessor(this->ptr(), int_(index)); } + +inline dict_accessor dict::operator[] (std::string_view key) const { return dict_accessor(this->ptr(), str(key)); } + +inline dict_accessor dict::operator[] (const handle& key) const { return dict_accessor(this->ptr(), key); } + +} // namespace pybind11 diff --git a/include/pybind11/internal/builtins.h b/include/pybind11/internal/builtins.h new file mode 100644 index 00000000..aa616ca9 --- /dev/null +++ b/include/pybind11/internal/builtins.h @@ -0,0 +1,222 @@ +#pragma once +#include "types.h" +#include "type_traits.h" + +namespace pybind11 { + +inline object eval(std::string_view code, const handle& global = none{}, handle local = none{}) { + return vm->py_eval(code, global.ptr(), local.ptr()); +} + +inline void exec(std::string_view code, const handle& global = none{}, handle local = none{}) { + vm->py_exec(code, global.ptr(), local.ptr()); +} + +/// globas() in pkpy is immutable, your changes will not be reflected in the Python interpreter +inline dict globals() { + auto& proxy = eval("globals()")._as().attr(); + dict result; +#if PK_VERSION_MAJOR == 2 + proxy.apply( + [](pkpy::StrName key, pkpy::PyVar value, void* data) { + auto& dict = static_cast(data)->_as(); + auto key_ = pybind11::str(key.sv()).ptr(); + dict.set(vm, key_, value); + }, + &result); +#else + proxy.apply([&](pkpy::StrName key, pkpy::PyVar value) { + result.setitem(str(key.sv()), value); + }); +#endif + return result; +} + +// wrapper for builtin functions in Python +inline bool hasattr(const handle& obj, const handle& name) { + return vm->getattr(obj.ptr(), name._as(), false) != nullptr; +} + +inline bool hasattr(const handle& obj, const char* name) { return vm->getattr(obj.ptr(), name, false) != nullptr; } + +inline void delattr(const handle& obj, const handle& name) { vm->delattr(obj.ptr(), name._as()); } + +inline void delattr(const handle& obj, const char* name) { vm->delattr(obj.ptr(), name); } + +inline object getattr(const handle& obj, const handle& name) { return vm->getattr(obj.ptr(), name._as()); } + +inline object getattr(const handle& obj, const char* name) { return vm->getattr(obj.ptr(), name); } + +inline object getattr(const handle& obj, const handle& name, const handle& default_) { + auto attr = vm->getattr(obj.ptr(), name._as(), false); + if(attr) { return attr; } + return default_; +} + +inline object getattr(const handle& obj, const char* name, const handle& default_) { + auto attr = vm->getattr(obj.ptr(), name, false); + if(attr) { return attr; } + return default_; +} + +inline void setattr(const handle& obj, const handle& name, const handle& value) { + vm->setattr(obj.ptr(), name._as(), value.ptr()); +} + +inline void setattr(const handle& obj, const char* name, const handle& value) { + vm->setattr(obj.ptr(), name, value.ptr()); +} + +template +inline bool isinstance(const handle& obj) { + return type_visitor::check(obj); +} + +template <> +inline bool isinstance(const handle&) = delete; + +inline bool isinstance(const handle& obj, const handle& type) { + return vm->isinstance(obj.ptr(), type._as()); +} + +inline int64_t hash(const handle& obj) { return vm->py_hash(obj.ptr()); } + +template +struct type_caster; + +template +handle cast(T&& value, return_value_policy policy, handle parent) { + // decay_t can resolve c-array type, but remove_cv_ref_t can't. + using underlying_type = std::decay_t; + + if constexpr(std::is_convertible_v) { + return std::forward(value); + } else { + static_assert(!is_multiple_pointer_v, "multiple pointer is not supported."); + static_assert(!std::is_void_v>, + "void* is not supported, consider using py::capsule."); + + // resolve for automatic policy. + if(policy == return_value_policy::automatic) { + policy = std::is_pointer_v ? return_value_policy::take_ownership + : std::is_lvalue_reference_v ? return_value_policy::copy + : return_value_policy::move; + } else if(policy == return_value_policy::automatic_reference) { + policy = std::is_pointer_v ? return_value_policy::reference + : std::is_lvalue_reference_v ? return_value_policy::copy + : return_value_policy::move; + } + + return type_caster::cast(std::forward(value), policy, parent); + } +} + +template +T cast(const handle& obj, bool convert) { + assert(obj.ptr() != nullptr); + + type_caster caster = {}; + + if(caster.load(obj, convert)) { + return caster.value; + } else { + std::string msg = "cast python instance to c++ failed, "; + msg += "obj type is: {"; + msg += type::of(obj).name(); + msg += "}, target type is: {"; + msg += type_name(); + msg += "}."; + vm->TypeError(msg); + PK_UNREACHABLE(); + } +} + +struct kwargs_proxy { + handle value; +}; + +struct args_proxy { + handle value; + + kwargs_proxy operator* () { return kwargs_proxy{value}; } +}; + +template +args_proxy interface::operator* () const { + return args_proxy{ptr()}; +} + +template +handle interpreter::vectorcall(const handle& callable, const handle& self, const Args&... args) { + vm->s_data.push(callable.ptr()); + +#if PK_VERSION_MAJOR == 2 + vm->s_data.push(self ? self.ptr() : PY_NULL); +#else + vm->s_data.push(self ? self.ptr() : pkpy::PY_NULL); +#endif + + int argc = 0; + int kwargsc = 0; + + auto push_arg = [&](const handle& value) { + assert(value); + vm->s_data.push(value.ptr()); + argc++; + }; + + auto push_named_arg = [&](std::string_view name, const handle& value) { + assert(value); + vm->s_data.push(int_(pkpy::StrName(name).index).ptr()); + vm->s_data.push(value.ptr()); + kwargsc++; + }; + + auto foreach_ = [&](const auto& arg) { + using T = std::decay_t; + if constexpr(std::is_convertible_v) { + push_arg(arg); + } else if constexpr(std::is_same_v) { + pybind11::tuple args = arg.value.template cast(); + for(auto item: args) { + push_arg(item); + } + } else if constexpr(std::is_same_v) { + push_named_arg(arg.name, arg.default_); + } else if constexpr(std::is_same_v) { + pybind11::dict kwargs = arg.value.template cast(); + for(auto item: kwargs) { + str name = item.first.template cast(); + push_named_arg(name, item.second); + } + } else { + static_assert(dependent_false, "unsupported type"); + } + }; + (foreach_(args), ...); + + return vm->vectorcall(argc, kwargsc); +} + +template +template +inline object interface::operator() (Args&&... args) const { + auto _cast = [&](auto&& arg) { + using T = std::decay_t; + if constexpr(std::is_same_v || std::is_same_v || + std::is_same_v || std::is_convertible_v) { + return arg; + } else { + return pybind11::cast(std::forward(arg), policy); + } + }; + return interpreter::vectorcall(ptr(), handle(), _cast(std::forward(args))...); +} + +template +void print(Args&&... args) { + handle print = getattr(vm->builtins, "print"); + print(std::forward(args)...); +} + +} // namespace pybind11 diff --git a/include/pybind11/internal/cast.h b/include/pybind11/internal/cast.h new file mode 100644 index 00000000..acc0897a --- /dev/null +++ b/include/pybind11/internal/cast.h @@ -0,0 +1,174 @@ +#pragma once +#include "instance.h" +#include "accessor.h" + +namespace pybind11 { + +using pkpy::is_floating_point_v; +using pkpy::is_integral_v; + +template +constexpr inline bool is_string_v = + std::is_same_v || std::is_same_v || std::is_same_v; + +template +constexpr bool is_pointer_v = std::is_pointer_v && !std::is_same_v && !std::is_same_v; + +template +struct type_caster; + +template <> +struct type_caster { + bool value; + + bool load(const handle& src, bool) { + if(isinstance(src)) { + value = pkpy::_py_cast(vm, src.ptr()); + return true; + } + + return false; + } + + static handle cast(bool src, return_value_policy, handle) { return src ? vm->True : vm->False; } +}; + +template +struct type_caster>> { + T value; + + bool load(const handle& src, bool convert) { + if(isinstance(src)) { + value = pkpy::_py_cast(vm, src.ptr()); + return true; + } + + return false; + } + + static handle cast(T src, return_value_policy, handle) { return pkpy::py_var(vm, src); } +}; + +template +struct type_caster>> { + T value; + + bool load(const handle& src, bool convert) { + if(isinstance(src)) { + value = pkpy::_py_cast(vm, src.ptr()); + return true; + } + + if(convert && isinstance(src)) { + value = pkpy::_py_cast(vm, src.ptr()); + return true; + } + + return false; + } + + static handle cast(T src, return_value_policy, handle) { return pkpy::py_var(vm, src); } +}; + +template +struct type_caster>> { + T value; + + bool load(const handle& src, bool) { + if(isinstance(src)) { + auto& str = src._as(); + if constexpr(std::is_same_v) { + value = str; + } else if constexpr(std::is_same_v) { + value = str; + } else if constexpr(std::is_same_v) { + value = str.c_str(); + } + return true; + } + + return false; + } + + template + static handle cast(U&& src, return_value_policy, handle) { + return str(std::forward(src)); + } +}; + +template +struct type_caster>> { + T value; + + bool load(const handle& src, bool) { + if(isinstance(src)) { + value = src; + return true; + } + + return false; + } + + static handle cast(const handle& src, return_value_policy, handle) { return src; } +}; + +template +struct type_caster { + struct value_wrapper { + T* pointer; + + operator T& () { return *pointer; } + }; + + value_wrapper value; + + using underlying_type = std::remove_pointer_t; + + bool load(handle src, bool convert) { + if(isinstance(src)) { + auto& i = src._as(); + value.pointer = &i._as(); + return true; + } + + return false; + } + + template + static handle cast(U&& value, return_value_policy policy, const handle& parent = handle()) { + const auto& info = typeid(underlying_type); + auto type = type_visitor::type(); + return instance::create(std::forward(value), type, policy, parent.ptr()); + // TODO: support implicit cast + } +}; + +template +struct type_caster || std::is_reference_v>> { + using underlying = + std::remove_cv_t, std::remove_pointer_t, std::remove_reference_t>>; + + struct wrapper { + type_caster caster; + + operator T () { + if constexpr(std::is_pointer_v) { + return caster.value.pointer; + } else { + return caster.value; + } + } + }; + + wrapper value; + + bool load(const handle& src, bool convert) { return value.caster.load(src, convert); } + + template + static handle cast(U&& value, return_value_policy policy, const handle& parent) { + return type_caster::cast(std::forward(value), policy, parent); + } +}; + +} // namespace pybind11 + diff --git a/include/pybind11/internal/class.h b/include/pybind11/internal/class.h new file mode 100644 index 00000000..70d850d4 --- /dev/null +++ b/include/pybind11/internal/class.h @@ -0,0 +1,186 @@ +#pragma once + +#include "module.h" +#include + +namespace pybind11 { + +struct dynamic_attr {}; + +template +class class_ : public type { +protected: + handle m_scope; + +public: + using type::type; + using underlying_type = T; + + template + class_(const handle& scope, const char* name, const Args&... args) : + m_scope(scope), type(type_visitor::create(scope, name)) { + auto& info = type_info::of(); + info.name = name; + + // bind __new__ + interpreter::bind_func(m_ptr, pkpy::__new__, -1, [](pkpy::VM* vm, pkpy::ArgsView args) { + auto cls = handle(args[0])._as(); + + // check if the class has constructor, if not, raise error + if(vm->find_name_in_mro(cls, pkpy::__init__) == nullptr) { + vm->RuntimeError("if you want to create instance of bound class, you must bind constructor for it"); + } + + auto var = instance::create(cls, &type_info::of()); + + if constexpr(types_count_v != 0) { +#if PK_VERSION_MAJOR == 2 + var.get()->_attr = new pkpy::NameDict(); +#else + var->_enable_instance_dict(); +#endif + } + + return var; + }); + } + + /// bind constructor + template + class_& def(init, const Extra&... extra) { + if constexpr(!std::is_constructible_v) { + static_assert(std::is_constructible_v, "Invalid constructor arguments"); + } else { + impl::bind_function( + *this, + "__init__", + [](T* self, Args... args) { + new (self) T(args...); + }, + 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>; + + 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...); + } + + return *this; + } + + /// bind operators + template + class_& def(Operator op, const Extras&... extras) { + op.execute(*this, extras...); + return *this; + } + + // TODO: factory function + + /// 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...); + return *this; + } + + template + class_& def_readwrite(const char* name, MP mp, const Extras&... extras) { + if constexpr(!std::is_member_object_pointer_v) { + static_assert(std::is_member_object_pointer_v, "def_readwrite only supports pointer to data member"); + } else { + impl::bind_property(*this, name, mp, mp, extras...); + } + return *this; + } + + template + class_& def_readonly(const char* name, MP mp, const Extras&... extras) { + if constexpr(!std::is_member_object_pointer_v) { + static_assert(std::is_member_object_pointer_v, "def_readonly only supports pointer to data member"); + } else { + impl::bind_property(*this, name, mp, nullptr, extras...); + } + return *this; + } + + template + class_& def_property(const char* name, Getter&& g, Setter&& s, const Extras&... extras) { + impl::bind_property(*this, name, std::forward(g), std::forward(s), extras...); + return *this; + } + + template + class_& def_property_readonly(const char* name, Getter&& mp, const Extras&... extras) { + impl::bind_property(*this, name, std::forward(mp), nullptr, extras...); + return *this; + } + + template + class_& def_readwrite_static(const char* name, Var& mp, const Extras&... extras) { + static_assert( + dependent_false, + "define static properties requires metaclass. This is a complex feature with few use cases, so it may never be implemented."); + return *this; + } + + template + class_& def_readonly_static(const char* name, Var& mp, const Extras&... extras) { + static_assert( + dependent_false, + "define static properties requires metaclass. This is a complex feature with few use cases, so it may never be implemented."); + return *this; + } + + template + class_& def_property_static(const char* name, Getter&& g, Setter&& s, const Extras&... extras) { + static_assert( + dependent_false, + "define static properties requires metaclass. This is a complex feature with few use cases, so it may never be implemented."); + return *this; + } +}; + +template +class enum_ : public class_ { + std::vector> m_values; + +public: + using Base = class_; + using class_::class_; + + template + enum_(const handle& scope, const char* name, Args&&... args) : + class_(scope, name, std::forward(args)...) { + Base::def_property_readonly("value", [](T& self) { + return int_(static_cast>(self)); + }); + } + + enum_& value(const char* name, T value) { + handle var = type_caster::cast(value, return_value_policy::copy); + this->m_ptr->attr().set(name, var.ptr()); + m_values.emplace_back(name, var); + return *this; + } + + enum_& export_values() { + for(auto& [name, value]: m_values) { + Base::m_scope.ptr()->attr().set(name, value.ptr()); + } + return *this; + } +}; +} // namespace pybind11 + diff --git a/include/pybind11/internal/cpp_function.h b/include/pybind11/internal/cpp_function.h new file mode 100644 index 00000000..fa7a4364 --- /dev/null +++ b/include/pybind11/internal/cpp_function.h @@ -0,0 +1,551 @@ +#pragma once +#include "cast.h" +#include + +namespace pybind11 { + +// append the overload to the beginning of the overload list +struct prepend {}; + +template +struct init {}; + +// TODO: support more customized tags +// +// template +// struct keep_alive {}; +// +// template +// struct call_guard { +// static_assert(std::is_default_constructible_v, "call_guard must be default constructible"); +// }; +// +// struct kw_only {}; +// +// struct pos_only {}; + +class cpp_function : public function { + PYBIND11_TYPE_IMPLEMENT(function, pkpy::NativeFunc, vm->tp_native_func); + +public: + template + cpp_function(Fn&& f, const Extras&... extras) {} + + template + decltype(auto) get_userdata_as() { +#if PK_VERSION_MAJOR == 2 + return self()._userdata.as(); +#else + return self()._userdata._cast(); +#endif + } + + template + void set_userdata(T&& value) { + self()._userdata = std::forward(value); + } +}; + +} // namespace pybind11 + +namespace pybind11::impl { + +template , + typename IndexSequence = std::make_index_sequence>> +struct template_parser; + +class function_record { +private: + template + friend struct template_parser; + + struct arguments_t { + std::vector names; + std::vector defaults; + }; + + using destructor_t = void (*)(function_record*); + using wrapper_t = handle (*)(function_record&, pkpy::ArgsView, bool convert, handle parent); + + static_assert(std::is_trivially_copyable_v); + +private: + union { + void* data; + char buffer[16]; + }; + + wrapper_t wrapper = nullptr; + function_record* next = nullptr; + arguments_t* arguments = nullptr; + destructor_t destructor = nullptr; + const char* signature = nullptr; + return_value_policy policy = return_value_policy::automatic; + +public: + template + function_record(Fn&& f, const Extras&... extras) { + using Callable = std::decay_t; + + if constexpr(std::is_trivially_copyable_v && sizeof(Callable) <= sizeof(buffer)) { + // if the callable object is trivially copyable and the size is less than 16 bytes, store it in the + // buffer + new (buffer) auto(std::forward(f)); + destructor = [](function_record* self) { + reinterpret_cast(self->buffer)->~Callable(); + }; + } else { + // otherwise, store it in the heap + data = new auto(std::forward(f)); + destructor = [](function_record* self) { + delete static_cast(self->data); + }; + } + + using Parser = template_parser>; + Parser::initialize(*this, extras...); + wrapper = Parser::wrapper; + } + + function_record(const function_record&) = delete; + function_record& operator= (const function_record&) = delete; + function_record& operator= (function_record&&) = delete; + + function_record(function_record&& other) noexcept { + std::memcpy(this, &other, sizeof(function_record)); + std::memset(&other, 0, sizeof(function_record)); + } + + ~function_record() { + if(destructor) { destructor(this); } + if(arguments) { delete arguments; } + if(next) { delete next; } + if(signature) { delete[] signature; } + } + + void append(function_record* record) { + function_record* p = this; + while(p->next) { + p = p->next; + } + p->next = record; + } + + template + T& _as() { + if constexpr(std::is_trivially_copyable_v && sizeof(T) <= sizeof(buffer)) { + return *reinterpret_cast(buffer); + } else { + return *static_cast(data); + } + } + + 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(*p, 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(*p, view, true, {}); + if(result) { return result; } + p = p->next; + } + + std::string msg = "no matching function found, function signature:\n"; + std::size_t index = 0; + p = this; + while(p != nullptr) { + msg += " "; + msg += p->signature; + msg += "\n"; + p = p->next; + } + vm->TypeError(msg); + PK_UNREACHABLE(); + } +}; + +template +handle invoke(Fn&& fn, + std::index_sequence, + std::tuple...>& casters, + return_value_policy policy, + handle parent) { + using underlying_type = std::decay_t; + using return_type = callable_return_t; + + constexpr bool is_void = std::is_void_v; + constexpr bool is_member_function_pointer = std::is_member_function_pointer_v; + + if constexpr(is_member_function_pointer) { + // helper function to unpack the arguments to call the member pointer + auto unpack = [&](class_type_t& self, auto&... args) { + return (self.*fn)(args...); + }; + + if constexpr(!is_void) { + return pybind11::cast(unpack(std::get(casters).value...), policy, parent); + } else { + unpack(std::get(casters).value...); + return vm->None; + } + } else { + if constexpr(!is_void) { + return pybind11::cast(fn(std::get(casters).value...), policy, parent); + } else { + fn(std::get(casters).value...); + return vm->None; + } + } +} + +struct arguments_info_t { + int argc = 0; + int args_pos = -1; + int kwargs_pos = -1; +}; + +struct extras_info_t { + int doc_pos = -1; + int named_argc = 0; + int policy_pos = -1; +}; + +template +struct template_parser, std::tuple, std::index_sequence> { + constexpr static arguments_info_t parse_arguments() { + constexpr auto args_count = types_count_v; + constexpr auto kwargs_count = types_count_v; + + static_assert(args_count <= 1, "py::args can occur at most once"); + static_assert(kwargs_count <= 1, "py::kwargs can occur at most once"); + + constexpr auto args_pos = type_index_v; + constexpr auto kwargs_pos = type_index_v; + + if constexpr(kwargs_count == 1) { + static_assert(kwargs_pos == sizeof...(Args) - 1, "py::kwargs must be the last argument"); + + // FIXME: temporarily, args and kwargs must be at the end of the arguments list + if constexpr(args_count == 1) { + static_assert(args_pos == kwargs_pos - 1, "py::args must be before py::kwargs"); + } + } + + return {sizeof...(Args), args_pos, kwargs_pos}; + } + + constexpr static extras_info_t parse_extras() { + constexpr auto doc_count = types_count_v; + constexpr auto policy_count = types_count_v; + + static_assert(doc_count <= 1, "doc can occur at most once"); + static_assert(policy_count <= 1, "return_value_policy can occur at most once"); + + constexpr auto doc_pos = type_index_v; + constexpr auto policy_pos = type_index_v; + + 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"); + + return {doc_pos, named_argc, policy_pos}; + } + + constexpr inline static auto arguments_info = parse_arguments(); + constexpr inline static auto extras_info = parse_extras(); + + static void initialize(function_record& record, const Extras&... extras) { + auto extras_tuple = std::make_tuple(extras...); + constexpr static bool has_named_args = (extras_info.named_argc > 0); + // set return value policy + if constexpr(extras_info.policy_pos != -1) { record.policy = std::get(extras_tuple); } + + // TODO: set others + + // set default arguments + if constexpr(has_named_args) { + record.arguments = new function_record::arguments_t(); + + auto add_arguments = [&](const auto& arg) { + if constexpr(std::is_same_v>) { + auto& arguments = *record.arguments; + arguments.names.emplace_back(arg.name); + arguments.defaults.emplace_back(arg.default_); + } + }; + + (add_arguments(extras), ...); + } + + // set signature + { + std::string sig = "("; + std::size_t index = 0; + auto append = [&](auto _t) { + using T = pybind11_decay_t; + if constexpr(std::is_same_v) { + sig += "*args"; + } else if constexpr(std::is_same_v) { + sig += "**kwargs"; + } else if constexpr(has_named_args) { + sig += record.arguments->names[index].c_str(); + sig += ": "; + sig += type_info::of().name; + if(record.arguments->defaults[index]) { + sig += " = "; + sig += record.arguments->defaults[index].repr(); + } + } else { + sig += "_: "; + sig += type_info::of().name; + } + + if(index + 1 < arguments_info.argc) { sig += ", "; } + index++; + }; + (append(type_identity{}), ...); + sig += ")"; + char* buffer = new char[sig.size() + 1]; + std::memcpy(buffer, sig.data(), sig.size()); + buffer[sig.size()] = '\0'; + record.signature = buffer; + } + } + + static handle wrapper(function_record& record, pkpy::ArgsView view, bool convert, handle parent) { + constexpr auto argc = arguments_info.argc; + constexpr auto named_argc = extras_info.named_argc; + constexpr auto args_pos = arguments_info.args_pos; + constexpr auto kwargs_pos = arguments_info.kwargs_pos; + constexpr auto normal_argc = argc - (args_pos != -1) - (kwargs_pos != -1); + + // avoid gc call in bound function + vm->heap.gc_scope_lock(); + + // add 1 to avoid zero-size array when argc is 0 + handle stack[argc + 1] = {}; + + // ensure the number of passed arguments is no greater than the number of parameters + if(args_pos == -1 && view.size() > normal_argc) { return handle(); } + + // if have default arguments, load them + if constexpr(named_argc > 0) { + auto& defaults = record.arguments->defaults; + std::memcpy(stack, defaults.data(), defaults.size() * sizeof(handle)); + } + + // load arguments from call arguments + const auto size = std::min(view.size(), normal_argc); + std::memcpy(stack, view.begin(), size * sizeof(handle)); + + // pack the args + if constexpr(args_pos != -1) { + const auto n = std::max(view.size() - normal_argc, 0); + tuple args = tuple(n); + for(std::size_t i = 0; i < n; ++i) { + args[i] = view[normal_argc + i]; + } + stack[args_pos] = args; + } + + // resolve keyword arguments + const auto n = vm->s_data._sp - view.end(); + std::size_t index = 0; + + if constexpr(named_argc > 0) { + std::size_t arg_index = 0; + auto& arguments = *record.arguments; + + while(arg_index < named_argc && index < n) { + const auto key = pkpy::_py_cast(vm, view.end()[index]); + const auto value = view.end()[index + 1]; + const auto name = pkpy::StrName(key); + auto& arg_name = record.arguments->names[arg_index]; + + if(name == arg_name) { + stack[arg_index] = value; + index += 2; + } + + arg_index += 1; + } + } + + // pack the kwargs + if constexpr(kwargs_pos != -1) { + dict kwargs; + while(index < n) { + const auto key = pkpy::_py_cast(vm, view.end()[index]); + const str name = str(pkpy::StrName(key).sv()); + kwargs[name] = view.end()[index + 1]; + index += 2; + } + stack[kwargs_pos] = kwargs; + } + + // if have rest keyword arguments, call fails + if(index != n) { return handle(); } + + // check if all the arguments are valid + for(std::size_t i = 0; i < argc; ++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(record._as(), std::index_sequence{}, casters, record.policy, parent); + } + + return handle(); + } +}; + +inline auto _wrapper(pkpy::VM* vm, pkpy::ArgsView view) { + auto&& record = unpack(view); + return record(view).ptr(); +} + +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); + + // 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); + } else { + function_record* record = new function_record(std::forward(fn), extras...); + 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); + record->append(last); + } else { + // otherwise, append the new record to the end of the list + last->append(record); + } + } + + return callable; +} + +} // namespace pybind11::impl + +namespace pybind11::impl { + +template +pkpy::PyVar getter_wrapper(pkpy::VM* vm, pkpy::ArgsView view) { + handle result = vm->None; + auto&& getter = unpack(view); + constexpr auto policy = return_value_policy::reference_internal; + + if constexpr(std::is_member_pointer_v) { + using Self = class_type_t; + auto& self = handle(view[0])._as()._as(); + + if constexpr(std::is_member_object_pointer_v) { + // specialize for pointer to data member + result = cast(self.*getter, policy, view[0]); + } else { + // specialize for pointer to member function + result = cast((self.*getter)(), policy, view[0]); + } + } else { + // specialize for function pointer and lambda + using Self = remove_cvref_t>>; + auto& self = handle(view[0])._as()._as(); + + result = cast(getter(self), policy, view[0]); + } + + return result.ptr(); +} + +template +pkpy::PyVar setter_wrapper(pkpy::VM* vm, pkpy::ArgsView view) { + auto&& setter = unpack(view); + + if constexpr(std::is_member_pointer_v) { + using Self = class_type_t; + auto& self = handle(view[0])._as()._as(); + + if constexpr(std::is_member_object_pointer_v) { + // specialize for pointer to data member + type_caster> caster; + if(caster.load(view[1], true)) { + self.*setter = caster.value; + return vm->None; + } + } else { + // specialize for pointer to member function + type_caster>> caster; + if(caster.load(view[1], true)) { + (self.*setter)(caster.value); + return vm->None; + } + } + } else { + // specialize for function pointer and lambda + using Self = remove_cvref_t>>; + auto& self = handle(view[0])._as()._as(); + + type_caster>> caster; + if(caster.load(view[1], true)) { + setter(self, caster.value); + return vm->None; + } + } + + vm->TypeError("Unexpected argument type"); + PK_UNREACHABLE(); +} + +template +handle bind_property(const handle& obj, const char* name, Getter&& getter_, Setter&& setter_, const Extras&... extras) { + handle getter = none(); + handle setter = none(); + using Wrapper = pkpy::PyVar (*)(pkpy::VM*, pkpy::ArgsView); + + constexpr auto create = [](Wrapper wrapper, int argc, auto&& f) { + if constexpr(need_host>) { + // otherwise, store it in the type_info + void* data = interpreter::take_ownership(std::forward(f)); + // store the index in the object + return vm->heap.gcnew(vm->tp_native_func, wrapper, argc, data); + } else { + // if the function is trivially copyable and the size is less than 16 bytes, store it in the object + // directly + return vm->heap.gcnew(vm->tp_native_func, wrapper, argc, f); + } + }; + + getter = create(impl::getter_wrapper>, 1, std::forward(getter_)); + + if constexpr(!std::is_same_v) { + setter = create(impl::setter_wrapper>, 2, std::forward(setter_)); + } + + handle property = pybind11::property(getter, setter); + setattr(obj, name, property); + return property; +} + +} // namespace pybind11::impl diff --git a/include/pybind11/internal/instance.h b/include/pybind11/internal/instance.h new file mode 100644 index 00000000..03beeca1 --- /dev/null +++ b/include/pybind11/internal/instance.h @@ -0,0 +1,142 @@ +#pragma once +#include "kernel.h" + +namespace pybind11 { + +struct type_info { + std::string_view name; + std::size_t size; + std::size_t alignment; + void (*destructor)(void*); + const std::type_info* type; + + template + static type_info& of() { + static_assert(!std::is_reference_v && !std::is_const_v>, + "T must not be a reference type or const type."); + static type_info info = { + type_name(), + sizeof(T), + alignof(T), + [](void* ptr) { + ((T*)ptr)->~T(); + operator delete (ptr); + }, + &typeid(T), + }; + return info; + } +}; + +// all registered C++ class will be ensured as instance type. +class instance { +public: + // use to record the type information of C++ class. + +private: + enum Flag { + None = 0, + Own = 1 << 0, // if the instance is owned by C++ side. + Ref = 1 << 1, // need to mark the parent object. + }; + + Flag flag; + void* data; + const type_info* type; + pkpy::PyVar parent; + +public: + instance() noexcept : flag(Flag::None), data(nullptr), type(nullptr), parent(nullptr) {} + + instance(const instance&) = delete; + + instance(instance&& other) noexcept : flag(other.flag), data(other.data), type(other.type), parent(other.parent) { + other.flag = Flag::None; + other.data = nullptr; + other.type = nullptr; + other.parent = nullptr; + } + + static pkpy::PyVar create(pkpy::Type type, const type_info* info) noexcept { + instance instance; + instance.type = info; + instance.data = operator new (info->size); + instance.flag = Flag::Own; + return vm->heap.gcnew(type, std::move(instance)); + } + + template + static pkpy::PyVar create(T&& value, + pkpy::Type type, + return_value_policy policy = return_value_policy::automatic_reference, + pkpy::PyVar parent = nullptr) noexcept { + using underlying_type = remove_cvref_t; + + auto& _value = [&]() -> auto& { + // note that, pybind11 will ignore the const qualifier. + // in fact, try to modify a const value will result in undefined behavior. + if constexpr(std::is_pointer_v) { + return *reinterpret_cast(value); + } else { + return const_cast(value); + } + }(); + + using primary = std::remove_pointer_t; + instance instance; + instance.type = &type_info::of(); + + if(policy == return_value_policy::take_ownership) { + instance.data = &_value; + instance.flag = Flag::Own; + } else if(policy == return_value_policy::copy) { + if constexpr(std::is_copy_constructible_v) { + instance.data = ::new auto(_value); + instance.flag = Flag::Own; + } else { + std::string msg = "cannot use copy policy on non-copyable type: "; + msg += type_name(); + vm->RuntimeError(msg); + } + } else if(policy == return_value_policy::move) { + if constexpr(std::is_move_constructible_v) { + instance.data = ::new auto(std::move(_value)); + instance.flag = Flag::Own; + } else { + std::string msg = "cannot use move policy on non-moveable type: "; + msg += type_name(); + vm->RuntimeError(msg); + } + } else if(policy == return_value_policy::reference) { + instance.data = &_value; + instance.flag = Flag::None; + } else if(policy == return_value_policy::reference_internal) { + instance.data = &_value; + instance.flag = Flag::Ref; + instance.parent = parent; + } + + return vm->heap.gcnew(type, std::move(instance)); + } + + ~instance() { + if(flag & Flag::Own) { type->destructor(data); } + } + + template + T& _as() noexcept { + return *static_cast(data); + } + +#if PK_VERSION_MAJOR == 2 + void _gc_mark(pkpy::VM* vm) const noexcept { + if(parent && (flag & Flag::Ref)) { PK_OBJ_MARK(parent); } + } +#else + void _gc_mark() const noexcept { + if(parent && (flag & Flag::Ref)) { PK_OBJ_MARK(parent); } + } +#endif +}; + +} // namespace pybind11 diff --git a/include/pybind11/internal/kernel.h b/include/pybind11/internal/kernel.h new file mode 100644 index 00000000..bb117712 --- /dev/null +++ b/include/pybind11/internal/kernel.h @@ -0,0 +1,224 @@ +#pragma once +#include +#include +#include + +#include "type_traits.h" + +namespace pybind11::impl { +struct capsule { + void* ptr; + void (*destructor)(void*); + + template + capsule(T&& value) : + ptr(new auto(std::forward(value))), destructor([](void* ptr) { + delete static_cast*>(ptr); + }) {} + + capsule(void* ptr, void (*destructor)(void*)) : ptr(ptr), destructor(destructor) {} + + capsule(const capsule&) = delete; + + capsule(capsule&& other) noexcept : ptr(other.ptr), destructor(other.destructor) { + other.ptr = nullptr; + other.destructor = nullptr; + } + + ~capsule() { + if(ptr != nullptr && destructor != nullptr) destructor(ptr); + } +}; +} // namespace pybind11::impl + +namespace pybind11 { + +class handle; +class object; +class iterator; +class str; +class arg; +struct args_proxy; +struct kwargs_proxy; + +inline pkpy::VM* vm = nullptr; + +class interpreter { + inline static std::vector* _capsules = nullptr; + inline static std::vector* _init = nullptr; + +public: + inline static void initialize(bool enable_os = true) { + if(vm == nullptr) { + vm = new pkpy::VM(); + if(_init != nullptr) { + for(auto& fn: *_init) + fn(); + } + } + } + + inline static void finalize() { + if(_capsules != nullptr) { + delete _capsules; + _capsules = nullptr; + } + + if(vm != nullptr) { + delete vm; + vm = nullptr; + } + } + + template + inline static void* take_ownership(T&& value) { + if(_capsules == nullptr) _capsules = new std::vector(); + _capsules->emplace_back(std::forward(value)); + return _capsules->back().ptr; + } + + inline static void register_init(void (*init)()) { + if(_init == nullptr) _init = new std::vector(); + _init->push_back(init); + } + + inline static pkpy::PyVar bind_func(pkpy::PyVar scope, + pkpy::StrName name, + int argc, + pkpy::NativeFuncC fn, + pkpy::any any = {}, + pkpy::BindType type = pkpy::BindType::DEFAULT) { +#if PK_VERSION_MAJOR == 2 + return vm->bind_func(scope.get(), name, argc, fn, any, type); +#else + return vm->bind_func(scope, name, argc, fn, std::move(any), type); +#endif + } + + template + inline static handle vectorcall(const handle& self, const handle& func, const Args&... args); +}; + +template +constexpr inline bool need_host = !(std::is_trivially_copyable_v && (sizeof(T) <= 8)); + +template +decltype(auto) unpack(pkpy::ArgsView view) { + if constexpr(need_host) { + void* data = pkpy::lambda_get_userdata(view.begin()); + return *static_cast(data); + } else { + return pkpy::lambda_get_userdata(view.begin()); + } +} + +template +class accessor; + +namespace policy { +struct attr; +struct item; +struct tuple; +struct list; +struct dict; +} // namespace policy + +using attr_accessor = accessor; +using item_accessor = accessor; +using tuple_accessor = accessor; +using list_accessor = accessor; +using dict_accessor = accessor; + +template +T cast(const handle& obj, bool convert = false); + +enum class return_value_policy : uint8_t { + /** + * This is the default return value policy, which falls back to the policy + * return_value_policy::take_ownership when the return value is a pointer. + * Otherwise, it uses return_value::move or return_value::copy for rvalue + * and lvalue references, respectively. See below for a description of what + * all of these different policies do. + */ + automatic = 0, + + /** + * As above, but use policy return_value_policy::reference when the return + * value is a pointer. This is the default conversion policy for function + * arguments when calling Python functions manually from C++ code (i.e. via + * handle::operator()). You probably won't need to use this. + */ + automatic_reference, + + /** + * Reference an existing object (i.e. do not create a new copy) and take + * ownership. Python will call the destructor and delete operator when the + * object's reference count reaches zero. Undefined behavior ensues when + * the C++ side does the same.. + */ + take_ownership, + + /** + * Create a new copy of the returned object, which will be owned by + * Python. This policy is comparably safe because the lifetimes of the two + * instances are decoupled. + */ + copy, + + /** + * Use std::move to move the return value contents into a new instance + * that will be owned by Python. This policy is comparably safe because the + * lifetimes of the two instances (move source and destination) are + * decoupled. + */ + move, + + /** + * Reference an existing object, but do not take ownership. The C++ side + * is responsible for managing the object's lifetime and deallocating it + * when it is no longer used. Warning: undefined behavior will ensue when + * the C++ side deletes an object that is still referenced and used by + * Python. + */ + reference, + + /** + * This policy only applies to methods and properties. It references the + * object without taking ownership similar to the above + * return_value_policy::reference policy. In contrast to that policy, the + * function or property's implicit this argument (called the parent) is + * considered to be the the owner of the return value (the child). + * pybind11 then couples the lifetime of the parent to the child via a + * reference relationship that ensures that the parent cannot be garbage + * collected while Python is still using the child. More advanced + * variations of this scheme are also possible using combinations of + * return_value_policy::reference and the keep_alive call policy + */ + reference_internal +}; + +struct empty {}; + +template +void print(Args&&... args); + +class object; + +template +constexpr inline bool is_pyobject_v = std::is_base_of_v; + +#if PK_VERSION_MAJOR == 2 +using error_already_set = pkpy::TopLevelException; +#else +class error_already_set : std::exception { +public: + error_already_set() = default; + + const char* what() const noexcept override { return "An error occurred while calling a Python function."; } +}; +#endif + +inline void setattr(const handle& obj, const handle& name, const handle& value); +inline void setattr(const handle& obj, const char* name, const handle& value); + +} // namespace pybind11 diff --git a/include/pybind11/internal/module.h b/include/pybind11/internal/module.h new file mode 100644 index 00000000..420b11fc --- /dev/null +++ b/include/pybind11/internal/module.h @@ -0,0 +1,48 @@ +#pragma once +#include "cpp_function.h" + +namespace pybind11 { + +class module_ : public object { + +public: + using object::object; + + static module_ __main__() { return vm->_main; } + + static module_ import(const char* name) { + if(name == std::string_view{"__main__"}) { + return vm->_main; + } else { + return vm->py_import(name, false); + } + } + + module_ def_submodule(const char* name, const char* doc = nullptr) { + auto package = this->package()._as() + "." + this->name()._as(); + auto m = vm->new_module(name, package); + setattr(*this, name, m); + return m; + } + + template + module_& def(const char* name, Fn&& fn, const Extras... extras) { + impl::bind_function(*this, name, std::forward(fn), pkpy::BindType::DEFAULT, extras...); + return *this; + } +}; + +#define PYBIND11_EMBEDDED_MODULE(name, variable) \ + static void _pybind11_register_##name(pybind11::module_& variable); \ + namespace pybind11::impl { \ + auto _module_##name = [] { \ + interpreter::register_init([] { \ + pybind11::module_ m = vm->new_module(#name, ""); \ + _pybind11_register_##name(m); \ + }); \ + return 1; \ + }(); \ + } \ + static void _pybind11_register_##name(pybind11::module_& variable) + +} // namespace pybind11 diff --git a/include/pybind11/internal/object.h b/include/pybind11/internal/object.h new file mode 100644 index 00000000..ef1ffe15 --- /dev/null +++ b/include/pybind11/internal/object.h @@ -0,0 +1,268 @@ +#pragma once +#include "instance.h" + +namespace pybind11 { + +template +class interface { +private: + pkpy::PyVar ptr() const { return static_cast(this)->ptr(); } + +public: + bool is_none() const { +#if PK_VERSION_MAJOR == 2 + return ptr().operator== (vm->None.get()); +#else + return ptr() == vm->None; +#endif + } + + bool is(const interface& other) const { +#if PK_VERSION_MAJOR == 2 + return ptr().operator== (other.ptr().get()); +#else + return ptr() == other.ptr(); +#endif + } + + bool in(const interface& other) const { + return pybind11::cast(vm->call(vm->py_op("contains"), other.ptr(), ptr())); + } + + bool contains(const interface& other) const { + return pybind11::cast(vm->call(vm->py_op("contains"), ptr(), other.ptr())); + } + +protected: + attr_accessor attr(pkpy::StrName key) const; + +public: + iterator begin() const; + iterator end() const; + + attr_accessor attr(const char* key) const; + attr_accessor attr(const handle& key) const; + attr_accessor doc() const; + + item_accessor operator[] (int index) const; + item_accessor operator[] (const char* key) const; + item_accessor operator[] (const handle& key) const; + + args_proxy operator* () const; + object operator- () const; + object operator~() const; + + template + object operator() (Args&&... args) const; + + str package() const; + str name() const; + str repr() const; + +public: + template + T cast() const { + return pybind11::cast(ptr()); + } + + // this is a internal function, use to interact with pocketpy python + template + decltype(auto) _as() const { + static_assert(!std::is_reference_v, "T must not be a reference type."); +#if PK_VERSION_MAJOR == 2 + if constexpr(pkpy::is_sso_v) { + return pkpy::_py_cast(vm, ptr()); + } else { + return ptr().template obj_get(); + } +#else + return (((pkpy::Py_*)ptr())->_value); +#endif + } +}; + +/// a lightweight wrapper for python objects +class handle : public interface { +protected: + mutable pkpy::PyVar m_ptr = nullptr; + +public: + handle() = default; + handle(const handle& h) = default; + handle& operator= (const handle& other) = default; + + handle(std::nullptr_t) = delete; + + handle(pkpy::PyVar ptr) : m_ptr(ptr) {} + +#if PK_VERSION_MAJOR == 2 + handle(pkpy::PyObject* ptr) : m_ptr(ptr) {} +#endif + + pkpy::PyVar ptr() const { return m_ptr; } + + explicit operator bool () const { return m_ptr != nullptr; } +}; + +// a helper class to visit type +struct type_visitor { + + template + constexpr static bool is_type = std::is_same_v>; + + template + static pkpy::Type type() { + if constexpr(is_pyobject_v) { + if constexpr(is_type) { + // for some type, they have according type in python, e.g. bool, int, float + // so just return the according type + return T::type_or_check(); + } else { + // for other type, they don't have according type in python, like iterable, iterator + static_assert(dependent_false, "type_or_check not defined"); + } + } else { +#if PK_VERSION_MAJOR == 2 + // for C++ type, lookup the type in the type map + auto type = vm->_cxx_typeid_map.try_get(typeid(T)); + // if found, return the type + if(type) return *type; +#else + auto result = vm->_cxx_typeid_map.find(typeid(T)); + if(result != vm->_cxx_typeid_map.end()) { return result->second; } +#endif + // if not found, raise error + std::string msg = "can not c++ instance cast to object, type: {"; + msg += type_name(); + msg += "} is not registered."; + vm->TypeError(msg); + PK_UNREACHABLE(); + } + } + + template + static handle create(const handle& scope, const char* name, bool is_builtin = false) { + pkpy::Type type = vm->tp_object; +#if PK_VERSION_MAJOR == 2 + pkpy::PyTypeInfo::Vt vt = pkpy::PyTypeInfo::Vt::get(); + + if(is_builtin) { vt = pkpy::PyTypeInfo::Vt::get(); } + + if constexpr(!std::is_same_v) { + type = type_visitor::type(); + vt = {}; + } + + handle result = vm->new_type_object(scope.ptr().get(), name, type, true, vt); + if(!is_builtin) { vm->_cxx_typeid_map.insert(typeid(T), result._as()); } +#else + if constexpr(!std::is_same_v) { type = type_visitor::type(); } + handle result = vm->new_type_object(scope.ptr(), name, type, true); + if(!is_builtin) (vm->_cxx_typeid_map.try_emplace(typeid(T), result._as())); +#endif + // set __module__ + setattr(scope, name, result); + return result; + } + + template + static bool check(const handle& obj) { + if constexpr(is_pyobject_v) { + if constexpr(is_type) { + return vm->isinstance(obj.ptr(), T::type_or_check()); + } else { + // some type, like iterable, iterator, they don't have according type in python + // but they have a function to check the type, then just call the function + return T::type_or_check(obj); + } + } else { + return vm->isinstance(obj.ptr(), type()); + } + } +}; + +// undef in pybind11.h +#define PYBIND11_TYPE_IMPLEMENT(parent, name, tp) \ + \ +private: \ + using underlying_type = name; \ + \ + inline static auto type_or_check = [] { \ + return tp; \ + }; \ + \ + decltype(auto) self() const { return _as(); } \ + \ + template \ + static handle create(Args&&... args) { \ + if constexpr(pkpy::is_sso_v) { \ + return pkpy::py_var(vm, std::forward(args)...); \ + } else { \ + return vm->heap.gcnew(type_or_check(), std::forward(args)...); \ + } \ + } \ + \ + friend type_visitor; \ + using parent::parent; + +/*=============================================================================// +// pkpy does not use reference counts, so object is just fot API compatibility // +//=============================================================================*/ +class object : public handle { + PYBIND11_TYPE_IMPLEMENT(handle, empty, vm->tp_object); + +public: + object(const handle& h) : handle(h) { + // object is must not null ptr + assert(h.ptr() != nullptr); + } +}; + +// undef after usage +#define PYBIND11_BINARY_OPERATOR(OP, NAME) \ + inline object operator OP (const handle& lhs, const handle& rhs) { return handle(vm->py_op(NAME))(lhs, rhs); } + +#define PYBIND11_INPLACE_OPERATOR(OP, NAME) \ + inline object operator OP (handle& lhs, const handle& rhs) { \ + handle result = handle(vm->py_op(NAME))(lhs, rhs); \ + return lhs = result; \ + } + +#define PYBIND11_BINARY_LOGIC_OPERATOR(OP, NAME) \ + inline bool operator OP (const handle& lhs, const handle& rhs) { \ + return pybind11::cast(handle(vm->py_op(NAME))(lhs, rhs)); \ + } + +PYBIND11_BINARY_OPERATOR(+, "add"); +PYBIND11_BINARY_OPERATOR(-, "sub"); +PYBIND11_BINARY_OPERATOR(*, "mul"); +PYBIND11_BINARY_OPERATOR(/, "truediv"); +PYBIND11_BINARY_OPERATOR(%, "mod"); +PYBIND11_BINARY_OPERATOR(|, "or_"); +PYBIND11_BINARY_OPERATOR(&, "and_"); +PYBIND11_BINARY_OPERATOR(^, "xor"); +PYBIND11_BINARY_OPERATOR(<<, "lshift"); +PYBIND11_BINARY_OPERATOR(>>, "rshift"); + +PYBIND11_INPLACE_OPERATOR(+=, "iadd"); +PYBIND11_INPLACE_OPERATOR(-=, "isub"); +PYBIND11_INPLACE_OPERATOR(*=, "imul"); +PYBIND11_INPLACE_OPERATOR(/=, "itruediv"); +PYBIND11_INPLACE_OPERATOR(%=, "imod"); +PYBIND11_INPLACE_OPERATOR(|=, "ior"); +PYBIND11_INPLACE_OPERATOR(&=, "iand"); +PYBIND11_INPLACE_OPERATOR(^=, "ixor"); +PYBIND11_INPLACE_OPERATOR(<<=, "ilshift"); +PYBIND11_INPLACE_OPERATOR(>>=, "irshift"); + +PYBIND11_BINARY_LOGIC_OPERATOR(==, "eq"); +PYBIND11_BINARY_LOGIC_OPERATOR(!=, "ne"); +PYBIND11_BINARY_LOGIC_OPERATOR(<, "lt"); +PYBIND11_BINARY_LOGIC_OPERATOR(>, "gt"); +PYBIND11_BINARY_LOGIC_OPERATOR(<=, "le"); +PYBIND11_BINARY_LOGIC_OPERATOR(>=, "ge"); + +#undef PYBIND11_BINARY_OPERATOR +#undef PYBIND11_INPLACE_OPERATOR +#undef PYBIND11_BINARY_LOGIC_OPERATOR +}; // namespace pybind11 diff --git a/include/pybind11/internal/type_traits.h b/include/pybind11/internal/type_traits.h new file mode 100644 index 00000000..1bb17172 --- /dev/null +++ b/include/pybind11/internal/type_traits.h @@ -0,0 +1,170 @@ +#pragma once +#include +#include +#include + +namespace pybind11 { + +template +constexpr bool dependent_false = false; + +template +struct tuple_push_front; + +template +struct tuple_push_front> { + using type = std::tuple; +}; + +template +using tuple_push_front_t = typename tuple_push_front::type; + +// traits for function types +template +struct function_traits { + static_assert(dependent_false, "unsupported function type"); +}; + +#define PYBIND11_FUNCTION_TRAITS_SPECIALIZE(qualifiers) \ + template \ + struct function_traits { \ + using return_type = R; \ + using args_type = std::tuple; \ + constexpr static std::size_t args_count = sizeof...(Args); \ + }; + +PYBIND11_FUNCTION_TRAITS_SPECIALIZE() +PYBIND11_FUNCTION_TRAITS_SPECIALIZE(&) +PYBIND11_FUNCTION_TRAITS_SPECIALIZE(const) +PYBIND11_FUNCTION_TRAITS_SPECIALIZE(const&) +PYBIND11_FUNCTION_TRAITS_SPECIALIZE(noexcept) +PYBIND11_FUNCTION_TRAITS_SPECIALIZE(& noexcept) +PYBIND11_FUNCTION_TRAITS_SPECIALIZE(const noexcept) +PYBIND11_FUNCTION_TRAITS_SPECIALIZE(const& noexcept) + +#undef PYBIND11_FUNCTION_TRAITS_SPECIALIZE + +template +using function_return_t = typename function_traits::return_type; + +template +using function_args_t = typename function_traits::args_type; + +template +constexpr std::size_t function_args_count = function_traits::args_count; + +// traits for member pointers +template +struct member_traits; + +template +struct member_traits { + using member_type = M; + using class_type = C; +}; + +template +using member_type_t = typename member_traits::member_type; + +template +using class_type_t = typename member_traits::class_type; + +// some traits for distinguishing between function pointers, member function pointers and +// functors +using std::is_member_function_pointer_v; +using std::is_member_object_pointer_v; + +template +constexpr inline bool is_function_pointer_v = std::is_function_v>; + +template +constexpr bool is_functor_v = false; + +template +constexpr inline bool is_functor_v> = true; + +template +struct callable_traits; + +template +struct callable_traits>> { + using args_type = tuple_push_front_t&, function_args_t>>; + using return_type = function_return_t>; +}; + +template +struct callable_traits>> { + using args_type = function_args_t>; + using return_type = function_return_t>; +}; + +template +struct callable_traits>> { + using args_type = function_args_t>; + using return_type = function_return_t>; +}; + +template +using callable_args_t = typename callable_traits::args_type; + +template +using callable_return_t = typename callable_traits::return_type; + +template +constexpr std::size_t callable_args_count_v = std::tuple_size_v>; + +template +struct type_identity { + using type = T; +}; + +template +using remove_cvref_t = std::remove_cv_t>; + +template +constexpr inline std::size_t types_count_v = (std::is_same_v + ...); + +template +constexpr inline std::size_t types_count_v = 0; + +template +constexpr inline int type_index_v = [] { + bool arr[sizeof...(Ts) + 1] = {std::is_same_v...}; + for(int i = 0; i < sizeof...(Ts); ++i) { + if(arr[i]) return i; + } + return -1; +}(); + +static_assert(types_count_v == 2); +static_assert(types_count_v == 0); +static_assert(type_index_v == 0); +static_assert(type_index_v == 1); +static_assert(type_index_v == -1); + +template +constexpr inline bool is_multiple_pointer_v = std::is_pointer_v && is_multiple_pointer_v>; + +template +using pybind11_decay_t = std::decay_t>>; + +template +constexpr auto type_name() { +#if __GNUC__ || __clang__ + std::string_view name = __PRETTY_FUNCTION__; + std::size_t start = name.find('=') + 2; + std::size_t end = name.size() - 1; + return std::string_view{name.data() + start, end - start}; +#elif _MSC_VER + std::string_view name = __FUNCSIG__; + std::size_t start = name.find('<') + 1; + std::size_t end = name.rfind(">("); + name = std::string_view{name.data() + start, end - start}; + start = name.find(' '); + return start == std::string_view::npos ? name : std::string_view{name.data() + start + 1, name.size() - start - 1}; +#else + static_assert(false, "Unsupported compiler"); +#endif +} + +} // namespace pybind11 diff --git a/include/pybind11/internal/types.h b/include/pybind11/internal/types.h new file mode 100644 index 00000000..1c079ca4 --- /dev/null +++ b/include/pybind11/internal/types.h @@ -0,0 +1,395 @@ +#pragma once +#include "object.h" + +namespace pybind11 { + +template +handle cast(T&& value, return_value_policy policy = return_value_policy::automatic_reference, handle parent = {}); + +struct arg { + const char* name; + handle default_; + + arg(const char* name) : name(name), default_() {} + + template + arg& operator= (T&& value) { + default_ = cast(std::forward(value)); + return *this; + } +}; + +// undef in pybind11.h +#define PYBIND11_REGISTER_INIT(func) \ + static inline int _register = [] { \ + interpreter::register_init(func); \ + return 0; \ + }(); + +class none : public object { +#if PK_VERSION_MAJOR == 2 + PYBIND11_TYPE_IMPLEMENT(object, empty, vm->tp_none_type); +#else + PYBIND11_TYPE_IMPLEMENT(object, empty, [](const handle& obj) { + return obj.is_none(); + }); +#endif + +public: + none() : object(vm->None) {} +}; + +/// corresponding to type in Python +class type : public object { + PYBIND11_TYPE_IMPLEMENT(object, pkpy::Type, vm->tp_type); + +public: + template + static handle handle_of() { + return type_visitor::type(); + } + + static type of(const handle& obj) { return type(vm->_t(obj.ptr())); } +}; + +/// corresponding to bool in Python +class bool_ : public object { + PYBIND11_TYPE_IMPLEMENT(object, bool, vm->tp_bool); + +public: + bool_(bool value) : object(create(value)) {} + + operator bool () const { return self(); } +}; + +/// corresponding to int in Python +class int_ : public object { + PYBIND11_TYPE_IMPLEMENT(object, pkpy::i64, vm->tp_int); + +public: + int_(int64_t value) : object(create(value)) {} + + operator int64_t () const { return self(); } +}; + +/// corresponding to float in Python +class float_ : public object { + PYBIND11_TYPE_IMPLEMENT(object, pkpy::f64, vm->tp_float); + +public: + float_(double value) : object(create(value)) {} + + operator double () const { return self(); } +}; + +class iterable : public object { + PYBIND11_TYPE_IMPLEMENT(object, empty, [](const handle& obj) { + return vm->getattr(obj.ptr(), pkpy::__iter__, false) != nullptr; + }); +}; + +class iterator : public object { + PYBIND11_TYPE_IMPLEMENT(object, empty, [](const handle& obj) { + return vm->getattr(obj.ptr(), pkpy::__next__, false) != nullptr && + vm->getattr(obj.ptr(), pkpy::__iter__, false) != nullptr; + }); + + handle m_value; + + iterator(pkpy::PyVar n, pkpy::PyVar s) : object(n), m_value(s) {} + +public: + iterator(const handle& obj) : object(obj) { m_value = vm->py_next(obj.ptr()); } + + iterator operator++ () { + m_value = vm->py_next(m_ptr); + return *this; + } + + iterator operator++ (int) { + m_value = vm->py_next(m_ptr); + return *this; + } + + const handle& operator* () const { return m_value; } + + friend bool operator== (const iterator& lhs, const iterator& rhs) { return lhs.m_value.is(rhs.m_value); } + + friend bool operator!= (const iterator& lhs, const iterator& rhs) { return !(lhs == rhs); } + + static iterator sentinel() { return iterator(vm->None, vm->StopIteration); } +}; + +class str : public object { + PYBIND11_TYPE_IMPLEMENT(object, pkpy::Str, vm->tp_str); + +public: + str(const char* c, int len) : object(create(c, len)) {}; + + str(const char* c = "") : str(c, strlen(c)) {} + + str(const std::string& s) : str(s.data(), s.size()) {} + + str(std::string_view sv) : str(sv.data(), sv.size()) {} + + // explicit str(const bytes& b); + explicit str(handle h); + + operator std::string_view () const { return self().sv(); } + + template + str format(Args&&... args) const; +}; + +// class bytes : public object { +// public: +// using object::object; +// }; + +// class bytearray : public object { +// public: +// using object::object; +// }; + +class tuple : public object { + PYBIND11_TYPE_IMPLEMENT(object, pkpy::Tuple, vm->tp_tuple); + +public: + tuple(int n) : object(create(n)) {} + + template 1)>* = nullptr> + tuple(Args&&... args) : object(create(sizeof...(Args))) { + int index = 0; + ((self()[index++] = pybind11::cast(std::forward(args)).ptr()), ...); + } + + int size() const { return self().size(); } + + bool empty() const { return size() == 0; } + + tuple_accessor operator[] (int i) const; +}; + +class list : public object { + PYBIND11_TYPE_IMPLEMENT(object, pkpy::List, vm->tp_list) + +public: + list() : object(create(0)) {} + + list(int n) : object(create(n)) {} + + template 1)>* = nullptr> + list(Args&&... args) : object(create(sizeof...(Args))) { + int index = 0; + ((self()[index++] = pybind11::cast(std::forward(args)).ptr()), ...); + } + + int size() const { return self().size(); } + + bool empty() const { return size() == 0; } + + void clear() { self().clear(); } + + list_accessor operator[] (int i) const; + + void append(const handle& obj) { self().push_back(obj.ptr()); } + + void extend(const handle& iterable) { + for(auto& item: iterable) { + append(item); + } + } + + void insert(int index, const handle& obj) { +#if PK_VERSION_MAJOR == 2 + const auto pos = self().begin() + index; + self().insert(pos, obj.ptr()); +#else + self().insert(index, obj.ptr()); +#endif + } +}; + +class slice : public object { + PYBIND11_TYPE_IMPLEMENT(object, pkpy::Slice, vm->tp_slice); + +public: +}; + +// class set : public object { +// public: +// using object::object; +// // set() : object(vm->new_object(pkpy::VM::tp_set), true) {} +// }; +// + +class dict : public object { + PYBIND11_TYPE_IMPLEMENT(object, pkpy::Dict, vm->tp_dict); + +public: +#if PK_VERSION_MAJOR == 2 + dict() : object(create()) {} + + template , arg> && ...)>> + dict(Args&&... args) : object(create()) { + auto foreach_ = [&](pybind11::arg& arg) { + setitem(str(arg.name), arg.default_); + }; + (foreach_(args), ...); + } + + void setitem(const handle& key, const handle& value) { self().set(vm, key.ptr(), value.ptr()); } + + handle getitem(const handle& key) const { return self().try_get(vm, key.ptr()); } + + struct iterator { + pkpy_DictIter iter; + std::pair value; + + iterator operator++ () { + bool is_ended = pkpy_DictIter__next(&iter, (PyVar*)&value.first, (PyVar*)&value.second); + if(!is_ended) { + iter._dict = nullptr; + iter._index = -1; + } + return *this; + } + + std::pair operator* () const { return value; } + + bool operator== (const iterator& other) const { + return iter._dict == other.iter._dict && iter._index == other.iter._index; + } + + bool operator!= (const iterator& other) const { return !(*this == other); } + }; + + iterator begin() const { + iterator iter{self().iter(), {}}; + ++iter; + return iter; + } + + iterator end() const { return {nullptr, -1}; } +#else + dict() : object(create(vm)) {} + + template , arg> && ...)>> + dict(Args&&... args) : object(create(vm)) { + auto foreach_ = [&](pybind11::arg& arg) { + setitem(str(arg.name), arg.default_); + }; + (foreach_(args), ...); + } + + void setitem(const handle& key, const handle& value) { self().set(key.ptr(), value.ptr()); } + + handle getitem(const handle& key) const { return self().try_get(key.ptr()); } + + struct iterator { + pkpy::Dict::Item* items; + pkpy::Dict::ItemNode* nodes; + int index; + + iterator operator++ () { + index = nodes[index].next; + if(index == -1) { + items = nullptr; + nodes = nullptr; + } + return *this; + } + + std::pair operator* () const { return {items[index].first, items[index].second}; } + + bool operator== (const iterator& other) const { + return items == other.items && nodes == other.nodes && index == other.index; + } + + bool operator!= (const iterator& other) const { return !(*this == other); } + }; + + iterator begin() const { + auto index = self()._head_idx; + if(index == -1) { + return end(); + } else { + return {self()._items, self()._nodes, index}; + } + } + + iterator end() const { return {nullptr, nullptr, -1}; } + + template + bool contains(Key&& key) const { + return self().contains(vm, pybind11::cast(std::forward(key)).ptr()); + } +#endif + + int size() const { return self().size(); } + + bool empty() const { return size() == 0; } + + void clear() { self().clear(); } + + dict_accessor operator[] (int index) const; + dict_accessor operator[] (std::string_view) const; + dict_accessor operator[] (const handle& key) const; +}; + +class function : public object { + PYBIND11_TYPE_IMPLEMENT(object, pkpy::Function, vm->tp_function); +}; + +// +// class buffer : public object { +// public: +// using object::object; +//}; +// +// class memory_view : public object { +// public: +// using object::object; +//}; +// +class capsule : public object { + PYBIND11_REGISTER_INIT([] { + type_visitor::create(vm->builtins, "capsule", true); + }); + + PYBIND11_TYPE_IMPLEMENT(object, impl::capsule, handle(vm->builtins->attr("capsule"))._as()); + +public: + template + capsule(T&& value) : object(create(std::forward(value))) {} + + template + T& cast() const { + return *static_cast(self().ptr); + } +}; + +class property : public object { + PYBIND11_TYPE_IMPLEMENT(object, pkpy::Property, vm->tp_property); + +public: +#if PK_VERSION_MAJOR == 2 + property(handle getter, handle setter) : object(create(getter.ptr(), setter.ptr())) {} +#else + property(handle getter, handle setter) : object(create(pkpy::Property{getter.ptr(), setter.ptr()})) {} +#endif + + handle getter() const { return self().getter; } + + handle setter() const { return self().setter; } +}; + +class args : public tuple { + PYBIND11_TYPE_IMPLEMENT(tuple, struct empty, vm->tp_tuple); +}; + +class kwargs : public dict { + PYBIND11_TYPE_IMPLEMENT(dict, struct empty, vm->tp_dict); +}; + +} // namespace pybind11 diff --git a/include/pybind11/operators.h b/include/pybind11/operators.h new file mode 100644 index 00000000..c34920b1 --- /dev/null +++ b/include/pybind11/operators.h @@ -0,0 +1,201 @@ +#pragma once +#include "pybind11.h" + +namespace pybind11::impl { + +enum op_id : int { + op_add, + op_sub, + op_mul, + op_div, + op_mod, + op_divmod, + op_pow, + op_lshift, + op_rshift, + op_and, + op_xor, + op_or, + op_neg, + op_pos, + op_abs, + op_invert, + op_int, + op_long, + op_float, + op_str, + op_cmp, + op_gt, + op_ge, + op_lt, + op_le, + op_eq, + op_ne, + op_iadd, + op_isub, + op_imul, + op_idiv, + op_imod, + op_ilshift, + op_irshift, + op_iand, + op_ixor, + op_ior, + op_complex, + op_bool, + op_nonzero, + op_repr, + op_truediv, + op_itruediv, + op_hash +}; + +enum op_type : int { + op_l, /* base type on left */ + op_r, /* base type on right */ + op_u /* unary operator */ +}; + +struct self_t {}; + +const static self_t self = self_t(); + +/// Type for an unused type slot +struct undefined_t {}; + +/// Don't warn about an unused variable +inline self_t __self() { return self; } + +/// base template of operator implementations +template +struct op_impl {}; + +/// Operator implementation generator +template +struct op_ { + constexpr static bool op_enable_if_hook = true; + + template + void execute(Class& cl, const Extra&... extra) const { + using Base = typename Class::underlying_type; + using L_type = std::conditional_t::value, Base, L>; + using R_type = std::conditional_t::value, Base, R>; + using op = op_impl; + cl.def(op::name(), &op::execute, extra...); + } + + template + void execute_cast(Class& cl, const Extra&... extra) const { + using Base = typename Class::type; + using L_type = std::conditional_t::value, Base, L>; + using R_type = std::conditional_t::value, Base, R>; + using op = op_impl; + cl.def(op::name(), &op::execute_cast, extra...); + } +}; + +#define PYBIND11_BINARY_OPERATOR(id, rid, op, expr) \ + template \ + struct op_impl { \ + static char const* name() { return "__" #id "__"; } \ + static auto execute(const L& l, const R& r) -> decltype(expr) { return (expr); } \ + static B execute_cast(const L& l, const R& r) { return B(expr); } \ + }; \ + \ + template \ + struct op_impl { \ + static char const* name() { return "__" #rid "__"; } \ + static auto execute(const R& r, const L& l) -> decltype(expr) { return (expr); } \ + static B execute_cast(const R& r, const L& l) { return B(expr); } \ + }; \ + \ + inline op_ op(const self_t&, const self_t&) { \ + return op_(); \ + } \ + \ + template \ + op_ op(const self_t&, const T&) { \ + return op_(); \ + } \ + \ + template \ + op_ op(const T&, const self_t&) { \ + return op_(); \ + } + +#define PYBIND11_INPLACE_OPERATOR(id, op, expr) \ + template \ + struct op_impl { \ + static char const* name() { return "__" #id "__"; } \ + static auto execute(L& l, const R& r) -> decltype(expr) { return expr; } \ + static B execute_cast(L& l, const R& r) { return B(expr); } \ + }; \ + \ + template \ + op_ op(const self_t&, const T&) { \ + return op_(); \ + } + +#define PYBIND11_UNARY_OPERATOR(id, op, expr) \ + template \ + struct op_impl { \ + static char const* name() { return "__" #id "__"; } \ + static auto execute(const L& l) -> decltype(expr) { return expr; } \ + static B execute_cast(const L& l) { return B(expr); } \ + }; \ + \ + inline op_ op(const self_t&) { \ + return op_(); \ + } + +PYBIND11_BINARY_OPERATOR(sub, rsub, operator-, l - r) +PYBIND11_BINARY_OPERATOR(add, radd, operator+, l + r) +PYBIND11_BINARY_OPERATOR(mul, rmul, operator*, l* r) +PYBIND11_BINARY_OPERATOR(truediv, rtruediv, operator/, l / r) +PYBIND11_BINARY_OPERATOR(mod, rmod, operator%, l % r) +PYBIND11_BINARY_OPERATOR(lshift, rlshift, operator<<, l << r) +PYBIND11_BINARY_OPERATOR(rshift, rrshift, operator>>, l >> r) +PYBIND11_BINARY_OPERATOR(and, rand, operator&, l& r) +PYBIND11_BINARY_OPERATOR(xor, rxor, operator^, l ^ r) +PYBIND11_BINARY_OPERATOR(eq, eq, operator==, l == r) +PYBIND11_BINARY_OPERATOR(ne, ne, operator!=, l != r) +PYBIND11_BINARY_OPERATOR(or, ror, operator|, l | r) +PYBIND11_BINARY_OPERATOR(gt, lt, operator>, l > r) +PYBIND11_BINARY_OPERATOR(ge, le, operator>=, l >= r) +PYBIND11_BINARY_OPERATOR(lt, gt, operator<, l < r) +PYBIND11_BINARY_OPERATOR(le, ge, operator<=, l <= r) +// PYBIND11_BINARY_OPERATOR(pow, rpow, pow, std::pow(l, r)) +PYBIND11_INPLACE_OPERATOR(iadd, operator+=, l += r) +PYBIND11_INPLACE_OPERATOR(isub, operator-=, l -= r) +PYBIND11_INPLACE_OPERATOR(imul, operator*=, l *= r) +PYBIND11_INPLACE_OPERATOR(itruediv, operator/=, l /= r) +PYBIND11_INPLACE_OPERATOR(imod, operator%=, l %= r) +PYBIND11_INPLACE_OPERATOR(ilshift, operator<<=, l <<= r) +PYBIND11_INPLACE_OPERATOR(irshift, operator>>=, l >>= r) +PYBIND11_INPLACE_OPERATOR(iand, operator&=, l &= r) +PYBIND11_INPLACE_OPERATOR(ixor, operator^=, l ^= r) +PYBIND11_INPLACE_OPERATOR(ior, operator|=, l |= r) + +PYBIND11_UNARY_OPERATOR(neg, operator-, -l) +PYBIND11_UNARY_OPERATOR(pos, operator+, +l) + +// WARNING: This usage of `abs` should only be done for existing STL overloads. +// Adding overloads directly in to the `std::` namespace is advised against: +// https://en.cppreference.com/w/cpp/language/extending_std + +// PYBIND11_UNARY_OPERATOR(abs, abs, std::abs(l)) +// PYBIND11_UNARY_OPERATOR(hash, hash, std::hash()(l)) +// PYBIND11_UNARY_OPERATOR(invert, operator~, (~l)) +// PYBIND11_UNARY_OPERATOR(bool, operator!, !!l) +// PYBIND11_UNARY_OPERATOR(int, int_, (int)l) +// PYBIND11_UNARY_OPERATOR(float, float_, (double)l) + +#undef PYBIND11_BINARY_OPERATOR +#undef PYBIND11_INPLACE_OPERATOR +#undef PYBIND11_UNARY_OPERATOR + +} // namespace pybind11::impl + +namespace pybind11 { +using impl::self; +} diff --git a/include/pybind11/pybind11.h b/include/pybind11/pybind11.h new file mode 100644 index 00000000..f0424576 --- /dev/null +++ b/include/pybind11/pybind11.h @@ -0,0 +1,21 @@ +#pragma once +#include "internal/class.h" + +namespace pybind11 { + +namespace literals { +inline arg operator""_a (const char* c, size_t) { return arg(c); } +} // namespace literals + +struct scoped_interpreter { + scoped_interpreter() { interpreter::initialize(); } + + ~scoped_interpreter() { interpreter::finalize(); } +}; + +} // namespace pybind11 + +// namespace pybind11 + +#undef PYBIND11_TYPE_IMPLEMENT +#undef PYBIND11_REGISTER_INIT diff --git a/include/pybind11/stl.h b/include/pybind11/stl.h new file mode 100644 index 00000000..10e8e976 --- /dev/null +++ b/include/pybind11/stl.h @@ -0,0 +1,144 @@ +#include "pybind11.h" + +#include +#include +#include +#include +#include + +#include +#include + +namespace pybind11 { + +template +struct type_caster> { + + struct wrapper { + std::array container = {}; + + operator std::array&& () { return std::move(container); } + }; + + wrapper value; + + bool load(const handle& src, bool convert) { + if(!isinstance(src)) { return false; } + auto list = src.cast(); + + if(list.size() != N) { return false; } + + for(std::size_t i = 0; i < N; ++i) { + type_caster caster; + if(!caster.load(list[i], convert)) { return false; } + value.container[i] = caster.value; + } + + return true; + } + + template + static handle cast(U&& src, return_value_policy policy, handle parent) { + auto list = pybind11::list(); + for(auto& item: src) { + list.append(pybind11::cast(item, policy, parent)); + } + return list; + } +}; + +template +constexpr bool is_py_list_like_v = false; + +template +constexpr bool is_py_list_like_v> = true; + +template +constexpr bool is_py_list_like_v> = true; + +template +constexpr bool is_py_list_like_v> = true; + +template +struct type_caster>> { + + struct wrapper { + T container; + + operator T&& () { return std::move(container); } + }; + + wrapper value; + + bool load(const handle& src, bool convert) { + if(!isinstance(src)) { return false; } + auto list = src.cast(); + + for(auto item: list) { + type_caster caster; + if(!caster.load(item, convert)) { return false; } + value.container.push_back(caster.value); + } + + return true; + } + + template + static handle cast(U&& src, return_value_policy policy, handle parent) { + auto list = pybind11::list(); + for(auto& item: src) { + list.append(pybind11::cast(item, policy, parent)); + } + return list; + } +}; + +template +constexpr bool is_py_map_like_v = false; + +template +constexpr bool is_py_map_like_v> = true; + +template +constexpr bool is_py_map_like_v> = true; + +template +struct type_caster>> { + + struct wrapper { + T container; + + operator T&& () { return std::move(container); } + }; + + wrapper value; + + bool load(const handle& src, bool convert) { + if(!isinstance(src)) { return false; } + auto dict = src.cast(); + + for(auto item: dict) { + type_caster key_caster; + if(!key_caster.load(item.first, convert)) { return false; } + + type_caster value_caster; + if(!value_caster.load(item.second, convert)) { return false; } + + value.container.try_emplace(key_caster.value, value_caster.value); + } + + return true; + } + + template + static handle cast(U&& src, return_value_policy policy, handle parent) { + auto dict = pybind11::dict(); + for(auto& [key, value]: src) { + dict[pybind11::cast(key, policy, parent)] = pybind11::cast(value, policy, parent); + } + return dict; + } +}; + +} // namespace pybind11 +