#pragma once #include "module.h" #include "type_traits.h" namespace pkbind { struct dynamic_attr {}; template class class_ : public type { protected: handle m_scope; public: using type::type; using underlying_type = T; template class_(handle scope, const char* name, const Args&... args) : type(py_newtype(name, std::is_same_v ? tp_object : type::of().index(), scope.ptr(), [](void* data) { static_cast(data)->~instance(); })), m_scope(scope) { m_type_map->try_emplace(typeid(T), this->index()); auto& info = type_info::of(); info.name = name; py_newfunction( py_tpgetmagic(this->index(), py_MagicNames::__new__), "__new__(type, *args, **kwargs)", [](int, py_Ref stack) { auto cls = py_offset(stack, 0); [[maybe_unused]] auto args = py_offset(stack, 1); [[maybe_unused]] auto kwargs = py_offset(stack, 2); auto info = &type_info::of(); int slot = ((std::is_same_v || ...) ? -1 : 0); void* data = py_newobject(retv, steal(cls).index(), slot, sizeof(instance)); new (data) instance{instance::Flag::Own, operator new (info->size), info}; return true; }, nullptr, 0); } /// bind constructor template 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( *this, "__init__", [](T* self, Args... args) { new (self) T(args...); }, extra...); return *this; } } 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(), extra...); return *this; } } /// bind member function template class_& def(const char* name, Fn&& f, const Extra&... extra) { 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), extra...); } return *this; } /// bind operators template class_& def(Operator op, const Extras&... extras) { op.execute(*this, extras...); return *this; } /// bind static function template class_& def_static(const char* name, Fn&& f, const Extra&... extra) { impl::bind_function(*this, name, std::forward(f), 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](class_type_t& self) -> auto& { return self.*mp; }, [mp](class_type_t& self, const member_type_t& value) { self.*mp = value; }, 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](class_type_t& self) -> auto& { return self.*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_; 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)); }); } enum_& value(const char* name, T value) { auto var = pkbind::cast(value, return_value_policy::copy); setattr(*this, name, var); m_values.emplace_back(name, std::move(var)); return *this; } enum_& export_values() { for(auto& [name, value]: m_values) { setattr(Base::m_scope, name, value); } return *this; } }; } // namespace pkbind