54#if LASS_PLATFORM_TYPE == LASS_PLATFORM_TYPE_WIN32
55# pragma warning(disable: 4996)
67PyMethodDef createPyMethodDef(
const char *ml_name, PyCFunction ml_meth,
int ml_flags,
const char *ml_doc)
70 temp.ml_name =
const_cast<char*
>(ml_name);
71 temp.ml_meth = ml_meth;
72 temp.ml_flags = ml_flags;
73 temp.ml_doc =
const_cast<char*
>(ml_doc);
79PyGetSetDef createPyGetSetDef(
const char* name, getter get, setter set,
const char* doc,
void* closure )
82 temp.name =
const_cast<char*
>(name);
85 temp.doc =
const_cast<char*
>(doc);
86 temp.closure = closure;
92void dealloc(PyObject* obj)
94 delete static_cast<PyObjectPlus*
>(obj);
99int traverse(PyObject* self, visitproc visit,
void* arg)
101 Py_VISIT(Py_TYPE(self));
108 const char*
name,
const char*
doc, Py_ssize_t typeSize,
109 richcmpfunc richcmp,
ClassDefinition* parent, TClassRegisterHook registerHook):
110 slots_({{ 0,
nullptr }}),
112 classRegisterHook_(registerHook),
115 implicitConvertersSlot_(0),
120 static_cast<int>(typeSize),
122 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC,
126 setSlot(Py_tp_dealloc, &dealloc);
127 setSlot(Py_tp_traverse, &traverse);
130 setSlot(Py_tp_richcompare, richcmp);
132 methods_.push_back(impl::createPyMethodDef( 0, 0, 0, 0 ));
133 getSetters_.push_back(impl::createPyGetSetDef( 0, 0, 0, 0, 0 ));
146 LASS_ENFORCE(isFrozen_)(
name())(
" is not frozen yet");
147 return reinterpret_cast<PyTypeObject*
>(type_.get());
154 LASS_ENFORCE(isFrozen_)(
name())(
" is not frozen yet");
155 return reinterpret_cast<PyTypeObject*
>(type_.get());
191 LASS_ASSERT(!slots_.empty() && slots_.back().slot == 0);
192 LASS_ASSERT(slotId > 0);
193 TSlots::iterator i = std::lower_bound(slots_.begin(), slots_.end() - 1, slotId, [](
const PyType_Slot& a,
TSlotID b) { return a.slot < b; });
194 if (i != slots_.end() && i->slot == slotId)
204 LASS_ASSERT(!isFrozen_);
205 LASS_ASSERT(!slots_.empty() && slots_.back().slot == 0);
206 LASS_ASSERT(slotId > 0);
208 TSlots::iterator i = std::lower_bound(slots_.begin(), slots_.end() - 1, slotId, [](
const PyType_Slot& a,
TSlotID b) { return a.slot < b; });
209 if (i != slots_.end() && i->slot == slotId)
211 void* old = i->pfunc;
217 PyType_Slot slot = { slotId, value };
218 slots_.insert(i, slot);
226 overloadChain =
setSlot(Py_tp_new, dispatcher);
232 TMethods::iterator i = std::find_if(methods_.begin(), methods_.end(), NamePredicate(
name));
233 if (i == methods_.end())
235 methods_.insert(methods_.begin(), createPyMethodDef(
name, dispatcher, METH_VARARGS ,
doc));
236 overloadChain.setNull();
240 LASS_ASSERT(i->ml_flags == METH_VARARGS);
241 overloadChain.setPyCFunction(i->ml_meth);
242 i->ml_meth = dispatcher;
245 i->ml_doc =
const_cast<char*
>(
doc);
252 compareFuncs_.push_back(CompareFunc(dispatcher, slot.slot));
257 setSlot(slot.slot, dispatcher);
262 setSlot(slot.slot, dispatcher);
267 overloadChain.setBinaryfunc(
setSlot(slot.slot, dispatcher));
272 overloadChain.setTernaryfunc(
setSlot(slot.slot, dispatcher));
277 overloadChain.setSsizeArgfunc(
setSlot(slot.slot, dispatcher));
282 overloadChain.setSsizeObjArgProcfunc(
setSlot(slot.slot, dispatcher));
287 overloadChain.setObjObjProcfunc(
setSlot(slot.slot, dispatcher));
292 overloadChain.setObjObjArgProcfunc(
setSlot(slot.slot, dispatcher));
297 overloadChain.setGetIterFunc(
setSlot(slot.slot, dispatcher));
302 overloadChain.setIterNextFunc(
setSlot(slot.slot, dispatcher));
307 overloadChain.setArgKwfunc(
setSlot(slot.slot, dispatcher));
312 setSlot(slot.slot, dispatcher);
317 getSetters_.insert(getSetters_.begin(), impl::createPyGetSetDef(
name, get, set,
doc, 0));
322 std::vector<PyMethodDef>::iterator i = ::std::find_if(methods_.begin(), methods_.end(), NamePredicate(
name));
323 if (i == methods_.end())
325 methods_.insert(methods_.begin(), createPyMethodDef(
name, dispatcher, METH_VARARGS | METH_STATIC,
doc));
330 LASS_ASSERT(i->ml_flags == (METH_VARARGS | METH_STATIC));
331 overloadChain = i->ml_meth;
332 i->ml_meth = dispatcher;
335 i->ml_doc =
const_cast<char*
>(
doc);
342 LASS_ASSERT(std::count_if(innerClasses_.begin(), innerClasses_.end(), NamePredicate(innerClass.
name())) == 0);
343 innerClasses_.push_back(&innerClass);
349 innerEnums_.push_back(enumDefinition);
359 LASS_ASSERT(!isFrozen_);
369 if (parent_ == &PyObjectPlus::_lassPyClassDef)
371 if (impl::initLassModule() != 0)
376 if (!parent_->isFrozen_)
378 PyErr_Format(PyExc_AssertionError,
"Parent class %s of %s is not frozen yet", parent_->className_, className_);
380 parent_->subClasses_.push_back(
this);
383 const char* moduleName =
nullptr;
386 moduleName = PyModule_GetName(module);
391 LASS_ASSERT(!spec_.name || std::strcmp(spec_.name, moduleName) == 0);
394 const size_t n = std::strlen(moduleName) + std::strlen(className_) + 2;
395 char* buf =
static_cast<char*
>(std::malloc(n));
396 const int r = ::snprintf(buf, n,
"%s.%s", moduleName, className_);
397 LASS_ENFORCE(r > 0 &&
static_cast<size_t>(r) < n);
403 LASS_ASSERT(!spec_.name || std::strcmp(spec_.name, className_) == 0);
406 spec_.name = className_;
412 setSlot(Py_tp_base, parent_ ? parent_->type() : &PyBaseObject_Type);
413 setSlot(Py_tp_methods, &methods_[0]);
414 setSlot(Py_tp_getset, &getSetters_[0]);
417 setSlot(Py_tp_doc,
const_cast<char*
>(doc_));
419 LASS_ASSERT(slots_.back().slot == 0);
420 spec_.slots = &slots_[0];
422#if PY_VERSION_HEX >= 0x030a0000
423 if (
getSlot(Py_tp_new) ==
nullptr)
426 spec_.flags |= Py_TPFLAGS_DISALLOW_INSTANTIATION;
430 type_.reset(PyType_FromModuleAndSpec(module, &spec_,
nullptr));
437 PyObject*
type = type_.get();
439 const char* qualname = className_;
442 const size_t n = std::strlen(scopeName) + std::strlen(className_) + 2;
443 char* buf =
static_cast<char*
>(std::malloc(n));
444 const int r = ::snprintf(buf, n,
"%s.%s", scopeName, className_);
445 LASS_ENFORCE(r > 0 &&
static_cast<size_t>(r) < n);
447 TPyObjPtr objQualname(pyBuildSimpleObject(qualname));
448 if (!objQualname || PyObject_SetAttrString(
type,
"__qualname__", objQualname.get()) != 0)
453 for (TStaticMembers::const_iterator i = statics_.begin(); i != statics_.end(); ++i)
456 if (!obj || PyObject_SetAttrString(
type, i->name(), obj.get()) != 0)
461 for (TClassDefs::const_iterator i = innerClasses_.begin(); i != innerClasses_.end(); ++i)
464 const char* shortName = innerClass->name();
465 PyObject* innerType = innerClass->freezeDefinition(module, qualname);
466 if (!innerType || PyObject_SetAttrString(
type, shortName, innerType) != 0)
471 for (
auto def : innerEnums_)
473 PyObject* enumType = def->freezeDefinition(moduleName, qualname);
474 if (!enumType || PyObject_SetAttrString(
type, def->name(), enumType) != 0)
480 if (classRegisterHook_)
482 classRegisterHook_();
485#if PY_VERSION_HEX >= 0x030a0000
486 reinterpret_cast<PyTypeObject*
>(
type)->tp_flags |= Py_TPFLAGS_IMMUTABLETYPE;
497 if (other == Py_None)
523 TPyObjPtr args(Py_BuildValue(
"(O)", other));
524 const TCompareFuncs::const_iterator end = compareFuncs_.end();
525 for (TCompareFuncs::const_iterator i = compareFuncs_.begin(); i != end; ++i)
529 PyObject* result = (i->dispatcher)(self, args.get());\
530 if (result || (PyErr_Occurred() && !PyErr_ExceptionMatches(PyExc_TypeError)))
539 static const char* symbols[] = {
"<",
"<=",
"==",
"!=",
">",
">=" };
540 LASS_ASSERT(op >= 0 && op <= Py_GE);
541 std::ostringstream buffer;
542 buffer <<
"Comparison operator " << symbols[op] <<
" not implemented for this type";
543 PyErr_SetString(PyExc_NotImplementedError, buffer.str().c_str());
547 return parent_->callRichCompare(self, other, op);
Base class of all enum definitions.
void addGetSetter(const char *name, const char *doc, getter get, setter set)
Add a property with optional getter/setter.
void setDocIfNotNull(const char *doc)
Set the class docstring if non-null (keeps existing one if nullptr).
PyObject * callRichCompare(PyObject *self, PyObject *other, int op)
Dispatch rich-compare for this class (used by operator slots).
void addMethod(const char *name, const char *doc, PyCFunction dispatcher, OverloadLink &overloadChain)
Add a named method.
void * setSlot(TSlotID slotId, void *value)
Set raw pointer for a given slot id.
ClassDefinition(const char *name, const char *doc, Py_ssize_t typeSize, richcmpfunc richcmp, ClassDefinition *parent, TClassRegisterHook registerHook)
Construct a class definition.
~ClassDefinition()
Destructor.
void addInnerClass(ClassDefinition &innerClass)
Add a nested class definition (inner class).
int TSlotID
Function to call during registration of the class (optional).
PyTypeObject * type()
Get the Python type object (available after freezeDefinition() has been called).
const char * doc() const
Get the class docstring.
void addStaticMethod(const char *name, const char *doc, PyCFunction dispatcher, PyCFunction &overloadChain)
Add a static method with overload support.
void addInnerEnum(EnumDefinitionBase *enumDefinition)
Add a nested enum definition (inner enum).
void setDoc(const char *doc)
Set the class docstring.
void addConstructor(newfunc dispatcher, newfunc &overloadChain)
Add a constructor overload (__init__ dispatcher).
const char * name() const
Get the class name.
PyObject * freezeDefinition(PyObject *module=nullptr)
Finalize the definition and create the Python type.
void * getSlot(TSlotID slotId)
Get raw pointer from a given slot id.
PyObjectPtr< PyObject >::Type TPyObjPtr
PyObjectPtr to a PyObject.
Comprehensive C++ to Python binding library.
Library for Assembled Shared Sources.