Last active
January 10, 2024 22:25
-
-
Save bstaletic/76a27beaabd3e01739663ecc7a4edefb to your computer and use it in GitHub Desktop.
WIP minimal reproducer for Yannick
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <memory> | |
#include <pybind11/cast.h> | |
#include <pybind11/options.h> | |
#include <pybind11/pybind11.h> | |
#include <pybind11/pytypes.h> | |
class A {}; | |
class B {}; | |
namespace py = pybind11; | |
struct type_record { | |
type_record() | |
: multiple_inheritance(false), dynamic_attr(false), buffer_protocol(false), | |
default_holder(true), module_local(false), is_final(false) {} | |
/// Handle to the parent scope | |
PyObject* scope; | |
/// Name of the class | |
const char *name = nullptr; | |
// Pointer to RTTI type_info data structure | |
const std::type_info *type = nullptr; | |
/// How large is the underlying C++ type? | |
size_t type_size = 0; | |
/// What is the alignment of the underlying C++ type? | |
size_t type_align = 0; | |
/// How large is the type's holder? | |
size_t holder_size = 0; | |
/// The global operator new can be overridden with a class-specific variant | |
void *(*operator_new)(size_t) = nullptr; | |
/// Optional docstring | |
const char *doc = nullptr; | |
/// Multiple inheritance marker | |
bool multiple_inheritance : 1; | |
/// Does the class manage a __dict__? | |
bool dynamic_attr : 1; | |
/// Does the class implement the buffer protocol? | |
bool buffer_protocol : 1; | |
/// Is the default (unique_ptr) holder type used? | |
bool default_holder : 1; | |
/// Is the class definition local to the module shared object? | |
bool module_local : 1; | |
/// Is the class inheritable from python classes? | |
bool is_final : 1; | |
}; | |
extern "C" inline PyObject *meta_call(PyObject *type, PyObject *args, PyObject *kwargs) { | |
PyObject *self = PyType_Type.tp_call(type, args, kwargs); | |
return self; | |
} | |
extern "C" inline int meta_setattro(PyObject* obj, PyObject* name, PyObject* value) { | |
return PyType_Type.tp_setattro(obj, name, value); | |
} | |
inline PyTypeObject* make_default_metaclass() { | |
constexpr auto *name = "pybind11_type"; | |
PyObject *name_obj = PyUnicode_FromString(name); | |
/* Danger zone: from now (and until PyType_Ready), make sure to | |
issue no Python C API calls which could potentially invoke the | |
garbage collector (the GC will call type_traverse(), which will in | |
turn find the newly constructed type in an invalid state) */ | |
auto heap_type = (PyHeapTypeObject *) PyType_Type.tp_alloc(&PyType_Type, 0); | |
Py_INCREF(name_obj); | |
heap_type->ht_name = name_obj; | |
auto type = &heap_type->ht_type; | |
type->tp_name = name; | |
Py_INCREF(&PyType_Type); | |
type->tp_base = &PyType_Type; | |
type->tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HEAPTYPE; | |
type->tp_call = meta_call; | |
type->tp_setattro = meta_setattro; | |
PyObject_SetAttrString((PyObject *) type, "__module__", PyUnicode_FromString("pybind11_builtins")); | |
Py_INCREF(name_obj); | |
PyObject_SetAttrString((PyObject *) type, "__qualname__", name_obj); | |
return type; | |
} | |
extern "C" inline int object_init(PyObject *self, PyObject *, PyObject *) { | |
PyTypeObject *type = Py_TYPE(self); | |
PyErr_SetString(PyExc_TypeError, ""); | |
return -1; | |
} | |
inline PyObject *make_new_python_type(const ::type_record &rec) { | |
PyObject* name = PyUnicode_FromString(rec.name); | |
auto qualname = name; | |
PyObject* module_; | |
if (PyObject_HasAttrString(rec.scope, "__module__")) { | |
module_ = PyObject_GetAttrString(rec.scope, "__module__"); | |
} else if (PyObject_HasAttrString(rec.scope, "__name__")) { | |
module_ = PyObject_GetAttrString(rec.scope, "__name__"); | |
} | |
const auto *full_name = rec.name; | |
PyObject* bases = PyTuple_New(0); | |
PyTypeObject* default_metaclass = ::make_default_metaclass(); | |
auto *base = py::detail::make_object_base_type(default_metaclass); | |
/* Danger zone: from now (and until PyType_Ready), make sure to | |
issue no Python C API calls which could potentially invoke the | |
garbage collector (the GC will call type_traverse(), which will in | |
turn find the newly constructed type in an invalid state) */ | |
auto *heap_type = (PyHeapTypeObject *) default_metaclass->tp_alloc(default_metaclass, 0); | |
heap_type->ht_name = name; | |
#ifdef PYBIND11_BUILTIN_QUALNAME | |
heap_type->ht_qualname = qualname.inc_ref().ptr(); | |
#endif | |
auto *type = &heap_type->ht_type; | |
type->tp_name = full_name; | |
type->tp_base = py::detail::type_incref((PyTypeObject *) base); | |
type->tp_basicsize = static_cast<ssize_t>(sizeof(py::detail::instance)); | |
/* Don't inherit base __init__ */ | |
type->tp_init = object_init; | |
/* Supported protocols */ | |
type->tp_as_number = &heap_type->as_number; | |
type->tp_as_sequence = &heap_type->as_sequence; | |
type->tp_as_mapping = &heap_type->as_mapping; | |
type->tp_as_async = &heap_type->as_async; | |
/* Flags */ | |
type->tp_flags |= Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HEAPTYPE | Py_TPFLAGS_BASETYPE; | |
/* Register type with the parent scope */ | |
PyObject_SetAttrString(rec.scope, rec.name, (PyObject*)type); | |
if (module_) { // Needed by pydoc | |
PyObject_SetAttrString((PyObject*)type, "__module__", module_); | |
} | |
return (PyObject *) type; | |
} | |
auto class_b(PyObject* scope, const char* name) { | |
using holder_type = std::unique_ptr<B>; | |
::type_record record; | |
record.scope = scope; | |
record.name = name; | |
record.type = &typeid(B); | |
record.type_size = sizeof(B); | |
record.type_align = alignof(B); | |
record.holder_size = sizeof(std::unique_ptr<B>); | |
record.default_holder = true; | |
PyObject* m_ptr = ::make_new_python_type(record); | |
return m_ptr; | |
} | |
auto class_a(PyObject* scope, const char* name) { | |
using holder_type = std::unique_ptr<A>; | |
::type_record record; | |
record.scope = scope; | |
record.name = name; | |
record.type = &typeid(A); | |
record.type_size = sizeof(A); | |
record.type_align = alignof(A); | |
record.holder_size = sizeof(std::unique_ptr<A>); | |
record.default_holder = true; | |
PyObject* m_ptr = ::make_new_python_type(record); | |
return m_ptr; | |
} | |
PYBIND11_MODULE(mwe_pypy, m) { | |
auto ap = class_a(m.ptr(), "A"); | |
auto bp = class_b(m.ptr(), "B"); | |
auto a_tuple_p = PyTuple_New(1); | |
PyTuple_SetItem(a_tuple_p, 0, ap); | |
auto B_bases = PyObject_SetAttrString(bp, "__bases__", a_tuple_p); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment