Explorar o código

remove `PY_CLASS` macro

blueloveTH hai 1 ano
pai
achega
f944113567

+ 6 - 10
3rd/lua_bridge/src/lua_bridge.cpp

@@ -38,8 +38,6 @@ struct PyLuaObject{
 };
 
 struct PyLuaTable: PyLuaObject{
-    PY_CLASS(PyLuaTable, lua, Table)
-
     static void _register(VM* vm, PyObject* mod, PyObject* type){
         Type t = PK_OBJ_GET(Type, type);
         PyTypeInfo* ti = &vm->_all_types[t];
@@ -198,8 +196,6 @@ static PyObject* lua_popx_multi_to_python(VM* vm, int count){
 }
 
 struct PyLuaFunction: PyLuaObject{
-    PY_CLASS(PyLuaFunction, lua, Function)
-
     static void _register(VM* vm, PyObject* mod, PyObject* type){
         vm->bind_notimplemented_constructor<PyLuaFunction>(type);
         vm->_all_types[PK_OBJ_GET(Type, type)].subclass_enabled = false;
@@ -274,13 +270,13 @@ void lua_push_from_python(VM* vm, PyObject* val){
         }
     }
 
-    if(is_type(val, PyLuaTable::_type(vm))){
+    if(vm->is_user_type<PyLuaTable>(val)){
         const PyLuaTable& table = _CAST(PyLuaTable&, val);
         lua_rawgeti(_L, LUA_REGISTRYINDEX, table.r);
         return;
     }
 
-    if(is_type(val, PyLuaFunction::_type(vm))){
+    if(vm->is_user_type<PyLuaFunction>(val)){
         const PyLuaFunction& func = _CAST(PyLuaFunction&, val);
         lua_rawgeti(_L, LUA_REGISTRYINDEX, func.r);
         return;
@@ -311,11 +307,11 @@ PyObject* lua_popx_to_python(VM* vm) {
             return VAR(val);
         }
         case LUA_TTABLE: {
-            PyObject* obj = vm->heap.gcnew<PyLuaTable>(PyLuaTable::_type(vm));
+            PyObject* obj = vm->new_user_object<PyLuaTable>();
             return obj;
         }
         case LUA_TFUNCTION: {
-            PyObject* obj = vm->heap.gcnew<PyLuaFunction>(PyLuaFunction::_type(vm));
+            PyObject* obj = vm->new_user_object<PyLuaFunction>();
             return obj;
         }
         default: {
@@ -335,8 +331,8 @@ void initialize_lua_bridge(VM* vm, lua_State* newL){
     }
     _L = newL;
 
-    PyLuaTable::register_class(vm, mod);
-    PyLuaFunction::register_class(vm, mod);
+    vm->register_user_class<PyLuaTable>(mod, "Table");
+    vm->register_user_class<PyLuaFunction>(mod, "Function");
 
     vm->bind(mod, "dostring(__source: str)", [](VM* vm, ArgsView args){
         const char* source = CAST(CString, args[0]);

+ 1 - 1
docs/bindings.md

@@ -169,7 +169,7 @@ struct wrapped__Point{
 int main(){
     VM* vm = new VM();
     // register the wrapper class in builtins
-    wrapped__Point::register_class(vm, vm->builtins);
+    vm->register_user_class<wrapped__Point>(vm->builtins);
 
     // use the Point class
     vm->exec("a = Point(1, 2)");

+ 1 - 1
docs/cheatsheet.md

@@ -141,7 +141,7 @@ Access extended python types
 
 ```cpp
 // VoidP was defined by `PY_CLASS` macro
-Type voidp_t = VoidP::_type(vm);
+Type voidp_t = vm->_tp_user<VoidP>();
 ```
 
 Check if an object is a python type

+ 2 - 2
include/pocketpy/bindings.h

@@ -142,7 +142,7 @@ void _bind(VM* vm, PyObject* obj, const char* sig, Ret(T::*func)(Params...)){
         vm->bind_func<1>(type, "from_struct", [](VM* vm, ArgsView args){            \
             C99Struct& s = CAST(C99Struct&, args[0]);                               \
             if(s.size != sizeof(vT)) vm->ValueError("size mismatch");               \
-            PyObject* obj = vm->heap.gcnew<wT>(wT::_type(vm));                      \
+            PyObject* obj = vm->new_user_object<wT>();                              \
             memcpy(_CAST(wT&, obj)._(), s.p, sizeof(vT));                           \
             return obj;                                                             \
         }, {}, BindType::STATICMETHOD);                                             \
@@ -163,7 +163,7 @@ void _bind(VM* vm, PyObject* obj, const char* sig, Ret(T::*func)(Params...)){
         });                                                                         \
         vm->bind__eq__(PK_OBJ_GET(Type, type), [](VM* vm, PyObject* _0, PyObject* _1){  \
             wT& self = _CAST(wT&, _0);                                              \
-            if(!vm->isinstance(_1, wT::_type(vm))) return vm->NotImplemented;       \
+            if(!vm->isinstance(_1, vm->_tp_user<wT>())) return vm->NotImplemented;  \
             wT& other = _CAST(wT&, _1);                                             \
             return VAR(self == other);                                              \
         });                                                                         \

+ 4 - 12
include/pocketpy/cffi.h

@@ -6,22 +6,14 @@
 namespace pkpy {
 
 #define PY_CLASS(T, mod, name)                  \
-    static Type _type(VM* vm) { return vm->_cxx_typeid_map[typeid(T)]; }   \
-    static PyObject* register_class(VM* vm, PyObject* mod, Type base=0) {   \
-        std::string_view mod_name = PK_OBJ_GET(Str, mod->attr("__name__")).sv();   \
-        if(mod_name != #mod) throw std::runtime_error(_S("register_class() failed: ", mod_name, " != ", #mod).str()); \
-        PyObject* type = vm->new_type_object(mod, #name, base);             \
-        mod->attr().set(#name, type);                                       \
-        vm->_cxx_typeid_map[typeid(T)] = PK_OBJ_GET(Type, type);           \
-        T::_register(vm, mod, type);                                        \
-        return type;                                                        \
+    [[deprecated]] static Type _type(VM* vm) { return vm->_cxx_typeid_map[typeid(T)]; }    \
+    [[deprecated]] static PyObject* register_class(VM* vm, PyObject* mod, Type base=0) {   \
+        return vm->register_user_class<T>(mod, #name, base);                \
     }                                                                       
 
-#define VAR_T(T, ...) vm->heap.gcnew<T>(T::_type(vm), __VA_ARGS__)
+#define VAR_T(T, ...) vm->heap.gcnew<T>(vm->_tp_user<T>(), __VA_ARGS__)
 
 struct VoidP{
-    PY_CLASS(VoidP, c, void_p)
-
     void* ptr;
     VoidP(const void* ptr): ptr(const_cast<void*>(ptr)){}
 

+ 1 - 1
include/pocketpy/common.h

@@ -20,7 +20,7 @@
 #include <typeindex>
 #include <initializer_list>
 
-#define PK_VERSION				"1.4.6"
+#define PK_VERSION				"1.5.0"
 
 #include "config.h"
 #include "export.h"

+ 0 - 5
include/pocketpy/iter.h

@@ -7,7 +7,6 @@
 namespace pkpy{
 
 struct RangeIter{
-    PY_CLASS(RangeIter, builtins, _range_iterator)
     Range r;
     i64 current;
     RangeIter(Range r) : r(r), current(r.start) {}
@@ -16,7 +15,6 @@ struct RangeIter{
 };
 
 struct ArrayIter{
-    PY_CLASS(ArrayIter, builtins, _array_iterator)
     PyObject* ref;
     PyObject** begin;
     PyObject** end;
@@ -30,7 +28,6 @@ struct ArrayIter{
 };
 
 struct StringIter{
-    PY_CLASS(StringIter, builtins, _string_iterator)
     PyObject* ref;
     int i;      // byte index
     StringIter(PyObject* ref) : ref(ref), i(0) {}
@@ -39,7 +36,6 @@ struct StringIter{
 };
 
 struct Generator{
-    PY_CLASS(Generator, builtins, generator)
     Frame frame;
     int state;      // 0,1,2
     List s_backup;
@@ -58,7 +54,6 @@ struct Generator{
 };
 
 struct DictItemsIter{
-    PY_CLASS(DictItemsIter, builtins, _dict_items_iterator)
     PyObject* ref;
     int i;
     DictItemsIter(PyObject* ref) : ref(ref) {

+ 0 - 8
include/pocketpy/linalg.h

@@ -7,8 +7,6 @@ namespace pkpy{
 inline bool isclose(float a, float b){ return std::fabs(a - b) < 1e-4; }
 
 struct Vec2{
-    PY_CLASS(Vec2, linalg, vec2)
-
     Vec2* _() { return this; }
     static void _register(VM* vm, PyObject* mod, PyObject* type);
 
@@ -35,8 +33,6 @@ struct Vec2{
 };
 
 struct Vec3{
-    PY_CLASS(Vec3, linalg, vec3)
-
     Vec3* _() { return this; }
     static void _register(VM* vm, PyObject* mod, PyObject* type);
 
@@ -62,8 +58,6 @@ struct Vec3{
 };
 
 struct Vec4{
-    PY_CLASS(Vec4, linalg, vec4)
-
     Vec4* _() { return this; }
     static void _register(VM* vm, PyObject* mod, PyObject* type);
 
@@ -88,8 +82,6 @@ struct Vec4{
 };
 
 struct Mat3x3{
-    PY_CLASS(Mat3x3, linalg, mat3x3)
-
     Mat3x3* _(){ return this; }
     static void _register(VM* vm, PyObject* mod, PyObject* type);
 

+ 24 - 0
include/pocketpy/vm.h

@@ -425,6 +425,30 @@ public:
     PyObject* bind(PyObject*, const char*, NativeFuncC, UserData userdata={}, BindType bt=BindType::DEFAULT);
     PyObject* bind_property(PyObject*, const char*, NativeFuncC fget, NativeFuncC fset=nullptr);
 
+    template<typename T>
+    PyObject* register_user_class(PyObject* mod, StrName name, Type base=0, bool subclass_enabled=false){
+        PyObject* type = new_type_object(mod, name, base, subclass_enabled);
+        mod->attr().set(name, type);
+        _cxx_typeid_map[typeid(T)] = PK_OBJ_GET(Type, type);
+        T::_register(vm, mod, type);
+        return type;
+    }
+
+    template<typename T, typename ...Args>
+    PyObject* new_user_object(Args&&... args){
+        return heap.gcnew<T>(_tp_user<T>(), std::forward<Args>(args)...);
+    }
+
+    template<typename T>
+    Type _tp_user(){
+        return _find_type_in_cxx_typeid_map<T>();
+    }
+
+    template<typename T>
+    bool is_user_type(PyObject* obj){
+        return _tp(obj) == _tp_user<T>();
+    }
+
     template<typename T>
     Type _find_type_in_cxx_typeid_map(){
         auto it = _cxx_typeid_map.find(typeid(T));

+ 10 - 12
src/array2d.cpp

@@ -4,7 +4,6 @@ namespace pkpy{
 
 struct Array2d{
     PK_ALWAYS_PASS_BY_POINTER(Array2d)
-    PY_CLASS(Array2d, array2d, array2d)
 
     PyObject** data;
     int n_cols;
@@ -105,7 +104,7 @@ struct Array2d{
 
             if(is_type(xy[0], VM::tp_slice) && is_type(xy[1], VM::tp_slice)){
                 HANDLE_SLICE();
-                PyObject* new_array_obj = vm->heap.gcnew<Array2d>(Array2d::_type(vm));
+                PyObject* new_array_obj = vm->new_user_object<Array2d>();
                 Array2d& new_array = PK_OBJ_GET(Array2d, new_array_obj);
                 new_array.init(stop_col - start_col, stop_row - start_row);
                 for(int j = start_row; j < stop_row; j++){
@@ -150,7 +149,7 @@ struct Array2d{
                     return;
                 }
 
-                if(!is_type(_2, Array2d::_type(vm))){
+                if(!vm->is_user_type<Array2d>(_2)){
                     vm->TypeError(_S("expected int/float/str/bool/None or an array2d instance"));
                 }
 
@@ -192,7 +191,7 @@ struct Array2d{
         vm->bind(type, "map(self, f)", [](VM* vm, ArgsView args){
             Array2d& self = PK_OBJ_GET(Array2d, args[0]);
             PyObject* f = args[1];
-            PyObject* new_array_obj = vm->heap.gcnew<Array2d>(Array2d::_type(vm));
+            PyObject* new_array_obj = vm->new_user_object<Array2d>();
             Array2d& new_array = PK_OBJ_GET(Array2d, new_array_obj);
             new_array.init(self.n_cols, self.n_rows);
             for(int i = 0; i < new_array.numel; i++){
@@ -203,7 +202,7 @@ struct Array2d{
 
         vm->bind(type, "copy(self)", [](VM* vm, ArgsView args){
             Array2d& self = PK_OBJ_GET(Array2d, args[0]);
-            PyObject* new_array_obj = vm->heap.gcnew<Array2d>(Array2d::_type(vm));
+            PyObject* new_array_obj = vm->new_user_object<Array2d>();
             Array2d& new_array = PK_OBJ_GET(Array2d, new_array_obj);
             new_array.init(self.n_cols, self.n_rows);
             for(int i = 0; i < new_array.numel; i++){
@@ -255,7 +254,7 @@ struct Array2d{
 
         vm->bind__eq__(PK_OBJ_GET(Type, type), [](VM* vm, PyObject* _0, PyObject* _1){
             Array2d& self = PK_OBJ_GET(Array2d, _0);
-            if(!is_type(_1, Array2d::_type(vm))) return vm->NotImplemented;
+            if(!vm->is_user_type<Array2d>(_1)) return vm->NotImplemented;
             Array2d& other = PK_OBJ_GET(Array2d, _1);
             if(self.n_cols != other.n_cols || self.n_rows != other.n_rows) return vm->False;
             for(int i = 0; i < self.numel; i++){
@@ -266,7 +265,7 @@ struct Array2d{
 
         vm->bind(type, "count_neighbors(self, value, neighborhood='Moore') -> array2d[int]", [](VM* vm, ArgsView args){
             Array2d& self = PK_OBJ_GET(Array2d, args[0]);
-            PyObject* new_array_obj = vm->heap.gcnew<Array2d>(Array2d::_type(vm));
+            PyObject* new_array_obj = vm->new_user_object<Array2d>();
             Array2d& new_array = PK_OBJ_GET(Array2d, new_array_obj);
             new_array.init(self.n_cols, self.n_rows);
             PyObject* value = args[1];
@@ -347,8 +346,7 @@ struct Array2d{
 
 struct Array2dIter{
     PK_ALWAYS_PASS_BY_POINTER(Array2dIter)
-    PY_CLASS(Array2dIter, array2d, _array2d_iterator)
-    
+
     PyObject* ref;
     int i;
     Array2dIter(PyObject* ref) : ref(ref), i(0) {}
@@ -375,10 +373,10 @@ struct Array2dIter{
 void add_module_array2d(VM* vm){
     PyObject* mod = vm->new_module("array2d");
 
-    Array2d::register_class(vm, mod);
-    Array2dIter::register_class(vm, mod);
+    vm->register_user_class<Array2d>(mod, "array2d");
+    vm->register_user_class<Array2dIter>(mod, "_array2d_iter");
 
-    vm->bind__iter__(Array2d::_type(vm), [](VM* vm, PyObject* _0){
+    vm->bind__iter__(vm->_tp_user<Array2d>(), [](VM* vm, PyObject* _0){
         return VAR_T(Array2dIter, _0);
     });
 }

+ 7 - 6
src/cffi.cpp

@@ -21,7 +21,7 @@ namespace pkpy{
 
 #define BIND_CMP(name, op)  \
         vm->bind##name(PK_OBJ_GET(Type, type), [](VM* vm, PyObject* lhs, PyObject* rhs){        \
-            if(!vm->isinstance(rhs, VoidP::_type(vm))) return vm->NotImplemented;               \
+            if(!vm->isinstance(rhs, vm->_tp_user<VoidP>())) return vm->NotImplemented;          \
             void* _0 = PK_OBJ_GET(VoidP, lhs).ptr;                                              \
             void* _1 = PK_OBJ_GET(VoidP, rhs).ptr;                                              \
             return VAR(_0 op _1);                                                               \
@@ -96,7 +96,7 @@ namespace pkpy{
 
         vm->bind__eq__(PK_OBJ_GET(Type, type), [](VM* vm, PyObject* lhs, PyObject* rhs){
             C99Struct& self = _CAST(C99Struct&, lhs);
-            if(!is_type(rhs, C99Struct::_type(vm))) return vm->NotImplemented;
+            if(!vm->is_user_type<C99Struct>(rhs)) return vm->NotImplemented;
             C99Struct& other = _CAST(C99Struct&, rhs);
             bool ok = self.size == other.size && memcmp(self.p, other.p, self.size) == 0;
             return VAR(ok);
@@ -161,15 +161,16 @@ void add_module_c(VM* vm){
         return vm->None;
     });
 
-    VoidP::register_class(vm, mod);
-    C99Struct::register_class(vm, mod);
+    vm->register_user_class<VoidP>(mod, "void_p");
+    vm->register_user_class<C99Struct>(mod, "struct");
+    
     mod->attr().set("NULL", VAR_T(VoidP, nullptr));
 
     vm->bind(mod, "p_cast(ptr: 'void_p', cls: type[T]) -> T", [](VM* vm, ArgsView args){
         VoidP& ptr = CAST(VoidP&, args[0]);
         vm->check_type(args[1], vm->tp_type);
         Type cls = PK_OBJ_GET(Type, args[1]);
-        if(!vm->issubclass(cls, VoidP::_type(vm))){
+        if(!vm->issubclass(cls, vm->_tp_user<VoidP>())){
             vm->ValueError("expected a subclass of void_p");
         }
         return vm->heap.gcnew<VoidP>(cls, ptr.ptr);
@@ -194,7 +195,7 @@ void add_module_c(VM* vm){
         T val = CAST(T, args[0]);                                       \
         return VAR_T(C99Struct, &val, sizeof(T));                       \
     });                                                                 \
-    type = vm->new_type_object(mod, CNAME "_p", VoidP::_type(vm));      \
+    type = vm->new_type_object(mod, CNAME "_p", vm->_tp_user<VoidP>()); \
     mod->attr().set(CNAME "_p", type);                                  \
     type_t = PK_OBJ_GET(Type, type);                                    \
     vm->bind_method<0>(type, "read", [](VM* vm, ArgsView args){         \

+ 6 - 11
src/collections.cpp

@@ -4,7 +4,6 @@ namespace pkpy
 {
     struct PyDequeIter // Iterator for the deque type
     {
-        PY_CLASS(PyDequeIter, collections, _deque_iterator)
         PyObject *ref;
         bool is_reversed;
         std::deque<PyObject *>::iterator begin, end, current;
@@ -48,7 +47,6 @@ namespace pkpy
     }
     struct PyDeque
     {
-        PY_CLASS(PyDeque, collections, deque);
         PyDeque(VM *vm, PyObject *iterable, PyObject *maxlen); // constructor
         // PyDeque members
         std::deque<PyObject *> dequeItems;
@@ -108,9 +106,7 @@ namespace pkpy
         vm->bind__iter__(PK_OBJ_GET(Type, type), [](VM *vm, PyObject* _0)
         {
             PyDeque &self = _CAST(PyDeque &, _0);
-            return vm->heap.gcnew<PyDequeIter>(
-                PyDequeIter::_type(vm), _0,
-                self.dequeItems.begin(), self.dequeItems.end());
+            return vm->new_user_object<PyDequeIter>(_0, self.dequeItems.begin(), self.dequeItems.end());
         });
 
         vm->bind__repr__(PK_OBJ_GET(Type, type), [](VM *vm, PyObject* _0)
@@ -134,7 +130,7 @@ namespace pkpy
         vm->bind__eq__(PK_OBJ_GET(Type, type), [](VM *vm, PyObject* _0, PyObject* _1)
         {
             const PyDeque &self = _CAST(PyDeque&, _0);
-            if(!is_type(_0, PyDeque::_type(vm))) return vm->NotImplemented;
+            if(!vm->is_user_type<PyDeque>(_0)) return vm->NotImplemented;
             const PyDeque &other = _CAST(PyDeque&, _1);
             if (self.dequeItems.size() != other.dequeItems.size()) return vm->False;
             for (int i = 0; i < self.dequeItems.size(); i++){
@@ -214,8 +210,8 @@ namespace pkpy
                  {
                      auto _lock = vm->heap.gc_scope_lock(); // locking the heap
                      PyDeque &self = _CAST(PyDeque &, args[0]);
-                     PyObject *newDequeObj = vm->heap.gcnew<PyDeque>(PyDeque::_type(vm), vm, vm->None, vm->None); // create the empty deque
-                     PyDeque &newDeque = _CAST(PyDeque &, newDequeObj);                                           // cast it to PyDeque so we can use its methods
+                     PyObject *newDequeObj = vm->new_user_object<PyDeque>(vm, vm->None, vm->None); // create the empty deque
+                     PyDeque &newDeque = _CAST(PyDeque &, newDequeObj);                            // cast it to PyDeque so we can use its methods
                      for (auto it = self.dequeItems.begin(); it != self.dequeItems.end(); ++it)
                          newDeque.insertObj(false, true, -1, *it);
                      return newDequeObj;
@@ -546,12 +542,11 @@ namespace pkpy
             PK_OBJ_MARK(obj);
     }
     /// @brief registers the PyDeque class
-    /// @param vm is needed for the new_module and register_class
     void add_module_collections(VM *vm)
     {
         PyObject *mod = vm->new_module("collections");
-        PyDeque::register_class(vm, mod);
-        PyDequeIter::register_class(vm, mod);
+        vm->register_user_class<PyDeque>(mod, "deque");
+        vm->register_user_class<PyDequeIter>(mod, "_deque_iter");
         CodeObject_ code = vm->compile(kPythonLibs_collections, "collections.py", EXEC_MODE);
         vm->_exec(code, mod);
     }

+ 1 - 3
src/io.cpp

@@ -10,8 +10,6 @@ namespace pkpy{
 #if PK_ENABLE_OS
 
 struct FileIO {
-    PY_CLASS(FileIO, io, FileIO)
-
     FILE* fp;
     bool is_text;
 
@@ -142,7 +140,7 @@ void FileIO::close(){
 
 void add_module_io(VM* vm){
     PyObject* mod = vm->new_module("io");
-    FileIO::register_class(vm, mod);
+    vm->register_user_class<FileIO>(mod, "FileIO");
 
     mod->attr().set("SEEK_SET", VAR(SEEK_SET));
     mod->attr().set("SEEK_CUR", VAR(SEEK_CUR));

+ 11 - 10
src/linalg.cpp

@@ -24,15 +24,15 @@ namespace pkpy{
 
 #define BIND_VEC_FUNCTION_1(D, name)        \
         vm->bind_method<1>(type, #name, [](VM* vm, ArgsView args){          \
-            Vec##D& self = _CAST(Vec##D&, args[0]);                     \
-            Vec##D& other = CAST(Vec##D&, args[1]);                     \
+            Vec##D& self = _CAST(Vec##D&, args[0]);                         \
+            Vec##D& other = CAST(Vec##D&, args[1]);                         \
             return VAR(self.name(other));                                   \
         });
 
-#define BIND_VEC_MUL_OP(D)                                                                \
+#define BIND_VEC_MUL_OP(D)                                                                  \
         vm->bind__mul__(PK_OBJ_GET(Type, type), [](VM* vm, PyObject* _0, PyObject* _1){     \
             Vec##D& self = _CAST(Vec##D&, _0);                                          \
-            if(is_type(_1, Vec##D::_type(vm))){                                \
+            if(vm->is_user_type<Vec##D>(_1)){                                               \
                 Vec##D& other = _CAST(Vec##D&, _1);                                     \
                 return VAR(self * other);                                                   \
             }                                                                               \
@@ -369,11 +369,11 @@ static Vec2 SmoothDamp(Vec2 current, Vec2 target, Vec2& currentVelocity, float s
 
         vm->bind__matmul__(PK_OBJ_GET(Type, type), [](VM* vm, PyObject* _0, PyObject* _1){
             Mat3x3& self = _CAST(Mat3x3&, _0);
-            if(is_type(_1, Mat3x3::_type(vm))){
+            if(vm->is_user_type<Mat3x3>(_1)){
                 const Mat3x3& other = _CAST(Mat3x3&, _1);
                 return VAR_T(Mat3x3, self.matmul(other));
             }
-            if(is_type(_1, Vec3::_type(vm))){
+            if(vm->is_user_type<Vec3>(_1)){
                 const Vec3& other = _CAST(Vec3&, _1);
                 return VAR_T(Vec3, self.matmul(other));
             }
@@ -540,10 +540,11 @@ static Vec2 SmoothDamp(Vec2 current, Vec2 target, Vec2& currentVelocity, float s
 
 void add_module_linalg(VM* vm){
     PyObject* linalg = vm->new_module("linalg");
-    Vec2::register_class(vm, linalg);
-    Vec3::register_class(vm, linalg);
-    Vec4::register_class(vm, linalg);
-    Mat3x3::register_class(vm, linalg);
+
+    vm->register_user_class<Vec2>(linalg, "vec2");
+    vm->register_user_class<Vec3>(linalg, "vec3");
+    vm->register_user_class<Vec4>(linalg, "vec4");
+    vm->register_user_class<Mat3x3>(linalg, "mat3x3");
 
     PyObject* float_p = vm->_modules["c"]->attr("float_p");
     linalg->attr().set("vec2_p", float_p);

+ 2 - 6
src/modules.cpp

@@ -13,8 +13,6 @@ void add_module_operator(VM* vm){
 }
 
 struct PyStructTime{
-    PY_CLASS(PyStructTime, time, struct_time)
-
     int tm_year;
     int tm_mon;
     int tm_mday;
@@ -56,7 +54,7 @@ struct PyStructTime{
 
 void add_module_time(VM* vm){
     PyObject* mod = vm->new_module("time");
-    PyStructTime::register_class(vm, mod);
+    vm->register_user_class<PyStructTime>(mod, "struct_time");
 
     vm->bind_func<0>(mod, "time", [](VM* vm, ArgsView args) {
         auto now = std::chrono::system_clock::now();
@@ -298,8 +296,6 @@ struct _LpGuard{
 
 // line_profiler wrapper
 struct LineProfilerW{
-    PY_CLASS(LineProfilerW, line_profiler, LineProfiler)
-
     LineProfiler profiler;
 
     static void _register(VM* vm, PyObject* mod, PyObject* type){
@@ -352,7 +348,7 @@ _LpGuard::~_LpGuard(){
 
 void add_module_line_profiler(VM *vm){
     PyObject* mod = vm->new_module("line_profiler");
-    LineProfilerW::register_class(vm, mod);
+    vm->register_user_class<LineProfilerW>(mod, "LineProfiler");
 }
 #else
 void add_module_line_profiler(VM* vm){

+ 6 - 6
src/pocketpy.cpp

@@ -1383,7 +1383,7 @@ void init_builtins(VM* _vm) {
     });
 
     _vm->bind_method<0>(VM::tp_dict, "items", [](VM* vm, ArgsView args) {
-        return vm->heap.gcnew<DictItemsIter>(DictItemsIter::_type(vm), args[0]);
+        return vm->new_user_object<DictItemsIter>(args[0]);
     });
 
     _vm->bind_method<1>(VM::tp_dict, "update", [](VM* vm, ArgsView args) {
@@ -1495,11 +1495,11 @@ void init_builtins(VM* _vm) {
         return VAR(self.msg);
     });
 
-    RangeIter::register_class(_vm, _vm->builtins);
-    ArrayIter::register_class(_vm, _vm->builtins);
-    StringIter::register_class(_vm, _vm->builtins);
-    Generator::register_class(_vm, _vm->builtins);
-    DictItemsIter::register_class(_vm, _vm->builtins);
+    _vm->register_user_class<RangeIter>(_vm->builtins, "_range_iter");
+    _vm->register_user_class<ArrayIter>(_vm->builtins, "_array_iter");
+    _vm->register_user_class<StringIter>(_vm->builtins, "_string_iter");
+    _vm->register_user_class<Generator>(_vm->builtins, "generator");
+    _vm->register_user_class<DictItemsIter>(_vm->builtins, "_dict_items_iter");
 }
 
 void VM::post_init(){

+ 1 - 1
src/pocketpy_c.cpp

@@ -277,7 +277,7 @@ bool pkpy_is_voidp(pkpy_vm* vm_handle, int i){
     PK_ASSERT_NO_ERROR()
     PK_PROTECTED(
         PyObject* item = stack_item(vm, i);
-        return is_type(item, VoidP::_type(vm));
+        return vm->is_user_type<VoidP>(item);
     )
 }
 

+ 2 - 3
src/random.cpp

@@ -126,7 +126,6 @@ struct mt19937{
 namespace pkpy{
 
 struct Random{
-    PY_CLASS(Random, random, Random)
     mt19937 gen;
 
     Random(){
@@ -215,8 +214,8 @@ struct Random{
 
 void add_module_random(VM* vm){
     PyObject* mod = vm->new_module("random");
-    Random::register_class(vm, mod);
-    PyObject* instance = vm->heap.gcnew<Random>(Random::_type(vm));
+    vm->register_user_class<Random>(mod, "Random");
+    PyObject* instance = vm->new_user_object<Random>();
     mod->attr().set("seed", vm->getattr(instance, "seed"));
     mod->attr().set("random", vm->getattr(instance, "random"));
     mod->attr().set("uniform", vm->getattr(instance, "uniform"));