2024-05-30 23:14:31 +08:00

148 lines
5.3 KiB
C++

#pragma once
#include "kernel.h"
namespace pybind11 {
struct type_info {
const char* name;
std::size_t size;
std::size_t alignment;
void (*destructor)(void*);
void (*copy)(void*, const void*);
void (*move)(void*, void*);
const std::type_info* type;
template <typename T>
static type_info& of() {
static_assert(!std::is_reference_v<T> && !std::is_const_v<std::remove_reference_t<T>>,
"T must not be a reference type or const type.");
static type_info info = {
typeid(T).name(),
sizeof(T),
alignof(T),
[](void* ptr) {
((T*)ptr)->~T();
operator delete (ptr);
},
[](void* dst, const void* src) { new (dst) T(*(const T*)src); },
[](void* dst, void* src) { new (dst) T(std::move(*(T*)src)); },
&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;
// pkpy::PyVar
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;
}
template <typename T>
static pkpy::PyVar create(pkpy::Type type) {
instance instance;
instance.type = &type_info::of<T>();
instance.data = operator new (sizeof(T));
instance.flag = Flag::Own;
return vm->new_object<pybind11::instance>(type, std::move(instance));
}
template <typename T>
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 = std::remove_cv_t<std::remove_reference_t<T>>;
// 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;
}
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<underlying_type>) {
return *reinterpret_cast<underlying_type*>(value);
} else {
return const_cast<underlying_type&>(value);
}
}();
instance instance;
instance.type = &type_info::of<underlying_type>();
if(policy == return_value_policy::take_ownership) {
instance.data = &_value;
instance.flag = Flag::Own;
} else if(policy == return_value_policy::copy) {
instance.data = ::new auto(_value);
instance.flag = Flag::Own;
} else if(policy == return_value_policy::move) {
instance.data = ::new auto(std::move(_value));
instance.flag = Flag::Own;
} 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->new_object<pybind11::instance>(type, std::move(instance));
}
~instance() {
if(flag & Flag::Own) {
type->destructor(data);
}
}
void _gc_mark(pkpy::VM* vm) const noexcept {
if(parent && (flag & Flag::Ref)) {
PK_OBJ_MARK(parent);
}
}
template <typename T>
T& cast() noexcept {
return *static_cast<T*>(data);
}
};
} // namespace pybind11