Migrate static PyTypeObject to inline, delete PyTypeCache workarounds
All 27 PyTypeObject declarations in namespace mcrfpydef headers changed from `static` to `inline` (C++17), ensuring a single global instance across translation units. This fixes the root cause of stale-type-pointer segfaults where only the McRFPy_API.cpp copy was PyType_Ready'd. Replaced ~20 PyTypeCache call sites and 2 PyRAII::PyTypeRef lookups with direct &mcrfpydef::Type references. Deleted PyTypeCache.h/.cpp, PyObjectUtils.h, and PyRAII.h (all were workarounds for the static bug). 228/228 tests pass. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
6fdf7279ce
commit
bb72040396
37 changed files with 74 additions and 600 deletions
|
|
@ -5,7 +5,6 @@
|
||||||
#include "MeshLayer.h" // For MeshVertex
|
#include "MeshLayer.h" // For MeshVertex
|
||||||
#include "../platform/GLContext.h"
|
#include "../platform/GLContext.h"
|
||||||
#include "../PyTexture.h"
|
#include "../PyTexture.h"
|
||||||
#include "../PyTypeCache.h"
|
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
|
|
@ -355,8 +354,8 @@ int Billboard::init(PyObject* self, PyObject* args, PyObject* kwds) {
|
||||||
|
|
||||||
// Handle texture
|
// Handle texture
|
||||||
if (textureObj && textureObj != Py_None) {
|
if (textureObj && textureObj != Py_None) {
|
||||||
PyTypeObject* textureType = PyTypeCache::Texture();
|
PyTypeObject* textureType = &mcrfpydef::PyTextureType;
|
||||||
if (textureType && PyObject_IsInstance(textureObj, (PyObject*)textureType)) {
|
if (PyObject_IsInstance(textureObj, (PyObject*)textureType)) {
|
||||||
PyTextureObject* texPy = (PyTextureObject*)textureObj;
|
PyTextureObject* texPy = (PyTextureObject*)textureObj;
|
||||||
if (texPy->data) {
|
if (texPy->data) {
|
||||||
selfObj->data->setTexture(texPy->data);
|
selfObj->data->setTexture(texPy->data);
|
||||||
|
|
@ -443,12 +442,7 @@ int Billboard::set_texture(PyObject* self, PyObject* value, void* closure) {
|
||||||
obj->data->setTexture(nullptr);
|
obj->data->setTexture(nullptr);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
// Use PyTypeCache to get properly initialized type object
|
PyTypeObject* textureType = &mcrfpydef::PyTextureType;
|
||||||
PyTypeObject* textureType = PyTypeCache::Texture();
|
|
||||||
if (!textureType) {
|
|
||||||
PyErr_SetString(PyExc_RuntimeError, "Texture type not initialized");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
if (PyObject_IsInstance(value, (PyObject*)textureType)) {
|
if (PyObject_IsInstance(value, (PyObject*)textureType)) {
|
||||||
PyTextureObject* texPy = (PyTextureObject*)value;
|
PyTextureObject* texPy = (PyTextureObject*)value;
|
||||||
if (texPy->data) {
|
if (texPy->data) {
|
||||||
|
|
|
||||||
|
|
@ -246,7 +246,7 @@ public:
|
||||||
|
|
||||||
namespace mcrfpydef {
|
namespace mcrfpydef {
|
||||||
// ColorLayer type
|
// ColorLayer type
|
||||||
static PyTypeObject PyColorLayerType = {
|
inline PyTypeObject PyColorLayerType = {
|
||||||
.ob_base = {.ob_base = {.ob_refcnt = 1, .ob_type = NULL}, .ob_size = 0},
|
.ob_base = {.ob_base = {.ob_refcnt = 1, .ob_type = NULL}, .ob_size = 0},
|
||||||
.tp_name = "mcrfpy.ColorLayer",
|
.tp_name = "mcrfpy.ColorLayer",
|
||||||
.tp_basicsize = sizeof(PyColorLayerObject),
|
.tp_basicsize = sizeof(PyColorLayerObject),
|
||||||
|
|
@ -299,7 +299,7 @@ namespace mcrfpydef {
|
||||||
};
|
};
|
||||||
|
|
||||||
// TileLayer type
|
// TileLayer type
|
||||||
static PyTypeObject PyTileLayerType = {
|
inline PyTypeObject PyTileLayerType = {
|
||||||
.ob_base = {.ob_base = {.ob_refcnt = 1, .ob_type = NULL}, .ob_size = 0},
|
.ob_base = {.ob_base = {.ob_refcnt = 1, .ob_type = NULL}, .ob_size = 0},
|
||||||
.tp_name = "mcrfpy.TileLayer",
|
.tp_name = "mcrfpy.TileLayer",
|
||||||
.tp_basicsize = sizeof(PyTileLayerObject),
|
.tp_basicsize = sizeof(PyTileLayerObject),
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,6 @@
|
||||||
#include "McRFPy_Automation.h"
|
#include "McRFPy_Automation.h"
|
||||||
// Note: McRFPy_Libtcod.h removed in #215 - functionality moved to mcrfpy.bresenham()
|
// Note: McRFPy_Libtcod.h removed in #215 - functionality moved to mcrfpy.bresenham()
|
||||||
#include "McRFPy_Doc.h"
|
#include "McRFPy_Doc.h"
|
||||||
#include "PyTypeCache.h" // Thread-safe cached Python types
|
|
||||||
#include "platform.h"
|
#include "platform.h"
|
||||||
#include "PyAnimation.h"
|
#include "PyAnimation.h"
|
||||||
#include "PyDrawable.h"
|
#include "PyDrawable.h"
|
||||||
|
|
@ -756,14 +755,6 @@ PyObject* PyInit_mcrfpy()
|
||||||
// - line() functionality replaced by mcrfpy.bresenham()
|
// - line() functionality replaced by mcrfpy.bresenham()
|
||||||
// - compute_fov() redundant with Grid.compute_fov()
|
// - compute_fov() redundant with Grid.compute_fov()
|
||||||
|
|
||||||
// Initialize PyTypeCache for thread-safe type lookups
|
|
||||||
// This must be done after all types are added to the module
|
|
||||||
if (!PyTypeCache::initialize(m)) {
|
|
||||||
// Failed to initialize type cache - this is a critical error
|
|
||||||
// Error message already set by PyTypeCache::initialize
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
//McRFPy_API::mcrf_module = m;
|
//McRFPy_API::mcrf_module = m;
|
||||||
return m;
|
return m;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -38,7 +38,7 @@ public:
|
||||||
};
|
};
|
||||||
|
|
||||||
namespace mcrfpydef {
|
namespace mcrfpydef {
|
||||||
static PyTypeObject PyAnimationType = {
|
inline PyTypeObject PyAnimationType = {
|
||||||
.ob_base = {.ob_base = {.ob_refcnt = 1, .ob_type = NULL}, .ob_size = 0},
|
.ob_base = {.ob_base = {.ob_refcnt = 1, .ob_type = NULL}, .ob_size = 0},
|
||||||
.tp_name = "mcrfpy.Animation",
|
.tp_name = "mcrfpy.Animation",
|
||||||
.tp_basicsize = sizeof(PyAnimationObject),
|
.tp_basicsize = sizeof(PyAnimationObject),
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,5 @@
|
||||||
#include "PyColor.h"
|
#include "PyColor.h"
|
||||||
#include "McRFPy_API.h"
|
#include "McRFPy_API.h"
|
||||||
#include "PyObjectUtils.h"
|
|
||||||
#include "PyRAII.h"
|
|
||||||
#include "McRFPy_Doc.h"
|
#include "McRFPy_Doc.h"
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
|
|
@ -288,33 +286,28 @@ int PyColor::set_member(PyObject* obj, PyObject* value, void* closure)
|
||||||
|
|
||||||
PyColorObject* PyColor::from_arg(PyObject* args)
|
PyColorObject* PyColor::from_arg(PyObject* args)
|
||||||
{
|
{
|
||||||
// Use RAII for type reference management
|
PyTypeObject* type = &mcrfpydef::PyColorType;
|
||||||
PyRAII::PyTypeRef type("Color", McRFPy_API::mcrf_module);
|
|
||||||
if (!type) {
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if args is already a Color instance
|
// Check if args is already a Color instance
|
||||||
if (PyObject_IsInstance(args, (PyObject*)type.get())) {
|
if (PyObject_IsInstance(args, (PyObject*)type)) {
|
||||||
Py_INCREF(args); // Return new reference so caller can safely DECREF
|
Py_INCREF(args); // Return new reference so caller can safely DECREF
|
||||||
return (PyColorObject*)args;
|
return (PyColorObject*)args;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create new Color object using RAII
|
// Create new Color object
|
||||||
PyRAII::PyObjectRef obj(type->tp_alloc(type.get(), 0), true);
|
PyObject* obj = type->tp_alloc(type, 0);
|
||||||
if (!obj) {
|
if (!obj) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize the object
|
// Initialize the object
|
||||||
int err = init((PyColorObject*)obj.get(), args, NULL);
|
int err = init((PyColorObject*)obj, args, NULL);
|
||||||
if (err) {
|
if (err) {
|
||||||
// obj will be automatically cleaned up when it goes out of scope
|
Py_DECREF(obj);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Release ownership and return
|
return (PyColorObject*)obj;
|
||||||
return (PyColorObject*)obj.release();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Color helper method implementations
|
// Color helper method implementations
|
||||||
|
|
|
||||||
|
|
@ -39,7 +39,7 @@ public:
|
||||||
};
|
};
|
||||||
|
|
||||||
namespace mcrfpydef {
|
namespace mcrfpydef {
|
||||||
static PyTypeObject PyColorType = {
|
inline PyTypeObject PyColorType = {
|
||||||
.ob_base = {.ob_base = {.ob_refcnt = 1, .ob_type = NULL}, .ob_size = 0},
|
.ob_base = {.ob_base = {.ob_refcnt = 1, .ob_type = NULL}, .ob_size = 0},
|
||||||
.tp_name = "mcrfpy.Color",
|
.tp_name = "mcrfpy.Color",
|
||||||
.tp_basicsize = sizeof(PyColorObject),
|
.tp_basicsize = sizeof(PyColorObject),
|
||||||
|
|
|
||||||
|
|
@ -77,7 +77,7 @@ public:
|
||||||
};
|
};
|
||||||
|
|
||||||
namespace mcrfpydef {
|
namespace mcrfpydef {
|
||||||
static PyTypeObject PyDiscreteMapType = {
|
inline PyTypeObject PyDiscreteMapType = {
|
||||||
.ob_base = {.ob_base = {.ob_refcnt = 1, .ob_type = NULL}, .ob_size = 0},
|
.ob_base = {.ob_base = {.ob_refcnt = 1, .ob_type = NULL}, .ob_size = 0},
|
||||||
.tp_name = "mcrfpy.DiscreteMap",
|
.tp_name = "mcrfpy.DiscreteMap",
|
||||||
.tp_basicsize = sizeof(PyDiscreteMapObject),
|
.tp_basicsize = sizeof(PyDiscreteMapObject),
|
||||||
|
|
|
||||||
|
|
@ -30,7 +30,7 @@ public:
|
||||||
};
|
};
|
||||||
|
|
||||||
namespace mcrfpydef {
|
namespace mcrfpydef {
|
||||||
static PyTypeObject PyFontType = {
|
inline PyTypeObject PyFontType = {
|
||||||
.ob_base = {.ob_base = {.ob_refcnt = 1, .ob_type = NULL}, .ob_size = 0},
|
.ob_base = {.ob_base = {.ob_refcnt = 1, .ob_type = NULL}, .ob_size = 0},
|
||||||
.tp_name = "mcrfpy.Font",
|
.tp_name = "mcrfpy.Font",
|
||||||
.tp_basicsize = sizeof(PyFontObject),
|
.tp_basicsize = sizeof(PyFontObject),
|
||||||
|
|
|
||||||
|
|
@ -88,7 +88,7 @@ public:
|
||||||
};
|
};
|
||||||
|
|
||||||
namespace mcrfpydef {
|
namespace mcrfpydef {
|
||||||
static PyTypeObject PyHeightMapType = {
|
inline PyTypeObject PyHeightMapType = {
|
||||||
.ob_base = {.ob_base = {.ob_refcnt = 1, .ob_type = NULL}, .ob_size = 0},
|
.ob_base = {.ob_base = {.ob_refcnt = 1, .ob_type = NULL}, .ob_size = 0},
|
||||||
.tp_name = "mcrfpy.HeightMap",
|
.tp_name = "mcrfpy.HeightMap",
|
||||||
.tp_basicsize = sizeof(PyHeightMapObject),
|
.tp_basicsize = sizeof(PyHeightMapObject),
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,7 @@ public:
|
||||||
};
|
};
|
||||||
|
|
||||||
namespace mcrfpydef {
|
namespace mcrfpydef {
|
||||||
static PyTypeObject PyKeyboardType = {
|
inline PyTypeObject PyKeyboardType = {
|
||||||
.ob_base = {.ob_base = {.ob_refcnt = 1, .ob_type = NULL}, .ob_size = 0},
|
.ob_base = {.ob_base = {.ob_refcnt = 1, .ob_type = NULL}, .ob_size = 0},
|
||||||
.tp_name = "mcrfpy.Keyboard",
|
.tp_name = "mcrfpy.Keyboard",
|
||||||
.tp_basicsize = sizeof(PyKeyboardObject),
|
.tp_basicsize = sizeof(PyKeyboardObject),
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,7 @@ public:
|
||||||
};
|
};
|
||||||
|
|
||||||
namespace mcrfpydef {
|
namespace mcrfpydef {
|
||||||
static PyTypeObject PyMouseType = {
|
inline PyTypeObject PyMouseType = {
|
||||||
.ob_base = {.ob_base = {.ob_refcnt = 1, .ob_type = NULL}, .ob_size = 0},
|
.ob_base = {.ob_base = {.ob_refcnt = 1, .ob_type = NULL}, .ob_size = 0},
|
||||||
.tp_name = "mcrfpy.Mouse",
|
.tp_name = "mcrfpy.Mouse",
|
||||||
.tp_basicsize = sizeof(PyMouseObject),
|
.tp_basicsize = sizeof(PyMouseObject),
|
||||||
|
|
|
||||||
|
|
@ -62,7 +62,7 @@ public:
|
||||||
};
|
};
|
||||||
|
|
||||||
namespace mcrfpydef {
|
namespace mcrfpydef {
|
||||||
static PyTypeObject PyMusicType = {
|
inline PyTypeObject PyMusicType = {
|
||||||
.ob_base = {.ob_base = {.ob_refcnt = 1, .ob_type = NULL}, .ob_size = 0},
|
.ob_base = {.ob_base = {.ob_refcnt = 1, .ob_type = NULL}, .ob_size = 0},
|
||||||
.tp_name = "mcrfpy.Music",
|
.tp_name = "mcrfpy.Music",
|
||||||
.tp_basicsize = sizeof(PyMusicObject),
|
.tp_basicsize = sizeof(PyMusicObject),
|
||||||
|
|
|
||||||
|
|
@ -1,76 +0,0 @@
|
||||||
#pragma once
|
|
||||||
#include "Common.h"
|
|
||||||
#include "Python.h"
|
|
||||||
#include "McRFPy_API.h"
|
|
||||||
#include "PyRAII.h"
|
|
||||||
|
|
||||||
namespace PyObjectUtils {
|
|
||||||
|
|
||||||
// Template for getting Python type object from module
|
|
||||||
template<typename T>
|
|
||||||
PyTypeObject* getPythonType(const char* typeName) {
|
|
||||||
PyTypeObject* type = (PyTypeObject*)PyObject_GetAttrString(McRFPy_API::mcrf_module, typeName);
|
|
||||||
if (!type) {
|
|
||||||
PyErr_Format(PyExc_RuntimeError, "Could not find %s type in module", typeName);
|
|
||||||
}
|
|
||||||
return type;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Generic function to create a Python object of given type
|
|
||||||
inline PyObject* createPyObjectGeneric(const char* typeName) {
|
|
||||||
PyTypeObject* type = getPythonType<void>(typeName);
|
|
||||||
if (!type) return nullptr;
|
|
||||||
|
|
||||||
PyObject* obj = type->tp_alloc(type, 0);
|
|
||||||
Py_DECREF(type);
|
|
||||||
|
|
||||||
return obj;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Helper function to allocate and initialize a Python object with data
|
|
||||||
template<typename PyObjType, typename DataType>
|
|
||||||
PyObject* createPyObjectWithData(const char* typeName, DataType data) {
|
|
||||||
PyTypeObject* type = getPythonType<void>(typeName);
|
|
||||||
if (!type) return nullptr;
|
|
||||||
|
|
||||||
PyObjType* obj = (PyObjType*)type->tp_alloc(type, 0);
|
|
||||||
Py_DECREF(type);
|
|
||||||
|
|
||||||
if (obj) {
|
|
||||||
obj->data = data;
|
|
||||||
}
|
|
||||||
return (PyObject*)obj;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Function to convert UIDrawable to appropriate Python object
|
|
||||||
// This is moved to UICollection.cpp to avoid circular dependencies
|
|
||||||
|
|
||||||
// RAII-based object creation example
|
|
||||||
inline PyObject* createPyObjectGenericRAII(const char* typeName) {
|
|
||||||
PyRAII::PyTypeRef type(typeName, McRFPy_API::mcrf_module);
|
|
||||||
if (!type) {
|
|
||||||
PyErr_Format(PyExc_RuntimeError, "Could not find %s type in module", typeName);
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
PyObject* obj = type->tp_alloc(type.get(), 0);
|
|
||||||
// Return the new reference (caller owns it)
|
|
||||||
return obj;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Example of using PyObjectRef for safer reference management
|
|
||||||
template<typename PyObjType, typename DataType>
|
|
||||||
PyObject* createPyObjectWithDataRAII(const char* typeName, DataType data) {
|
|
||||||
PyRAII::PyObjectRef obj = PyRAII::createObject<PyObjType>(typeName, McRFPy_API::mcrf_module);
|
|
||||||
if (!obj) {
|
|
||||||
PyErr_Format(PyExc_RuntimeError, "Could not create %s object", typeName);
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Access the object through the RAII wrapper
|
|
||||||
((PyObjType*)obj.get())->data = data;
|
|
||||||
|
|
||||||
// Release ownership to return to Python
|
|
||||||
return obj.release();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
138
src/PyRAII.h
138
src/PyRAII.h
|
|
@ -1,138 +0,0 @@
|
||||||
#pragma once
|
|
||||||
#include "Python.h"
|
|
||||||
#include <utility>
|
|
||||||
|
|
||||||
namespace PyRAII {
|
|
||||||
|
|
||||||
// RAII wrapper for PyObject* that automatically manages reference counting
|
|
||||||
class PyObjectRef {
|
|
||||||
private:
|
|
||||||
PyObject* ptr;
|
|
||||||
|
|
||||||
public:
|
|
||||||
// Constructors
|
|
||||||
PyObjectRef() : ptr(nullptr) {}
|
|
||||||
|
|
||||||
explicit PyObjectRef(PyObject* p, bool steal_ref = false) : ptr(p) {
|
|
||||||
if (ptr && !steal_ref) {
|
|
||||||
Py_INCREF(ptr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Copy constructor
|
|
||||||
PyObjectRef(const PyObjectRef& other) : ptr(other.ptr) {
|
|
||||||
if (ptr) {
|
|
||||||
Py_INCREF(ptr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Move constructor
|
|
||||||
PyObjectRef(PyObjectRef&& other) noexcept : ptr(other.ptr) {
|
|
||||||
other.ptr = nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Destructor
|
|
||||||
~PyObjectRef() {
|
|
||||||
Py_XDECREF(ptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Copy assignment
|
|
||||||
PyObjectRef& operator=(const PyObjectRef& other) {
|
|
||||||
if (this != &other) {
|
|
||||||
Py_XDECREF(ptr);
|
|
||||||
ptr = other.ptr;
|
|
||||||
if (ptr) {
|
|
||||||
Py_INCREF(ptr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Move assignment
|
|
||||||
PyObjectRef& operator=(PyObjectRef&& other) noexcept {
|
|
||||||
if (this != &other) {
|
|
||||||
Py_XDECREF(ptr);
|
|
||||||
ptr = other.ptr;
|
|
||||||
other.ptr = nullptr;
|
|
||||||
}
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Access operators
|
|
||||||
PyObject* get() const { return ptr; }
|
|
||||||
PyObject* operator->() const { return ptr; }
|
|
||||||
PyObject& operator*() const { return *ptr; }
|
|
||||||
operator bool() const { return ptr != nullptr; }
|
|
||||||
|
|
||||||
// Release ownership (for returning to Python)
|
|
||||||
PyObject* release() {
|
|
||||||
PyObject* temp = ptr;
|
|
||||||
ptr = nullptr;
|
|
||||||
return temp;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Reset with new pointer
|
|
||||||
void reset(PyObject* p = nullptr, bool steal_ref = false) {
|
|
||||||
if (p != ptr) {
|
|
||||||
Py_XDECREF(ptr);
|
|
||||||
ptr = p;
|
|
||||||
if (ptr && !steal_ref) {
|
|
||||||
Py_INCREF(ptr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Helper class for managing PyTypeObject* references from module lookups
|
|
||||||
class PyTypeRef {
|
|
||||||
private:
|
|
||||||
PyTypeObject* type;
|
|
||||||
|
|
||||||
public:
|
|
||||||
PyTypeRef() : type(nullptr) {}
|
|
||||||
|
|
||||||
explicit PyTypeRef(const char* typeName, PyObject* module) {
|
|
||||||
type = (PyTypeObject*)PyObject_GetAttrString(module, typeName);
|
|
||||||
// GetAttrString returns a new reference, so we own it
|
|
||||||
}
|
|
||||||
|
|
||||||
~PyTypeRef() {
|
|
||||||
Py_XDECREF((PyObject*)type);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Delete copy operations to prevent accidental reference issues
|
|
||||||
PyTypeRef(const PyTypeRef&) = delete;
|
|
||||||
PyTypeRef& operator=(const PyTypeRef&) = delete;
|
|
||||||
|
|
||||||
// Allow move operations
|
|
||||||
PyTypeRef(PyTypeRef&& other) noexcept : type(other.type) {
|
|
||||||
other.type = nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
PyTypeRef& operator=(PyTypeRef&& other) noexcept {
|
|
||||||
if (this != &other) {
|
|
||||||
Py_XDECREF((PyObject*)type);
|
|
||||||
type = other.type;
|
|
||||||
other.type = nullptr;
|
|
||||||
}
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
PyTypeObject* get() const { return type; }
|
|
||||||
PyTypeObject* operator->() const { return type; }
|
|
||||||
operator bool() const { return type != nullptr; }
|
|
||||||
};
|
|
||||||
|
|
||||||
// Convenience function to create a new object with RAII
|
|
||||||
template<typename PyObjType>
|
|
||||||
PyObjectRef createObject(const char* typeName, PyObject* module) {
|
|
||||||
PyTypeRef type(typeName, module);
|
|
||||||
if (!type) {
|
|
||||||
return PyObjectRef();
|
|
||||||
}
|
|
||||||
|
|
||||||
PyObject* obj = type->tp_alloc(type.get(), 0);
|
|
||||||
// tp_alloc returns a new reference, so we steal it
|
|
||||||
return PyObjectRef(obj, true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -47,7 +47,7 @@ public:
|
||||||
};
|
};
|
||||||
|
|
||||||
namespace mcrfpydef {
|
namespace mcrfpydef {
|
||||||
static PyTypeObject PySceneType = {
|
inline PyTypeObject PySceneType = {
|
||||||
.ob_base = {.ob_base = {.ob_refcnt = 1, .ob_type = NULL}, .ob_size = 0},
|
.ob_base = {.ob_base = {.ob_refcnt = 1, .ob_type = NULL}, .ob_size = 0},
|
||||||
.tp_name = "mcrfpy.Scene",
|
.tp_name = "mcrfpy.Scene",
|
||||||
.tp_basicsize = sizeof(PySceneObject),
|
.tp_basicsize = sizeof(PySceneObject),
|
||||||
|
|
|
||||||
|
|
@ -59,7 +59,7 @@ public:
|
||||||
};
|
};
|
||||||
|
|
||||||
namespace mcrfpydef {
|
namespace mcrfpydef {
|
||||||
static PyTypeObject PySoundType = {
|
inline PyTypeObject PySoundType = {
|
||||||
.ob_base = {.ob_base = {.ob_refcnt = 1, .ob_type = NULL}, .ob_size = 0},
|
.ob_base = {.ob_base = {.ob_refcnt = 1, .ob_type = NULL}, .ob_size = 0},
|
||||||
.tp_name = "mcrfpy.Sound",
|
.tp_name = "mcrfpy.Sound",
|
||||||
.tp_basicsize = sizeof(PySoundObject),
|
.tp_basicsize = sizeof(PySoundObject),
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
#include "PyTexture.h"
|
#include "PyTexture.h"
|
||||||
#include "McRFPy_API.h"
|
#include "McRFPy_API.h"
|
||||||
#include "McRFPy_Doc.h"
|
#include "McRFPy_Doc.h"
|
||||||
#include "PyTypeCache.h"
|
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
|
||||||
|
|
@ -93,13 +92,8 @@ sf::Sprite PyTexture::sprite(int index, sf::Vector2f pos, sf::Vector2f s)
|
||||||
|
|
||||||
PyObject* PyTexture::pyObject()
|
PyObject* PyTexture::pyObject()
|
||||||
{
|
{
|
||||||
PyTypeObject* type = PyTypeCache::Texture();
|
PyTypeObject* type = &mcrfpydef::PyTextureType;
|
||||||
if (!type) {
|
|
||||||
PyErr_SetString(PyExc_RuntimeError, "Failed to get Texture type from cache");
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
PyObject* obj = PyTexture::pynew(type, Py_None, Py_None);
|
PyObject* obj = PyTexture::pynew(type, Py_None, Py_None);
|
||||||
// PyTypeCache returns borrowed reference — no DECREF needed
|
|
||||||
|
|
||||||
if (!obj) {
|
if (!obj) {
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
@ -275,12 +269,7 @@ PyObject* PyTexture::composite(PyObject* cls, PyObject* args, PyObject* kwds)
|
||||||
std::vector<sf::Image> images;
|
std::vector<sf::Image> images;
|
||||||
unsigned int tex_w = 0, tex_h = 0;
|
unsigned int tex_w = 0, tex_h = 0;
|
||||||
|
|
||||||
// Use PyTypeCache for reliable, leak-free isinstance check
|
PyTypeObject* texture_type = &mcrfpydef::PyTextureType;
|
||||||
PyTypeObject* texture_type = PyTypeCache::Texture();
|
|
||||||
if (!texture_type) {
|
|
||||||
PyErr_SetString(PyExc_RuntimeError, "Failed to get Texture type from cache");
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (Py_ssize_t i = 0; i < count; i++) {
|
for (Py_ssize_t i = 0; i < count; i++) {
|
||||||
PyObject* item = PyList_GetItem(layers_list, i);
|
PyObject* item = PyList_GetItem(layers_list, i);
|
||||||
|
|
@ -311,7 +300,6 @@ PyObject* PyTexture::composite(PyObject* cls, PyObject* args, PyObject* kwds)
|
||||||
}
|
}
|
||||||
images.push_back(std::move(img));
|
images.push_back(std::move(img));
|
||||||
}
|
}
|
||||||
// PyTypeCache returns borrowed reference — no DECREF needed
|
|
||||||
|
|
||||||
// Alpha-composite all layers bottom-to-top
|
// Alpha-composite all layers bottom-to-top
|
||||||
sf::Image result;
|
sf::Image result;
|
||||||
|
|
|
||||||
|
|
@ -60,7 +60,7 @@ public:
|
||||||
};
|
};
|
||||||
|
|
||||||
namespace mcrfpydef {
|
namespace mcrfpydef {
|
||||||
static PyTypeObject PyTextureType = {
|
inline PyTypeObject PyTextureType = {
|
||||||
.ob_base = {.ob_base = {.ob_refcnt = 1, .ob_type = NULL}, .ob_size = 0},
|
.ob_base = {.ob_base = {.ob_refcnt = 1, .ob_type = NULL}, .ob_size = 0},
|
||||||
.tp_name = "mcrfpy.Texture",
|
.tp_name = "mcrfpy.Texture",
|
||||||
.tp_basicsize = sizeof(PyTextureObject),
|
.tp_basicsize = sizeof(PyTextureObject),
|
||||||
|
|
|
||||||
|
|
@ -48,7 +48,7 @@ public:
|
||||||
};
|
};
|
||||||
|
|
||||||
namespace mcrfpydef {
|
namespace mcrfpydef {
|
||||||
static PyTypeObject PyTimerType = {
|
inline PyTypeObject PyTimerType = {
|
||||||
.ob_base = {.ob_base = {.ob_refcnt = 1, .ob_type = NULL}, .ob_size = 0},
|
.ob_base = {.ob_base = {.ob_refcnt = 1, .ob_type = NULL}, .ob_size = 0},
|
||||||
.tp_name = "mcrfpy.Timer",
|
.tp_name = "mcrfpy.Timer",
|
||||||
.tp_basicsize = sizeof(PyTimerObject),
|
.tp_basicsize = sizeof(PyTimerObject),
|
||||||
|
|
|
||||||
|
|
@ -1,135 +0,0 @@
|
||||||
// PyTypeCache.cpp - Thread-safe Python type caching implementation
|
|
||||||
#include "PyTypeCache.h"
|
|
||||||
|
|
||||||
// Static member definitions
|
|
||||||
std::atomic<PyTypeObject*> PyTypeCache::entity_type{nullptr};
|
|
||||||
std::atomic<PyTypeObject*> PyTypeCache::grid_type{nullptr};
|
|
||||||
std::atomic<PyTypeObject*> PyTypeCache::frame_type{nullptr};
|
|
||||||
std::atomic<PyTypeObject*> PyTypeCache::caption_type{nullptr};
|
|
||||||
std::atomic<PyTypeObject*> PyTypeCache::sprite_type{nullptr};
|
|
||||||
std::atomic<PyTypeObject*> PyTypeCache::texture_type{nullptr};
|
|
||||||
std::atomic<PyTypeObject*> PyTypeCache::color_type{nullptr};
|
|
||||||
std::atomic<PyTypeObject*> PyTypeCache::vector_type{nullptr};
|
|
||||||
std::atomic<PyTypeObject*> PyTypeCache::font_type{nullptr};
|
|
||||||
std::atomic<bool> PyTypeCache::initialized{false};
|
|
||||||
std::mutex PyTypeCache::init_mutex;
|
|
||||||
|
|
||||||
PyTypeObject* PyTypeCache::cacheType(PyObject* module, const char* name,
|
|
||||||
std::atomic<PyTypeObject*>& cache) {
|
|
||||||
PyObject* type_obj = PyObject_GetAttrString(module, name);
|
|
||||||
if (!type_obj) {
|
|
||||||
PyErr_Format(PyExc_RuntimeError,
|
|
||||||
"PyTypeCache: Failed to get type '%s' from module", name);
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!PyType_Check(type_obj)) {
|
|
||||||
Py_DECREF(type_obj);
|
|
||||||
PyErr_Format(PyExc_TypeError,
|
|
||||||
"PyTypeCache: '%s' is not a type object", name);
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Store in cache - we keep the reference permanently
|
|
||||||
// Using memory_order_release ensures the pointer is visible to other threads
|
|
||||||
// after they see initialized=true
|
|
||||||
cache.store((PyTypeObject*)type_obj, std::memory_order_release);
|
|
||||||
|
|
||||||
return (PyTypeObject*)type_obj;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool PyTypeCache::initialize(PyObject* module) {
|
|
||||||
std::lock_guard<std::mutex> lock(init_mutex);
|
|
||||||
|
|
||||||
// Double-check pattern - might have been initialized while waiting for lock
|
|
||||||
if (initialized.load(std::memory_order_acquire)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Cache all types
|
|
||||||
if (!cacheType(module, "Entity", entity_type)) return false;
|
|
||||||
if (!cacheType(module, "Grid", grid_type)) return false;
|
|
||||||
if (!cacheType(module, "Frame", frame_type)) return false;
|
|
||||||
if (!cacheType(module, "Caption", caption_type)) return false;
|
|
||||||
if (!cacheType(module, "Sprite", sprite_type)) return false;
|
|
||||||
if (!cacheType(module, "Texture", texture_type)) return false;
|
|
||||||
if (!cacheType(module, "Color", color_type)) return false;
|
|
||||||
if (!cacheType(module, "Vector", vector_type)) return false;
|
|
||||||
if (!cacheType(module, "Font", font_type)) return false;
|
|
||||||
|
|
||||||
// Mark as initialized - release ensures all stores above are visible
|
|
||||||
initialized.store(true, std::memory_order_release);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void PyTypeCache::finalize() {
|
|
||||||
std::lock_guard<std::mutex> lock(init_mutex);
|
|
||||||
|
|
||||||
if (!initialized.load(std::memory_order_acquire)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Release all cached references
|
|
||||||
auto release = [](std::atomic<PyTypeObject*>& cache) {
|
|
||||||
PyTypeObject* type = cache.exchange(nullptr, std::memory_order_acq_rel);
|
|
||||||
if (type) {
|
|
||||||
Py_DECREF(type);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
release(entity_type);
|
|
||||||
release(grid_type);
|
|
||||||
release(frame_type);
|
|
||||||
release(caption_type);
|
|
||||||
release(sprite_type);
|
|
||||||
release(texture_type);
|
|
||||||
release(color_type);
|
|
||||||
release(vector_type);
|
|
||||||
release(font_type);
|
|
||||||
|
|
||||||
initialized.store(false, std::memory_order_release);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool PyTypeCache::isInitialized() {
|
|
||||||
return initialized.load(std::memory_order_acquire);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Type accessors - lock-free reads after initialization
|
|
||||||
// Using memory_order_acquire ensures we see the pointer stored during init
|
|
||||||
|
|
||||||
PyTypeObject* PyTypeCache::Entity() {
|
|
||||||
return entity_type.load(std::memory_order_acquire);
|
|
||||||
}
|
|
||||||
|
|
||||||
PyTypeObject* PyTypeCache::Grid() {
|
|
||||||
return grid_type.load(std::memory_order_acquire);
|
|
||||||
}
|
|
||||||
|
|
||||||
PyTypeObject* PyTypeCache::Frame() {
|
|
||||||
return frame_type.load(std::memory_order_acquire);
|
|
||||||
}
|
|
||||||
|
|
||||||
PyTypeObject* PyTypeCache::Caption() {
|
|
||||||
return caption_type.load(std::memory_order_acquire);
|
|
||||||
}
|
|
||||||
|
|
||||||
PyTypeObject* PyTypeCache::Sprite() {
|
|
||||||
return sprite_type.load(std::memory_order_acquire);
|
|
||||||
}
|
|
||||||
|
|
||||||
PyTypeObject* PyTypeCache::Texture() {
|
|
||||||
return texture_type.load(std::memory_order_acquire);
|
|
||||||
}
|
|
||||||
|
|
||||||
PyTypeObject* PyTypeCache::Color() {
|
|
||||||
return color_type.load(std::memory_order_acquire);
|
|
||||||
}
|
|
||||||
|
|
||||||
PyTypeObject* PyTypeCache::Vector() {
|
|
||||||
return vector_type.load(std::memory_order_acquire);
|
|
||||||
}
|
|
||||||
|
|
||||||
PyTypeObject* PyTypeCache::Font() {
|
|
||||||
return font_type.load(std::memory_order_acquire);
|
|
||||||
}
|
|
||||||
|
|
@ -1,64 +0,0 @@
|
||||||
#pragma once
|
|
||||||
// PyTypeCache.h - Thread-safe caching of Python type objects
|
|
||||||
//
|
|
||||||
// This module provides a centralized, thread-safe way to cache Python type
|
|
||||||
// references. It eliminates the refcount leaks from repeated
|
|
||||||
// PyObject_GetAttrString calls and is compatible with free-threading (PEP 703).
|
|
||||||
//
|
|
||||||
// Usage:
|
|
||||||
// PyTypeObject* entity_type = PyTypeCache::Entity();
|
|
||||||
// if (!entity_type) return NULL; // Error already set
|
|
||||||
//
|
|
||||||
// The cache is populated during module initialization and the types are
|
|
||||||
// held for the lifetime of the interpreter.
|
|
||||||
|
|
||||||
#include "Python.h"
|
|
||||||
#include <mutex>
|
|
||||||
#include <atomic>
|
|
||||||
|
|
||||||
class PyTypeCache {
|
|
||||||
public:
|
|
||||||
// Initialize the cache - call once during module init after types are ready
|
|
||||||
// Returns false and sets Python error on failure
|
|
||||||
static bool initialize(PyObject* module);
|
|
||||||
|
|
||||||
// Finalize - release references (call during module cleanup if needed)
|
|
||||||
static void finalize();
|
|
||||||
|
|
||||||
// Type accessors - return borrowed references (no DECREF needed)
|
|
||||||
// These are thread-safe and lock-free after initialization
|
|
||||||
static PyTypeObject* Entity();
|
|
||||||
static PyTypeObject* Grid();
|
|
||||||
static PyTypeObject* Frame();
|
|
||||||
static PyTypeObject* Caption();
|
|
||||||
static PyTypeObject* Sprite();
|
|
||||||
static PyTypeObject* Texture();
|
|
||||||
static PyTypeObject* Color();
|
|
||||||
static PyTypeObject* Vector();
|
|
||||||
static PyTypeObject* Font();
|
|
||||||
|
|
||||||
// Check if initialized
|
|
||||||
static bool isInitialized();
|
|
||||||
|
|
||||||
private:
|
|
||||||
// Cached type pointers - atomic for thread-safe reads
|
|
||||||
static std::atomic<PyTypeObject*> entity_type;
|
|
||||||
static std::atomic<PyTypeObject*> grid_type;
|
|
||||||
static std::atomic<PyTypeObject*> frame_type;
|
|
||||||
static std::atomic<PyTypeObject*> caption_type;
|
|
||||||
static std::atomic<PyTypeObject*> sprite_type;
|
|
||||||
static std::atomic<PyTypeObject*> texture_type;
|
|
||||||
static std::atomic<PyTypeObject*> color_type;
|
|
||||||
static std::atomic<PyTypeObject*> vector_type;
|
|
||||||
static std::atomic<PyTypeObject*> font_type;
|
|
||||||
|
|
||||||
// Initialization flag
|
|
||||||
static std::atomic<bool> initialized;
|
|
||||||
|
|
||||||
// Mutex for initialization (only used during init, not for reads)
|
|
||||||
static std::mutex init_mutex;
|
|
||||||
|
|
||||||
// Helper to fetch and cache a type
|
|
||||||
static PyTypeObject* cacheType(PyObject* module, const char* name,
|
|
||||||
std::atomic<PyTypeObject*>& cache);
|
|
||||||
};
|
|
||||||
|
|
@ -1,7 +1,5 @@
|
||||||
#include "PyVector.h"
|
#include "PyVector.h"
|
||||||
#include "PyObjectUtils.h"
|
|
||||||
#include "McRFPy_Doc.h"
|
#include "McRFPy_Doc.h"
|
||||||
#include "PyRAII.h"
|
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
|
|
||||||
PyGetSetDef PyVector::getsetters[] = {
|
PyGetSetDef PyVector::getsetters[] = {
|
||||||
|
|
@ -262,20 +260,16 @@ int PyVector::set_member(PyObject* obj, PyObject* value, void* closure)
|
||||||
|
|
||||||
PyVectorObject* PyVector::from_arg(PyObject* args)
|
PyVectorObject* PyVector::from_arg(PyObject* args)
|
||||||
{
|
{
|
||||||
// Use RAII for type reference management
|
PyTypeObject* type = &mcrfpydef::PyVectorType;
|
||||||
PyRAII::PyTypeRef type("Vector", McRFPy_API::mcrf_module);
|
|
||||||
if (!type) {
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if args is already a Vector instance
|
// Check if args is already a Vector instance
|
||||||
if (PyObject_IsInstance(args, (PyObject*)type.get())) {
|
if (PyObject_IsInstance(args, (PyObject*)type)) {
|
||||||
Py_INCREF(args); // Return new reference so caller can safely DECREF
|
Py_INCREF(args); // Return new reference so caller can safely DECREF
|
||||||
return (PyVectorObject*)args;
|
return (PyVectorObject*)args;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create new Vector object using RAII
|
// Create new Vector object
|
||||||
PyRAII::PyObjectRef obj(type->tp_alloc(type.get(), 0), true);
|
PyObject* obj = type->tp_alloc(type, 0);
|
||||||
if (!obj) {
|
if (!obj) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
@ -283,25 +277,27 @@ PyVectorObject* PyVector::from_arg(PyObject* args)
|
||||||
// Handle different input types
|
// Handle different input types
|
||||||
if (PyTuple_Check(args)) {
|
if (PyTuple_Check(args)) {
|
||||||
// It's already a tuple, pass it directly to init
|
// It's already a tuple, pass it directly to init
|
||||||
int err = init((PyVectorObject*)obj.get(), args, NULL);
|
int err = init((PyVectorObject*)obj, args, NULL);
|
||||||
if (err) {
|
if (err) {
|
||||||
// obj will be automatically cleaned up when it goes out of scope
|
Py_DECREF(obj);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Wrap single argument in a tuple for init
|
// Wrap single argument in a tuple for init
|
||||||
PyRAII::PyObjectRef tuple(PyTuple_Pack(1, args), true);
|
PyObject* tuple = PyTuple_Pack(1, args);
|
||||||
if (!tuple) {
|
if (!tuple) {
|
||||||
|
Py_DECREF(obj);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
int err = init((PyVectorObject*)obj.get(), tuple.get(), NULL);
|
int err = init((PyVectorObject*)obj, tuple, NULL);
|
||||||
|
Py_DECREF(tuple);
|
||||||
if (err) {
|
if (err) {
|
||||||
|
Py_DECREF(obj);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Release ownership and return
|
return (PyVectorObject*)obj;
|
||||||
return (PyVectorObject*)obj.release();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Arithmetic operations
|
// Arithmetic operations
|
||||||
|
|
|
||||||
|
|
@ -63,7 +63,7 @@ namespace mcrfpydef {
|
||||||
extern PyNumberMethods PyVector_as_number;
|
extern PyNumberMethods PyVector_as_number;
|
||||||
extern PySequenceMethods PyVector_as_sequence;
|
extern PySequenceMethods PyVector_as_sequence;
|
||||||
|
|
||||||
static PyTypeObject PyVectorType = {
|
inline PyTypeObject PyVectorType = {
|
||||||
.ob_base = {.ob_base = {.ob_refcnt = 1, .ob_type = NULL}, .ob_size = 0},
|
.ob_base = {.ob_base = {.ob_refcnt = 1, .ob_type = NULL}, .ob_size = 0},
|
||||||
.tp_name = "mcrfpy.Vector",
|
.tp_name = "mcrfpy.Vector",
|
||||||
.tp_basicsize = sizeof(PyVectorObject),
|
.tp_basicsize = sizeof(PyVectorObject),
|
||||||
|
|
|
||||||
|
|
@ -47,7 +47,7 @@ public:
|
||||||
};
|
};
|
||||||
|
|
||||||
namespace mcrfpydef {
|
namespace mcrfpydef {
|
||||||
static PyTypeObject PyWindowType = {
|
inline PyTypeObject PyWindowType = {
|
||||||
.ob_base = {.ob_base = {.ob_refcnt = 1, .ob_type = NULL}, .ob_size = 0},
|
.ob_base = {.ob_base = {.ob_refcnt = 1, .ob_type = NULL}, .ob_size = 0},
|
||||||
.tp_name = "mcrfpy.Window",
|
.tp_name = "mcrfpy.Window",
|
||||||
.tp_basicsize = sizeof(PyWindowObject),
|
.tp_basicsize = sizeof(PyWindowObject),
|
||||||
|
|
|
||||||
|
|
@ -111,7 +111,7 @@ public:
|
||||||
extern PyMethodDef UIArc_methods[];
|
extern PyMethodDef UIArc_methods[];
|
||||||
|
|
||||||
namespace mcrfpydef {
|
namespace mcrfpydef {
|
||||||
static PyTypeObject PyUIArcType = {
|
inline PyTypeObject PyUIArcType = {
|
||||||
.ob_base = {.ob_base = {.ob_refcnt = 1, .ob_type = NULL}, .ob_size = 0},
|
.ob_base = {.ob_base = {.ob_refcnt = 1, .ob_type = NULL}, .ob_size = 0},
|
||||||
.tp_name = "mcrfpy.Arc",
|
.tp_name = "mcrfpy.Arc",
|
||||||
.tp_basicsize = sizeof(PyUIArcObject),
|
.tp_basicsize = sizeof(PyUIArcObject),
|
||||||
|
|
|
||||||
|
|
@ -50,7 +50,7 @@ public:
|
||||||
extern PyMethodDef UICaption_methods[];
|
extern PyMethodDef UICaption_methods[];
|
||||||
|
|
||||||
namespace mcrfpydef {
|
namespace mcrfpydef {
|
||||||
static PyTypeObject PyUICaptionType = {
|
inline PyTypeObject PyUICaptionType = {
|
||||||
.ob_base = {.ob_base = {.ob_refcnt = 1, .ob_type = NULL}, .ob_size = 0},
|
.ob_base = {.ob_base = {.ob_refcnt = 1, .ob_type = NULL}, .ob_size = 0},
|
||||||
.tp_name = "mcrfpy.Caption",
|
.tp_name = "mcrfpy.Caption",
|
||||||
.tp_basicsize = sizeof(PyUICaptionObject),
|
.tp_basicsize = sizeof(PyUICaptionObject),
|
||||||
|
|
|
||||||
|
|
@ -100,7 +100,7 @@ public:
|
||||||
extern PyMethodDef UICircle_methods[];
|
extern PyMethodDef UICircle_methods[];
|
||||||
|
|
||||||
namespace mcrfpydef {
|
namespace mcrfpydef {
|
||||||
static PyTypeObject PyUICircleType = {
|
inline PyTypeObject PyUICircleType = {
|
||||||
.ob_base = {.ob_base = {.ob_refcnt = 1, .ob_type = NULL}, .ob_size = 0},
|
.ob_base = {.ob_base = {.ob_refcnt = 1, .ob_type = NULL}, .ob_size = 0},
|
||||||
.tp_name = "mcrfpy.Circle",
|
.tp_name = "mcrfpy.Circle",
|
||||||
.tp_basicsize = sizeof(PyUICircleObject),
|
.tp_basicsize = sizeof(PyUICircleObject),
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,6 @@
|
||||||
#include "UIArc.h"
|
#include "UIArc.h"
|
||||||
#include "3d/Viewport3D.h"
|
#include "3d/Viewport3D.h"
|
||||||
#include "McRFPy_API.h"
|
#include "McRFPy_API.h"
|
||||||
#include "PyObjectUtils.h"
|
|
||||||
#include "PythonObjectCache.h"
|
#include "PythonObjectCache.h"
|
||||||
#include <climits>
|
#include <climits>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
|
|
||||||
|
|
@ -339,9 +339,6 @@ typedef struct {
|
||||||
} PyUICollectionIterObject;
|
} PyUICollectionIterObject;
|
||||||
|
|
||||||
namespace mcrfpydef {
|
namespace mcrfpydef {
|
||||||
// DEPRECATED: RET_PY_INSTANCE macro has been replaced with template functions in PyObjectUtils.h
|
|
||||||
// The macro was difficult to debug and used static type references that could cause initialization order issues.
|
|
||||||
// Use PyObjectUtils::convertDrawableToPython() or PyObjectUtils::createPyObject<T>() instead.
|
|
||||||
|
|
||||||
//TODO: add this method to class scope; move implementation to .cpp file
|
//TODO: add this method to class scope; move implementation to .cpp file
|
||||||
/*
|
/*
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,6 @@
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <libtcod.h>
|
#include <libtcod.h>
|
||||||
#include "PyObjectUtils.h"
|
|
||||||
#include "PyVector.h"
|
#include "PyVector.h"
|
||||||
#include "PythonObjectCache.h"
|
#include "PythonObjectCache.h"
|
||||||
#include "PyFOV.h"
|
#include "PyFOV.h"
|
||||||
|
|
|
||||||
|
|
@ -124,7 +124,7 @@ public:
|
||||||
extern PyMethodDef UIEntity_all_methods[];
|
extern PyMethodDef UIEntity_all_methods[];
|
||||||
|
|
||||||
namespace mcrfpydef {
|
namespace mcrfpydef {
|
||||||
static PyTypeObject PyUIEntityType = {
|
inline PyTypeObject PyUIEntityType = {
|
||||||
.ob_base = {.ob_base = {.ob_refcnt = 1, .ob_type = NULL}, .ob_size = 0},
|
.ob_base = {.ob_base = {.ob_refcnt = 1, .ob_type = NULL}, .ob_size = 0},
|
||||||
.tp_name = "mcrfpy.Entity",
|
.tp_name = "mcrfpy.Entity",
|
||||||
.tp_basicsize = sizeof(PyUIEntityObject),
|
.tp_basicsize = sizeof(PyUIEntityObject),
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,6 @@
|
||||||
#include "UIEntity.h"
|
#include "UIEntity.h"
|
||||||
#include "UIGrid.h"
|
#include "UIGrid.h"
|
||||||
#include "McRFPy_API.h"
|
#include "McRFPy_API.h"
|
||||||
#include "PyTypeCache.h"
|
|
||||||
#include "PythonObjectCache.h"
|
#include "PythonObjectCache.h"
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
|
@ -49,11 +48,7 @@ PyObject* UIEntityCollectionIter::next(PyUIEntityCollectionIterObject* self)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Otherwise create and return a new Python Entity object
|
// Otherwise create and return a new Python Entity object
|
||||||
PyTypeObject* entity_type = PyTypeCache::Entity();
|
PyTypeObject* entity_type = &mcrfpydef::PyUIEntityType;
|
||||||
if (!entity_type) {
|
|
||||||
PyErr_SetString(PyExc_RuntimeError, "Entity type not initialized in cache");
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto o = (PyUIEntityObject*)entity_type->tp_alloc(entity_type, 0);
|
auto o = (PyUIEntityObject*)entity_type->tp_alloc(entity_type, 0);
|
||||||
if (!o) return NULL;
|
if (!o) return NULL;
|
||||||
|
|
@ -119,11 +114,7 @@ PyObject* UIEntityCollection::getitem(PyUIEntityCollectionObject* self, Py_ssize
|
||||||
}
|
}
|
||||||
|
|
||||||
// Otherwise, create a new base Entity object
|
// Otherwise, create a new base Entity object
|
||||||
PyTypeObject* entity_type = PyTypeCache::Entity();
|
PyTypeObject* entity_type = &mcrfpydef::PyUIEntityType;
|
||||||
if (!entity_type) {
|
|
||||||
PyErr_SetString(PyExc_RuntimeError, "Entity type not initialized in cache");
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto o = (PyUIEntityObject*)entity_type->tp_alloc(entity_type, 0);
|
auto o = (PyUIEntityObject*)entity_type->tp_alloc(entity_type, 0);
|
||||||
if (!o) return NULL;
|
if (!o) return NULL;
|
||||||
|
|
@ -174,12 +165,8 @@ int UIEntityCollection::setitem(PyUIEntityCollectionObject* self, Py_ssize_t ind
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Type checking using cached type
|
// Type checking
|
||||||
PyTypeObject* entity_type = PyTypeCache::Entity();
|
PyTypeObject* entity_type = &mcrfpydef::PyUIEntityType;
|
||||||
if (!entity_type) {
|
|
||||||
PyErr_SetString(PyExc_RuntimeError, "Entity type not initialized in cache");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!PyObject_IsInstance(value, (PyObject*)entity_type)) {
|
if (!PyObject_IsInstance(value, (PyObject*)entity_type)) {
|
||||||
PyErr_SetString(PyExc_TypeError, "EntityCollection can only contain Entity objects");
|
PyErr_SetString(PyExc_TypeError, "EntityCollection can only contain Entity objects");
|
||||||
|
|
@ -219,9 +206,9 @@ int UIEntityCollection::contains(PyUIEntityCollectionObject* self, PyObject* val
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Type checking using cached type
|
// Type checking
|
||||||
PyTypeObject* entity_type = PyTypeCache::Entity();
|
PyTypeObject* entity_type = &mcrfpydef::PyUIEntityType;
|
||||||
if (!entity_type || !PyObject_IsInstance(value, (PyObject*)entity_type)) {
|
if (!PyObject_IsInstance(value, (PyObject*)entity_type)) {
|
||||||
return 0; // Not an Entity, can't be in collection
|
return 0; // Not an Entity, can't be in collection
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -257,12 +244,7 @@ PyObject* UIEntityCollection::concat(PyUIEntityCollectionObject* self, PyObject*
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
PyTypeObject* entity_type = PyTypeCache::Entity();
|
PyTypeObject* entity_type = &mcrfpydef::PyUIEntityType;
|
||||||
if (!entity_type) {
|
|
||||||
Py_DECREF(result_list);
|
|
||||||
PyErr_SetString(PyExc_RuntimeError, "Entity type not initialized in cache");
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add all elements from self
|
// Add all elements from self
|
||||||
Py_ssize_t idx = 0;
|
Py_ssize_t idx = 0;
|
||||||
|
|
@ -296,11 +278,7 @@ PyObject* UIEntityCollection::inplace_concat(PyUIEntityCollectionObject* self, P
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
PyTypeObject* entity_type = PyTypeCache::Entity();
|
PyTypeObject* entity_type = &mcrfpydef::PyUIEntityType;
|
||||||
if (!entity_type) {
|
|
||||||
PyErr_SetString(PyExc_RuntimeError, "Entity type not initialized in cache");
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
// First, validate ALL items before modifying anything
|
// First, validate ALL items before modifying anything
|
||||||
Py_ssize_t other_len = PySequence_Length(other);
|
Py_ssize_t other_len = PySequence_Length(other);
|
||||||
|
|
@ -380,12 +358,7 @@ PyObject* UIEntityCollection::subscript(PyUIEntityCollectionObject* self, PyObje
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
PyTypeObject* entity_type = PyTypeCache::Entity();
|
PyTypeObject* entity_type = &mcrfpydef::PyUIEntityType;
|
||||||
if (!entity_type) {
|
|
||||||
Py_DECREF(result_list);
|
|
||||||
PyErr_SetString(PyExc_RuntimeError, "Entity type not initialized in cache");
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto it = self->data->begin();
|
auto it = self->data->begin();
|
||||||
for (Py_ssize_t i = 0, cur = start; i < slicelength; i++, cur += step) {
|
for (Py_ssize_t i = 0, cur = start; i < slicelength; i++, cur += step) {
|
||||||
|
|
@ -474,11 +447,7 @@ int UIEntityCollection::ass_subscript(PyUIEntityCollectionObject* self, PyObject
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
PyTypeObject* entity_type = PyTypeCache::Entity();
|
PyTypeObject* entity_type = &mcrfpydef::PyUIEntityType;
|
||||||
if (!entity_type) {
|
|
||||||
PyErr_SetString(PyExc_RuntimeError, "Entity type not initialized in cache");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Validate all items first
|
// Validate all items first
|
||||||
std::vector<std::shared_ptr<UIEntity>> new_items;
|
std::vector<std::shared_ptr<UIEntity>> new_items;
|
||||||
|
|
@ -577,11 +546,7 @@ PyMappingMethods UIEntityCollection::mpmethods = {
|
||||||
|
|
||||||
PyObject* UIEntityCollection::append(PyUIEntityCollectionObject* self, PyObject* o)
|
PyObject* UIEntityCollection::append(PyUIEntityCollectionObject* self, PyObject* o)
|
||||||
{
|
{
|
||||||
PyTypeObject* entity_type = PyTypeCache::Entity();
|
PyTypeObject* entity_type = &mcrfpydef::PyUIEntityType;
|
||||||
if (!entity_type) {
|
|
||||||
PyErr_SetString(PyExc_RuntimeError, "Entity type not initialized in cache");
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!PyObject_IsInstance(o, (PyObject*)entity_type)) {
|
if (!PyObject_IsInstance(o, (PyObject*)entity_type)) {
|
||||||
PyErr_SetString(PyExc_TypeError, "Only Entity objects can be added to EntityCollection");
|
PyErr_SetString(PyExc_TypeError, "Only Entity objects can be added to EntityCollection");
|
||||||
|
|
@ -627,11 +592,7 @@ PyObject* UIEntityCollection::append(PyUIEntityCollectionObject* self, PyObject*
|
||||||
|
|
||||||
PyObject* UIEntityCollection::remove(PyUIEntityCollectionObject* self, PyObject* o)
|
PyObject* UIEntityCollection::remove(PyUIEntityCollectionObject* self, PyObject* o)
|
||||||
{
|
{
|
||||||
PyTypeObject* entity_type = PyTypeCache::Entity();
|
PyTypeObject* entity_type = &mcrfpydef::PyUIEntityType;
|
||||||
if (!entity_type) {
|
|
||||||
PyErr_SetString(PyExc_RuntimeError, "Entity type not initialized in cache");
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!PyObject_IsInstance(o, (PyObject*)entity_type)) {
|
if (!PyObject_IsInstance(o, (PyObject*)entity_type)) {
|
||||||
PyErr_SetString(PyExc_TypeError, "EntityCollection.remove requires an Entity object");
|
PyErr_SetString(PyExc_TypeError, "EntityCollection.remove requires an Entity object");
|
||||||
|
|
@ -675,12 +636,7 @@ PyObject* UIEntityCollection::extend(PyUIEntityCollectionObject* self, PyObject*
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
PyTypeObject* entity_type = PyTypeCache::Entity();
|
PyTypeObject* entity_type = &mcrfpydef::PyUIEntityType;
|
||||||
if (!entity_type) {
|
|
||||||
Py_DECREF(iterator);
|
|
||||||
PyErr_SetString(PyExc_RuntimeError, "Entity type not initialized in cache");
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
// FIXED: Validate ALL items first before modifying anything
|
// FIXED: Validate ALL items first before modifying anything
|
||||||
// (Following the pattern from inplace_concat)
|
// (Following the pattern from inplace_concat)
|
||||||
|
|
@ -785,11 +741,7 @@ PyObject* UIEntityCollection::pop(PyUIEntityCollectionObject* self, PyObject* ar
|
||||||
list->erase(it);
|
list->erase(it);
|
||||||
|
|
||||||
// Create Python object for the entity
|
// Create Python object for the entity
|
||||||
PyTypeObject* entity_type = PyTypeCache::Entity();
|
PyTypeObject* entity_type = &mcrfpydef::PyUIEntityType;
|
||||||
if (!entity_type) {
|
|
||||||
PyErr_SetString(PyExc_RuntimeError, "Entity type not initialized in cache");
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
PyUIEntityObject* py_entity = (PyUIEntityObject*)entity_type->tp_alloc(entity_type, 0);
|
PyUIEntityObject* py_entity = (PyUIEntityObject*)entity_type->tp_alloc(entity_type, 0);
|
||||||
if (!py_entity) {
|
if (!py_entity) {
|
||||||
|
|
@ -817,11 +769,7 @@ PyObject* UIEntityCollection::insert(PyUIEntityCollectionObject* self, PyObject*
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
PyTypeObject* entity_type = PyTypeCache::Entity();
|
PyTypeObject* entity_type = &mcrfpydef::PyUIEntityType;
|
||||||
if (!entity_type) {
|
|
||||||
PyErr_SetString(PyExc_RuntimeError, "Entity type not initialized in cache");
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!PyObject_IsInstance(o, (PyObject*)entity_type)) {
|
if (!PyObject_IsInstance(o, (PyObject*)entity_type)) {
|
||||||
PyErr_SetString(PyExc_TypeError, "EntityCollection.insert requires an Entity object");
|
PyErr_SetString(PyExc_TypeError, "EntityCollection.insert requires an Entity object");
|
||||||
|
|
@ -874,11 +822,7 @@ PyObject* UIEntityCollection::index_method(PyUIEntityCollectionObject* self, PyO
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
PyTypeObject* entity_type = PyTypeCache::Entity();
|
PyTypeObject* entity_type = &mcrfpydef::PyUIEntityType;
|
||||||
if (!entity_type) {
|
|
||||||
PyErr_SetString(PyExc_RuntimeError, "Entity type not initialized in cache");
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!PyObject_IsInstance(value, (PyObject*)entity_type)) {
|
if (!PyObject_IsInstance(value, (PyObject*)entity_type)) {
|
||||||
PyErr_SetString(PyExc_TypeError, "EntityCollection.index requires an Entity object");
|
PyErr_SetString(PyExc_TypeError, "EntityCollection.index requires an Entity object");
|
||||||
|
|
@ -910,8 +854,8 @@ PyObject* UIEntityCollection::count(PyUIEntityCollectionObject* self, PyObject*
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
PyTypeObject* entity_type = PyTypeCache::Entity();
|
PyTypeObject* entity_type = &mcrfpydef::PyUIEntityType;
|
||||||
if (!entity_type || !PyObject_IsInstance(value, (PyObject*)entity_type)) {
|
if (!PyObject_IsInstance(value, (PyObject*)entity_type)) {
|
||||||
return PyLong_FromLong(0);
|
return PyLong_FromLong(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -969,11 +913,7 @@ PyObject* UIEntityCollection::find(PyUIEntityCollectionObject* self, PyObject* a
|
||||||
std::string pattern(name);
|
std::string pattern(name);
|
||||||
bool has_wildcard = (pattern.find('*') != std::string::npos);
|
bool has_wildcard = (pattern.find('*') != std::string::npos);
|
||||||
|
|
||||||
PyTypeObject* entity_type = PyTypeCache::Entity();
|
PyTypeObject* entity_type = &mcrfpydef::PyUIEntityType;
|
||||||
if (!entity_type) {
|
|
||||||
PyErr_SetString(PyExc_RuntimeError, "Entity type not initialized in cache");
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (has_wildcard) {
|
if (has_wildcard) {
|
||||||
PyObject* results = PyList_New(0);
|
PyObject* results = PyList_New(0);
|
||||||
|
|
|
||||||
|
|
@ -76,7 +76,7 @@ public:
|
||||||
extern PyMethodDef UIFrame_methods[];
|
extern PyMethodDef UIFrame_methods[];
|
||||||
|
|
||||||
namespace mcrfpydef {
|
namespace mcrfpydef {
|
||||||
static PyTypeObject PyUIFrameType = {
|
inline PyTypeObject PyUIFrameType = {
|
||||||
.ob_base = {.ob_base = {.ob_refcnt = 1, .ob_type = NULL}, .ob_size = 0},
|
.ob_base = {.ob_base = {.ob_refcnt = 1, .ob_type = NULL}, .ob_size = 0},
|
||||||
.tp_name = "mcrfpy.Frame",
|
.tp_name = "mcrfpy.Frame",
|
||||||
.tp_basicsize = sizeof(PyUIFrameObject),
|
.tp_basicsize = sizeof(PyUIFrameObject),
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,6 @@
|
||||||
#include "McRFPy_API.h"
|
#include "McRFPy_API.h"
|
||||||
#include "PythonObjectCache.h"
|
#include "PythonObjectCache.h"
|
||||||
#include "PyAlignment.h"
|
#include "PyAlignment.h"
|
||||||
#include "PyTypeCache.h" // Thread-safe cached Python types
|
|
||||||
#include "UIEntity.h"
|
#include "UIEntity.h"
|
||||||
#include "Profiler.h"
|
#include "Profiler.h"
|
||||||
#include "PyFOV.h"
|
#include "PyFOV.h"
|
||||||
|
|
@ -1928,16 +1927,7 @@ PyObject* UIGrid::py_entities_in_radius(PyUIGridObject* self, PyObject* args, Py
|
||||||
PyObject* result = PyList_New(entities.size());
|
PyObject* result = PyList_New(entities.size());
|
||||||
if (!result) return PyErr_NoMemory();
|
if (!result) return PyErr_NoMemory();
|
||||||
|
|
||||||
// Cache Entity type for efficiency
|
PyTypeObject* entity_type = &mcrfpydef::PyUIEntityType;
|
||||||
static PyTypeObject* cached_entity_type = nullptr;
|
|
||||||
if (!cached_entity_type) {
|
|
||||||
cached_entity_type = (PyTypeObject*)PyObject_GetAttrString(McRFPy_API::mcrf_module, "Entity");
|
|
||||||
if (!cached_entity_type) {
|
|
||||||
Py_DECREF(result);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
Py_INCREF(cached_entity_type);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (size_t i = 0; i < entities.size(); i++) {
|
for (size_t i = 0; i < entities.size(); i++) {
|
||||||
auto& entity = entities[i];
|
auto& entity = entities[i];
|
||||||
|
|
@ -1948,7 +1938,7 @@ PyObject* UIGrid::py_entities_in_radius(PyUIGridObject* self, PyObject* args, Py
|
||||||
PyList_SET_ITEM(result, i, entity->self);
|
PyList_SET_ITEM(result, i, entity->self);
|
||||||
} else {
|
} else {
|
||||||
// Create new Python Entity wrapper
|
// Create new Python Entity wrapper
|
||||||
auto pyEntity = (PyUIEntityObject*)cached_entity_type->tp_alloc(cached_entity_type, 0);
|
auto pyEntity = (PyUIEntityObject*)entity_type->tp_alloc(entity_type, 0);
|
||||||
if (!pyEntity) {
|
if (!pyEntity) {
|
||||||
Py_DECREF(result);
|
Py_DECREF(result);
|
||||||
return PyErr_NoMemory();
|
return PyErr_NoMemory();
|
||||||
|
|
|
||||||
|
|
@ -255,7 +255,7 @@ public:
|
||||||
extern PyMethodDef UIGrid_all_methods[];
|
extern PyMethodDef UIGrid_all_methods[];
|
||||||
|
|
||||||
namespace mcrfpydef {
|
namespace mcrfpydef {
|
||||||
static PyTypeObject PyUIGridType = {
|
inline PyTypeObject PyUIGridType = {
|
||||||
.ob_base = {.ob_base = {.ob_refcnt = 1, .ob_type = NULL}, .ob_size = 0},
|
.ob_base = {.ob_base = {.ob_refcnt = 1, .ob_type = NULL}, .ob_size = 0},
|
||||||
.tp_name = "mcrfpy.Grid",
|
.tp_name = "mcrfpy.Grid",
|
||||||
.tp_basicsize = sizeof(PyUIGridObject),
|
.tp_basicsize = sizeof(PyUIGridObject),
|
||||||
|
|
|
||||||
|
|
@ -97,7 +97,7 @@ public:
|
||||||
extern PyMethodDef UILine_methods[];
|
extern PyMethodDef UILine_methods[];
|
||||||
|
|
||||||
namespace mcrfpydef {
|
namespace mcrfpydef {
|
||||||
static PyTypeObject PyUILineType = {
|
inline PyTypeObject PyUILineType = {
|
||||||
.ob_base = {.ob_base = {.ob_refcnt = 1, .ob_type = NULL}, .ob_size = 0},
|
.ob_base = {.ob_base = {.ob_refcnt = 1, .ob_type = NULL}, .ob_size = 0},
|
||||||
.tp_name = "mcrfpy.Line",
|
.tp_name = "mcrfpy.Line",
|
||||||
.tp_basicsize = sizeof(PyUILineObject),
|
.tp_basicsize = sizeof(PyUILineObject),
|
||||||
|
|
|
||||||
|
|
@ -83,7 +83,7 @@ public:
|
||||||
extern PyMethodDef UISprite_methods[];
|
extern PyMethodDef UISprite_methods[];
|
||||||
|
|
||||||
namespace mcrfpydef {
|
namespace mcrfpydef {
|
||||||
static PyTypeObject PyUISpriteType = {
|
inline PyTypeObject PyUISpriteType = {
|
||||||
.ob_base = {.ob_base = {.ob_refcnt = 1, .ob_type = NULL}, .ob_size = 0},
|
.ob_base = {.ob_base = {.ob_refcnt = 1, .ob_type = NULL}, .ob_size = 0},
|
||||||
.tp_name = "mcrfpy.Sprite",
|
.tp_name = "mcrfpy.Sprite",
|
||||||
.tp_basicsize = sizeof(PyUISpriteObject),
|
.tp_basicsize = sizeof(PyUISpriteObject),
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue