ソースを参照

add `pkpy::any`

blueloveTH 1 年間 前
コミット
4a1cf10e74

+ 1 - 1
amalgamate.py

@@ -6,7 +6,7 @@ with open("include/pocketpy/opcodes.h", "rt", encoding='utf-8') as f:
 	OPCODES_TEXT = '\n' + f.read() + '\n'
 
 pipeline = [
-	["config.h", "export.h", "_generated.h", "common.h", "memory.h", "vector.h", "str.h", "tuplelist.h", "namedict.h", "error.h"],
+	["config.h", "export.h", "_generated.h", "common.h", "memory.h", "any.h", "vector.h", "str.h", "tuplelist.h", "namedict.h", "error.h"],
 	["obj.h", "dict.h", "codeobject.h", "frame.h", "profiler.h"],
 	["gc.h", "vm.h", "ceval.h", "lexer.h", "expr.h", "compiler.h", "repl.h"],
 	["cffi.h", "bindings.h", "iter.h", "base64.h", "csv.h", "collections.h", "array2d.h", "dataclasses.h", "random.h", "linalg.h", "easing.h", "io.h", "modules.h"],

+ 5 - 0
build_g.sh

@@ -0,0 +1,5 @@
+SRC=$(find src/ -name "*.cpp")
+
+FLAGS="-std=c++17 -O1 -stdlib=libc++ -Iinclude -frtti -Wfatal-errors -g"
+
+clang++ $FLAGS -o main -O1 src2/main.cpp $SRC

+ 0 - 5
build_with_warnings.sh

@@ -1,5 +0,0 @@
-SRC=$(find src/ -name "*.cpp")
-
-FLAGS="-std=c++17 -O1 -stdlib=libc++ -Iinclude -frtti -W -Wno-unused-parameter -Wno-sign-compare"
-
-clang++ $FLAGS -o main -O1 src2/main.cpp $SRC

+ 71 - 0
include/pocketpy/any.h

@@ -0,0 +1,71 @@
+#pragma once
+
+#include "common.h"
+
+namespace pkpy {
+
+struct any{
+    struct vtable{
+        const std::type_index type;
+        void (*deleter)(void*);
+
+        template<typename T>
+        inline static vtable* get(){
+            static_assert(std::is_same_v<T, std::decay_t<T>>);
+            if constexpr (is_sso_v<T>){
+                static vtable vt{ typeid(T), [](void*){} };
+                return &vt;
+            }else{
+                static vtable vt{ typeid(T), [](void* ptr){ delete static_cast<T*>(ptr); } };
+                return &vt;
+            }
+        }
+    };
+
+    void* data;
+    vtable* _vt;
+
+    any() : data(nullptr), _vt(nullptr) {}
+
+    explicit operator bool() const { return data != nullptr; }
+
+    template<typename T>
+    any(T&& value){
+        using U = std::decay_t<T>;
+        static_assert(!std::is_same_v<U, any>, "any(const any&) is deleted");
+        if constexpr (is_sso_v<U>){
+            memcpy(&data, &value, sizeof(U));
+        }else{
+            data = new U(std::forward<T>(value));
+        }
+        _vt = vtable::get<U>();
+    }
+
+    any(any&& other) noexcept;
+    any& operator=(any&& other) noexcept;
+
+    const std::type_index type_id() const{
+        return _vt ? _vt->type : typeid(void);
+    }
+
+    any(const any& other) = delete;
+    any& operator=(const any& other) = delete;
+
+    ~any() { if(data) _vt->deleter(data); }
+};
+
+template<typename T>
+bool any_cast(const any& a, T** out){
+    static_assert(std::is_same_v<T, std::decay_t<T>>);
+    if(a.type_id() != typeid(T)) return false;
+    if constexpr (is_sso_v<T>){
+        *out = (T*)(&a.data);
+    }else{
+        *out = static_cast<T*>(a.data);
+    }
+    return true;
+}
+
+using UserData = any;
+
+} // namespace pkpy

+ 13 - 29
include/pocketpy/codeobject.h

@@ -2,6 +2,7 @@
 
 #include "obj.h"
 #include "error.h"
+#include "any.h"
 
 namespace pkpy{
 
@@ -120,27 +121,6 @@ struct FuncDecl {
     void _gc_mark() const;
 };
 
-struct UserData{
-    char data[12];
-    bool empty;
-
-    UserData(): empty(true) {}
-    template<typename T>
-    UserData(T t): empty(false){
-        static_assert(std::is_trivially_copyable_v<T>);
-        static_assert(sizeof(T) <= sizeof(data));
-        memcpy(data, &t, sizeof(T));
-    }
-
-    template <typename T>
-    T get() const{
-        static_assert(std::is_trivially_copyable_v<T>);
-        static_assert(sizeof(T) <= sizeof(data));
-        PK_DEBUG_ASSERT(!empty);
-        return reinterpret_cast<const T&>(data);
-    }
-};
-
 struct NativeFunc {
     NativeFuncC f;
 
@@ -152,12 +132,11 @@ struct NativeFunc {
 
     UserData _userdata;
 
-    void set_userdata(UserData data) {
-        if(!_userdata.empty && !data.empty){
-            // override is not supported
-            throw std::runtime_error("userdata already set");
+    void set_userdata(UserData&& data) {
+        if(_userdata){
+            throw std::runtime_error("NativeFunc userdata already set");
         }
-        _userdata = data;
+        _userdata = std::move(data);
     }
 
     NativeFunc(NativeFuncC f, int argc): f(f), argc(argc) {}
@@ -205,9 +184,14 @@ struct Py_<NativeFunc> final: PyObject {
 };
 
 template<typename T>
-T lambda_get_userdata(PyObject** p){
-    if(p[-1] != PY_NULL) return PK_OBJ_GET(NativeFunc, p[-1])._userdata.get<T>();
-    else return PK_OBJ_GET(NativeFunc, p[-2])._userdata.get<T>();
+T& lambda_get_userdata(PyObject** p){
+    static_assert(std::is_same_v<T, std::decay_t<T>>);
+    UserData* ud;
+    if(p[-1] != PY_NULL) ud = &PK_OBJ_GET(NativeFunc, p[-1])._userdata;
+    else ud = &PK_OBJ_GET(NativeFunc, p[-2])._userdata;
+    T* out;
+    if(!any_cast(*ud, &out)) throw std::runtime_error("lambda_get_userdata: any_cast failed");
+    return *out;
 }
 
 } // namespace pkpy

+ 5 - 4
include/pocketpy/common.h

@@ -128,11 +128,12 @@ struct Type {
 struct PyObject;
 #define PK_BITS(p) (reinterpret_cast<i64>(p))
 
-// is_pod<> for c++17 and c++20
+// is_pod_v<> for c++17 and c++20
 template<typename T>
-struct is_pod {
-	static constexpr bool value = std::is_trivially_copyable_v<T> && std::is_standard_layout_v<T>;
-};
+inline constexpr bool is_pod_v = std::is_trivially_copyable_v<T> && std::is_standard_layout_v<T>;
+
+template<typename T>
+inline constexpr bool is_sso_v = is_pod_v<T> && sizeof(T) <= sizeof(void*);
 
 #define PK_ALWAYS_PASS_BY_POINTER(T) \
 	T(const T&) = delete; \

+ 2 - 2
include/pocketpy/namedict.h

@@ -19,7 +19,7 @@ constexpr T default_invalid_value(){
 template<typename V>
 struct SmallNameDict{
     using K = StrName;
-    static_assert(is_pod<V>::value);
+    static_assert(is_pod_v<V>);
 
     bool _is_small;
     uint16_t _size;
@@ -103,7 +103,7 @@ struct LargeNameDict {
 
     using Item = NameDictItem<T>;
     static constexpr uint16_t kInitialCapacity = 32;
-    static_assert(is_pod<T>::value);
+    static_assert(is_pod_v<T>);
 
     bool _is_small;
     float _load_factor;

+ 1 - 1
include/pocketpy/vector.h

@@ -11,7 +11,7 @@ struct pod_vector{
     static constexpr int N = 64 / SizeT;
 
     // static_assert(64 % SizeT == 0);
-    static_assert(is_pod<T>::value);
+    static_assert(is_pod_v<T>);
     static_assert(N >= 4);
 
     int _size;

+ 1 - 1
include/pocketpy/vm.h

@@ -308,7 +308,7 @@ public:
 #if PK_REGION("General Bindings")
     PyObject* bind_func(PyObject* obj, StrName name, int argc, NativeFuncC fn, UserData userdata={}, BindType bt=BindType::DEFAULT);
     PyObject* bind_func(Type type, StrName name, int argc, NativeFuncC fn, UserData userdata={}, BindType bt=BindType::DEFAULT){
-        return bind_func(_t(type), name, argc, fn, userdata, bt);
+        return bind_func(_t(type), name, argc, fn, std::move(userdata), bt);
     }
     PyObject* bind_property(PyObject*, const char*, NativeFuncC fget, NativeFuncC fset=nullptr);
     template<typename T, typename F, bool ReadOnly=false>

+ 19 - 0
src/any.cpp

@@ -0,0 +1,19 @@
+#include "pocketpy/any.h"
+
+namespace pkpy{
+
+any::any(any&& other) noexcept: data(other.data), _vt(other._vt){
+    other.data = nullptr;
+    other._vt = nullptr;
+}
+
+any& any::operator=(any&& other) noexcept{
+    if(data) _vt->deleter(data);
+    data = other.data;
+    _vt = other._vt;
+    other.data = nullptr;
+    other._vt = nullptr;
+    return *this;
+}
+
+}   // namespace pkpy

+ 3 - 3
src/vm.cpp

@@ -1229,7 +1229,7 @@ void VM::setattr(PyObject* obj, StrName name, PyObject* value){
 
 PyObject* VM::bind_func(PyObject* obj, StrName name, int argc, NativeFuncC fn, UserData userdata, BindType bt) {
     PyObject* nf = VAR(NativeFunc(fn, argc));
-    PK_OBJ_GET(NativeFunc, nf).set_userdata(userdata);
+    PK_OBJ_GET(NativeFunc, nf).set_userdata(std::move(userdata));
     switch(bt){
         case BindType::DEFAULT: break;
         case BindType::STATICMETHOD: nf = VAR(StaticMethod(nf)); break;
@@ -1240,7 +1240,7 @@ PyObject* VM::bind_func(PyObject* obj, StrName name, int argc, NativeFuncC fn, U
 }
 
 PyObject* VM::bind(PyObject* obj, const char* sig, NativeFuncC fn, UserData userdata, BindType bt){
-    return bind(obj, sig, nullptr, fn, userdata, bt);
+    return bind(obj, sig, nullptr, fn, std::move(userdata), bt);
 }
 
 PyObject* VM::bind(PyObject* obj, const char* sig, const char* docstring, NativeFuncC fn, UserData userdata, BindType bt){
@@ -1257,7 +1257,7 @@ PyObject* VM::bind(PyObject* obj, const char* sig, const char* docstring, Native
     FuncDecl_ decl = co->func_decls[0];
     decl->docstring = docstring;
     PyObject* f_obj = VAR(NativeFunc(fn, decl));
-    PK_OBJ_GET(NativeFunc, f_obj).set_userdata(userdata);
+    PK_OBJ_GET(NativeFunc, f_obj).set_userdata(std::move(userdata));
 
     switch(bt){
         case BindType::STATICMETHOD: