55#if LASS_PLATFORM_TYPE == LASS_PLATFORM_TYPE_WIN32
56# pragma warning(disable: 4996)
68PyMethodDef createPyMethodDef(
const char *ml_name, PyCFunction ml_meth,
int ml_flags,
const char *ml_doc)
71 temp.ml_name =
const_cast<char*
>(ml_name);
72 temp.ml_meth = ml_meth;
73 temp.ml_flags = ml_flags;
74 temp.ml_doc =
const_cast<char*
>(ml_doc);
80PyGetSetDef createPyGetSetDef(
const char* name, getter get, setter set,
const char* doc,
void* closure )
83 temp.name =
const_cast<char*
>(name);
86 temp.doc =
const_cast<char*
>(doc);
87 temp.closure = closure;
93void dealloc(PyObject* obj)
95 delete static_cast<PyObjectPlus*
>(obj);
101 const char*
name,
const char*
doc, Py_ssize_t typeSize,
102 richcmpfunc richcmp,
ClassDefinition* parent, TClassRegisterHook registerHook):
103 slots_({{ 0,
nullptr }}),
105 classRegisterHook_(registerHook),
108 implicitConvertersSlot_(0),
113 static_cast<int>(typeSize),
115 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
119 setSlot(Py_tp_dealloc, &dealloc);
122 setSlot(Py_tp_richcompare, richcmp);
124 methods_.push_back(impl::createPyMethodDef( 0, 0, 0, 0 ));
125 getSetters_.push_back(impl::createPyGetSetDef( 0, 0, 0, 0, 0 ));
138 LASS_ENFORCE(type_)(
name())(
" is not frozen yet");
139 return reinterpret_cast<PyTypeObject*
>(type_.get());
146 LASS_ENFORCE(type_)(
name())(
" is not frozen yet");
147 return reinterpret_cast<PyTypeObject*
>(type_.get());
183 LASS_ASSERT(!slots_.empty() && slots_.back().slot == 0);
184 LASS_ASSERT(slotId > 0);
185 TSlots::iterator i = std::lower_bound(slots_.begin(), slots_.end() - 1, slotId, [](
const PyType_Slot& a,
TSlotID b) { return a.slot < b; });
186 if (i != slots_.end() && i->slot == slotId)
196 LASS_ASSERT(!isFrozen_);
197 LASS_ASSERT(!slots_.empty() && slots_.back().slot == 0);
198 LASS_ASSERT(slotId > 0);
200 TSlots::iterator i = std::lower_bound(slots_.begin(), slots_.end() - 1, slotId, [](
const PyType_Slot& a,
TSlotID b) { return a.slot < b; });
201 if (i != slots_.end() && i->slot == slotId)
203 void* old = i->pfunc;
209 PyType_Slot slot = { slotId, value };
210 slots_.insert(i, slot);
218 overloadChain =
setSlot(Py_tp_new, dispatcher);
224 TMethods::iterator i = std::find_if(methods_.begin(), methods_.end(), NamePredicate(
name));
225 if (i == methods_.end())
227 methods_.insert(methods_.begin(), createPyMethodDef(
name, dispatcher, METH_VARARGS ,
doc));
228 overloadChain.setNull();
232 LASS_ASSERT(i->ml_flags == METH_VARARGS);
233 overloadChain.setPyCFunction(i->ml_meth);
234 i->ml_meth = dispatcher;
237 i->ml_doc =
const_cast<char*
>(
doc);
244 compareFuncs_.push_back(CompareFunc(dispatcher, slot.slot));
249 setSlot(slot.slot, dispatcher);
254 setSlot(slot.slot, dispatcher);
259 overloadChain.setBinaryfunc(
setSlot(slot.slot, dispatcher));
264 overloadChain.setTernaryfunc(
setSlot(slot.slot, dispatcher));
269 overloadChain.setSsizeArgfunc(
setSlot(slot.slot, dispatcher));
274 overloadChain.setSsizeObjArgProcfunc(
setSlot(slot.slot, dispatcher));
279 overloadChain.setObjObjProcfunc(
setSlot(slot.slot, dispatcher));
284 overloadChain.setObjObjArgProcfunc(
setSlot(slot.slot, dispatcher));
289 overloadChain.setGetIterFunc(
setSlot(slot.slot, dispatcher));
294 overloadChain.setIterNextFunc(
setSlot(slot.slot, dispatcher));
299 overloadChain.setArgKwfunc(
setSlot(slot.slot, dispatcher));
304 setSlot(slot.slot, dispatcher);
309 getSetters_.insert(getSetters_.begin(), impl::createPyGetSetDef(
name, get, set,
doc, 0));
314 std::vector<PyMethodDef>::iterator i = ::std::find_if(methods_.begin(), methods_.end(), NamePredicate(
name));
315 if (i == methods_.end())
317 methods_.insert(methods_.begin(), createPyMethodDef(
name, dispatcher, METH_VARARGS | METH_STATIC,
doc));
322 LASS_ASSERT(i->ml_flags == (METH_VARARGS | METH_STATIC));
323 overloadChain = i->ml_meth;
324 i->ml_meth = dispatcher;
327 i->ml_doc =
const_cast<char*
>(
doc);
334 LASS_ASSERT(std::count_if(innerClasses_.begin(), innerClasses_.end(), NamePredicate(innerClass.
name())) == 0);
335 innerClasses_.push_back(&innerClass);
341 innerEnums_.push_back(enumDefinition);
351 LASS_ASSERT(!isFrozen_);
361 if (parent_ == &PyObjectPlus::_lassPyClassDef)
363 if (impl::initLassModule() != 0)
370 PyErr_Format(PyExc_AssertionError,
"Parent class %s of %s is not frozen yet", parent_->className_, className_);
373 parent_->subClasses_.push_back(
this);
376 const char* moduleName =
nullptr;
379 moduleName = PyModule_GetName(module);
384 LASS_ASSERT(!spec_.name || std::strcmp(spec_.name, moduleName) == 0);
387 const size_t n = std::strlen(moduleName) + std::strlen(className_) + 2;
388 char* buf =
static_cast<char*
>(std::malloc(n));
394 const int r = ::snprintf(buf, n,
"%s.%s", moduleName, className_);
395 LASS_ENFORCE(r > 0 &&
static_cast<size_t>(r) < n);
401 LASS_ASSERT(!spec_.name || std::strcmp(spec_.name, className_) == 0);
404 spec_.name = className_;
410 setSlot(Py_tp_base, parent_ ? parent_->type() : &PyBaseObject_Type);
411 setSlot(Py_tp_methods, &methods_[0]);
412 setSlot(Py_tp_getset, &getSetters_[0]);
415 setSlot(Py_tp_doc,
const_cast<char*
>(doc_));
417 LASS_ASSERT(slots_.back().slot == 0);
418 spec_.slots = &slots_[0];
420 if (
getSlot(Py_tp_new) ==
nullptr)
423 spec_.flags |= Py_TPFLAGS_DISALLOW_INSTANTIATION;
426 type_.reset(PyType_FromModuleAndSpec(module, &spec_,
nullptr));
433 PyObject*
type = type_.get();
435 const char* qualname = className_;
436 std::string scopedQualname;
439 scopedQualname = stde::safe_format(
"%s.%s", scopeName, className_);
440 qualname = scopedQualname.data();
441 TPyObjPtr objQualname(pyBuildSimpleObject(qualname));
442 if (!objQualname || PyObject_SetAttrString(
type,
"__qualname__", objQualname.get()) != 0)
447 for (TStaticMembers::const_iterator i = statics_.begin(); i != statics_.end(); ++i)
450 if (!obj || PyObject_SetAttrString(
type, i->name(), obj.get()) != 0)
455 for (TClassDefs::const_iterator i = innerClasses_.begin(); i != innerClasses_.end(); ++i)
458 const char* shortName = innerClass->name();
459 PyObject* innerType = innerClass->freezeDefinition(module, qualname);
460 if (!innerType || PyObject_SetAttrString(
type, shortName, innerType) != 0)
465 for (
auto def : innerEnums_)
467 PyObject* enumType = def->freezeDefinition(moduleName, qualname);
468 if (!enumType || PyObject_SetAttrString(
type, def->name(), enumType) != 0)
474 if (classRegisterHook_)
476 classRegisterHook_();
479 if (freezeType() != 0)
490int ClassDefinition::freezeType()
492 PyTypeObject*
type =
reinterpret_cast<PyTypeObject*
>(type_.get());
493#if PY_VERSION_HEX >= 0x030e0000
494 if (!PyType_HasFeature(
type, Py_TPFLAGS_IMMUTABLETYPE))
496 if (parent_ && !PyType_HasFeature(parent_->type(), Py_TPFLAGS_IMMUTABLETYPE))
499 LASS_ASSERT(std::find(parent_->subClasses_.begin(), parent_->subClasses_.end(),
this) != parent_->subClasses_.end());
502 if (PyType_Freeze(
type) != 0)
509 for (
auto *subClass : subClasses_)
512 LASS_ASSERT(subClass->isFrozen_);
513 if (subClass->freezeType() != 0)
517 LASS_ASSERT(PyType_HasFeature(subClass->type(), Py_TPFLAGS_IMMUTABLETYPE));
520 type->tp_flags |= Py_TPFLAGS_IMMUTABLETYPE;
531 if (other == Py_None)
557 TPyObjPtr args(Py_BuildValue(
"(O)", other));
558 const TCompareFuncs::const_iterator end = compareFuncs_.end();
559 for (TCompareFuncs::const_iterator i = compareFuncs_.begin(); i != end; ++i)
563 PyObject* result = (i->dispatcher)(self, args.get());\
564 if (result || (PyErr_Occurred() && !PyErr_ExceptionMatches(PyExc_TypeError)))
573 static const char* symbols[] = {
"<",
"<=",
"==",
"!=",
">",
">=" };
574 LASS_ASSERT(op >= 0 && op <= Py_GE);
575 std::ostringstream buffer;
576 buffer <<
"Comparison operator " << symbols[op] <<
" not implemented for this type";
577 PyErr_SetString(PyExc_NotImplementedError, buffer.str().c_str());
581 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.