diff --git a/amalgamate.py b/amalgamate.py index ccfb0eb6..dc3a5393 100644 --- a/amalgamate.py +++ b/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"], diff --git a/build_g.sh b/build_g.sh new file mode 100644 index 00000000..2cfa2c90 --- /dev/null +++ b/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 diff --git a/build_with_warnings.sh b/build_with_warnings.sh deleted file mode 100644 index f2d5022f..00000000 --- a/build_with_warnings.sh +++ /dev/null @@ -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 diff --git a/include/pocketpy/any.h b/include/pocketpy/any.h new file mode 100644 index 00000000..464c6627 --- /dev/null +++ b/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 + inline static vtable* get(){ + static_assert(std::is_same_v>); + if constexpr (is_sso_v){ + static vtable vt{ typeid(T), [](void*){} }; + return &vt; + }else{ + static vtable vt{ typeid(T), [](void* ptr){ delete static_cast(ptr); } }; + return &vt; + } + } + }; + + void* data; + vtable* _vt; + + any() : data(nullptr), _vt(nullptr) {} + + explicit operator bool() const { return data != nullptr; } + + template + any(T&& value){ + using U = std::decay_t; + static_assert(!std::is_same_v, "any(const any&) is deleted"); + if constexpr (is_sso_v){ + memcpy(&data, &value, sizeof(U)); + }else{ + data = new U(std::forward(value)); + } + _vt = vtable::get(); + } + + 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 +bool any_cast(const any& a, T** out){ + static_assert(std::is_same_v>); + if(a.type_id() != typeid(T)) return false; + if constexpr (is_sso_v){ + *out = (T*)(&a.data); + }else{ + *out = static_cast(a.data); + } + return true; +} + +using UserData = any; + +} // namespace pkpy \ No newline at end of file diff --git a/include/pocketpy/codeobject.h b/include/pocketpy/codeobject.h index caf16d55..ac3ef81e 100644 --- a/include/pocketpy/codeobject.h +++ b/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 - UserData(T t): empty(false){ - static_assert(std::is_trivially_copyable_v); - static_assert(sizeof(T) <= sizeof(data)); - memcpy(data, &t, sizeof(T)); - } - - template - T get() const{ - static_assert(std::is_trivially_copyable_v); - static_assert(sizeof(T) <= sizeof(data)); - PK_DEBUG_ASSERT(!empty); - return reinterpret_cast(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_ final: PyObject { }; template -T lambda_get_userdata(PyObject** p){ - if(p[-1] != PY_NULL) return PK_OBJ_GET(NativeFunc, p[-1])._userdata.get(); - else return PK_OBJ_GET(NativeFunc, p[-2])._userdata.get(); +T& lambda_get_userdata(PyObject** p){ + static_assert(std::is_same_v>); + 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 \ No newline at end of file diff --git a/include/pocketpy/common.h b/include/pocketpy/common.h index 569ea78a..7b3229bc 100644 --- a/include/pocketpy/common.h +++ b/include/pocketpy/common.h @@ -128,11 +128,12 @@ struct Type { struct PyObject; #define PK_BITS(p) (reinterpret_cast(p)) -// is_pod<> for c++17 and c++20 +// is_pod_v<> for c++17 and c++20 template -struct is_pod { - static constexpr bool value = std::is_trivially_copyable_v && std::is_standard_layout_v; -}; +inline constexpr bool is_pod_v = std::is_trivially_copyable_v && std::is_standard_layout_v; + +template +inline constexpr bool is_sso_v = is_pod_v && sizeof(T) <= sizeof(void*); #define PK_ALWAYS_PASS_BY_POINTER(T) \ T(const T&) = delete; \ diff --git a/include/pocketpy/namedict.h b/include/pocketpy/namedict.h index b97cb506..43a925e3 100644 --- a/include/pocketpy/namedict.h +++ b/include/pocketpy/namedict.h @@ -19,7 +19,7 @@ constexpr T default_invalid_value(){ template struct SmallNameDict{ using K = StrName; - static_assert(is_pod::value); + static_assert(is_pod_v); bool _is_small; uint16_t _size; @@ -103,7 +103,7 @@ struct LargeNameDict { using Item = NameDictItem; static constexpr uint16_t kInitialCapacity = 32; - static_assert(is_pod::value); + static_assert(is_pod_v); bool _is_small; float _load_factor; diff --git a/include/pocketpy/vector.h b/include/pocketpy/vector.h index 8c10d41b..cc334c5e 100644 --- a/include/pocketpy/vector.h +++ b/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::value); + static_assert(is_pod_v); static_assert(N >= 4); int _size; diff --git a/include/pocketpy/vm.h b/include/pocketpy/vm.h index 603f1c09..de960d02 100644 --- a/include/pocketpy/vm.h +++ b/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 diff --git a/src/any.cpp b/src/any.cpp new file mode 100644 index 00000000..8ec4be7c --- /dev/null +++ b/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 \ No newline at end of file diff --git a/src/vm.cpp b/src/vm.cpp index 126d10f8..1f3e5a66 100644 --- a/src/vm.cpp +++ b/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: