ykiko 2773b9993e
Implement dynamic dlib (#309)
* lazy cpp_function and capsule.

* remove retv.

* remove type_map.

* remove object pool from initialize.

* support dynamic library.

* remove vector_bool.

* remove unused header.

* fix export name.

* fix test name.

* some fix

* some fix

* ...

---------

Co-authored-by: blueloveTH <blueloveth@foxmail.com>
2024-09-22 21:48:10 +08:00

206 lines
6.8 KiB
C++

#pragma once
#include "types.h"
namespace pkbind {
template <typename... Args>
inline void print(Args&&... args) {
handle print = py_getbuiltin(py_name("print"));
print(std::forward<Args>(args)...);
}
inline object eval(std::string_view code, handle globals = none(), handle locals = none()) {
if(globals.is_none() && locals.is_none()) {
std::string src{code};
raise_call<py_eval>(src.c_str(), nullptr);
return object::from_ret();
} else {
handle eval = py_getbuiltin(py_name("eval"));
return eval(str(code),
globals.is_none() ? dict() : globals,
locals.is_none() ? dict() : locals);
}
}
inline object exec(std::string_view code, handle globals = none(), handle locals = none()) {
if(globals.is_none() && locals.is_none()) {
std::string src{code};
raise_call<py_exec>(src.c_str(), "exec", EXEC_MODE, nullptr);
return object::from_ret();
} else {
handle exec = py_getbuiltin(py_name("exec"));
return exec(str(code), globals, locals);
}
}
inline object locals() {
handle locals = py_getbuiltin(py_name("locals"));
return locals();
}
inline object globals() {
handle globals = py_getbuiltin(py_name("globals"));
return globals();
}
inline bool hasattr(handle obj, name name) {
auto pc = py_peek(0);
auto result = py_getattr(obj.ptr(), name.index());
if(result) {
return true;
} else {
py_clearexc(pc);
return false;
}
}
inline object getattr(handle obj, name name) {
raise_call<py_getattr>(obj.ptr(), name.index());
return object::from_ret();
}
inline void setattr(handle obj, name name, handle value) {
raise_call<py_setattr>(obj.ptr(), name.index(), value.ptr());
}
inline void delattr(handle obj, name name) { raise_call<py_delattr>(obj.ptr(), name.index()); }
inline py_i64 hash(handle obj) {
py_i64 result = 0;
raise_call<py_hash>(obj.ptr(), &result);
return result;
}
inline bool isinstance(handle obj, type type) { return py_isinstance(obj.ptr(), type.index()); }
inline bool python_error::match(type type) const { return isinstance(m_exception.ptr(), type); }
template <typename T>
constexpr inline bool is_pyobject_v =
std::is_base_of_v<object, std::decay_t<T>> || std::is_same_v<type, T>;
template <typename T>
inline type type::of() {
if constexpr(is_pyobject_v<T>) {
if constexpr(is_check_v<T>) {
return type(tp_object);
} else {
return type(T::type_or_check());
}
} else {
auto it = m_type_map.find(typeid(T));
if(it != m_type_map.end()) {
return type(it->second);
} else {
// if not found, raise error
std::string msg = "can not c++ instance cast to object, type: {";
msg += type_name<T>();
msg += "} is not registered.";
throw std::runtime_error(msg);
}
}
}
template <typename T>
inline bool type::isinstance(handle obj) {
if constexpr(is_pyobject_v<T>) {
// for every python object wrapper type, there must be a `type_or_check` method.
// for some types, it returns the underlying type in pkpy, e.g., `int_` -> `tp_int`.
// for other types that may not have a corresponding type in pkpy, it returns a check
// function. e.g., `iterable` -> `[](handle h){ return hasattr(h, "iter"); }`.
auto type_or_check = T::type_or_check();
if constexpr(is_check_v<T>) {
return type_or_check(obj);
} else {
return pkbind::isinstance(obj, type(type_or_check));
}
} else {
return pkbind::isinstance(obj, of<T>());
}
}
inline bool issubclass(type derived, type base) {
return py_issubclass(derived.index(), base.index());
}
template <typename T>
inline bool isinstance(handle obj) {
return type::isinstance<T>(obj);
}
template <>
inline bool isinstance<handle>(handle) = delete;
template <typename T, typename SFINAE = void>
struct type_caster;
template <typename T>
object cast(T&& value,
return_value_policy policy = return_value_policy::automatic_reference,
handle parent = {}) {
// decay_t can resolve c-array type, but remove_cv_ref_t can't.
using underlying_type = std::decay_t<T>;
if constexpr(std::is_convertible_v<underlying_type, handle> &&
!is_pyobject_v<underlying_type>) {
return object(std::forward<T>(value), object::realloc_t{});
} else if constexpr(is_unique_pointer_v<underlying_type>) {
using pointer = typename underlying_type::pointer;
return type_caster<pointer>::cast(value.release(),
return_value_policy::take_ownership,
parent);
} else {
static_assert(!is_multiple_pointer_v<underlying_type>,
"multiple pointer is not supported.");
static_assert(!std::is_void_v<std::remove_pointer_t<underlying_type>>,
"void* is not supported, consider using py::capsule.");
// resolve for automatic policy.
if(policy == return_value_policy::automatic) {
policy = std::is_pointer_v<underlying_type> ? return_value_policy::take_ownership
: std::is_lvalue_reference_v<T&&> ? return_value_policy::copy
: return_value_policy::move;
} else if(policy == return_value_policy::automatic_reference) {
policy = std::is_pointer_v<underlying_type> ? return_value_policy::reference
: std::is_lvalue_reference_v<T&&> ? return_value_policy::copy
: return_value_policy::move;
}
return type_caster<underlying_type>::cast(std::forward<T>(value), policy, parent);
}
}
template <typename T>
T cast(handle obj, bool convert = true) {
using caster_t = type_caster<T>;
constexpr auto is_dangling_v =
(std::is_reference_v<T> || is_pointer_v<T>) && caster_t::is_temporary_v;
static_assert(!is_dangling_v, "dangling reference or pointer is not allowed.");
assert(obj.ptr() != nullptr);
caster_t caster;
if(caster.load(obj, convert)) {
if constexpr(std::is_reference_v<T>) {
return caster.value();
} else {
return std::move(caster.value());
}
} else {
std::string msg = "cast python instance to c++ failed, obj type is: {";
msg += type::of(obj).name();
msg += "}, target type is: {";
msg += type_name<T>();
msg += "}.";
throw cast_error(msg);
}
}
template <typename Derived>
template <typename T>
T interface<Derived>::cast() const {
return pkbind::cast<T>(handle(this->ptr()), true);
}
} // namespace pkbind