Skip to content

Instantly share code, notes, and snippets.

@bstaletic
Last active January 10, 2024 22:25
Show Gist options
  • Save bstaletic/76a27beaabd3e01739663ecc7a4edefb to your computer and use it in GitHub Desktop.
Save bstaletic/76a27beaabd3e01739663ecc7a4edefb to your computer and use it in GitHub Desktop.
WIP minimal reproducer for Yannick
#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