Просмотр исходного кода

pybind11 for pkpy 2.0 (#299)

* update pkbind.

* some fix.

* some fix.

* update job name.

* fix CI.

* remove iostream.
ykiko 1 год назад
Родитель
Сommit
7e99584ddc

+ 76 - 0
.github/workflows/pybind11.yml

@@ -0,0 +1,76 @@
+name: PKBIND Build and Test
+
+on:
+  push:
+    paths-ignore:
+      - "docs/**"
+      - "web/**"
+      - "**.md"
+  pull_request:
+    paths-ignore:
+      - "docs/**"
+      - "web/**"
+      - "**.md"
+
+jobs:
+  build_linux:
+    runs-on: ubuntu-latest
+    steps:
+      - name: Checkout code
+        uses: actions/checkout@v4
+
+      - name: Set up GCC
+        run: |
+          sudo apt-get update
+          sudo apt-get install -y gcc g++
+
+      - name: Set up CMake
+        uses: jwlawson/actions-setup-cmake@v1.10
+
+      - name: Test
+        run: |
+          cd include/pybind11/tests
+          cmake -B build
+          cmake --build build --config Release --parallel
+          ./build/PKBIND_TEST
+
+  build_win:
+    runs-on: windows-latest
+    steps:
+      - name: Checkout code
+        uses: actions/checkout@v4
+
+      - name: Set up MSVC
+        uses: ilammy/msvc-dev-cmd@v1
+
+      - name: Set up CMake
+        uses: jwlawson/actions-setup-cmake@v1.10
+
+      - name: Test
+        run: |
+          cd include\pybind11\tests
+          cmake -B build
+          cmake --build build --config Release --parallel
+          build\Release\PKBIND_TEST.exe
+
+  build_mac:
+    runs-on: macos-latest
+    steps:
+      - name: Checkout code
+        uses: actions/checkout@v4
+
+      - name: Set up Clang
+        run: |
+          brew install llvm
+          echo 'export PATH="/usr/local/opt/llvm/bin:$PATH"' >> ~/.zshrc
+          source ~/.zshrc
+
+      - name: Set up CMake
+        uses: jwlawson/actions-setup-cmake@v1.10
+
+      - name: Test
+        run: |
+          cd include/pybind11/tests
+          cmake -B build -DENABLE_TEST=ON
+          cmake --build build --config Release --parallel
+          ./build/PKBIND_TEST

+ 3 - 0
include/pybind11/embed.h

@@ -0,0 +1,3 @@
+#pragma once
+
+#include "pybind11.h"

+ 164 - 0
include/pybind11/internal/accessor.h

@@ -0,0 +1,164 @@
+#pragma once
+
+#include "builtins.h"
+
+namespace pkbind {
+
+template <typename policy>
+class accessor : public interface<accessor<policy>> {
+    using key_type = typename policy::key_type;
+
+    friend interface<handle>;
+    friend interface<accessor<policy>>;
+    friend tuple;
+    friend list;
+    friend dict;
+
+    accessor(handle obj, key_type key) : m_obj(obj), m_value(), m_key(key) {}
+
+public:
+    auto ptr() const {
+        if(m_value.empty()) {
+            m_value = borrow(policy::get(m_obj, m_key));
+        }
+        return m_value.ptr();
+    }
+
+    template <typename Value>
+    accessor& operator= (Value&& value) && {
+        policy::set(m_obj, m_key, pkbind::cast(std::forward<Value>(value)));
+        return *this;
+    }
+
+    template <typename Value>
+    accessor& operator= (Value&& value) & {
+        m_value = std::forward<Value>(value);
+        return *this;
+    }
+
+    operator handle () const { return ptr(); }
+
+private:
+    handle m_obj;
+    mutable object m_value;
+    key_type m_key;
+};
+
+namespace policy {
+
+struct attr {
+    using key_type = name;
+
+    static handle get(handle obj, name key) {
+        raise_call<py_getattr>(obj.ptr(), key.index());
+        return py_retval();
+    }
+
+    static void set(handle obj, name key, handle value) { raise_call<py_setattr>(obj.ptr(), key.index(), value.ptr()); }
+};
+
+template <typename Key>
+struct item {
+    using key_type = Key;
+
+    static handle get(handle obj, int key) { return get(obj, int_(key)); }
+
+    static handle get(handle obj, name key) { return get(obj, str(key)); }
+
+    static handle get(handle obj, handle key) {
+        raise_call<py_getitem>(obj.ptr(), key.ptr());
+        return py_retval();
+    }
+
+    static void set(handle obj, int key, handle value) { set(obj, int_(key), value); }
+
+    static void set(handle obj, name key, handle value) { set(obj, str(key), value); }
+
+    static void set(handle obj, handle key, handle value) { raise_call<py_setitem>(obj.ptr(), key.ptr(), value.ptr()); }
+};
+
+struct tuple {
+    using key_type = int;
+
+    static handle get(handle obj, int key) { return py_tuple_getitem(obj.ptr(), key); }
+
+    static void set(handle obj, int key, handle value) { py_tuple_setitem(obj.ptr(), key, value.ptr()); }
+};
+
+struct list {
+    using key_type = int;
+
+    static handle get(handle obj, int key) { return py_list_getitem(obj.ptr(), key); }
+
+    static void set(handle obj, int key, handle value) { py_list_setitem(obj.ptr(), key, value.ptr()); }
+};
+
+template <typename Key>
+struct dict {
+    using key_type = Key;
+
+    static handle get(handle obj, int key) { return get(obj, int_(key)); }
+
+    static handle get(handle obj, name key) { return get(obj, str(key)); }
+
+    static handle get(handle obj, handle key) {
+        raise_call<py_dict_getitem>(obj.ptr(), key.ptr());
+        return py_retval();
+    }
+
+    static void set(handle obj, int key, handle value) { set(obj, int_(key), value); }
+
+    static void set(handle obj, name key, handle value) { set(obj, str(key), value); }
+
+    static void set(handle obj, handle key, handle value) {
+        raise_call<py_dict_setitem>(obj.ptr(), key.ptr(), value.ptr());
+    }
+};
+
+}  // namespace policy
+
+// implement other methods of interface
+
+template <typename Derived>
+inline attr_accessor interface<Derived>::attr(name key) const {
+    return {ptr(), key};
+}
+
+template <typename Derived>
+inline item_accessor<int> interface<Derived>::operator[] (int key) const {
+    return {ptr(), key};
+}
+
+template <typename Derived>
+inline item_accessor<name> interface<Derived>::operator[] (name key) const {
+    return {ptr(), key};
+}
+
+template <typename Derived>
+inline item_accessor<handle> interface<Derived>::operator[] (handle key) const {
+    return {ptr(), key};
+}
+
+template <typename... Args>
+object str::format(Args&&... args) {
+    return attr("format")(std::forward<Args>(args)...);
+}
+
+inline tuple_accessor tuple::operator[] (int index) const { return {m_ptr, index}; }
+
+inline list_accessor list::operator[] (int index) const { return {m_ptr, index}; };
+
+inline dict_accessor<int> dict::operator[] (int key) const { return {m_ptr, key}; }
+
+inline dict_accessor<name> dict::operator[] (name key) const { return {m_ptr, key}; }
+
+inline dict_accessor<handle> dict::operator[] (handle key) const { return {m_ptr, key}; }
+
+inline dict::iterator::iterator(handle h) : items(h.attr("items")()), iter(items.begin()) {}
+
+inline std::pair<object, object> dict::iterator::operator* () const {
+    tuple pair = *iter;
+    return {borrow(pair[0]), borrow(pair[1])};
+}
+
+}  // namespace pkbind

+ 149 - 64
include/pybind11/internal/builtins.h

@@ -2,107 +2,192 @@
 
 #include "types.h"
 
-namespace pybind11 {
-inline void exec(const char* code, handle global = {}, handle local = {}) {
-    vm->py_exec(code, global.ptr(), local.ptr());
+namespace pkbind {
+
+template <typename... Args>
+inline void print(Args&&... args) {
+    handle print = py_getbuiltin(py_name("print"));
+    print(std::forward<Args>(args)...);
+}
+
+inline object eval(std::string_view code, handle globals = none(), handle locals = none()) {
+    if(globals.is_none() && locals.is_none()) {
+        std::string src{code};
+        raise_call<py_eval>(src.c_str(), nullptr);
+        return object::from_ret();
+    } else {
+        handle eval = py_getbuiltin(py_name("eval"));
+        return eval(str(code), globals.is_none() ? dict() : globals, locals.is_none() ? dict() : locals);
+    }
 }
 
-// wrapper for builtin functions in Python
-inline bool hasattr(const handle& obj, const handle& name) {
-    auto& key = _builtin_cast<pkpy::Str>(name);
-    return vm->getattr(obj.ptr(), key, false) != nullptr;
+inline object exec(std::string_view code, handle globals = none(), handle locals = none()) {
+    if(globals.is_none() && locals.is_none()) {
+        std::string src{code};
+        raise_call<py_exec>(src.c_str(), "exec", EXEC_MODE, nullptr);
+        return object::from_ret();
+    } else {
+        handle exec = py_getbuiltin(py_name("exec"));
+        return exec(str(code), globals, locals);
+    }
 }
 
-inline bool hasattr(const handle& obj, const char* name) { return vm->getattr(obj.ptr(), name, false) != nullptr; }
-
-inline void delattr(const handle& obj, const handle& name) {
-    auto& key = _builtin_cast<pkpy::Str>(name);
-    vm->delattr(obj.ptr(), key);
+inline object locals() {
+    handle locals = py_getbuiltin(py_name("locals"));
+    return locals();
 }
 
-inline void delattr(const handle& obj, const char* name) { vm->delattr(obj.ptr(), name); }
-
-inline object getattr(const handle& obj, const handle& name) {
-    auto& key = _builtin_cast<pkpy::Str>(name);
-    return reinterpret_borrow<object>(vm->getattr(obj.ptr(), key));
+inline object globals() {
+    handle globals = py_getbuiltin(py_name("globals"));
+    return globals();
 }
 
-inline object getattr(const handle& obj, const char* name) {
-    return reinterpret_borrow<object>(vm->getattr(obj.ptr(), name));
+inline bool hasattr(handle obj, name name) {
+    auto pc = py_peek(0);
+    auto result = py_getattr(obj.ptr(), name.index());
+    if(result) {
+        return true;
+    } else {
+        py_clearexc(pc);
+        return false;
+    }
 }
 
-inline object getattr(const handle& obj, const handle& name, const handle& default_) {
-    if(!hasattr(obj, name)) { return reinterpret_borrow<object>(default_); }
-    return getattr(obj, name);
+inline object getattr(handle obj, name name) {
+    raise_call<py_getattr>(obj.ptr(), name.index());
+    return object::from_ret();
 }
 
-inline object getattr(const handle& obj, const char* name, const handle& default_) {
-    if(!hasattr(obj, name)) { return reinterpret_borrow<object>(default_); }
-    return getattr(obj, name);
+inline void setattr(handle obj, name name, handle value) {
+    raise_call<py_setattr>(obj.ptr(), name.index(), value.ptr());
 }
 
-inline void setattr(const handle& obj, const handle& name, const handle& value) {
-    auto& key = _builtin_cast<pkpy::Str>(name);
-    vm->setattr(obj.ptr(), key, value.ptr());
-}
+inline void delattr(handle obj, name name) { raise_call<py_delattr>(obj.ptr(), name.index()); }
 
-inline void setattr(const handle& obj, const char* name, const handle& value) {
-    vm->setattr(obj.ptr(), name, value.ptr());
+inline py_i64 hash(handle obj) {
+    py_i64 result = 0;
+    raise_call<py_hash>(obj.ptr(), &result);
+    return result;
 }
 
-template <typename T>
-inline bool isinstance(const handle& obj) {
-    pkpy::Type cls = _builtin_cast<pkpy::Type>(type::handle_of<T>().ptr());
-    return vm->isinstance(obj.ptr(), cls);
-}
+inline bool isinstance(handle obj, type type) { return py_isinstance(obj.ptr(), type.index()); }
 
-template <>
-inline bool isinstance<handle>(const handle&) = delete;
+inline bool python_error::match(type type) const { return isinstance(m_exception.ptr(), type); }
 
-template <>
-inline bool isinstance<iterable>(const handle& obj) {
-    return hasattr(obj, "__iter__");
+template <typename T>
+constexpr inline bool is_pyobject_v = std::is_base_of_v<object, std::decay_t<T>> || std::is_same_v<type, T>;
+
+template <typename T>
+inline type type::of() {
+    if constexpr(is_pyobject_v<T>) {
+        if constexpr(is_check_v<T>) {
+            return type(tp_object);
+        } else {
+            return type(T::type_or_check());
+        }
+    } else {
+        auto it = m_type_map->find(typeid(T));
+        if(it != m_type_map->end()) {
+            return type(it->second);
+        } else {
+            // if not found, raise error
+            std::string msg = "can not c++ instance cast to object, type: {";
+            msg += type_name<T>();
+            msg += "} is not registered.";
+            throw std::runtime_error(msg);
+        }
+    }
 }
 
-template <>
-inline bool isinstance<iterator>(const handle& obj) {
-    return hasattr(obj, "__iter__") && hasattr(obj, "__next__");
+template <typename T>
+inline bool type::isinstance(handle obj) {
+    if constexpr(is_pyobject_v<T>) {
+        // for every python object wrapper type, there must be a `type_or_check` method.
+        // for some types, it returns the underlying type in pkpy, e.g., `int_` -> `tp_int`.
+        // for other types that may not have a corresponding type in pkpy, it returns a check function.
+        // e.g., `iterable` -> `[](handle h){ return hasattr(h, "iter"); }`.
+        auto type_or_check = T::type_or_check();
+        if constexpr(is_check_v<T>) {
+            return type_or_check(obj);
+        } else {
+            return pkbind::isinstance(obj, type(type_or_check));
+        }
+    } else {
+        return pkbind::isinstance(obj, of<T>());
+    }
 }
 
-inline bool isinstance(const handle& obj, const handle& type) {
-    return vm->isinstance(obj.ptr(), _builtin_cast<pkpy::Type>(type));
+inline bool issubclass(type derived, type base) { return py_issubclass(derived.index(), base.index()); }
+
+template <typename T>
+inline bool isinstance(handle obj) {
+    return type::isinstance<T>(obj);
 }
 
-inline int64_t hash(const handle& obj) { return vm->py_hash(obj.ptr()); }
+template <>
+inline bool isinstance<handle>(handle) = delete;
 
 template <typename T, typename SFINAE = void>
 struct type_caster;
 
 template <typename T>
-handle
-    _cast(T&& value, return_value_policy policy = return_value_policy::automatic_reference, handle parent = handle()) {
-    using U = std::remove_pointer_t<std::remove_cv_t<std::remove_reference_t<T>>>;
-    return type_caster<U>::cast(std::forward<T>(value), policy, parent);
-}
+object cast(T&& value, return_value_policy policy = return_value_policy::automatic_reference, handle parent = {}) {
+    // decay_t can resolve c-array type, but remove_cv_ref_t can't.
+    using underlying_type = std::decay_t<T>;
+
+    if constexpr(std::is_convertible_v<underlying_type, handle> && !is_pyobject_v<underlying_type>) {
+        return object(std::forward<T>(value), object::realloc_t{});
+    } else if constexpr(is_unique_pointer_v<underlying_type>) {
+        using pointer = typename underlying_type::pointer;
+        return type_caster<pointer>::cast(value.release(), return_value_policy::take_ownership, parent);
+    } else {
+        static_assert(!is_multiple_pointer_v<underlying_type>, "multiple pointer is not supported.");
+        static_assert(!std::is_void_v<std::remove_pointer_t<underlying_type>>,
+                      "void* is not supported, consider using py::capsule.");
+
+        // resolve for automatic policy.
+        if(policy == return_value_policy::automatic) {
+            policy = std::is_pointer_v<underlying_type> ? return_value_policy::take_ownership
+                     : std::is_lvalue_reference_v<T&&>  ? return_value_policy::copy
+                                                        : return_value_policy::move;
+        } else if(policy == return_value_policy::automatic_reference) {
+            policy = std::is_pointer_v<underlying_type> ? return_value_policy::reference
+                     : std::is_lvalue_reference_v<T&&>  ? return_value_policy::copy
+                                                        : return_value_policy::move;
+        }
 
-template <typename T>
-object
-    cast(T&& value, return_value_policy policy = return_value_policy::automatic_reference, handle parent = handle()) {
-    return reinterpret_borrow<object>(_cast(std::forward<T>(value), policy, parent));
+        return type_caster<underlying_type>::cast(std::forward<T>(value), policy, parent);
+    }
 }
 
 template <typename T>
-T cast(handle obj, bool convert = false) {
-    using Caster = type_caster<std::remove_pointer_t<std::remove_cv_t<std::remove_reference_t<T>>>>;
-    Caster caster;
+T cast(handle obj, bool convert = true) {
+    using caster_t = type_caster<T>;
+    constexpr auto is_dangling_v = (std::is_reference_v<T> || is_pointer_v<T>) && caster_t::is_temporary_v;
+    static_assert(!is_dangling_v, "dangling reference or pointer is not allowed.");
+    assert(obj.ptr() != nullptr);
 
+    caster_t caster;
     if(caster.load(obj, convert)) {
-        if constexpr(std::is_rvalue_reference_v<T>) {
-            return std::move(caster.value);
+        if constexpr(std::is_reference_v<T>) {
+            return caster.value();
         } else {
-            return caster.value;
+            return std::move(caster.value());
         }
+    } else {
+        std::string msg = "cast python instance to c++ failed, obj type is: {";
+        msg += type::of(obj).name();
+        msg += "}, target type is: {";
+        msg += type_name<T>();
+        msg += "}.";
+        throw cast_error(msg);
     }
-    throw std::runtime_error("Unable to cast Python instance to C++ type");
 }
-}  // namespace pybind11
+
+template <typename Derived>
+template <typename T>
+T interface<Derived>::cast() const {
+    return pkbind::cast<T>(handle(this->ptr()), true);
+}
+
+}  // namespace pkbind

+ 113 - 89
include/pybind11/internal/cast.h

@@ -1,166 +1,190 @@
 #pragma once
 
 #include "instance.h"
-#include "builtins.h"
-#include "type_traits.h"
 
-namespace pybind11 {
-
-using pkpy::is_floating_point_v;
-using pkpy::is_integral_v;
+namespace pkbind {
 
 template <typename T>
-constexpr inline bool is_string_v = std::is_same_v<T, char*> || std::is_same_v<T, const char*> ||
-                                    std::is_same_v<T, std::string> || std::is_same_v<T, std::string_view>;
+constexpr inline bool is_string_v =
+    std::is_same_v<T, const char*> || std::is_same_v<T, std::string> || std::is_same_v<T, std::string_view>;
+
+/// The `type_caster` class is responsible for converting between Python objects and C++ objects.
+///
+/// The static method `type_caster<T>::cast(...)` is used to convert a C++ object to a Python object.
+/// If the conversion fails, an exception is thrown.
+///
+/// The method `type_caster<T>::load(...)` is used to try to convert a Python object to a C++ object.
+/// If the conversion is successful, it returns true, and you can then call `type_caster<T>::value()`
+/// to access the resulting C++ object. If the conversion fails, it returns false.
+///
+/// NOTE: The type T could be a reference type or a pointer type. What is the lifetime of the reference or pointer?
+/// It depends on the referenced type. For some types, such as bool, int, float, etc., the loaded value is stored
+/// in the type_caster itself, so the lifetime of the reference is no longer than the lifetime of the type_caster
+/// object. For other user-registered types, the lifetime of the reference is the same as the corresponding Python
+/// object. A static variable `is_temporary_v` is used to indicate whether the loaded value is temporary or not.
+
+template <typename T, typename SFINAE>
+struct type_caster {
+    T* data;
 
-template <typename T>
-constexpr bool is_pyobject_v = std::is_base_of_v<handle, T>;
+    static_assert(!std::is_pointer_v<T>, "type caster for pointer type must be specialized.");
+    static_assert(!std::is_reference_v<T>, "type caster for reference type must be specialized.");
+
+    template <typename U>
+    static object cast(U&& value, return_value_policy policy, handle parent) {
+        // TODO: support implicit cast
+        return instance::create(type::of<T>(), std::forward<U>(value), parent, policy);
+    }
 
-template <typename T, typename>
-struct type_caster;
+    bool load(handle src, bool convert) {
+        if(isinstance<T>(src)) {
+            auto& i = *static_cast<instance*>(py_touserdata(src.ptr()));
+            data = &i.as<T>();
+            return true;
+        }
+
+        return false;
+    }
+
+    T& value() { return *data; }
+
+    constexpr inline static bool is_temporary_v = false;
+};
 
 template <>
 struct type_caster<bool> {
-    bool value;
+    bool data;
+
+    static object cast(bool src, return_value_policy, handle) { return bool_(src); }
 
-    bool load(const handle& src, bool) {
-        if(isinstance<pybind11::bool_>(src)) {
-            value = pkpy::_py_cast<bool>(vm, src.ptr());
+    bool load(handle src, bool) {
+        if(isinstance<pkbind::bool_>(src)) {
+            data = py_tobool(src.ptr());
             return true;
         }
 
         return false;
     }
 
-    static handle cast(bool src, return_value_policy, handle) { return src ? vm->True : vm->False; }
+    bool& value() { return data; }
+
+    constexpr inline static bool is_temporary_v = true;
 };
 
 template <typename T>
-struct type_caster<T, std::enable_if_t<is_integral_v<T>>> {
-    T value;
+struct type_caster<T, std::enable_if_t<is_integer_v<T>>> {
+    T data;
+
+    static object cast(T src, return_value_policy, handle) { return int_(src); }
 
-    bool load(const handle& src, bool convert) {
-        if(isinstance<pybind11::int_>(src)) {
-            value = pkpy::_py_cast<T>(vm, src.ptr());
+    bool load(handle src, bool) {
+        if(isinstance<int_>(src)) {
+            data = static_cast<T>(py_toint(src.ptr()));
             return true;
         }
 
         return false;
     }
 
-    static handle cast(T src, return_value_policy, handle) { return pkpy::py_var(vm, src); }
+    T& value() { return data; }
+
+    constexpr inline static bool is_temporary_v = true;
 };
 
 template <typename T>
 struct type_caster<T, std::enable_if_t<is_floating_point_v<T>>> {
-    T value;
+    T data;
 
-    bool load(const handle& src, bool convert) {
-        if(isinstance<pybind11::float_>(src)) {
-            value = pkpy::_py_cast<T>(vm, src.ptr());
+    static object cast(T src, return_value_policy, handle) { return float_(src); }
+
+    bool load(handle src, bool convert) {
+        if(isinstance<pkbind::float_>(src)) {
+            data = py_tofloat(src.ptr());
             return true;
         }
 
-        if(convert && isinstance<pybind11::int_>(src)) {
-            value = pkpy::_py_cast<int64_t>(vm, src.ptr());
+        if(convert && isinstance<pkbind::int_>(src)) {
+            data = py_toint(src.ptr());
             return true;
         }
 
         return false;
     }
 
-    static handle cast(T src, return_value_policy, handle) { return pkpy::py_var(vm, src); }
+    T& value() { return data; }
+
+    constexpr inline static bool is_temporary_v = true;
 };
 
 template <typename T>
 struct type_caster<T, std::enable_if_t<is_string_v<T>>> {
-    T value;
+    T data;
+
+    template <typename U>
+    static object cast(U&& src, return_value_policy, handle) {
+        return str(std::forward<U>(src));
+    }
 
-    bool load(const handle& src, bool) {
-        if(isinstance<pybind11::str>(src)) {
-            // FIXME: support other kinds of string
-            value = pkpy::_py_cast<std::string>(vm, src.ptr());
+    bool load(handle src, bool) {
+        if(isinstance<pkbind::str>(src)) {
+            data = py_tostr(src.ptr());
             return true;
         }
 
         return false;
     }
 
-    static handle cast(const std::string& src, return_value_policy, handle) { return pkpy::py_var(vm, src); }
+    T& value() { return data; }
+
+    constexpr inline static bool is_temporary_v = true;
 };
 
 template <typename T>
 struct type_caster<T, std::enable_if_t<is_pyobject_v<T>>> {
-    T value;
-
-    bool load(const handle& src, bool) {
-        if(isinstance<T>(src)) {
-            value = reinterpret_borrow<T>(src);
-            return true;
-        }
-
-        return false;
-    }
+    T data;
 
     template <typename U>
-    static handle cast(U&& src, return_value_policy, handle) {
-        return std::forward<U>(src);
+    static object cast(U&& src, return_value_policy, handle) {
+        return object(std::forward<U>(src));
     }
-};
-
-template <typename T, typename>
-struct type_caster {
-    value_wrapper<T> value;
-
-    using underlying_type = std::remove_pointer_t<decltype(value.pointer)>;
 
-    bool load(handle src, bool convert) {
-        if(isinstance<underlying_type>(src)) {
-            auto& i = _builtin_cast<instance>(src);
-            value.pointer = &i.cast<underlying_type>();
+    bool load(handle src, bool) {
+        if(isinstance<T>(src)) {
+            data = T(src.ptr(), object::realloc_t{});
             return true;
         }
 
         return false;
     }
 
-    template <typename U>
-    static handle cast(U&& value, return_value_policy policy, const handle& parent = handle()) {
-        // TODO: support implicit cast
-        const auto& info = typeid(underlying_type);
-        bool existed = vm->_cxx_typeid_map.find(info) != vm->_cxx_typeid_map.end();
-        if(existed) {
-            auto type = vm->_cxx_typeid_map[info];
-            return instance::create(std::forward<U>(value), type, policy, parent.ptr());
-        }
-        vm->TypeError("type not registered");
-    }
+    T& value() { return data; }
+
+    constexpr inline static bool is_temporary_v = true;
 };
 
 template <typename T>
-struct type_caster<T, std::enable_if_t<std::is_pointer_v<T> || std::is_reference_v<T>>> {
-    using underlying = std::conditional_t<std::is_pointer_v<T>, std::remove_pointer_t<T>, std::remove_reference_t<T>>;
-
-    struct wrapper {
-        type_caster<underlying> caster;
-
-        operator T () {
-            if constexpr(std::is_pointer_v<T>) {
-                return caster.value.pointer;
-            } else {
-                return caster.value;
-            }
-        }
-    };
+struct type_caster<T, std::enable_if_t<is_pointer_v<T> || std::is_reference_v<T>>> {
+    using underlying =
+        std::remove_cv_t<std::conditional_t<is_pointer_v<T>, std::remove_pointer_t<T>, std::remove_reference_t<T>>>;
 
-    wrapper value;
-
-    bool load(const handle& src, bool convert) { return value.caster.load(src, convert); }
+    type_caster<underlying> caster;
 
     template <typename U>
-    static handle cast(U&& value, return_value_policy policy, const handle& parent) {
+    static object cast(U&& value, return_value_policy policy, handle parent) {
         return type_caster<underlying>::cast(std::forward<U>(value), policy, parent);
     }
+
+    bool load(handle src, bool convert) { return caster.load(src, convert); }
+
+    T value() {
+        if constexpr(std::is_pointer_v<T>) {
+            return &caster.value();
+        } else {
+            return caster.value();
+        }
+    }
+
+    constexpr inline static bool is_temporary_v = type_caster<underlying>::is_temporary_v;
 };
-}  // namespace pybind11
 
+}  // namespace pkbind

+ 108 - 57
include/pybind11/internal/class.h

@@ -1,75 +1,93 @@
 #pragma once
 
-#include "cpp_function.h"
+#include "module.h"
+#include "type_traits.h"
 
-namespace pybind11 {
+namespace pkbind {
 
-class module : public object {
+struct dynamic_attr {};
 
-public:
-    using object::object;
-
-    static module import(const char* name) {
-        if(name == std::string_view{"__main__"}) {
-            return module{vm->_main, true};
-        } else {
-            return module{vm->py_import(name, false), true};
-        }
-    }
-};
-
-// TODO:
-// 1. inheritance
-// 2. virtual function
-// 3. factory function
-
-template <typename T, typename... Others>
+template <typename T, typename Base = void>
 class class_ : public type {
+protected:
+    handle m_scope;
+
 public:
     using type::type;
+    using underlying_type = T;
 
     template <typename... Args>
-    class_(const handle& scope, const char* name, Args&&... args) :
-        type(vm->new_type_object(scope.ptr(), name, vm->tp_object, false, pkpy::PyTypeInfo::Vt::get<instance>()),
-             true) {
-        pkpy::PyVar mod = scope.ptr();
-        mod->attr().set(name, m_ptr);
-        vm->_cxx_typeid_map[typeid(T)] = _builtin_cast<pkpy::Type>(m_ptr);
-        vm->bind_func(m_ptr, "__new__", -1, [](pkpy::VM* vm, pkpy::ArgsView args) {
-            auto cls = _builtin_cast<pkpy::Type>(args[0]);
-            return instance::create<T>(cls);
-        });
+    class_(handle scope, const char* name, const Args&... args) :
+        type(py_newtype(name,
+                        std::is_same_v<Base, void> ? tp_object : type::of<Base>().index(),
+                        scope.ptr(),
+                        [](void* data) {
+                            static_cast<instance*>(data)->~instance();
+                        })),
+        m_scope(scope) {
+        m_type_map->try_emplace(typeid(T), this->index());
+
+        auto& info = type_info::of<T>();
+        info.name = name;
+
+        py_newfunction(
+            py_tpgetmagic(this->index(), py_MagicNames::__new__),
+            "__new__(type, *args, **kwargs)",
+            [](int, py_Ref stack) {
+                auto cls = py_offset(stack, 0);
+                [[maybe_unused]] auto args = py_offset(stack, 1);
+                [[maybe_unused]] auto kwargs = py_offset(stack, 2);
+
+                auto info = &type_info::of<T>();
+                int slot = ((std::is_same_v<dynamic_attr, Args> || ...) ? -1 : 0);
+                void* data = py_newobject(retv, steal<type>(cls).index(), slot, sizeof(instance));
+                new (data) instance{instance::Flag::Own, operator new (info->size), info};
+                return true;
+            },
+            nullptr,
+            0);
     }
 
     /// bind constructor
     template <typename... Args, typename... Extra>
-    class_& def(init<Args...>, const Extra&... extra) {
+    class_& def(impl::constructor<Args...>, const Extra&... extra) {
         if constexpr(!std::is_constructible_v<T, Args...>) {
             static_assert(std::is_constructible_v<T, Args...>, "Invalid constructor arguments");
         } else {
-            bind_function(
+            impl::bind_function<true, false>(
                 *this,
                 "__init__",
                 [](T* self, Args... args) {
                     new (self) T(args...);
                 },
-                pkpy::BindType::DEFAULT,
                 extra...);
             return *this;
         }
     }
 
+    template <typename Fn, typename... Extra>
+    class_& def(impl::factory<Fn> factory, const Extra&... extra) {
+        using ret = callable_return_t<Fn>;
+        if constexpr(!std::is_same_v<T, ret>) {
+            static_assert(std::is_same_v<T, ret>, "Factory function must return the class type");
+        } else {
+            impl::bind_function<true, false>(*this, "__init__", factory.make(), extra...);
+            return *this;
+        }
+    }
+
     /// bind member function
     template <typename Fn, typename... Extra>
     class_& def(const char* name, Fn&& f, const Extra&... extra) {
-        using first = std::tuple_element_t<0, callable_args_t<remove_cvref_t<Fn>>>;
-        constexpr bool is_first_base_of_v = std::is_reference_v<first> && std::is_base_of_v<T, remove_cvref_t<first>>;
+        using first = remove_cvref_t<std::tuple_element_t<0, callable_args_t<remove_cvref_t<Fn>>>>;
+        constexpr bool is_first_base_of_v = std::is_base_of_v<first, T> || std::is_same_v<first, T>;
 
         if constexpr(!is_first_base_of_v) {
-            static_assert(is_first_base_of_v,
-                          "If you want to bind member function, the first argument must be the base class");
+            static_assert(
+                is_first_base_of_v,
+                "If you want to bind member function, the first argument must be the base class");
         } else {
-            bind_function(*this, name, std::forward<Fn>(f), pkpy::BindType::DEFAULT, extra...);
+            impl::bind_function<true, false>(*this, name, std::forward<Fn>(f), extra...);
         }
 
         return *this;
@@ -82,21 +100,29 @@ public:
         return *this;
     }
 
-    // TODO: factory function
-
     /// bind static function
     template <typename Fn, typename... Extra>
     class_& def_static(const char* name, Fn&& f, const Extra&... extra) {
-        bind_function(*this, name, std::forward<Fn>(f), pkpy::BindType::STATICMETHOD, extra...);
+        impl::bind_function<false, true>(*this, name, std::forward<Fn>(f), extra...);
         return *this;
     }
 
     template <typename MP, typename... Extras>
     class_& def_readwrite(const char* name, MP mp, const Extras&... extras) {
         if constexpr(!std::is_member_object_pointer_v<MP>) {
-            static_assert(std::is_member_object_pointer_v<MP>, "def_readwrite only supports pointer to data member");
+            static_assert(std::is_member_object_pointer_v<MP>,
+                          "def_readwrite only supports pointer to data member");
         } else {
-            bind_property(*this, name, mp, mp, extras...);
+            impl::bind_property(
+                *this,
+                name,
+                [mp](class_type_t<MP>& self) -> auto& {
+                    return self.*mp;
+                },
+                [mp](class_type_t<MP>& self, const member_type_t<MP>& value) {
+                    self.*mp = value;
+                },
+                extras...);
         }
         return *this;
     }
@@ -104,22 +130,34 @@ public:
     template <typename MP, typename... Extras>
     class_& def_readonly(const char* name, MP mp, const Extras&... extras) {
         if constexpr(!std::is_member_object_pointer_v<MP>) {
-            static_assert(std::is_member_object_pointer_v<MP>, "def_readonly only supports pointer to data member");
+            static_assert(std::is_member_object_pointer_v<MP>,
+                          "def_readonly only supports pointer to data member");
         } else {
-            bind_property(*this, name, mp, nullptr, extras...);
+            impl::bind_property(
+                *this,
+                name,
+                [mp](class_type_t<MP>& self) -> auto& {
+                    return self.*mp;
+                },
+                nullptr,
+                extras...);
         }
         return *this;
     }
 
     template <typename Getter, typename Setter, typename... Extras>
     class_& def_property(const char* name, Getter&& g, Setter&& s, const Extras&... extras) {
-        bind_property(*this, name, std::forward<Getter>(g), std::forward<Setter>(s), extras...);
+        impl::bind_property(*this,
+                            name,
+                            std::forward<Getter>(g),
+                            std::forward<Setter>(s),
+                            extras...);
         return *this;
     }
 
     template <typename Getter, typename... Extras>
     class_& def_property_readonly(const char* name, Getter&& mp, const Extras&... extras) {
-        bind_property(*this, name, std::forward<Getter>(mp), nullptr, extras...);
+        impl::bind_property(*this, name, std::forward<Getter>(mp), nullptr, extras...);
         return *this;
     }
 
@@ -150,28 +188,41 @@ public:
 
 template <typename T, typename... Others>
 class enum_ : public class_<T, Others...> {
-    std::map<const char*, pkpy::PyVar> m_values;
+    std::vector<std::pair<const char*, object>> m_values;
 
 public:
-    using class_<T, Others...>::class_;
+    using Base = class_<T, Others...>;
 
     template <typename... Args>
     enum_(const handle& scope, const char* name, Args&&... args) :
-        class_<T, Others...>(scope, name, std::forward<Args>(args)...) {}
+        class_<T, Others...>(scope, name, std::forward<Args>(args)...) {
+
+        Base::def(init([](int value) {
+            return static_cast<T>(value);
+        }));
+
+        Base::def("__eq__", [](T& self, T& other) {
+            return self == other;
+        });
+
+        Base::def_property_readonly("value", [](T& self) {
+            return int_(static_cast<std::underlying_type_t<T>>(self));
+        });
+    }
 
     enum_& value(const char* name, T value) {
-        handle var = type_caster<T>::cast(value, return_value_policy::copy);
-        this->m_ptr->attr().set(name, var.ptr());
-        m_values[name] = var.ptr();
+        auto var = pkbind::cast(value, return_value_policy::copy);
+        setattr(*this, name, var);
+        m_values.emplace_back(name, std::move(var));
         return *this;
     }
 
     enum_& export_values() {
-        pkpy::PyVar mod = this->m_ptr->attr("__module__");
         for(auto& [name, value]: m_values) {
-            mod->attr().set(name, value);
+            setattr(Base::m_scope, name, value);
         }
         return *this;
     }
 };
-}  // namespace pybind11
+
+}  // namespace pkbind

+ 0 - 358
include/pybind11/internal/cpp_function.h

@@ -1,358 +0,0 @@
-#pragma once
-
-#include "cast.h"
-#include <bitset>
-
-namespace pybind11 {
-
-template <std::size_t Nurse, std::size_t... Patients>
-struct keep_alive {};
-
-template <typename T>
-struct call_guard {
-    static_assert(std::is_default_constructible_v<T>, "call_guard must be default constructible");
-};
-
-// append the overload to the beginning of the overload list
-struct prepend {};
-
-template <typename... Args>
-struct init {};
-
-// TODO: support more customized tags
-// struct kw_only {};
-//
-// struct pos_only {};
-//
-// struct default_arg {};
-//
-// struct arg {
-//    const char* name;
-//    const char* description;
-// };
-//
-// struct default_arg {
-//    const char* name;
-//    const char* description;
-//    const char* value;
-// };
-
-template <typename Fn,
-          typename Extra,
-          typename Args = callable_args_t<std::decay_t<Fn>>,
-          typename IndexSequence = std::make_index_sequence<std::tuple_size_v<Args>>>
-struct generator;
-
-class function_record {
-    union {
-        void* data;
-        char buffer[16];
-    };
-
-    // TODO: optimize the function_record size to reduce memory usage
-    const char* name;
-    function_record* next;
-    void (*destructor)(function_record*);
-    return_value_policy policy = return_value_policy::automatic;
-    handle (*wrapper)(function_record&, pkpy::ArgsView, bool convert, handle parent);
-
-    template <typename Fn, typename Extra, typename Args, typename IndexSequence>
-    friend struct generator;
-
-public:
-    template <typename Fn, typename... Extras>
-    function_record(Fn&& f, const char* name, const Extras&... extras) : name(name), next(nullptr) {
-
-        if constexpr(sizeof(f) <= sizeof(buffer)) {
-            new (buffer) auto(std::forward<Fn>(f));
-            destructor = [](function_record* self) {
-                reinterpret_cast<Fn*>(self->buffer)->~Fn();
-            };
-        } else {
-            data = new auto(std::forward<Fn>(f));
-            destructor = [](function_record* self) {
-                delete static_cast<Fn*>(self->data);
-            };
-        }
-
-        using Generator = generator<std::decay_t<Fn>, std::tuple<Extras...>>;
-        Generator::initialize(*this, extras...);
-        wrapper = Generator::generate();
-    }
-
-    ~function_record() { destructor(this); }
-
-    template <typename Fn>
-    auto& cast() {
-        if constexpr(sizeof(Fn) <= sizeof(buffer)) {
-            return *reinterpret_cast<Fn*>(buffer);
-        } else {
-            return *static_cast<Fn*>(data);
-        }
-    }
-
-    void append(function_record* record) {
-        function_record* p = this;
-        while(p->next != nullptr) {
-            p = p->next;
-        }
-        p->next = record;
-    }
-
-    handle operator() (pkpy::ArgsView view) {
-        function_record* p = this;
-        // foreach function record and call the function with not convert
-        while(p != nullptr) {
-            handle result = p->wrapper(*this, view, false, {});
-            if(result) { return result; }
-            p = p->next;
-        }
-
-        p = this;
-        // foreach function record and call the function with convert
-        while(p != nullptr) {
-            handle result = p->wrapper(*this, view, true, {});
-            if(result) { return result; }
-            p = p->next;
-        }
-
-        vm->TypeError("no matching function found");
-    }
-};
-
-template <typename Fn, std::size_t... Is, typename... Args>
-handle invoke(Fn&& fn,
-              std::index_sequence<Is...>,
-              std::tuple<type_caster<Args>...>& casters,
-              return_value_policy policy,
-              handle parent) {
-    using underlying_type = std::decay_t<Fn>;
-    using ret = callable_return_t<underlying_type>;
-
-    // if the return type is void, return None
-    if constexpr(std::is_void_v<ret>) {
-        // resolve the member function pointer
-        if constexpr(std::is_member_function_pointer_v<underlying_type>) {
-            [&](class_type_t<underlying_type>& self, auto&... args) {
-                (self.*fn)(args...);
-            }(std::get<Is>(casters).value...);
-        } else {
-            fn(std::get<Is>(casters).value...);
-        }
-        return vm->None;
-    } else {
-        // resolve the member function pointer
-        if constexpr(std::is_member_function_pointer_v<remove_cvref_t<Fn>>) {
-            return type_caster<ret>::cast(
-                [&](class_type_t<underlying_type>& self, auto&... args) {
-                    return (self.*fn)(args...);
-                }(std::get<Is>(casters).value...),
-                policy,
-                parent);
-        } else {
-            return type_caster<ret>::cast(fn(std::get<Is>(casters).value...), policy, parent);
-        }
-    }
-}
-
-template <typename Fn, typename... Args, std::size_t... Is, typename... Extras>
-struct generator<Fn, std::tuple<Extras...>, std::tuple<Args...>, std::index_sequence<Is...>> {
-    static void initialize(function_record& record, const Extras&... extras) {}
-
-    static auto generate() {
-        return +[](function_record& self, pkpy::ArgsView view, bool convert, handle parent) {
-            // FIXME:
-            // Temporarily, args and kwargs must be at the end of the arguments list
-            // Named arguments are not supported yet
-            constexpr bool has_args = types_count_v<args, remove_cvref_t<Args>...> != 0;
-            constexpr bool has_kwargs = types_count_v<kwargs, remove_cvref_t<Args>...> != 0;
-            constexpr std::size_t count = sizeof...(Args) - has_args - has_kwargs;
-
-            handle stack[sizeof...(Args)] = {};
-
-            // initialize the stack
-
-            if(!has_args && (view.size() != count)) { return handle(); }
-
-            if(has_args && (view.size() < count)) { return handle(); }
-
-            for(std::size_t i = 0; i < count; ++i) {
-                stack[i] = view[i];
-            }
-
-            // pack the args and kwargs
-            if constexpr(has_args) {
-                const auto n = view.size() - count;
-                pkpy::PyVar var = vm->new_object<pkpy::Tuple>(vm->tp_tuple, n);
-                auto& tuple = var.obj_get<pkpy::Tuple>();
-                for(std::size_t i = 0; i < n; ++i) {
-                    tuple[i] = view[count + i];
-                }
-                stack[count] = var;
-            }
-
-            if constexpr(has_kwargs) {
-                const auto n = vm->s_data._sp - view.end();
-                pkpy::PyVar var = vm->new_object<pkpy::Dict>(vm->tp_dict);
-                auto& dict = var.obj_get<pkpy::Dict>();
-
-                for(std::size_t i = 0; i < n; i += 2) {
-                    pkpy::i64 index = pkpy::_py_cast<pkpy::i64>(vm, view[count + i]);
-                    pkpy::PyVar str = vm->new_object<pkpy::Str>(vm->tp_str, pkpy::StrName(index).sv());
-                    dict.set(vm, str, view[count + i + 1]);
-                }
-
-                stack[count + 1] = var;
-            }
-
-            // check if all the arguments are not valid
-            for(std::size_t i = 0; i < sizeof...(Args); ++i) {
-                if(!stack[i]) { return handle(); }
-            }
-
-            // ok, all the arguments are valid, call the function
-            std::tuple<type_caster<Args>...> casters;
-
-            // check type compatibility
-            if(((std::get<Is>(casters).load(stack[Is], convert)) && ...)) {
-                return invoke(self.cast<Fn>(), std::index_sequence<Is...>{}, casters, self.policy, parent);
-            }
-
-            return handle();
-        };
-    }
-};
-
-constexpr inline static auto _wrapper = +[](pkpy::VM*, pkpy::ArgsView view) {
-    auto& record = pkpy::lambda_get_userdata<function_record>(view.begin());
-    return record(view).ptr();
-};
-
-class cpp_function : public function {
-public:
-    template <typename Fn, typename... Extras>
-    cpp_function(Fn&& f, const Extras&... extras) {
-        pkpy::any userdata = function_record(std::forward<Fn>(f), "anonymous", extras...);
-        m_ptr = vm->bind_func(nullptr, "", -1, _wrapper, std::move(userdata));
-        inc_ref();
-    }
-};
-
-template <typename Fn, typename... Extras>
-handle bind_function(const handle& obj, const char* name, Fn&& fn, pkpy::BindType type, const Extras&... extras) {
-    // do not use cpp_function directly to avoid unnecessary reference count change
-    pkpy::PyVar var = obj.ptr();
-    pkpy::PyVar callable = var->attr().try_get(name);
-
-    // if the function is not bound yet, bind it
-    if(!callable) {
-        pkpy::any userdata = function_record(std::forward<Fn>(fn), name, extras...);
-        callable = vm->bind_func(var, name, -1, _wrapper, std::move(userdata));
-    } else {
-        auto& userdata = callable.obj_get<pkpy::NativeFunc>()._userdata;
-        function_record* record = new function_record(std::forward<Fn>(fn), name, extras...);
-
-        constexpr bool is_prepend = (types_count_v<prepend, Extras...> != 0);
-        if constexpr(is_prepend) {
-            // if prepend is specified, append the new record to the beginning of the list
-            function_record* last = (function_record*)userdata.data;
-            userdata.data = record;
-            record->append(last);
-        } else {
-            // otherwise, append the new record to the end of the list
-            function_record* last = (function_record*)userdata.data;
-            last->append(record);
-        }
-    }
-    return callable;
-}
-
-template <typename Getter_, typename Setter_, typename... Extras>
-handle
-    bind_property(const handle& obj, const char* name, Getter_&& getter_, Setter_&& setter_, const Extras&... extras) {
-    pkpy::PyVar var = obj.ptr();
-    pkpy::PyVar getter = vm->None;
-    pkpy::PyVar setter = vm->None;
-    using Getter = std::decay_t<Getter_>;
-    using Setter = std::decay_t<Setter_>;
-
-    getter = vm->new_object<pkpy::NativeFunc>(
-        vm->tp_native_func,
-        [](pkpy::VM* vm, pkpy::ArgsView view) -> pkpy::PyVar {
-            auto& getter = pkpy::lambda_get_userdata<Getter>(view.begin());
-
-            if constexpr(std::is_member_pointer_v<Getter>) {
-                using Self = class_type_t<Getter>;
-                auto& self = _builtin_cast<instance>(view[0]).cast<Self>();
-
-                if constexpr(std::is_member_object_pointer_v<Getter>) {
-                    return type_caster<member_type_t<Getter>>::cast(self.*getter,
-                                                                    return_value_policy::reference_internal,
-                                                                    view[0])
-                        .ptr();
-                } else {
-                    return type_caster<callable_return_t<Getter>>::cast((self.*getter)(),
-                                                                        return_value_policy::reference_internal,
-                                                                        view[0])
-                        .ptr();
-                }
-
-            } else {
-                using Self = std::tuple_element_t<0, callable_args_t<Getter>>;
-                auto& self = _builtin_cast<instance>(view[0]).cast<Self>();
-
-                return type_caster<callable_return_t<Getter>>::cast(getter(self),
-                                                                    return_value_policy::reference_internal,
-                                                                    view[0])
-                    .ptr();
-            }
-        },
-        1,
-        std::forward<Getter_>(getter_));
-
-    if constexpr(!std::is_same_v<Setter, std::nullptr_t>) {
-        setter = vm->new_object<pkpy::NativeFunc>(
-            vm->tp_native_func,
-            [](pkpy::VM* vm, pkpy::ArgsView view) -> pkpy::PyVar {
-                auto& setter = pkpy::lambda_get_userdata<Setter>(view.begin());
-
-                if constexpr(std::is_member_pointer_v<Setter>) {
-                    using Self = class_type_t<Setter>;
-                    auto& self = _builtin_cast<instance>(view[0]).cast<Self>();
-
-                    if constexpr(std::is_member_object_pointer_v<Setter>) {
-                        type_caster<member_type_t<Setter>> caster;
-                        if(caster.load(view[1], true)) {
-                            self.*setter = caster.value;
-                            return vm->None;
-                        }
-                    } else {
-                        type_caster<std::tuple_element_t<1, callable_args_t<Setter>>> caster;
-                        if(caster.load(view[1], true)) {
-                            (self.*setter)(caster.value);
-                            return vm->None;
-                        }
-                    }
-                } else {
-                    using Self = std::tuple_element_t<0, callable_args_t<Setter>>;
-                    auto& self = _builtin_cast<instance>(view[0]).cast<Self>();
-
-                    type_caster<std::tuple_element_t<1, callable_args_t<Setter>>> caster;
-                    if(caster.load(view[1], true)) {
-                        setter(self, caster.value);
-                        return vm->None;
-                    }
-                }
-
-                vm->TypeError("invalid argument");
-            },
-            2,
-            std::forward<Setter_>(setter_));
-    }
-
-    pkpy::PyVar property = vm->new_object<pkpy::Property>(vm->tp_property, getter, setter);
-    var->attr().set(name, property);
-    return property;
-}
-
-}  // namespace pybind11

+ 115 - 0
include/pybind11/internal/error.h

@@ -0,0 +1,115 @@
+#pragma once
+
+#include "object.h"
+
+namespace pkbind {
+
+/// represent a all exception raised by python.
+class python_error : public std::exception {
+public:
+    python_error(char* what, object exception) : m_what(what), m_exception(std::move(exception)) {}
+
+    const char* what() const noexcept override { return m_what; }
+
+    // get the python  exception object
+    object& exception() { return m_exception; }
+
+    ~python_error() { std::free(m_what); }
+
+    bool match(py_Type type) const { return py_isinstance(m_exception.ptr(), type); }
+
+    bool match(class type) const;
+
+private:
+    char* m_what;
+    object m_exception;
+};
+
+using error_already_set = python_error;
+
+template <auto Fn, typename... Args>
+inline auto raise_call(Args&&... args) {
+    auto pc = py_peek(0);
+    auto result = Fn(std::forward<Args>(args)...);
+
+    using type = decltype(result);
+    if constexpr(std::is_same_v<type, bool>) {
+        if(result != false) {
+            return result;
+        }
+    } else if constexpr(std::is_same_v<type, int>) {
+        if(result != -1) {
+            return result;
+        }
+    } else {
+        static_assert(dependent_false<type>, "invalid return type");
+    }
+
+    bool o = py_matchexc(tp_Exception);
+    object e = object::from_ret();
+    auto what = py_formatexc();
+    py_clearexc(pc);
+    throw python_error(what, std::move(e));
+}
+
+class stop_iteration {};
+
+class cast_error : public std::runtime_error {
+    using std::runtime_error::runtime_error;
+};
+
+class index_error : public std::runtime_error {
+    using std::runtime_error::runtime_error;
+};
+
+class key_error : public std::runtime_error {
+    using std::runtime_error::runtime_error;
+};
+
+class value_error : public std::runtime_error {
+    using std::runtime_error::runtime_error;
+};
+
+class type_error : public std::runtime_error {
+    using std::runtime_error::runtime_error;
+};
+
+class import_error : public std::runtime_error {
+    using std::runtime_error::runtime_error;
+};
+
+class attribute_error : public std::runtime_error {
+    using std::runtime_error::runtime_error;
+};
+
+inline object::operator bool () const { return raise_call<py_bool>(m_ptr); }
+
+#define PKBIND_BINARY_OPERATOR(name, lop, rop)                                                                         \
+    inline object operator name (handle lhs, handle rhs) {                                                             \
+        raise_call<py_binaryop>(lhs.ptr(), rhs.ptr(), lop, rop);                                                       \
+        return object(retv, object::realloc_t{});                                                                      \
+    }
+
+PKBIND_BINARY_OPERATOR(==, __eq__, __eq__)
+PKBIND_BINARY_OPERATOR(!=, __ne__, __ne__)
+PKBIND_BINARY_OPERATOR(<, __lt__, __gt__)
+PKBIND_BINARY_OPERATOR(<=, __le__, __ge__)
+PKBIND_BINARY_OPERATOR(>, __gt__, __lt__)
+PKBIND_BINARY_OPERATOR(>=, __ge__, __le__)
+
+PKBIND_BINARY_OPERATOR(+, __add__, __radd__)
+PKBIND_BINARY_OPERATOR(-, __sub__, __rsub__)
+PKBIND_BINARY_OPERATOR(*, __mul__, __rmul__)
+PKBIND_BINARY_OPERATOR(/, __truediv__, __rtruediv__)
+PKBIND_BINARY_OPERATOR(%, __mod__, __rmod__)
+
+// FIXME: support __rand__ ...
+PKBIND_BINARY_OPERATOR(&, __and__, 0)
+PKBIND_BINARY_OPERATOR(|, __or__, 0)
+PKBIND_BINARY_OPERATOR(^, __xor__, 0)
+PKBIND_BINARY_OPERATOR(<<, __lshift__, 0)
+PKBIND_BINARY_OPERATOR(>>, __rshift__, 0)
+
+#undef PKBIND_BINARY_OPERATOR
+
+}  // namespace pkbind

+ 686 - 0
include/pybind11/internal/function.h

@@ -0,0 +1,686 @@
+#pragma once
+
+#include "cast.h"
+
+namespace pkbind {
+
+namespace impl {
+
+template <typename... Args>
+struct constructor {};
+
+template <typename Fn, typename Args = callable_args_t<Fn>>
+struct factory;
+
+template <typename Fn, typename... Args>
+struct factory<Fn, std::tuple<Args...>> {
+    Fn fn;
+
+    auto make() {
+        using Self = callable_return_t<Fn>;
+        return [fn = std::move(fn)](Self* self, Args... args) {
+            new (self) Self(fn(args...));
+        };
+    }
+};
+
+}  // namespace impl
+
+template <typename... Args>
+impl::constructor<Args...> init() {
+    return {};
+}
+
+template <typename Fn>
+impl::factory<Fn> init(Fn&& fn) {
+    return {std::forward<Fn>(fn)};
+}
+
+struct arg_with_default {
+    const char* name;
+    object value;
+};
+
+struct arg {
+    const char* name;
+
+    arg(const char* name) : name(name) {}
+
+    template <typename T>
+    arg_with_default operator= (T&& value) {
+        return arg_with_default{name, cast(std::forward<T>(value))};
+    }
+};
+
+struct kwargs_proxy {
+    handle value;
+};
+
+struct args_proxy {
+    handle value;
+
+    kwargs_proxy operator* () { return kwargs_proxy{value}; }
+};
+
+template <typename Derived>
+args_proxy interface<Derived>::operator* () const {
+    return args_proxy{handle(this->ptr())};
+}
+
+template <typename Derived>
+template <return_value_policy policy, typename... Args>
+object interface<Derived>::operator() (Args&&... args) const {
+    py_push(ptr());
+    py_pushnil();
+
+    int argc = 0;
+    int kwargsc = 0;
+
+    auto foreach = [&](auto&& argument) {
+        using type = std::decay_t<decltype(argument)>;
+        if constexpr(std::is_constructible_v<handle, type>) {
+            argc += 1;
+            py_push(handle(argument).ptr());
+        } else if constexpr(std::is_same_v<type, arg_with_default>) {
+            kwargsc += 1;
+            arg_with_default& default_ = argument;
+            py_pushname(name(default_.name).index());
+            py_push(default_.value.ptr());
+        } else if constexpr(std::is_same_v<type, args_proxy>) {
+            tuple args = argument.value.template cast<tuple>();
+            for(auto arg: args) {
+                argc += 1;
+                py_push(arg.ptr());
+            }
+        } else if constexpr(std::is_same_v<type, kwargs_proxy>) {
+            dict kwargs = argument.value.template cast<dict>();
+            kwargs.apply([&](handle key, handle value) {
+                kwargsc += 1;
+                name name = key.cast<std::string_view>();
+                py_pushname(name.index());
+                py_push(value.ptr());
+            });
+        } else {
+            argc += 1;
+            py_push(pkbind::cast(std::forward<decltype(argument)>(argument), policy).ptr());
+        }
+    };
+
+    (foreach(std::forward<Args>(args)), ...);
+
+    raise_call<py_vectorcall>(argc, kwargsc);
+
+    return object::from_ret();
+}
+
+class function : public object {
+    PKBIND_TYPE_IMPL(object, function, tp_function);
+};
+
+namespace impl {
+
+template <typename Callable,
+          typename Extra,
+          typename Args = callable_args_t<Callable>,
+          typename IndexSequence = std::make_index_sequence<std::tuple_size_v<Args>>>
+struct template_parser;
+
+class function_record {
+    template <typename C, typename E, typename A, typename I>
+    friend struct template_parser;
+
+    using destructor_t = void (*)(function_record*);
+    using wrapper_t = bool (*)(function_record&,
+                               std::vector<handle>&,
+                               std::vector<std::pair<handle, handle>>&,
+                               bool convert,
+                               handle parent);
+
+    struct arguments_t {
+        std::vector<std::string> names;
+        std::vector<object> defaults;
+    };
+
+public:
+    template <typename Fn, typename... Extras>
+    function_record(Fn&& f, const Extras&... extras) {
+        using Callable = std::decay_t<Fn>;
+
+        if constexpr(std::is_trivially_copyable_v<Callable> && sizeof(Callable) <= sizeof(buffer)) {
+            // if the callable object is trivially copyable and the size is less than 16 bytes,
+            // store it in the buffer
+            new (buffer) auto(std::forward<Fn>(f));
+            destructor = [](function_record* self) {
+                reinterpret_cast<Callable*>(self->buffer)->~Callable();
+            };
+        } else {
+            // otherwise, store it in the heap
+            data = new auto(std::forward<Fn>(f));
+            destructor = [](function_record* self) {
+                delete static_cast<Callable*>(self->data);
+            };
+        }
+
+        using Parser = template_parser<Callable, std::tuple<Extras...>>;
+        Parser::initialize(*this, extras...);
+        wrapper = Parser::call;
+    }
+
+    function_record(const function_record&) = delete;
+
+    function_record& operator= (const function_record&) = delete;
+
+    function_record(function_record&& other) noexcept {
+        std::memcpy(this, &other, sizeof(function_record));
+        std::memset(&other, 0, sizeof(function_record));
+    }
+
+    function_record& operator= (function_record&&) = delete;
+
+    ~function_record() {
+        if(destructor) { destructor(this); }
+        if(arguments) { delete arguments; }
+        if(next) { delete next; }
+        if(signature) { delete[] signature; }
+    }
+
+    void append(function_record* record) {
+        function_record* p = this;
+        while(p->next) {
+            p = p->next;
+        }
+        p->next = record;
+    }
+
+    template <typename T>
+    T& as() {
+        if constexpr(std::is_trivially_copyable_v<T> && sizeof(T) <= sizeof(buffer)) {
+            return *reinterpret_cast<T*>(buffer);
+        } else {
+            return *static_cast<T*>(data);
+        }
+    }
+
+    static function_record& from(handle h) {
+        auto slot = py_getslot(h.ptr(), 0);
+        return *static_cast<function_record*>(py_touserdata(slot));
+    }
+
+    void operator() (int argc, handle stack) {
+        function_record* p = this;
+
+        bool has_self = argc == 3;
+        std::vector<handle> args;
+        handle self = py_offset(stack.ptr(), 0);
+        if(has_self) { args.push_back(self); }
+
+        auto tuple = py_offset(stack.ptr(), 0 + has_self);
+        for(int i = 0; i < py_tuple_len(tuple); ++i) {
+            args.push_back(py_tuple_getitem(tuple, i));
+        }
+
+        auto dict = steal<pkbind::dict>(py_offset(stack.ptr(), 1 + has_self));
+
+        std::vector<std::pair<handle, handle>> kwargs;
+        dict.apply([&](handle key, handle value) {
+            kwargs.emplace_back(key, value);
+        });
+
+        // foreach function record and call the function with not convert
+        while(p != nullptr) {
+            auto result = p->wrapper(*p, args, kwargs, false, self);
+            if(result) { return; }
+            p = p->next;
+        }
+
+        p = this;
+        // foreach function record and call the function with convert
+        while(p != nullptr) {
+            auto result = p->wrapper(*p, args, kwargs, true, self);
+            if(result) { return; }
+            p = p->next;
+        }
+
+        std::string msg = "no matching function found, function signature:\n";
+        p = this;
+        while(p != nullptr) {
+            msg += "    ";
+            msg += p->signature;
+            msg += "\n";
+            p = p->next;
+        }
+        throw std::runtime_error(msg);
+    }
+
+private:
+    union {
+        void* data;
+        char buffer[16];
+    };
+
+    wrapper_t wrapper = nullptr;
+    function_record* next = nullptr;
+    arguments_t* arguments = nullptr;
+    destructor_t destructor = nullptr;
+    const char* signature = nullptr;
+    return_value_policy policy = return_value_policy::automatic;
+};
+
+template <typename Fn, std::size_t... Is, typename... Args>
+void invoke(Fn&& fn,
+            std::index_sequence<Is...>,
+            std::tuple<type_caster<Args>...>& casters,
+            return_value_policy policy,
+            handle parent) {
+    using underlying_type = std::decay_t<Fn>;
+    using return_type = callable_return_t<underlying_type>;
+
+    constexpr bool is_void = std::is_void_v<return_type>;
+    constexpr bool is_member_function_pointer = std::is_member_function_pointer_v<underlying_type>;
+
+    if constexpr(is_member_function_pointer) {
+        // helper function to unpack the arguments to call the member pointer
+        auto unpack = [&](class_type_t<underlying_type>& self, auto&... args) {
+            return (self.*fn)(args...);
+        };
+
+        if constexpr(!is_void) {
+            py_assign(py_retval(),
+                      pkbind::cast(unpack(std::get<Is>(casters).value()...), policy, parent).ptr());
+        } else {
+            unpack(std::get<Is>(casters).value()...);
+            py_newnone(py_retval());
+        }
+    } else {
+        if constexpr(!is_void) {
+            py_assign(py_retval(),
+                      pkbind::cast(fn(std::get<Is>(casters).value()...), policy, parent).ptr());
+        } else {
+            fn(std::get<Is>(casters).value()...);
+            py_newnone(py_retval());
+        }
+    }
+}
+
+template <typename Callable, typename... Extras, typename... Args, std::size_t... Is>
+struct template_parser<Callable,
+                       std::tuple<Extras...>,
+                       std::tuple<Args...>,
+                       std::index_sequence<Is...>> {
+    using types = type_list<Args...>;
+
+    /// count of the Callable parameters.
+    constexpr inline static auto argc = types::size;
+
+    // count the number of py::args and py::kwargs
+    constexpr inline static auto args_count = types::template count<pkbind::args>;
+    constexpr inline static auto kwargs_count = types::template count<pkbind::kwargs>;
+    static_assert(args_count <= 1, "py::args can occur at most once");
+    static_assert(kwargs_count <= 1, "py::kwargs can occur at most once");
+
+    /// find the position of py::args and py::kwargs
+    constexpr inline static auto args_pos = types::template find<pkbind::args>;
+    constexpr inline static auto kwargs_pos = types::template find<pkbind::kwargs>;
+
+    // FIXME: temporarily, args and kwargs must be at the end of the arguments list
+    /// if have py::kwargs, it must be at the end of the arguments list.
+    static_assert(kwargs_count == 0 || kwargs_pos == argc - 1,
+                  "py::kwargs must be the last parameter");
+    /// if have py::args, it must be before py::kwargs or at the end of the arguments list.
+    static_assert(args_count == 0 || args_pos == kwargs_pos - 1 || args_pos == argc - 1,
+                  "py::args must be before py::kwargs or at the end of the parameter list");
+
+    using extras = type_list<Extras...>;
+
+    // count the number of py::doc and py::return_value_policy
+    constexpr inline static auto doc_count = extras::template count<const char*>;
+    constexpr inline static auto policy_count = extras::template count<pkbind::return_value_policy>;
+    static_assert(doc_count <= 1, "doc can occur at most once");
+    static_assert(policy_count <= 1, "return_value_policy can occur at most once");
+
+    constexpr inline static auto policy_pos = extras::template find<pkbind::return_value_policy>;
+
+    constexpr inline static auto last_arg_without_default_pos =
+        types::template find_last<pkbind::arg>;
+    constexpr inline static auto first_arg_with_default_pos =
+        types::template find<pkbind::arg_with_default>;
+    static_assert(last_arg_without_default_pos < first_arg_with_default_pos ||
+                      first_arg_with_default_pos == -1,
+                  "parameter with default value must be after parameter without default value");
+
+    /// count of named parameters(explicit with name).
+    constexpr inline static auto named_only_argc = extras::template count<pkbind::arg>;
+    constexpr inline static auto named_default_argc =
+        extras::template count<pkbind::arg_with_default>;
+    constexpr inline static auto named_argc = named_only_argc + named_default_argc;
+
+    /// count of normal parameters(which are not py::args or py::kwargs).
+    constexpr inline static auto normal_argc = argc - (args_pos != -1) - (kwargs_pos != -1);
+
+    /// all parameters must either have no names or all must have names.
+    static_assert(named_argc == 0 || named_argc == normal_argc,
+                  "all parameters must either have no names or all must have names.");
+
+    static void initialize(function_record& record, const Extras&... extras) {
+        auto extras_tuple = std::make_tuple(extras...);
+        constexpr static bool has_named_args = (named_argc > 0);
+        if constexpr(policy_pos != -1) { record.policy = std::get<policy_pos>(extras_tuple); }
+
+        // TODO: set others
+
+        // set default arguments
+        if constexpr(has_named_args) {
+            record.arguments = new function_record::arguments_t();
+
+            auto add_arguments = [&](const auto& default_) {
+                using type = remove_cvref_t<decltype(default_)>;
+                if constexpr(std::is_same_v<arg, type>) {
+                    auto& arguments = *record.arguments;
+                    arguments.names.emplace_back(default_.name);
+                    arguments.defaults.emplace_back();
+                } else if constexpr(std::is_same_v<arg_with_default, type>) {
+                    auto& arguments = *record.arguments;
+                    arguments.names.emplace_back(default_.name);
+                    arguments.defaults.emplace_back(std::move(default_.value));
+                }
+            };
+
+            (add_arguments(extras), ...);
+        }
+
+        // set signature
+        {
+            std::string sig = "(";
+            std::size_t index = 0;
+            auto append = [&](auto _t) {
+                using T = remove_cvref_t<typename decltype(_t)::type>;
+                if constexpr(std::is_same_v<T, args>) {
+                    sig += "*args";
+                } else if constexpr(std::is_same_v<T, kwargs>) {
+                    sig += "**kwargs";
+                } else if constexpr(has_named_args) {
+                    sig += record.arguments->names[index].c_str();
+                    sig += ": ";
+                    sig += type_info::of<T>().name;
+                    if(!record.arguments->defaults[index].empty()) {
+                        sig += " = ";
+                        sig += record.arguments->defaults[index]
+                                   .attr("__repr__")()
+                                   .cast<std::string_view>();
+                    }
+                } else {
+                    sig += "_: ";
+                    sig += type_info::of<T>().name;
+                }
+                if(index + 1 < argc) { sig += ", "; }
+                index++;
+            };
+            (append(type_identity<Args>{}), ...);
+            sig += ")";
+            char* buffer = new char[sig.size() + 1];
+            std::memcpy(buffer, sig.data(), sig.size());
+            buffer[sig.size()] = '\0';
+            record.signature = buffer;
+        }
+    }
+
+    /// try to call a C++ function(store in function_record) with the arguments which are from
+    /// Python. if success, return true, otherwise return false.
+    static bool call(function_record& record,
+                     std::vector<handle>& args,
+                     std::vector<std::pair<handle, handle>>& kwargs,
+                     bool convert,
+                     handle parent) {
+        // first, we try to load arguments into the stack.
+        // use argc + 1 to avoid compile error when argc is 0.
+        handle stack[argc + 1] = {};
+
+        // if have default arguments, load them
+        if constexpr(named_default_argc > 0) {
+            auto& defaults = record.arguments->defaults;
+            for(std::size_t i = named_only_argc; i < named_argc; ++i) {
+                stack[i] = defaults[i];
+            }
+        }
+
+        // load arguments from call arguments
+        if(args.size() > normal_argc) {
+            if constexpr(args_pos == -1) { return false; }
+        }
+
+        for(std::size_t i = 0; i < std::min(normal_argc, (int)args.size()); ++i) {
+            stack[i] = args[i];
+        }
+
+        object repack_args;
+        // pack the args
+        if constexpr(args_pos != -1) {
+            const auto n =
+                static_cast<int>(args.size() > normal_argc ? args.size() - normal_argc : 0);
+            auto pack = tuple(n);
+            for(int i = 0; i < n; ++i) {
+                pack[i] = args[normal_argc + i];
+            }
+            repack_args = std::move(pack);
+            stack[args_pos] = repack_args;
+        }
+
+        // pack the kwargs
+        int index = 0;
+        if constexpr(named_argc != 0) {
+            int arg_index = 0;
+            while(arg_index < named_argc && index < kwargs.size()) {
+                const auto name = kwargs[index].first;
+                const auto value = kwargs[index].second;
+                if(name.cast<std::string_view>() == record.arguments->names[arg_index]) {
+                    stack[arg_index] = value;
+                    index += 1;
+                }
+                arg_index += 1;
+            }
+        }
+
+        object repacked_kwargs;
+        if constexpr(kwargs_pos != -1) {
+            auto pack = dict();
+            while(index < kwargs.size()) {
+                pack[kwargs[index].first] = kwargs[index].second;
+                index += 1;
+            }
+            repacked_kwargs = std::move(pack);
+            stack[kwargs_pos] = repacked_kwargs;
+        }
+
+        // check if all the arguments are valid
+        for(std::size_t i = 0; i < argc; ++i) {
+            if(!stack[i]) { return false; }
+        }
+
+        // ok, all the arguments are valid, call the function
+        std::tuple<type_caster<Args>...> casters;
+
+        if(((std::get<Is>(casters).load(stack[Is], convert)) && ...)) {
+            invoke(record.as<Callable>(),
+                   std::index_sequence<Is...>{},
+                   casters,
+                   record.policy,
+                   parent);
+            return true;
+        }
+
+        return false;
+    }
+};
+
+}  // namespace impl
+
+class cpp_function : public function {
+    inline static py_Type m_type = 0;
+
+    PKBIND_TYPE_IMPL(function, cpp_function, tp_function);
+
+    static void register_() {
+        m_type = py_newtype("function_record", tp_object, nullptr, [](void* data) {
+            static_cast<impl::function_record*>(data)->~function_record();
+        });
+    }
+
+    static bool is_function_record(handle h) {
+        if(isinstance<function>(h)) {
+            auto slot = py_getslot(h.ptr(), 0);
+            if(slot) { return py_typeof(slot) == m_type; }
+        }
+        return false;
+    }
+
+    template <typename Fn, typename... Extras>
+    cpp_function(bool is_method, const char* name, Fn&& fn, const Extras&... extras) :
+        function(alloc_t{}) {
+        // bind the function
+        std::string sig = name;
+        sig += is_method ? "(self, *args, **kwargs)" : "(*args, **kwargs)";
+        auto call = [](int argc, py_Ref stack) {
+            handle func = py_inspect_currentfunction();
+            auto data = py_touserdata(py_getslot(func.ptr(), 0));
+            auto& record = *static_cast<impl::function_record*>(data);
+            try {
+                record(argc, stack);
+                return true;
+            } catch(std::domain_error& e) {
+                py_exception(tp_ValueError, e.what());
+            } catch(std::invalid_argument& e) {
+                py_exception(tp_ValueError, e.what());
+            } catch(std::length_error& e) {
+                py_exception(tp_ValueError, e.what());
+            } catch(std::out_of_range& e) {
+                py_exception(tp_IndexError, e.what());
+            } catch(std::range_error& e) {
+                py_exception(tp_ValueError, e.what());
+            } catch(stop_iteration&) { StopIteration(); } catch(index_error& e) {
+                py_exception(tp_IndexError, e.what());
+            } catch(key_error& e) { py_exception(tp_KeyError, e.what()); } catch(value_error& e) {
+                py_exception(tp_ValueError, e.what());
+            } catch(type_error& e) {
+                py_exception(tp_TypeError, e.what());
+            } catch(import_error& e) {
+                py_exception(tp_ImportError, e.what());
+            } catch(attribute_error& e) {
+                py_exception(tp_AttributeError, e.what());
+            } catch(std::exception& e) { py_exception(tp_RuntimeError, e.what()); }
+            return false;
+        };
+        py_newfunction(m_ptr, sig.c_str(), call, nullptr, 1);
+
+        assert(m_type != 0 && "function record type not registered");
+        auto slot = py_getslot(m_ptr, 0);
+        void* data = py_newobject(slot, m_type, 0, sizeof(impl::function_record));
+        new (data) impl::function_record(std::forward<Fn>(fn), extras...);
+    }
+
+    template <typename Fn, typename... Extras>
+    cpp_function(Fn&& fn, const Extras&... extras) :
+        cpp_function("lambda", std::forward<Fn>(fn), extras...) {}
+};
+
+class property : public object {
+    PKBIND_TYPE_IMPL(object, property, tp_property);
+
+    property(handle getter, handle setter = none()) : object() {
+        auto start = py_peek(0);
+        py_push(getter.ptr());
+        py_push(setter.ptr());
+        raise_call<py_tpcall>(type::of<property>().index(), 2, start);
+        *this = object::from_ret();
+    }
+};
+
+class staticmethod : public object {
+    PKBIND_TYPE_IMPL(object, staticmethod, tp_staticmethod);
+
+    staticmethod(handle method) : object() {
+        auto start = py_peek(0);
+        py_push(method.ptr());
+        raise_call<py_tpcall>(type::of<staticmethod>().index(), 1, start);
+        *this = object::from_ret();
+    }
+};
+
+namespace impl {
+
+template <bool is_method, bool is_static, typename Fn, typename... Extras>
+void bind_function(handle obj, const char* name_, Fn&& fn, const Extras&... extras) {
+    constexpr bool has_named_args =
+        ((std::is_same_v<Extras, arg> || std::is_same_v<Extras, arg_with_default>) || ...);
+    auto name = py_name(name_);
+    auto func = py_getdict(obj.ptr(), name);
+
+    if(func && cpp_function::is_function_record(func)) {
+        auto slot = py_getslot(func, 0);
+        auto& record = *static_cast<function_record*>(py_touserdata(slot));
+        if constexpr(has_named_args && is_method) {
+            record.append(new function_record(std::forward<Fn>(fn), arg("self"), extras...));
+        } else {
+            record.append(new function_record(std::forward<Fn>(fn), extras...));
+        }
+    } else {
+        if constexpr(is_static) {
+            py_setdict(
+                obj.ptr(),
+                name,
+                staticmethod(cpp_function(is_method, name_, std::forward<Fn>(fn), extras...).ptr())
+                    .ptr());
+        } else {
+            if constexpr(has_named_args && is_method) {
+                py_setdict(
+                    obj.ptr(),
+                    name,
+                    cpp_function(is_method, name_, std::forward<Fn>(fn), arg("self"), extras...)
+                        .ptr());
+            } else {
+                py_setdict(obj.ptr(),
+                           name,
+                           cpp_function(is_method, name_, std::forward<Fn>(fn), extras...).ptr());
+            }
+        }
+    }
+}
+
+template <typename Getter, typename Setter, typename... Extras>
+void bind_property(handle obj,
+                   const char* name,
+                   Getter&& getter_,
+                   Setter&& setter_,
+                   const Extras&... extras) {
+    if constexpr(std::is_same_v<std::decay_t<Setter>, std::nullptr_t>) {
+        cpp_function getter(true,
+                            name,
+                            std::forward<Getter>(getter_),
+                            return_value_policy::reference_internal,
+                            extras...);
+        property prop(getter.ptr());
+        setattr(obj, name, prop);
+    } else {
+        cpp_function getter(true,
+                            name,
+                            std::forward<Getter>(getter_),
+                            return_value_policy::reference_internal,
+                            extras...);
+        cpp_function setter(true,
+                            name,
+                            std::forward<Setter>(setter_),
+                            return_value_policy::reference_internal,
+                            extras...);
+        property prop(getter.ptr(), setter.ptr());
+        setattr(obj, name, prop);
+    }
+}
+
+}  // namespace impl
+
+inline dict::dict(std::initializer_list<arg_with_default> args) : dict() {
+    for(auto& arg: args) {
+        this->operator[] (arg.name) = arg.value;
+    }
+}
+
+}  // namespace pkbind

+ 57 - 86
include/pybind11/internal/instance.h

@@ -1,15 +1,14 @@
 #pragma once
 
-#include "kernel.h"
+#include "accessor.h"
+
+namespace pkbind {
 
-namespace pybind11 {
 struct type_info {
-    const char* name;
+    std::string_view name;
     std::size_t size;
     std::size_t alignment;
     void (*destructor)(void*);
-    void (*copy)(void*, const void*);
-    void (*move)(void*, void*);
     const std::type_info* type;
 
     template <typename T>
@@ -17,18 +16,11 @@ struct type_info {
         static_assert(!std::is_reference_v<T> && !std::is_const_v<std::remove_reference_t<T>>,
                       "T must not be a reference type or const type.");
         static type_info info = {
-            typeid(T).name(),
+            type_name<T>(),
             sizeof(T),
             alignof(T),
             [](void* ptr) {
-                ((T*)ptr)->~T();
-                operator delete (ptr);
-            },
-            [](void* dst, const void* src) {
-                new (dst) T(*(const T*)src);
-            },
-            [](void* dst, void* src) {
-                new (dst) T(std::move(*(T*)src));
+                delete static_cast<T*>(ptr);
             },
             &typeid(T),
         };
@@ -37,11 +29,8 @@ struct type_info {
 };
 
 // all registered C++ class will be ensured as instance type.
-class instance {
-public:
+struct instance {
     // use to record the type information of C++ class.
-
-private:
     enum Flag {
         None = 0,
         Own = 1 << 0,  // if the instance is owned by C++ side.
@@ -50,96 +39,78 @@ private:
 
     Flag flag;
     void* data;
-    const type_info* type;
-    pkpy::PyVar parent;
-    // pkpy::PyVar
+    const type_info* info;
+    object parent;
 
 public:
-    instance() noexcept : flag(Flag::None), data(nullptr), type(nullptr), parent(nullptr) {}
-
-    instance(const instance&) = delete;
-
-    instance(instance&& other) noexcept : flag(other.flag), data(other.data), type(other.type), parent(other.parent) {
-        other.flag = Flag::None;
-        other.data = nullptr;
-        other.type = nullptr;
-        other.parent = nullptr;
-    }
-
-    template <typename T>
-    static pkpy::PyVar create(pkpy::Type type) {
-        instance instance;
-        instance.type = &type_info::of<T>();
-        instance.data = operator new (sizeof(T));
-        instance.flag = Flag::Own;
-        return vm->new_object<pybind11::instance>(type, std::move(instance));
-    }
-
-    template <typename T>
-    static pkpy::PyVar create(T&& value,
-                              pkpy::Type type,
-                              return_value_policy policy = return_value_policy::automatic_reference,
-                              pkpy::PyVar parent = nullptr) noexcept {
-        using underlying_type = std::remove_cv_t<std::remove_reference_t<T>>;
-
-        // resolve for automatic policy.
-        if(policy == return_value_policy::automatic) {
-            policy = std::is_pointer_v<underlying_type> ? return_value_policy::take_ownership
-                     : std::is_lvalue_reference_v<T&&>  ? return_value_policy::copy
-                                                        : return_value_policy::move;
-        } else if(policy == return_value_policy::automatic_reference) {
-            policy = std::is_pointer_v<underlying_type> ? return_value_policy::reference
-                     : std::is_lvalue_reference_v<T&&>  ? return_value_policy::copy
-                                                        : return_value_policy::move;
-        }
+    template <typename Value>
+    static object create(type type, Value&& value_, handle parent_, return_value_policy policy) {
+        using underlying_type = remove_cvref_t<Value>;
 
-        auto& _value = [&]() -> auto& {
-            /**
-             * note that, pybind11 will ignore the const qualifier.
-             * in fact, try to modify a const value will result in undefined behavior.
-             */
+        auto& value = [&]() -> auto& {
+            // note that, pybind11 will ignore the const qualifier.
+            // in fact, try to modify a const value will result in undefined behavior.
             if constexpr(std::is_pointer_v<underlying_type>) {
-                return *reinterpret_cast<underlying_type*>(value);
+                return *reinterpret_cast<underlying_type*>(value_);
             } else {
-                return const_cast<underlying_type&>(value);
+                return const_cast<underlying_type&>(value_);
             }
         }();
 
-        instance instance;
-        instance.type = &type_info::of<underlying_type>();
+        using primary = std::remove_pointer_t<underlying_type>;
+
+        auto info = &type_info::of<primary>();
+
+        void* data = nullptr;
+        Flag flag = Flag::None;
+        object parent;
 
         if(policy == return_value_policy::take_ownership) {
-            instance.data = &_value;
-            instance.flag = Flag::Own;
+            data = &value;
+            flag = Flag::Own;
         } else if(policy == return_value_policy::copy) {
-            instance.data = ::new auto(_value);
-            instance.flag = Flag::Own;
+            if constexpr(std::is_copy_constructible_v<primary>) {
+                data = new auto(value);
+                flag = Flag::Own;
+            } else {
+                std::string msg = "cannot use copy policy on non-copyable type: ";
+                msg += type_name<primary>();
+                throw std::runtime_error(msg);
+            }
         } else if(policy == return_value_policy::move) {
-            instance.data = ::new auto(std::move(_value));
-            instance.flag = Flag::Own;
+            if constexpr(std::is_move_constructible_v<primary>) {
+                data = new auto(std::move(value));
+                flag = Flag::Own;
+            } else {
+                std::string msg = "cannot use move policy on non-moveable type: ";
+                msg += type_name<primary>();
+                throw std::runtime_error(msg);
+            }
         } else if(policy == return_value_policy::reference) {
-            instance.data = &_value;
-            instance.flag = Flag::None;
+            data = &value;
+            flag = Flag::None;
         } else if(policy == return_value_policy::reference_internal) {
-            instance.data = &_value;
-            instance.flag = Flag::Ref;
-            instance.parent = parent;
+            data = &value;
+            flag = Flag::Ref;
+            parent = borrow(parent_);
         }
 
-        return vm->new_object<pybind11::instance>(type, std::move(instance));
+        object result(object::alloc_t{});
+        void* temp = py_newobject(result.ptr(), type.index(), 1, sizeof(instance));
+        new (temp) instance{flag, data, info, std::move(parent)};
+        return result;
     }
 
     ~instance() {
-        if(flag & Flag::Own) { type->destructor(data); }
-    }
-
-    void _gc_mark(pkpy::VM* vm) const noexcept {
-        if(parent && (flag & Flag::Ref)) { PK_OBJ_MARK(parent); }
+        if(flag & Flag::Own) {
+            info->destructor(data);
+        }
     }
 
     template <typename T>
-    T& cast() noexcept {
+    T& as() noexcept {
         return *static_cast<T*>(data);
     }
 };
-}  // namespace pybind11
+
+}  // namespace pkbind

+ 145 - 94
include/pybind11/internal/kernel.h

@@ -1,99 +1,150 @@
 #pragma once
 
-#include <pocketpy.h>
-
-namespace pybind11 {
-inline pkpy::VM* vm = nullptr;
-inline std::map<pkpy::PyVar, int*>* _ref_counts_map = nullptr;
-
-inline void initialize(bool enable_os = true) {
-    vm = new pkpy::VM(enable_os);
-    _ref_counts_map = new std::map<pkpy::PyVar, int*>();
-
-    // use to keep alive PyObject, when the object is hold by C++ side.
-    vm->heap._gc_marker_ex = [](pkpy::VM* vm) {
-        for(auto iter = _ref_counts_map->begin(); iter != _ref_counts_map->end();) {
-            auto ref_count = iter->second;
-            if(*ref_count != 0) {
-                // if ref count is not zero, then mark it.
-                PK_OBJ_MARK(iter->first);
-                ++iter;
-            } else {
-                // if ref count is zero, then delete it.
-                iter = _ref_counts_map->erase(iter);
-                delete ref_count;
+#include <array>
+#include <vector>
+#include <string>
+#include <cstdlib>
+#include <cstring>
+#include <cassert>
+#include <typeindex>
+#include <stdexcept>
+#include <unordered_map>
+
+#include "pocketpy.h"
+#include "type_traits.h"
+
+namespace pkbind {
+
+class handle;
+
+/// hold the object temporarily
+template <int N>
+struct reg_t {
+    py_Ref value;
+
+    void operator= (py_Ref ref) & { py_setreg(N, ref); }
+
+    operator py_Ref () & {
+        assert(value && "register is not initialized");
+        return value;
+    }
+
+    void operator= (handle value) &;
+
+    operator handle () &;
+
+    // pkpy provide user 8 registers.
+    // 8th register is used for object pool, so N is limited to [0, 7).
+    static_assert(N >= 0 && N <= 6, "N must be in [0, 7)");
+};
+
+struct retv_t {
+    py_Ref value;
+
+    void operator= (py_Ref ref) & { py_assign(value, ref); }
+
+    operator py_Ref () & {
+        assert(value && "return value is not initialized");
+        return value;
+    }
+
+    void operator= (handle value) &;
+
+    operator handle () &;
+};
+
+/// hold the object long time.
+struct object_pool {
+    inline static int cache = -1;
+    inline static py_Ref pool = nullptr;
+    inline static std::vector<int>* indices_ = nullptr;
+
+    struct object_ref {
+        py_Ref data;
+        int index;
+    };
+
+    static void initialize(int size) noexcept {
+        // use 8th register.
+        pool = py_getreg(7);
+        py_newtuple(pool, size);
+        indices_ = new std::vector<int>(size, 0);
+    }
+
+    static void finalize() noexcept {
+        delete indices_;
+        indices_ = nullptr;
+    }
+
+    /// alloc an object from pool, note that the object is uninitialized.
+    static object_ref alloc() {
+        auto& indices = *indices_;
+        if(cache != -1) {
+            auto index = cache;
+            cache = -1;
+            indices[index] = 1;
+            return {py_tuple_getitem(pool, index), index};
+        }
+
+        for(int i = 0; i < indices.size(); ++i) {
+            if(indices[i] == 0) {
+                indices[i] = 1;
+                return {py_tuple_getitem(pool, i), i};
             }
         }
-    };
-}
-
-inline void finalize() {
-    delete _ref_counts_map;
-    delete vm;
-}
-
-enum class return_value_policy : uint8_t {
-    /**
-     *  This is the default return value policy, which falls back to the policy
-     *  return_value_policy::take_ownership when the return value is a pointer.
-     *  Otherwise, it uses return_value::move or return_value::copy for rvalue
-     *  and lvalue references, respectively. See below for a description of what
-     *  all of these different policies do.
-     */
-    automatic = 0,
-
-    /**
-     *  As above, but use policy return_value_policy::reference when the return
-     *  value is a pointer. This is the default conversion policy for function
-     *  arguments when calling Python functions manually from C++ code (i.e. via
-     *  handle::operator()). You probably won't need to use this.
-     */
-    automatic_reference,
-
-    /**
-     *  Reference an existing object (i.e. do not create a new copy) and take
-     *  ownership. Python will call the destructor and delete operator when the
-     *  object's reference count reaches zero. Undefined behavior ensues when
-     *  the C++ side does the same..
-     */
-    take_ownership,
-
-    /**
-     *  Create a new copy of the returned object, which will be owned by
-     *  Python. This policy is comparably safe because the lifetimes of the two
-     *  instances are decoupled.
-     */
-    copy,
-
-    /**
-     *  Use std::move to move the return value contents into a new instance
-     *  that will be owned by Python. This policy is comparably safe because the
-     *  lifetimes of the two instances (move source and destination) are
-     *  decoupled.
-     */
-    move,
-
-    /**
-     *  Reference an existing object, but do not take ownership. The C++ side
-     *  is responsible for managing the object's lifetime and deallocating it
-     *  when it is no longer used. Warning: undefined behavior will ensue when
-     *  the C++ side deletes an object that is still referenced and used by
-     *  Python.
-     */
-    reference,
-
-    /**
-     *  This policy only applies to methods and properties. It references the
-     *  object without taking ownership similar to the above
-     *  return_value_policy::reference policy. In contrast to that policy, the
-     *  function or property's implicit this argument (called the parent) is
-     *  considered to be the the owner of the return value (the child).
-     *  pybind11 then couples the lifetime of the parent to the child via a
-     *  reference relationship that ensures that the parent cannot be garbage
-     *  collected while Python is still using the child. More advanced
-     *  variations of this scheme are also possible using combinations of
-     *  return_value_policy::reference and the keep_alive call policy
-     */
-    reference_internal
+
+        throw std::runtime_error("object pool is full");
+    }
+
+    /// alloc an object from pool, the object is initialized with ref.
+    static object_ref realloc(py_Ref ref) {
+        auto result = alloc();
+        py_assign(result.data, ref);
+        return result;
+    }
+
+    static void inc_ref(object_ref ref) {
+        if(!indices_) { return; }
+        if(ref.data == py_tuple_getitem(pool, ref.index)) {
+            auto& indices = *indices_;
+            indices[ref.index] += 1;
+        } else {
+            throw std::runtime_error("object_ref is invalid");
+        }
+    }
+
+    static void dec_ref(object_ref ref) {
+        if(!indices_) { return; }
+        if(ref.data == py_tuple_getitem(pool, ref.index)) {
+            auto& indices = *indices_;
+            indices[ref.index] -= 1;
+            assert(indices[ref.index] >= 0 && "ref count is negative");
+            if(indices[ref.index] == 0) { cache = ref.index; }
+        } else {
+            throw std::runtime_error("object_ref is invalid");
+        }
+    }
+};
+
+struct action {
+    using function = void (*)();
+    inline static std::vector<function> starts;
+
+    static void initialize() noexcept {
+        for(auto func: starts) {
+            func();
+        }
+    }
+
+    // register a function to be called at the start of the vm.
+    static void register_start(function func) { starts.push_back(func); }
 };
-}  // namespace pybind11
+
+template <int N>
+inline reg_t<N> reg;
+
+inline retv_t retv;
+
+inline std::unordered_map<std::type_index, py_Type>* m_type_map = nullptr;
+
+}  // namespace pkbind

+ 47 - 0
include/pybind11/internal/module.h

@@ -0,0 +1,47 @@
+#pragma once
+
+#include "function.h"
+
+namespace pkbind {
+
+class module : public object {
+    PKBIND_TYPE_IMPL(object, module, tp_module)
+
+    static module __main__() { return module(py_getmodule("__main__"), object::ref_t{}); }
+
+    static module import(const char* name) {
+        raise_call<py_import>(name);
+        return module(py_retval(), object::realloc_t{});
+    }
+
+    module def_submodule(const char* name, const char* doc = nullptr) {
+        // auto package = (attr("__package__").cast<std::string>() += ".") += attr("__name__").cast<std::string_view>();
+        auto fname = (attr("__name__").cast<std::string>() += ".") += name;
+        auto m = py_newmodule(fname.c_str());
+        setattr(*this, name, m);
+        return module(m, object::ref_t{});
+    }
+
+    template <typename Fn, typename... Extras>
+    module& def(const char* name, Fn&& fn, const Extras... extras) {
+        impl::bind_function<false, false>(*this, name, std::forward<Fn>(fn), extras...);
+        return *this;
+    }
+};
+
+using module_ = module;
+
+#define PYBIND11_EMBEDDED_MODULE(name, variable)                                                                       \
+    static void _pkbind_register_##name(::pkbind::module& variable);                                                   \
+    namespace pkbind::impl {                                                                                           \
+    auto _module_##name = [] {                                                                                         \
+        ::pkbind::action::register_start([] {                                                                          \
+            auto m = ::pkbind::module(py_newmodule(#name), ::pkbind::object::ref_t{});                                 \
+            _pkbind_register_##name(m);                                                                                \
+        });                                                                                                            \
+        return 1;                                                                                                      \
+    }();                                                                                                               \
+    }                                                                                                                  \
+    static void _pkbind_register_##name(::pkbind::module& variable)
+
+}  // namespace pkbind

+ 234 - 181
include/pybind11/internal/object.h

@@ -2,251 +2,304 @@
 
 #include "kernel.h"
 
-namespace pybind11 {
+namespace pkbind {
+
 class handle;
 class object;
-class attr_accessor;
-class item_accessor;
 class iterator;
 class str;
-class bytes;
-class iterable;
-class tuple;
-class dict;
-class list;
-class set;
-class function;
-class module;
-class type;
-class bool_;
-class int_;
-class float_;
-class str;
-class bytes;
 
-template <typename T>
-T& _builtin_cast(const handle& obj);
+struct arg;
+struct arg_with_default;
+struct args_proxy;
+
+enum class return_value_policy : uint8_t {
+    /**
+     *  This is the default return value policy, which falls back to the policy
+     *  return_value_policy::take_ownership when the return value is a pointer.
+     *  Otherwise, it uses return_value::move or return_value::copy for rvalue
+     *  and lvalue references, respectively. See below for a description of what
+     *  all of these different policies do.
+     */
+    automatic = 0,
+
+    /**
+     *  As above, but use policy return_value_policy::reference when the return
+     *  value is a pointer. This is the default conversion policy for function
+     *  arguments when calling Python functions manually from C++ code (i.e. via
+     *  handle::operator()). You probably won't need to use this.
+     */
+    automatic_reference,
+
+    /**
+     *  Reference an existing object (i.e. do not create a new copy) and take
+     *  ownership. Python will call the destructor and delete operator when the
+     *  object's reference count reaches zero. Undefined behavior ensues when
+     *  the C++ side does the same..
+     */
+    take_ownership,
+
+    /**
+     *  Create a new copy of the returned object, which will be owned by
+     *  Python. This policy is comparably safe because the lifetimes of the two
+     *  instances are decoupled.
+     */
+    copy,
+
+    /**
+     *  Use std::move to move the return value contents into a new instance
+     *  that will be owned by Python. This policy is comparably safe because the
+     *  lifetimes of the two instances (move source and destination) are
+     *  decoupled.
+     */
+    move,
+
+    /**
+     *  Reference an existing object, but do not take ownership. The C++ side
+     *  is responsible for managing the object's lifetime and deallocating it
+     *  when it is no longer used. Warning: undefined behavior will ensue when
+     *  the C++ side deletes an object that is still referenced and used by
+     *  Python.
+     */
+    reference,
+
+    /**
+     *  This policy only applies to methods and properties. It references the
+     *  object without taking ownership similar to the above
+     *  return_value_policy::reference policy. In contrast to that policy, the
+     *  function or property's implicit this argument (called the parent) is
+     *  considered to be the the owner of the return value (the child).
+     *  pybind11 then couples the lifetime of the parent to the child via a
+     *  reference relationship that ensures that the parent cannot be garbage
+     *  collected while Python is still using the child. More advanced
+     *  variations of this scheme are also possible using combinations of
+     *  return_value_policy::reference and the keep_alive call policy
+     */
+    reference_internal
+};
 
-template <typename T>
-T reinterpret_borrow(const handle& h);
+template <typename policy>
+class accessor;
 
-template <typename T>
-T reinterpret_steal(const handle& h);
+namespace policy {
 
-class handle {
-protected:
-    pkpy::PyVar m_ptr = nullptr;
-    mutable int* ref_count = nullptr;
+struct attr;
+template <typename Key>
+struct item;
+struct tuple;
+struct list;
+template <typename Key>
+struct dict;
+
+}  // namespace policy
 
+using attr_accessor = accessor<policy::attr>;
+template <typename Key>
+using item_accessor = accessor<policy::item<Key>>;
+using tuple_accessor = accessor<policy::tuple>;
+using list_accessor = accessor<policy::list>;
+template <typename Key>
+using dict_accessor = accessor<policy::dict<Key>>;
+
+/// call a pkpy function which may raise a python exception.
+template <auto Fn, typename... Args>
+auto raise_call(Args&&... args);
+
+/// an effective representation of a python small string.
+class name {
 public:
-    handle() = default;
-    handle(const handle& h) = default;
-    handle& operator= (const handle& other) = default;
-
-    handle(pkpy::PyVar ptr) : m_ptr(ptr) {}
-
-    pkpy::PyVar ptr() const { return m_ptr; }
-
-    int reference_count() const { return ref_count == nullptr ? 0 : *ref_count; }
-
-    const handle& inc_ref() const {
-        assert(m_ptr != nullptr);
-        if(ref_count == nullptr) {
-            auto iter = _ref_counts_map->find(m_ptr);
-            if(iter == _ref_counts_map->end()) {
-                ref_count = ::new int(1);
-                _ref_counts_map->insert({m_ptr, ref_count});
-            } else {
-                ref_count = iter->second;
-                *ref_count += 1;
-            }
-        } else {
-            *ref_count += 1;
-        }
-        return *this;
-    }
+    name() = default;
 
-    const handle& dec_ref() const {
-        assert(m_ptr != nullptr);
-        assert(ref_count != nullptr);
+    name(const name&) = default;
 
-        *ref_count -= 1;
-        try {
-            if(*ref_count == 0) {
-                _ref_counts_map->erase(m_ptr);
-                ::delete ref_count;
-                ref_count = nullptr;
-            }
-        } catch(std::exception& e) { std::cerr << "Error: " << e.what() << std::endl; }
+    name& operator= (const name&) = default;
 
-        return *this;
-    }
+    explicit name(py_Name data) : data(data) {}
 
-public:
-    template <typename T>
-    T cast() const;
+    name(const char* str) : data(py_name(str)) {}
 
-    explicit operator bool () const { return m_ptr.operator bool (); }
+    name(const char* data, int size) : data(py_namev({data, size})) {}
 
-    bool is(const handle& other) const { return m_ptr == other.m_ptr; }
+    name(std::string_view str) : name(str.data(), static_cast<int>(str.size())) {}
 
-    bool is_none() const { return m_ptr == vm->None; }
+    name(handle h);
 
-    bool in(const handle& other) const {
-        return pkpy::py_cast<bool>(vm, vm->call(vm->py_op("contains"), other.m_ptr, m_ptr));
-    }
+    py_Name index() const { return data; }
+
+    const char* c_str() const { return py_name2str(data); }
 
-    bool contains(const handle& other) const {
-        return pkpy::py_cast<bool>(vm, vm->call(vm->py_op("contains"), m_ptr, other.m_ptr));
+    operator std::string_view () const {
+        auto temp = py_name2sv(data);
+        return std::string_view(temp.data, temp.size);
     }
 
-    iterator begin() const;
-    iterator end() const;
+private:
+    py_Name data;
+};
 
-    str doc() const;
+template <typename Derived>
+class interface {
+public:
+    /// equal to `self is None` in python.
+    bool is_none() const { return py_isnone(ptr()); }
 
-    attr_accessor attr(const char* name) const;
-    attr_accessor attr(const handle& name) const;
-    attr_accessor attr(object&& name) const;
+    /// equal to `self is other` in python.
+    bool is(const interface& other) const { return py_isidentical(ptr(), other.ptr()); }
+
+    void assign(const interface& other) { py_assign(ptr(), other.ptr()); }
+
+    iterator begin() const;
+    iterator end() const;
 
-    item_accessor operator[] (int64_t key) const;
-    item_accessor operator[] (const char* key) const;
-    item_accessor operator[] (const handle& key) const;
-    item_accessor operator[] (object&& key) const;
+    attr_accessor attr(name key) const;
 
     object operator- () const;
     object operator~() const;
+    args_proxy operator* () const;
+
+    item_accessor<int> operator[] (int index) const;
+    item_accessor<name> operator[] (name key) const;
+    item_accessor<handle> operator[] (handle key) const;
 
     template <return_value_policy policy = return_value_policy::automatic, typename... Args>
     object operator() (Args&&... args) const;
 
-private:
-    friend object operator+ (const handle& lhs, const handle& rhs);
-    friend object operator- (const handle& lhs, const handle& rhs);
-    friend object operator* (const handle& lhs, const handle& rhs);
-    friend object operator% (const handle& lhs, const handle& rhs);
-    friend object operator/ (const handle& lhs, const handle& rhs);
-    friend object operator| (const handle& lhs, const handle& rhs);
-    friend object operator& (const handle& lhs, const handle& rhs);
-    friend object operator^ (const handle& lhs, const handle& rhs);
-    friend object operator<< (const handle& lhs, const handle& rhs);
-    friend object operator>> (const handle& lhs, const handle& rhs);
-
-    friend object operator+= (const handle& lhs, const handle& rhs);
-    friend object operator-= (const handle& lhs, const handle& rhs);
-    friend object operator*= (const handle& lhs, const handle& rhs);
-    friend object operator/= (const handle& lhs, const handle& rhs);
-    friend object operator%= (const handle& lhs, const handle& rhs);
-    friend object operator|= (const handle& lhs, const handle& rhs);
-    friend object operator&= (const handle& lhs, const handle& rhs);
-    friend object operator^= (const handle& lhs, const handle& rhs);
-    friend object operator<<= (const handle& lhs, const handle& rhs);
-    friend object operator>>= (const handle& lhs, const handle& rhs);
-
-    friend object operator== (const handle& lhs, const handle& rhs);
-    friend object operator!= (const handle& lhs, const handle& rhs);
-    friend object operator< (const handle& lhs, const handle& rhs);
-    friend object operator> (const handle& lhs, const handle& rhs);
-    friend object operator<= (const handle& lhs, const handle& rhs);
-    friend object operator>= (const handle& lhs, const handle& rhs);
+    auto doc() const { return attr("__doc__"); }
 
     template <typename T>
-    friend T& _builtin_cast(const handle& obj) {
-        // FIXME: 2.0 does not use Py_<T> anymore
-        static_assert(!std::is_reference_v<T>, "T must not be a reference type.");
-        return obj.ptr().obj_get<T>();
-    }
+    T cast() const;
+
+private:
+    py_Ref ptr() const { return static_cast<const Derived*>(this)->ptr(); }
 };
 
-static_assert(std::is_trivially_copyable_v<handle>);
+/// a simple wrapper to py_Ref.
+/// Note that it does not manage the lifetime of the object.
+class handle : public interface<handle> {
+public:
+    handle() = default;
+
+    handle(const handle&) = default;
+
+    handle& operator= (const handle&) = default;
+
+    handle(py_Ref ptr) : m_ptr(ptr) {}
+
+    auto ptr() const { return m_ptr; }
+
+    explicit operator bool () const { return m_ptr != nullptr; }
+
+protected:
+    py_Ref m_ptr = nullptr;
+};
 
 class object : public handle {
 public:
-    object(const object& other) : handle(other) { inc_ref(); }
+    object() = default;
 
-    object(object&& other) noexcept : handle(other) {
+    object(const object& other) : handle(other), m_index(other.m_index) {
+        if(other.in_pool()) { object_pool::inc_ref(other); }
+    }
+
+    object(object&& other) : handle(other), m_index(other.m_index) {
         other.m_ptr = nullptr;
-        other.ref_count = nullptr;
+        other.m_index = -1;
     }
 
     object& operator= (const object& other) {
         if(this != &other) {
-            dec_ref();
+            if(in_pool()) { object_pool::dec_ref(*this); }
+            if(other.in_pool()) { object_pool::inc_ref(other); }
             m_ptr = other.m_ptr;
-            ref_count = other.ref_count;
-            inc_ref();
+            m_index = other.m_index;
         }
         return *this;
     }
 
-    object& operator= (object&& other) noexcept {
+    object& operator= (object&& other) {
         if(this != &other) {
-            dec_ref();
+            if(in_pool()) { object_pool::dec_ref(*this); }
             m_ptr = other.m_ptr;
-            ref_count = other.ref_count;
+            m_index = other.m_index;
             other.m_ptr = nullptr;
-            other.ref_count = nullptr;
+            other.m_index = -1;
         }
         return *this;
     }
 
     ~object() {
-        if(m_ptr != nullptr) { dec_ref(); }
+        if(in_pool()) { object_pool::dec_ref(*this); }
     }
 
-protected:
-    object(const handle& h, bool borrow) : handle(h) {
-        if(borrow) { inc_ref(); }
-    }
+    bool is_singleton() const { return m_ptr && m_index == -1; }
 
-    template <typename T>
-    friend T reinterpret_borrow(const handle& h) {
-        return {h, true};
+    bool empty() const { return m_ptr == nullptr && m_index == -1; }
+
+    /// return whether the object is in the object pool.
+    bool in_pool() const { return m_ptr && m_index != -1; }
+
+public:
+    static auto type_or_check() { return tp_object; }
+
+    struct alloc_t {};
+
+    struct realloc_t {};
+
+    struct ref_t {};
+
+    object(alloc_t) {
+        auto ref = object_pool::alloc();
+        m_ptr = ref.data;
+        m_index = ref.index;
     }
 
-    template <typename T>
-    friend T reinterpret_steal(const handle& h) {
-        return {h, false};
+    object(handle h, realloc_t) {
+        auto ref = object_pool::realloc(h.ptr());
+        m_ptr = ref.data;
+        m_index = ref.index;
     }
+
+    object(handle h, ref_t) : handle(h) {}
+
+    static object from_ret() { return object(retv, realloc_t{}); }
+
+    operator object_pool::object_ref () const { return {m_ptr, m_index}; }
+
+    explicit operator bool () const;
+
+protected:
+    int m_index = -1;
 };
 
-inline void setattr(const handle& obj, const handle& name, const handle& value);
-inline void setitem(const handle& obj, const handle& key, const handle& value);
+template <typename T = object>
+T steal(handle h) {
+    return T(h, object::ref_t{});
+}
 
-#define PYBIND11_BINARY_OPERATOR(OP, NAME)                                                                             \
-    inline object operator OP (const handle& lhs, const handle& rhs) {                                                 \
-        return reinterpret_borrow<object>(vm->call(vm->py_op(NAME), lhs.m_ptr, rhs.m_ptr));                            \
-    }
+template <typename T = object>
+T borrow(handle h) {
+    return T(h, object::realloc_t{});
+}
+
+template <int N>
+void reg_t<N>::operator= (handle h) & {
+    py_setreg(N, h.ptr());
+}
+
+template <int N>
+reg_t<N>::operator handle () & {
+    assert(value && "register is not initialized");
+    return value;
+}
+
+inline void retv_t::operator= (handle h) & { py_assign(value, h.ptr()); }
+
+inline retv_t::operator handle () & {
+    assert(value && "return value is not initialized");
+    return value;
+}
+
+static_assert(std::is_trivially_copyable_v<name>);
+static_assert(std::is_trivially_copyable_v<handle>);
 
-PYBIND11_BINARY_OPERATOR(+, "add");
-PYBIND11_BINARY_OPERATOR(-, "sub");
-PYBIND11_BINARY_OPERATOR(*, "mul");
-PYBIND11_BINARY_OPERATOR(/, "truediv");
-PYBIND11_BINARY_OPERATOR(%, "mod");
-PYBIND11_BINARY_OPERATOR(|, "or_");
-PYBIND11_BINARY_OPERATOR(&, "and_");
-PYBIND11_BINARY_OPERATOR(^, "xor");
-PYBIND11_BINARY_OPERATOR(<<, "lshift");
-PYBIND11_BINARY_OPERATOR(>>, "rshift");
-
-PYBIND11_BINARY_OPERATOR(+=, "iadd");
-PYBIND11_BINARY_OPERATOR(-=, "isub");
-PYBIND11_BINARY_OPERATOR(*=, "imul");
-PYBIND11_BINARY_OPERATOR(/=, "itruediv");
-PYBIND11_BINARY_OPERATOR(%=, "imod");
-PYBIND11_BINARY_OPERATOR(|=, "ior");
-PYBIND11_BINARY_OPERATOR(&=, "iand");
-PYBIND11_BINARY_OPERATOR(^=, "ixor");
-PYBIND11_BINARY_OPERATOR(<<=, "ilshift");
-PYBIND11_BINARY_OPERATOR(>>=, "irshift");
-
-PYBIND11_BINARY_OPERATOR(==, "eq");
-PYBIND11_BINARY_OPERATOR(!=, "ne");
-PYBIND11_BINARY_OPERATOR(<, "lt");
-PYBIND11_BINARY_OPERATOR(>, "gt");
-PYBIND11_BINARY_OPERATOR(<=, "le");
-PYBIND11_BINARY_OPERATOR(>=, "ge");
-
-#undef PYBIND11_BINARY_OPERATOR
-
-}  // namespace pybind11
+}  // namespace pkbind

+ 134 - 53
include/pybind11/internal/type_traits.h

@@ -1,12 +1,86 @@
 #pragma once
 
 #include <tuple>
+#include <memory>
+#include <string_view>
 #include <type_traits>
 
-namespace pybind11 {
+namespace pkbind {
+
+template <typename T>
+struct type_identity {
+    using type = T;
+};
+
 template <typename T>
 constexpr bool dependent_false = false;
 
+template <typename T>
+using remove_cvref_t = std::remove_cv_t<std::remove_reference_t<T>>;
+
+/// check if T is one of Ts...
+template <typename T, typename... Ts>
+constexpr inline bool is_one_of = (std::is_same_v<T, Ts> || ...);
+
+using std::is_member_function_pointer_v;
+using std::is_member_object_pointer_v;
+
+/// check if T is a function pointer type
+template <typename T>
+constexpr inline bool is_function_pointer_v = std::is_function_v<std::remove_pointer_t<T>>;
+
+/// check if T is a functor type(has a unique operator())
+template <typename T, typename U = void>
+constexpr bool is_functor_v = false;
+
+template <typename T>
+constexpr inline bool is_functor_v<T, std::void_t<decltype(&T::operator())>> = true;
+
+template <typename T>
+constexpr inline bool is_integer_v = std::is_integral_v<T> && !is_one_of<T, bool, char, wchar_t>;
+
+template <typename T>
+constexpr inline bool is_floating_point_v = std::is_floating_point_v<T>;
+
+template <typename T>
+constexpr inline bool is_unique_pointer_v = false;
+
+template <typename T>
+constexpr inline bool is_unique_pointer_v<std::unique_ptr<T>> = true;
+
+template <typename T>
+constexpr bool is_pointer_v = std::is_pointer_v<T> && !std::is_same_v<T, const char*>;
+
+template <typename T>
+constexpr inline bool is_multiple_pointer_v = std::is_pointer_v<T> && is_multiple_pointer_v<std::remove_pointer_t<T>>;
+
+template <typename T>
+constexpr auto type_name() {
+#if __GNUC__ || __clang__
+    std::string_view name = __PRETTY_FUNCTION__;
+    // format is "auto type_name() [T = int]"
+    std::size_t start = name.find("= ") + 2;
+    std::size_t end = name.rfind(']');
+    return name.substr(start, end - start);
+#elif _MSC_VER
+    std::string_view name = __FUNCSIG__;
+    // format is possible one of three following:
+    // - "auto __cdecl type_name<int>(void)"
+    // - "auto __cdecl type_name<struct X>(void)"
+    // - "auto __cdecl type_name<class X>(void)"
+    std::size_t start = name.find('<') + 1;
+    std::size_t end = name.rfind(">(");
+    name = name.substr(start, end - start);
+    auto space = name.find(' ');
+    return space == std::string_view::npos ? name : name.substr(space + 1);
+#else
+    static_assert(false, "current compiler is not supported");
+#endif
+}
+
+static_assert(type_name<int>() == "int" && type_name<double>() == "double",
+              "type_name() test failed, please report this issue");
+
 template <typename T, typename Tuple>
 struct tuple_push_front;
 
@@ -24,9 +98,9 @@ struct function_traits {
     static_assert(dependent_false<Fn>, "unsupported function type");
 };
 
-#define PYBIND11_FUNCTION_TRAITS_SPECIALIZE(qualifiers)                                                                \
+#define PYBIND11_FUNCTION_TRAITS_SPECIALIZE(...)                                                                       \
     template <typename R, typename... Args>                                                                            \
-    struct function_traits<R(Args...) qualifiers> {                                                                    \
+    struct function_traits<R(Args...) __VA_ARGS__> {                                                                   \
         using return_type = R;                                                                                         \
         using args_type = std::tuple<Args...>;                                                                         \
         constexpr static std::size_t args_count = sizeof...(Args);                                                     \
@@ -70,17 +144,6 @@ using class_type_t = typename member_traits<T>::class_type;
 
 // some traits for distinguishing between function pointers, member function pointers and
 // functors
-using std::is_member_function_pointer_v;
-using std::is_member_object_pointer_v;
-
-template <typename T>
-constexpr inline bool is_function_pointer_v = std::is_function_v<std::remove_pointer_t<T>>;
-
-template <typename T, typename U = void>
-constexpr bool is_functor_v = false;
-
-template <typename T>
-constexpr inline bool is_functor_v<T, std::void_t<decltype(&T::operator())>> = true;
 
 template <typename T, typename SFINAE = void>
 struct callable_traits;
@@ -93,8 +156,8 @@ struct callable_traits<T, std::enable_if_t<is_member_function_pointer_v<T>>> {
 
 template <typename T>
 struct callable_traits<T, std::enable_if_t<is_function_pointer_v<T>>> {
-    using args_type = function_args_t<std::remove_pointer<T>>;
-    using return_type = function_return_t<std::remove_pointer<T>>;
+    using args_type = function_args_t<std::remove_pointer_t<T>>;
+    using return_type = function_return_t<std::remove_pointer_t<T>>;
 };
 
 template <typename T>
@@ -112,46 +175,64 @@ using callable_return_t = typename callable_traits<Callable>::return_type;
 template <typename Callable>
 constexpr std::size_t callable_args_count_v = std::tuple_size_v<callable_args_t<Callable>>;
 
-template <typename T>
-struct type_identity {
-    using type = T;
+template <typename... Ts>
+struct type_list {
+    constexpr inline static int size = sizeof...(Ts);
+
+    template <typename T>
+    constexpr inline static int count = [] {
+        int count = 0;
+        ((count += std::is_same_v<T, Ts>), ...);
+        return count;
+    }();
+
+    /// find first index of T in type_list.
+    template <typename T>
+    constexpr inline static int find = [] {
+        bool arr[size + 1] = {std::is_same_v<T, Ts>...};
+        for(int i = 0; i < size; ++i) {
+            if(arr[i]) return i;
+        }
+        return -1;
+    }();
+
+    template <typename T>
+    constexpr inline static int find_last = [] {
+        bool arr[size + 1] = {std::is_same_v<T, Ts>...};
+        for(int i = size - 1; i >= 0; --i) {
+            if(arr[i]) return i;
+        }
+        return -1;
+    }();
 };
 
-template <typename T>
-using remove_cvref_t = std::remove_cv_t<std::remove_reference_t<T>>;
-
-template <typename T, typename... Ts>
-constexpr inline std::size_t types_count_v = (std::is_same_v<T, Ts> + ...);
-
-template <typename T>
-constexpr inline std::size_t types_count_v<T> = 0;
-
-template <typename T>
-struct value_wrapper {
-    T* pointer;
-
-    operator T& () { return *pointer; }
+template <typename... Args>
+struct overload_cast_t {
+    template <typename Return>
+    constexpr auto operator() (Return (*pf)(Args...)) const noexcept -> decltype(pf) {
+        return pf;
+    }
+
+    template <typename Return, typename Class>
+    constexpr auto operator() (Return (Class::* pmf)(Args...), std::false_type = {}) const noexcept -> decltype(pmf) {
+        return pmf;
+    }
+
+    template <typename Return, typename Class>
+    constexpr auto operator() (Return (Class::* pmf)(Args...) const, std::true_type) const noexcept -> decltype(pmf) {
+        return pmf;
+    }
 };
 
-template <typename T>
-struct value_wrapper<T*> {
-    T* pointer;
+/// Syntax sugar for resolving overloaded function pointers:
+///  - regular: static_cast<Return (Class::*)(Arg0, Arg1, Arg2)>(&Class::func)
+///  - sweet:   overload_cast<Arg0, Arg1, Arg2>(&Class::func)
+template <typename... Args>
+constexpr inline overload_cast_t<Args...> overload_cast;
 
-    operator T* () { return pointer; }
-};
-
-template <typename T>
-struct value_wrapper<T&> {
-    T* pointer;
-
-    operator T& () { return *pointer; }
-};
-
-template <typename T>
-struct value_wrapper<T&&> {
-    T* pointer;
-
-    operator T&& () { return std::move(*pointer); }
-};
+/// Const member function selector for overload_cast
+///  - regular: static_cast<Return (Class::*)(Arg) const>(&Class::func)
+///  - sweet:   overload_cast<Arg>(&Class::func, const_)
+constexpr inline auto const_ = std::true_type{};
 
-}  // namespace pybind11
+}  // namespace pkbind

+ 294 - 140
include/pybind11/internal/types.h

@@ -1,208 +1,362 @@
 #pragma once
 
-#include "object.h"
+#include "error.h"
+
+namespace pkbind {
+
+#define PKBIND_TYPE_IMPL(parent, child, expr)                                                      \
+                                                                                                   \
+private:                                                                                           \
+    friend class type;                                                                             \
+    static auto type_or_check() { return expr; }                                                   \
+                                                                                                   \
+public:                                                                                            \
+    using parent::parent;                                                                          \
+    using parent::operator=;                                                                       \
+    child(const object& o) : parent(type::isinstance<child>(o) ? o : type::of<child>()(o)) {}      \
+    child(object&& o) :                                                                            \
+        parent(type::isinstance<child>(o) ? std::move(o) : type::of<child>()(std::move(o))) {}
 
-namespace pybind11 {
 class type : public object {
-public:
-    using object::object;
+protected:
     template <typename T>
-    static handle handle_of();
-};
+    constexpr inline static bool is_check_v =
+        std::is_invocable_r_v<bool, decltype(T::type_or_check()), handle>;
+
+    static auto type_or_check() { return tp_type; }
 
-class iterable : public object {
 public:
-    using object::object;
-    iterable() = delete;
+    using object ::object;
+    using object ::operator=;
+
+    // note: type is global instance, so we use ref_t.
+    explicit type(py_Type type) : object(py_tpobject(type), ref_t{}) {}
+
+    py_Type index() const { return py_totype(ptr()); }
+
+    const char* name() const { return py_tpname(index()); }
+
+    static type of(handle h) { return type(py_typeof(h.ptr())); }
+
+    template <typename T>
+    static type of();
+
+    template <typename T>
+    static bool isinstance(handle obj);
 };
 
-class iterator : public object {
-public:
-    using object::object;
-    iterator() = delete;
+class none : public object {
+    PKBIND_TYPE_IMPL(object, none, tp_NoneType);
+
+    // note: none is global instance, so we use ref_t.
+    none() : object(py_None, ref_t{}) {}
 };
 
-class list : public object {
-public:
-    using object::object;
+class bool_ : public object {
+    PKBIND_TYPE_IMPL(object, bool_, tp_bool);
+
+    // same as none, bool is a singleton.
+    bool_(bool value) : object(value ? py_True : py_False, ref_t{}) {}
 
-    list() : object(vm->new_object<pkpy::List>(pkpy::VM::tp_list), true) {}
+    explicit operator bool () { return py_tobool(ptr()); }
 };
 
-class tuple : public object {
-public:
-    using object::object;
+class int_ : public object {
+    PKBIND_TYPE_IMPL(object, int_, tp_int);
 
-    tuple(int n) : object(vm->new_object<pkpy::Tuple>(pkpy::VM::tp_tuple, n), true) {}
+    int_(py_i64 value) : object(alloc_t{}) { py_newint(m_ptr, value); }
 
-    //& operator[](int i){ return _args[i]; }
-    // PyVar operator[](int i) const { return _args[i]; }
+    explicit operator py_i64 () { return py_toint(ptr()); }
 };
 
-class set : public object {
-public:
-    using object::object;
-    // set() : object(vm->new_object<pkpy::Se>(pkpy::VM::tp_set), true) {}
+class float_ : public object {
+    PKBIND_TYPE_IMPL(object, float_, tp_float);
+
+    float_(py_f64 value) : object(alloc_t{}) { py_newfloat(m_ptr, value); }
+
+    explicit operator py_f64 () { return py_tofloat(ptr()); }
 };
 
-class dict : public object {
-public:
-    using object::object;
+bool hasattr(handle obj, name name);
 
-    dict() : object(vm->new_object<pkpy::Dict>(pkpy::VM::tp_dict), true) {}
+class iterable : public object {
+    PKBIND_TYPE_IMPL(object, iterable, [](handle h) {
+        return hasattr(h, "__iter__");
+    });
 };
 
-class str : public object {
+class iterator : public object {
+    template <typename Dervied>
+    friend class interface;
+
+    iterator() : object(), m_value() {}
+
+    iterator(handle h) : object(h, realloc_t{}), m_value() { operator++ (); }
 
 public:
-    using object::object;
-    str(const char* c, int len) :
-        object(vm->new_object<pkpy::Str>(pkpy::VM::tp_str, c, len), true) {
+    PKBIND_TYPE_IMPL(object, iterator, [](handle h) {
+        return hasattr(h, "__iter__") && hasattr(h, "__next__");
+    });
+
+    iterator& operator++ () {
+        int result = raise_call<py_next>(m_ptr);
+        if(result == 1) {
+            m_value = object(retv, realloc_t{});
+        } else if(result == 0) {
+            m_value = object();
+        }
+        return *this;
+    }
 
-        };
+    object operator* () const { return m_value; }
 
-    str(const char* c = "") : str(c, strlen(c)) {}
+    friend bool operator== (const iterator& lhs, const iterator& rhs) {
+        return lhs.m_value.ptr() == rhs.m_value.ptr();
+    }
 
-    str(const std::string& s) : str(s.data(), s.size()) {}
+    friend bool operator!= (const iterator& lhs, const iterator& rhs) { return !(lhs == rhs); }
 
-    str(std::string_view sv) : str(sv.data(), sv.size()) {}
+    static iterator sentinel() { return iterator(); }
 
-    explicit str(const bytes& b);
-    explicit str(handle h);
-    operator std::string () const;
+private:
+    object m_value;
+};
+
+template <typename Dervied>
+iterator interface<Dervied>::begin() const {
+    raise_call<py_iter>(ptr());
+    return iterator(retv);
+}
+
+template <typename Dervied>
+iterator interface<Dervied>::end() const {
+    return iterator::sentinel();
+}
+
+class str : public object {
+    PKBIND_TYPE_IMPL(object, str, tp_str);
+
+    str(const char* data, int size) : object(alloc_t{}) { py_newstrn(m_ptr, data, size); }
+
+    str(const char* data) : str(data, static_cast<int>(strlen(data))) {}
+
+    str(std::string_view s) : str(s.data(), static_cast<int>(s.size())) {}
 
     template <typename... Args>
-    str format(Args&&... args) const;
+    object format(Args&&... args);
 };
 
-class int_ : public object {
-public:
-    using object::object;
+class tuple : public object {
+    PKBIND_TYPE_IMPL(object, tuple, tp_tuple);
 
-    int_(int64_t value) : object(pkpy::py_var(vm, value), true) {}
-};
+    tuple(int size) : object(alloc_t{}) { py_newtuple(m_ptr, size); }
 
-class float_ : public object {
-public:
-    using object::object;
+    tuple(std::initializer_list<handle> args) : tuple(static_cast<int>(args.size())) {
+        int index = 0;
+        for(auto& arg: args) {
+            py_tuple_setitem(m_ptr, index++, arg.ptr());
+        }
+    }
 
-    float_(double value) : object(pkpy::py_var(vm, value), true) {}
-};
+    tuple_accessor operator[] (int index) const;
 
-class bool_ : public object {
-public:
-    using object::object;
+    int size() const { return py_tuple_len(m_ptr); }
 
-    bool_(bool value) : object(pkpy::py_var(vm, value), true) {}
-};
+    bool empty() const { return size() == 0; }
 
-class function : public object {
-public:
-    using object::object;
+    class iterator {
+        friend tuple;
+
+        iterator(py_Ref ptr, int index) : ptr(ptr), index(index) {}
+
+    public:
+        bool operator== (const iterator& other) const { return index == other.index; }
+
+        bool operator!= (const iterator& other) const { return index != other.index; }
+
+        iterator& operator++ () {
+            index++;
+            return *this;
+        }
+
+        object operator* () const { return borrow(py_tuple_getitem(ptr, index)); }
+
+    private:
+        py_Ref ptr;
+        int index;
+    };
+
+    iterator begin() const { return iterator(m_ptr, 0); }
+
+    iterator end() const { return iterator(m_ptr, size()); }
 };
 
-class attr_accessor : public object {
-private:
-    object key;
+class list : public object {
+    PKBIND_TYPE_IMPL(object, list, tp_list);
 
-public:
-    template <typename T>
-    attr_accessor(const object& obj, T&& key) : object(obj), key(std::forward<T>(key)){};
+    list() : object(alloc_t{}) { py_newlist(m_ptr); }
 
-    template <typename T>
-    attr_accessor& operator= (T&& value) & {
-        static_assert(std::is_base_of_v<object, std::decay_t<T>>, "T must be derived from object");
-        m_ptr = std::forward<T>(value);
-        return *this;
-    }
+    list(int size) : object(alloc_t{}) { py_newlistn(m_ptr, size); }
 
-    template <typename T>
-    attr_accessor& operator= (T&& value) && {
-        static_assert(std::is_base_of_v<object, std::decay_t<T>>, "T must be derived from object");
-        setattr(*this, key, std::forward<T>(value));
-        return *this;
+    list(std::initializer_list<handle> args) : list(static_cast<int>(args.size())) {
+        int index = 0;
+        for(auto& arg: args) {
+            py_list_setitem(m_ptr, index++, arg.ptr());
+        }
     }
+
+    list_accessor operator[] (int index) const;
+
+    int size() const { return py_list_len(m_ptr); }
+
+    bool empty() const { return size() == 0; }
+
+    void swap(int i, int j) { py_list_swap(m_ptr, i, j); }
+
+    void append(handle item) { py_list_append(m_ptr, item.ptr()); }
+
+    void insert(int index, handle item) { py_list_insert(m_ptr, index, item.ptr()); }
+
+    void remove(int index) { py_list_delitem(m_ptr, index); }
+
+    void clear() { py_list_clear(m_ptr); }
+
+    class iterator {
+        friend list;
+
+        iterator(py_Ref ptr, int index) : ptr(ptr), index(index) {}
+
+    public:
+        iterator(const iterator&) = default;
+
+        bool operator== (const iterator& other) const { return index == other.index; }
+
+        bool operator!= (const iterator& other) const { return index != other.index; }
+
+        iterator operator++ () {
+            index++;
+            return *this;
+        }
+
+        object operator* () const { return borrow(py_list_getitem(ptr, index)); }
+
+    private:
+        py_Ref ptr;
+        int index;
+    };
+
+    iterator begin() const { return iterator(m_ptr, 0); }
+
+    iterator end() const { return iterator(m_ptr, size()); }
 };
 
-inline attr_accessor handle::attr(const char* name) const {
-    return attr_accessor(reinterpret_borrow<object>(*this), str(name));
-}
+class dict : public object {
+    PKBIND_TYPE_IMPL(object, dict, tp_dict);
 
-inline attr_accessor handle::attr(const handle& name) const {
-    return attr_accessor(reinterpret_borrow<object>(*this), reinterpret_borrow<object>(name));
-}
+    dict() : object(alloc_t{}) { py_newdict(m_ptr); }
 
-inline attr_accessor handle::attr(object&& name) const {
-    return attr_accessor(reinterpret_borrow<object>(*this), std::move(name));
-}
+    dict(std::initializer_list<arg_with_default> args);
 
-class item_accessor : public object {
-public:
-    object key;
+    dict_accessor<int> operator[] (int key) const;
 
-public:
-    template <typename T>
-    item_accessor(const object& obj, T&& key) : object(obj), key(std::forward<T>(key)){};
+    dict_accessor<name> operator[] (name key) const;
 
-    template <typename T>
-    item_accessor& operator= (T&& value) & {
-        static_assert(std::is_base_of_v<object, std::decay_t<T>>, "T must be derived from object");
-        m_ptr = std::forward<T>(value);
-    }
+    dict_accessor<handle> operator[] (handle key) const;
 
-    template <typename T>
-    item_accessor& operator= (object&& value) && {
-        static_assert(std::is_base_of_v<object, std::decay_t<T>>, "T must be derived from object");
-        setitem(*this, key, std::forward<T>(value));
+    int size() const { return py_dict_len(m_ptr); }
+
+    bool empty() const { return size() == 0; }
+
+    template <typename Fn>
+    void apply(Fn&& fn) {
+        static_assert(std::is_invocable_v<Fn, py_Ref, py_Ref>);
+        using ret = std::invoke_result_t<Fn, py_Ref, py_Ref>;
+        auto callback = +[](py_Ref key, py_Ref value, void* data) -> bool {
+            auto fn = reinterpret_cast<Fn*>(data);
+            if constexpr(std::is_same_v<ret, void>) {
+                (*fn)(key, value);
+                return true;
+            } else {
+                return (*fn)(key, value);
+            }
+        };
+        raise_call<py_dict_apply>(m_ptr, callback, &fn);
     }
-};
 
-inline item_accessor handle::operator[] (int64_t key) const {
-    return item_accessor(reinterpret_borrow<object>(*this), int_(key));
-}
+    class iterator {
+        friend dict;
 
-inline item_accessor handle::operator[] (const char* key) const {
-    return item_accessor(reinterpret_borrow<object>(*this), str(key));
-}
+        iterator() : iter(pkbind::iterator::sentinel()) {}
 
-inline item_accessor handle::operator[] (const handle& key) const {
-    return item_accessor(reinterpret_borrow<object>(*this), reinterpret_borrow<object>(key));
-}
+        iterator(handle ptr);
 
-inline item_accessor handle::operator[] (object&& key) const {
-    return item_accessor(reinterpret_borrow<object>(*this), std::move(key));
-}
+    public:
+        iterator(const iterator&) = default;
+
+        bool operator== (const iterator& other) const { return iter == other.iter; }
+
+        bool operator!= (const iterator& other) const { return !(*this == other); }
+
+        iterator operator++ () {
+            ++iter;
+            return *this;
+        }
+
+        std::pair<object, object> operator* () const;
+
+    private:
+        object items;
+        pkbind::iterator iter;
+    };
+
+    iterator begin() const { return iterator(m_ptr); }
+
+    iterator end() const { return iterator(); }
+};
+
+class slice : public object {
+    PKBIND_TYPE_IMPL(object, slice, tp_slice);
+};
+
+class set : public object {};
 
 class args : public tuple {
-    using tuple::tuple;
+    PKBIND_TYPE_IMPL(tuple, args, tp_tuple);
 };
 
 class kwargs : public dict {
-    using dict::dict;
+    PKBIND_TYPE_IMPL(dict, kwargs, tp_dict);
 };
 
-template <typename T>
-handle type::handle_of() {
-    if constexpr(std::is_same_v<T, object>) { return vm->_t(vm->tp_object); }
-#define PYBIND11_TYPE_MAPPER(type, tp)                                                                                 \
-    else if constexpr(std::is_same_v<T, type>) { return vm->_t(vm->tp); }
-    PYBIND11_TYPE_MAPPER(type, tp_type)
-    PYBIND11_TYPE_MAPPER(str, tp_str)
-    PYBIND11_TYPE_MAPPER(int_, tp_int)
-    PYBIND11_TYPE_MAPPER(float_, tp_float)
-    PYBIND11_TYPE_MAPPER(bool_, tp_bool)
-    PYBIND11_TYPE_MAPPER(list, tp_list)
-    PYBIND11_TYPE_MAPPER(tuple, tp_tuple)
-    PYBIND11_TYPE_MAPPER(args, tp_tuple)
-    PYBIND11_TYPE_MAPPER(dict, tp_dict)
-    PYBIND11_TYPE_MAPPER(kwargs, tp_dict)
-#undef PYBIND11_TYPE_MAPPER
-    else {
-        auto result = vm->_cxx_typeid_map.find(typeid(T));
-        if(result != vm->_cxx_typeid_map.end()) { return vm->_t(result->second); }
-
-        vm->TypeError("Type not registered");
+// TODO:
+class capsule : public object {
+    struct capsule_impl {
+        void* data;
+        void (*destructor)(void*);
+    };
+
+    inline static py_Type m_type = 0;
+
+    PKBIND_TYPE_IMPL(object, capsule, m_type);
+
+    static void register_() {
+        m_type = py_newtype("capsule", tp_object, nullptr, [](void* data) {
+            auto impl = static_cast<capsule_impl*>(data);
+            if(impl->data && impl->destructor) { impl->destructor(impl->data); }
+        });
     }
-}
 
-}  // namespace pybind11
+    capsule(void* data, void (*destructor)(void*) = nullptr) : object(alloc_t{}) {
+        void* impl = py_newobject(m_ptr, m_type, 0, sizeof(capsule_impl));
+        new (impl) capsule_impl{data, destructor};
+    }
+
+    void* data() { return static_cast<capsule_impl*>(py_touserdata(m_ptr))->data; }
+
+    template <typename T>
+    T& cast() {
+        return *static_cast<T*>(data());
+    }
+};
+
+}  // namespace pkbind

+ 204 - 0
include/pybind11/operators.h

@@ -0,0 +1,204 @@
+#pragma once
+
+#include "pybind11.h"
+
+namespace pkbind::impl {
+
+enum op_id : int {
+    op_add,
+    op_sub,
+    op_mul,
+    op_div,
+    op_mod,
+    op_divmod,
+    op_pow,
+    op_lshift,
+    op_rshift,
+    op_and,
+    op_xor,
+    op_or,
+    op_neg,
+    op_pos,
+    op_abs,
+    op_invert,
+    op_int,
+    op_long,
+    op_float,
+    op_str,
+    op_cmp,
+    op_gt,
+    op_ge,
+    op_lt,
+    op_le,
+    op_eq,
+    op_ne,
+    op_iadd,
+    op_isub,
+    op_imul,
+    op_idiv,
+    op_imod,
+    op_ilshift,
+    op_irshift,
+    op_iand,
+    op_ixor,
+    op_ior,
+    op_complex,
+    op_bool,
+    op_nonzero,
+    op_repr,
+    op_truediv,
+    op_itruediv,
+    op_hash
+};
+
+enum op_type : int {
+    op_l, /* base type on left */
+    op_r, /* base type on right */
+    op_u  /* unary operator */
+};
+
+struct self_t {};
+
+const static self_t self = self_t();
+
+/// Type for an unused type slot
+struct undefined_t {};
+
+/// Don't warn about an unused variable
+inline self_t __self() { return self; }
+
+/// base template of operator implementations
+template <op_id, op_type, typename B, typename L, typename R>
+struct op_impl {};
+
+/// Operator implementation generator
+template <op_id id, op_type ot, typename L, typename R>
+struct op_ {
+    constexpr static bool op_enable_if_hook = true;
+
+    template <typename Class, typename... Extra>
+    void execute(Class& cl, const Extra&... extra) const {
+        using Base = typename Class::underlying_type;
+        using L_type = std::conditional_t<std::is_same<L, self_t>::value, Base, L>;
+        using R_type = std::conditional_t<std::is_same<R, self_t>::value, Base, R>;
+        using op = op_impl<id, ot, Base, L_type, R_type>;
+        cl.def(op::name(), &op::execute, extra...);
+    }
+
+    template <typename Class, typename... Extra>
+    void execute_cast(Class& cl, const Extra&... extra) const {
+        using Base = typename Class::type;
+        using L_type = std::conditional_t<std::is_same<L, self_t>::value, Base, L>;
+        using R_type = std::conditional_t<std::is_same<R, self_t>::value, Base, R>;
+        using op = op_impl<id, ot, Base, L_type, R_type>;
+        cl.def(op::name(), &op::execute_cast, extra...);
+    }
+};
+
+#define PKBIND_BINARY_OPERATOR(id, rid, op, expr)                                                                      \
+    template <typename B, typename L, typename R>                                                                      \
+    struct op_impl<op_##id, op_l, B, L, R> {                                                                           \
+        static char const* name() { return "__" #id "__"; }                                                            \
+        static auto execute(const L& l, const R& r) -> decltype(expr) { return (expr); }                               \
+        static B execute_cast(const L& l, const R& r) { return B(expr); }                                              \
+    };                                                                                                                 \
+                                                                                                                       \
+    template <typename B, typename L, typename R>                                                                      \
+    struct op_impl<op_##id, op_r, B, L, R> {                                                                           \
+        static char const* name() { return "__" #rid "__"; }                                                           \
+        static auto execute(const R& r, const L& l) -> decltype(expr) { return (expr); }                               \
+        static B execute_cast(const R& r, const L& l) { return B(expr); }                                              \
+    };                                                                                                                 \
+                                                                                                                       \
+    inline op_<op_##id, op_l, self_t, self_t> op(const self_t&, const self_t&) {                                       \
+        return op_<op_##id, op_l, self_t, self_t>();                                                                   \
+    }                                                                                                                  \
+                                                                                                                       \
+    template <typename T>                                                                                              \
+    op_<op_##id, op_l, self_t, T> op(const self_t&, const T&) {                                                        \
+        return op_<op_##id, op_l, self_t, T>();                                                                        \
+    }                                                                                                                  \
+                                                                                                                       \
+    template <typename T>                                                                                              \
+    op_<op_##id, op_r, T, self_t> op(const T&, const self_t&) {                                                        \
+        return op_<op_##id, op_r, T, self_t>();                                                                        \
+    }
+
+#define PKBIND_INPLACE_OPERATOR(id, op, expr)                                                                          \
+    template <typename B, typename L, typename R>                                                                      \
+    struct op_impl<op_##id, op_l, B, L, R> {                                                                           \
+        static char const* name() { return "__" #id "__"; }                                                            \
+        static auto execute(L& l, const R& r) -> decltype(expr) { return expr; }                                       \
+        static B execute_cast(L& l, const R& r) { return B(expr); }                                                    \
+    };                                                                                                                 \
+                                                                                                                       \
+    template <typename T>                                                                                              \
+    op_<op_##id, op_l, self_t, T> op(const self_t&, const T&) {                                                        \
+        return op_<op_##id, op_l, self_t, T>();                                                                        \
+    }
+
+#define PKBIND_UNARY_OPERATOR(id, op, expr)                                                                            \
+    template <typename B, typename L>                                                                                  \
+    struct op_impl<op_##id, op_u, B, L, undefined_t> {                                                                 \
+        static char const* name() { return "__" #id "__"; }                                                            \
+        static auto execute(const L& l) -> decltype(expr) { return expr; }                                             \
+        static B execute_cast(const L& l) { return B(expr); }                                                          \
+    };                                                                                                                 \
+                                                                                                                       \
+    inline op_<op_##id, op_u, self_t, undefined_t> op(const self_t&) {                                                 \
+        return op_<op_##id, op_u, self_t, undefined_t>();                                                              \
+    }
+
+PKBIND_BINARY_OPERATOR(sub, rsub, operator-, l - r)
+PKBIND_BINARY_OPERATOR(add, radd, operator+, l + r)
+PKBIND_BINARY_OPERATOR(mul, rmul, operator*, l* r)
+PKBIND_BINARY_OPERATOR(truediv, rtruediv, operator/, l / r)
+PKBIND_BINARY_OPERATOR(mod, rmod, operator%, l % r)
+PKBIND_BINARY_OPERATOR(lshift, rlshift, operator<<, l << r)
+PKBIND_BINARY_OPERATOR(rshift, rrshift, operator>>, l >> r)
+PKBIND_BINARY_OPERATOR(and, rand, operator&, l& r)
+PKBIND_BINARY_OPERATOR(xor, rxor, operator^, l ^ r)
+PKBIND_BINARY_OPERATOR(eq, eq, operator==, l == r)
+PKBIND_BINARY_OPERATOR(ne, ne, operator!=, l != r)
+PKBIND_BINARY_OPERATOR(or, ror, operator|, l | r)
+PKBIND_BINARY_OPERATOR(gt, lt, operator>, l > r)
+PKBIND_BINARY_OPERATOR(ge, le, operator>=, l >= r)
+PKBIND_BINARY_OPERATOR(lt, gt, operator<, l < r)
+PKBIND_BINARY_OPERATOR(le, ge, operator<=, l <= r)
+// PKBIND_BINARY_OPERATOR(pow,       rpow,         pow,          std::pow(l,  r))
+PKBIND_INPLACE_OPERATOR(iadd, operator+=, l += r)
+PKBIND_INPLACE_OPERATOR(isub, operator-=, l -= r)
+PKBIND_INPLACE_OPERATOR(imul, operator*=, l *= r)
+PKBIND_INPLACE_OPERATOR(itruediv, operator/=, l /= r)
+PKBIND_INPLACE_OPERATOR(imod, operator%=, l %= r)
+PKBIND_INPLACE_OPERATOR(ilshift, operator<<=, l <<= r)
+PKBIND_INPLACE_OPERATOR(irshift, operator>>=, l >>= r)
+PKBIND_INPLACE_OPERATOR(iand, operator&=, l &= r)
+PKBIND_INPLACE_OPERATOR(ixor, operator^=, l ^= r)
+PKBIND_INPLACE_OPERATOR(ior, operator|=, l |= r)
+
+PKBIND_UNARY_OPERATOR(neg, operator-, -l)
+PKBIND_UNARY_OPERATOR(pos, operator+, +l)
+
+// WARNING: This usage of `abs` should only be done for existing STL overloads.
+// Adding overloads directly in to the `std::` namespace is advised against:
+// https://en.cppreference.com/w/cpp/language/extending_std
+
+// PKBIND_UNARY_OPERATOR(abs, abs, std::abs(l))
+// PKBIND_UNARY_OPERATOR(hash, hash, std::hash<L>()(l))
+// PKBIND_UNARY_OPERATOR(invert, operator~, (~l))
+// PKBIND_UNARY_OPERATOR(bool, operator!, !!l)
+// PKBIND_UNARY_OPERATOR(int, int_, (int)l)
+// PKBIND_UNARY_OPERATOR(float, float_, (double)l)
+
+#undef PKBIND_BINARY_OPERATOR
+#undef PKBIND_INPLACE_OPERATOR
+#undef PKBIND_UNARY_OPERATOR
+
+}  // namespace pkbind::impl
+
+namespace pkbind {
+
+using impl::self;
+
+}  // namespace pkbind

+ 3 - 0
include/pybind11/pkbind.h

@@ -0,0 +1,3 @@
+#pragma once
+
+#include "pybind11.h"

+ 62 - 0
include/pybind11/pybind11.h

@@ -1,3 +1,65 @@
 #pragma once
 
 #include "internal/class.h"
+
+namespace pkbind {
+
+namespace literals {
+inline arg operator""_a (const char* c, size_t) { return arg(c); }
+}  // namespace literals
+
+inline bool initialized = false;
+
+/// initialize the vm.
+inline void initialize(int object_pool_size = 1024) {
+    if(!initialized) { py_initialize(); }
+
+    // initialize all registers.
+    reg<0>.value = py_getreg(0);
+    reg<1>.value = py_getreg(1);
+    reg<2>.value = py_getreg(2);
+    reg<3>.value = py_getreg(3);
+    reg<4>.value = py_getreg(4);
+    reg<5>.value = py_getreg(5);
+    reg<6>.value = py_getreg(6);
+
+    // initialize ret.
+    retv.value = py_retval();
+
+    // initialize object pool.
+    object_pool::initialize(object_pool_size);
+
+    m_type_map = new std::unordered_map<std::type_index, py_Type>();
+
+    // register types.
+    capsule::register_();
+    cpp_function::register_();
+
+    action::initialize();
+    initialized = true;
+}
+
+/// finalize the vm.
+inline void finalize(bool test = false) {
+    if(!initialized) { return; }
+    delete m_type_map;
+    m_type_map = nullptr;
+    object_pool::finalize();
+    if(test) {
+        py_resetvm();
+    } else {
+        py_finalize();
+    }
+}
+
+/// a RAII class to initialize and finalize python interpreter
+class scoped_interpreter {
+public:
+    scoped_interpreter(int object_pool_size = 1024) { initialize(object_pool_size); }
+
+    ~scoped_interpreter() { finalize(); }
+};
+
+}  // namespace pkbind
+
+namespace pybind11 = pkbind;

+ 170 - 0
include/pybind11/stl.h

@@ -0,0 +1,170 @@
+#include "pybind11.h"
+
+#include <array>
+#include <vector>
+#include <list>
+#include <deque>
+#include <forward_list>
+
+#include <map>
+#include <unordered_map>
+
+namespace pkbind {
+
+template <typename T, std::size_t N>
+struct type_caster<std::array<T, N>> {
+    std::array<T, N> data;
+
+    template <typename U>
+    static handle cast(U&& src, return_value_policy policy, handle parent) {
+        auto list = pkbind::list();
+        for(auto&& item: src) {
+            list.append(pkbind::cast(std::move(item), policy, parent));
+        }
+        return list;
+    }
+
+    bool load(handle src, bool convert) {
+        if(!isinstance<list>(src)) { return false; }
+        auto list = src.cast<pkbind::list>();
+
+        if(list.size() != N) { return false; }
+
+        for(int i = 0; i < N; ++i) {
+            type_caster<T> caster;
+            if(!caster.load(list[i], convert)) { return false; }
+            data[i] = std::move(caster.value());
+        }
+
+        return true;
+    }
+
+    std::array<T, N>& value() { return data; }
+
+    constexpr inline static bool is_temporary_v = true;
+};
+
+template <typename T>
+constexpr bool is_py_list_like_v = false;
+
+template <typename T, typename Allocator>
+constexpr bool is_py_list_like_v<std::vector<T, Allocator>> = true;
+
+template <typename T, typename Allocator>
+constexpr bool is_py_list_like_v<std::list<T, Allocator>> = true;
+
+template <typename T, typename Allocator>
+constexpr bool is_py_list_like_v<std::deque<T, Allocator>> = true;
+
+template <>
+struct type_caster<std::vector<bool>> {
+    std::vector<bool> data;
+
+    template <typename U>
+    static object cast(U&& src, return_value_policy policy, handle parent) {
+        auto list = pkbind::list();
+        for(auto&& item: src) {
+            list.append(pkbind::cast(bool(item), policy, parent));
+        }
+        return list;
+    }
+
+    bool load(handle src, bool convert) {
+        if(!isinstance<list>(src)) { return false; }
+
+        auto list = src.cast<pkbind::list>();
+        data.clear();
+        data.reserve(list.size());
+
+        for(auto item: list) {
+            type_caster<bool> caster;
+            if(!caster.load(item, convert)) { return false; }
+            data.push_back(caster.value());
+        }
+
+        return true;
+    }
+
+    std::vector<bool>& value() { return data; }
+
+    constexpr inline static bool is_temporary_v = true;
+};
+
+template <typename T>
+struct type_caster<T, std::enable_if_t<is_py_list_like_v<T>>> {
+    T data;
+
+    template <typename U>
+    static object cast(U&& src, return_value_policy policy, handle parent) {
+        auto list = pkbind::list();
+        for(auto&& item: src) {
+            list.append(pkbind::cast(std::move(item), policy, parent));
+        }
+        return list;
+    }
+
+    bool load(handle src, bool convert) {
+        if(!isinstance<list>(src)) { return false; }
+
+        auto list = src.cast<pkbind::list>();
+
+        for(auto item: list) {
+            type_caster<typename T::value_type> caster;
+            if(!caster.load(item, convert)) { return false; }
+            data.push_back(std::move(caster.value()));
+        }
+
+        return true;
+    }
+
+    T& value() { return data; }
+
+    constexpr inline static bool is_temporary_v = true;
+};
+
+template <typename T>
+constexpr bool is_py_map_like_v = false;
+
+template <typename Key, typename T, typename Compare, typename Allocator>
+constexpr bool is_py_map_like_v<std::map<Key, T, Compare, Allocator>> = true;
+
+template <typename Key, typename T, typename Hash, typename KeyEqual, typename Allocator>
+constexpr bool is_py_map_like_v<std::unordered_map<Key, T, Hash, KeyEqual, Allocator>> = true;
+
+template <typename T>
+struct type_caster<T, std::enable_if_t<is_py_map_like_v<T>>> {
+    T data;
+
+    template <typename U>
+    static object cast(U&& src, return_value_policy policy, handle parent) {
+        auto dict = pkbind::dict();
+        for(auto&& [key, value]: src) {
+            dict[pkbind::cast(std::move(key), policy, parent)] =
+                pkbind::cast(std::move(value), policy, parent);
+        }
+        return dict;
+    }
+
+    bool load(handle src, bool convert) {
+        if(!isinstance<dict>(src)) { return false; }
+        auto dict = src.cast<pkbind::dict>();
+
+        for(auto item: dict) {
+            type_caster<typename T::key_type> key_caster;
+            if(!key_caster.load(item.first, convert)) { return false; }
+
+            type_caster<typename T::mapped_type> value_caster;
+            if(!value_caster.load(item.second, convert)) { return false; }
+
+            data.try_emplace(std::move(key_caster.value()), std::move(value_caster.value()));
+        }
+
+        return true;
+    }
+
+    T& value() { return data; }
+
+    constexpr inline static bool is_temporary_v = true;
+};
+
+}  // namespace pkbind

+ 26 - 0
include/pybind11/tests/CMakeLists.txt

@@ -0,0 +1,26 @@
+cmake_minimum_required(VERSION 3.10)
+project(PKBIND_TEST)
+
+set(CMAKE_CXX_STANDARD 17)
+set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
+
+add_subdirectory("${CMAKE_CURRENT_SOURCE_DIR}/../../.." "${CMAKE_CURRENT_BINARY_DIR}/pocketpy")
+
+include(FetchContent)
+FetchContent_Declare(
+  googletest
+  URL https://github.com/google/googletest/archive/03597a01ee50ed33e9dfd640b249b4be3799d395.zip
+  DOWNLOAD_EXTRACT_TIMESTAMP true
+)
+
+if(WIN32)
+    set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)
+endif()
+
+FetchContent_MakeAvailable(googletest)
+
+file(GLOB CPP_SOURCES "${CMAKE_CURRENT_SOURCE_DIR}/*.cpp")
+add_executable(PKBIND_TEST ${CPP_SOURCES})
+
+target_include_directories(PKBIND_TEST PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/../..")
+target_link_libraries(PKBIND_TEST PRIVATE pocketpy gtest_main)

+ 121 - 0
include/pybind11/tests/builtins.cpp

@@ -0,0 +1,121 @@
+#include "test.h"
+
+namespace {
+
+int copy_constructor_calls = 0;
+int move_constructor_calls = 0;
+int destructor_calls = 0;
+
+struct Point {
+    int x;
+    int y;
+
+    Point(int x, int y) : x(x), y(y) {}
+
+    Point(const Point& other) : x(other.x), y(other.y) { ++copy_constructor_calls; }
+
+    Point(Point&& other) : x(other.x), y(other.y) { ++move_constructor_calls; }
+
+    ~Point() { ++destructor_calls; }
+
+    bool operator== (const Point& p) const { return x == p.x && y == p.y; }
+};
+
+TEST_F(PYBIND11_TEST, exec_and_eval) {
+    auto m = py::module::__main__();
+
+    py::dict locals = {py::arg("x") = 1, py::arg("y") = 2};
+    py::object obj = py::eval("x + y", py::none{}, locals);
+    EXPECT_EQ(obj.cast<int>(), 3);
+
+    py::exec("x = 1 + 2");
+    EXPECT_EQ(py::eval("x").cast<int>(), 3);
+
+    py::exec("y = 1 + 2", m.attr("__dict__"));
+    EXPECT_EQ(py::eval("y", m.attr("__dict__")).cast<int>(), 3);
+
+    EXPECT_EQ(locals["x"].cast<int>(), 1);
+    EXPECT_EQ(locals["y"].cast<int>(), 2);
+}
+
+TEST_F(PYBIND11_TEST, locals_and_globals) {
+    py::exec("x = 1");
+
+    auto globals = py::globals();
+    EXPECT_EQ(globals["x"].cast<int>(), 1);
+
+    globals["y"] = 2;
+    EXPECT_EQ(py::eval("y").cast<int>(), 2);
+}
+
+TEST_F(PYBIND11_TEST, cast) {
+    auto m = py::module::__main__();
+
+    py::class_<Point>(m, "Point")
+        .def(py::init<int, int>())
+        .def_readwrite("x", &Point::x)
+        .def_readwrite("y", &Point::y)
+        .def("__eq__", &Point::operator==);
+
+    Point p(1, 2);
+
+    // for py::cast's default policy
+
+    // if argument is lvalue, policy is copy
+    py::object o = py::cast(p);
+    EXPECT_EQ(py::cast<Point&>(o), p);
+    EXPECT_EQ(copy_constructor_calls, 1);
+    EXPECT_EQ(move_constructor_calls, 0);
+
+    // if argument is rvalue, policy is move
+    py::object o2 = py::cast(std::move(p));
+    EXPECT_EQ(py::cast<Point&>(o2), p);
+    EXPECT_EQ(copy_constructor_calls, 1);
+    EXPECT_EQ(move_constructor_calls, 1);
+
+    // if argument is pointer, policy is reference(no taking ownership)
+    py::object o3 = py::cast(&p);
+    EXPECT_EQ(py::cast<Point&>(o3), p);
+    EXPECT_EQ(copy_constructor_calls, 1);
+    EXPECT_EQ(move_constructor_calls, 1);
+
+    py::finalize(true);
+
+    EXPECT_EQ(destructor_calls, 2);
+}
+
+TEST_F(PYBIND11_TEST, cpp_call_py) {
+    auto m = py::module::__main__();
+
+    py::exec(R"(
+class Test:
+    def __init__(self, x, y):
+        self.x = x
+        self.y = y
+
+    def sum1(self, z):
+        return self.x + self.y + z
+    
+    def sum2(self, z, *args):
+        return self.x + self.y + z + sum(args)
+    
+    def sum3(self, z, n=0):
+        return self.x + self.y + z + n
+
+    def sum4(self, z, *args, **kwargs):
+        return self.x + self.y + z + sum(args) + kwargs['a'] + kwargs['b']
+)");
+
+    auto obj = py::eval("Test(1, 2)");
+
+    EXPECT_EQ(obj.attr("sum1")(3).cast<int>(), 6);
+    EXPECT_EQ(obj.attr("sum2")(3, 4, 5).cast<int>(), 15);
+
+    using namespace py::literals;
+
+    EXPECT_EQ(obj.attr("sum3")(3, "n"_a = 4).cast<int>(), 10);
+    EXPECT_EQ(obj.attr("sum4")(3, 4, 5, "a"_a = 6, "b"_a = 7).cast<int>(), 28);
+}
+
+}  // namespace
+

+ 225 - 0
include/pybind11/tests/class.cpp

@@ -0,0 +1,225 @@
+#include "test.h"
+
+namespace {
+
+struct Point {
+    int x;
+    int y;
+
+private:
+    int z;
+
+public:
+    inline static int constructor_calls = 0;
+    inline static int copy_constructor_calls = 0;
+    inline static int move_constructor_calls = 0;
+    inline static int destructor_calls = 0;
+
+    Point() : x(0), y(0), z(0) { constructor_calls++; }
+
+    Point(int x, int y, int z) : x(x), y(y), z(z) { constructor_calls++; }
+
+    Point(const Point& p) : x(p.x), y(p.y), z(p.z) {
+        copy_constructor_calls++;
+        constructor_calls++;
+    }
+
+    Point(Point&& p) noexcept : x(p.x), y(p.y), z(p.z) {
+        move_constructor_calls++;
+        constructor_calls++;
+    }
+
+    Point& operator= (const Point& p) {
+        x = p.x;
+        y = p.y;
+        z = p.z;
+        return *this;
+    }
+
+    ~Point() { destructor_calls++; }
+
+    int& get_z() { return z; }
+
+    void set_z(int z) { this->z = z; }
+
+    std::string stringfy() const {
+        return "(" + std::to_string(x) + ", " + std::to_string(y) + ", " + std::to_string(z) + ")";
+    }
+};
+
+struct Line {
+    Point start;
+    Point end;
+};
+
+TEST_F(PYBIND11_TEST, class) {
+    py::module_ m = py::module_::import("__main__");
+    py::class_<Point>(m, "Point")
+        .def(py::init<>())
+        .def(py::init<int, int, int>())
+        .def_readwrite("x", &Point::x)
+        .def_readwrite("y", &Point::y)
+        .def_property("z", &Point::get_z, &Point::set_z)
+        .def("stringfy", &Point::stringfy);
+
+    py::exec(R"(
+p = Point()
+assert p.stringfy() == '(0, 0, 0)'
+p = Point(1, 2, 3)
+assert p.x == 1
+assert p.y == 2
+assert p.z == 3
+assert p.stringfy() == '(1, 2, 3)'
+p.x = 10
+p.y = 20
+p.z = 30
+assert p.stringfy() == '(10, 20, 30)'
+)");
+
+    py::class_<Line> line(m, "Line");
+
+    line  // not bind constructor
+        .def_readwrite("start", &Line::start)
+        .def_readwrite("end", &Line::end);
+
+    // bind constructor
+    line.def(py::init<>());
+
+    py::exec(R"(
+l = Line()
+l.start = Point(1, 2, 3)
+l.end = Point(4, 5, 6)
+p = l.start
+assert l.start.stringfy() == '(1, 2, 3)'
+assert l.end.stringfy() == '(4, 5, 6)'
+)");
+
+    py::finalize(true);
+
+    EXPECT_EQ(Point::constructor_calls, Point::destructor_calls);
+    EXPECT_EQ(Point::copy_constructor_calls, 0);
+    EXPECT_EQ(Point::move_constructor_calls, 0);
+}
+
+TEST_F(PYBIND11_TEST, inheritance) {
+    static int constructor_calls = 0;
+
+    struct Point {
+        int x;
+        int y;
+
+        Point() : x(0), y(0) { constructor_calls++; }
+
+        Point(int x, int y) : x(x), y(y) { constructor_calls++; }
+    };
+
+    struct Point3D : Point {
+        int z;
+
+        Point3D() : Point(), z(0) { constructor_calls++; }
+
+        Point3D(int x, int y, int z) : Point(x, y), z(z) { constructor_calls++; }
+    };
+
+    py::module_ m = py::module_::import("__main__");
+
+    py::class_<Point>(m, "Point")
+        .def(py::init<>())
+        .def(py::init<int, int>())
+        .def_readwrite("x", &Point::x)
+        .def_readwrite("y", &Point::y);
+
+    py::class_<Point3D, Point>(m, "Point3D")
+        .def(py::init<>())
+        .def(py::init<int, int, int>())
+        .def_readwrite("z", &Point3D::z);
+
+    py::exec(R"(
+p = Point3D()
+assert type(p) == Point3D
+assert p.x == 0
+assert p.y == 0
+assert p.z == 0
+
+p = Point3D(1, 2, 3)
+assert p.x == 1
+assert p.y == 2
+assert p.z == 3
+
+p.x = 10
+p.y = 20
+p.z = 30
+assert p.x == 10
+assert p.y == 20
+assert p.z == 30
+)");
+
+    py::finalize(true);
+
+    EXPECT_EQ(constructor_calls, 4);
+}
+
+TEST_F(PYBIND11_TEST, dynamic_attr) {
+    py::module_ m = py::module_::import("__main__");
+
+    struct Point {
+        int x;
+        int y;
+
+        Point(int x, int y) : x(x), y(y) {}
+    };
+
+    py::class_<Point>(m, "Point", py::dynamic_attr())
+        .def(py::init<int, int>())
+        .def_readwrite("x", &Point::x)
+        .def_readwrite("y", &Point::y);
+
+    py::object p = py::eval("Point(1, 2)");
+    EXPECT_EQ(p.attr("x").cast<int>(), 1);
+    EXPECT_EQ(p.attr("y").cast<int>(), 2);
+
+    p.attr("z") = py::int_(3);
+    EXPECT_EQ(p.attr("z").cast<int>(), 3);
+}
+
+TEST_F(PYBIND11_TEST, enum) {
+    enum class Color { RED, Yellow, GREEN, BLUE };
+
+    py::module_ m = py::module_::import("__main__");
+
+    py::enum_<Color> color(m, "Color");
+
+    color.value("RED", Color::RED)
+        .value("Yellow", Color::Yellow)
+        .value("GREEN", Color::GREEN)
+        .value("BLUE", Color::BLUE);
+
+    EXPECT_EVAL_EQ("Color.RED", Color::RED);
+    EXPECT_EVAL_EQ("Color.Yellow", Color::Yellow);
+    EXPECT_EVAL_EQ("Color.GREEN", Color::GREEN);
+    EXPECT_EVAL_EQ("Color.BLUE", Color::BLUE);
+
+    EXPECT_EVAL_EQ("Color(0)", Color::RED);
+    EXPECT_EVAL_EQ("Color(1)", Color::Yellow);
+    EXPECT_EVAL_EQ("Color(2)", Color::GREEN);
+    EXPECT_EVAL_EQ("Color(3)", Color::BLUE);
+
+    EXPECT_EXEC_EQ("Color(0)", "Color.RED");
+    EXPECT_EXEC_EQ("Color(1)", "Color.Yellow");
+    EXPECT_EXEC_EQ("Color(2)", "Color.GREEN");
+    EXPECT_EXEC_EQ("Color(3)", "Color.BLUE");
+
+    EXPECT_EVAL_EQ("Color.RED.value", static_cast<int>(Color::RED));
+    EXPECT_EVAL_EQ("Color.Yellow.value", static_cast<int>(Color::Yellow));
+    EXPECT_EVAL_EQ("Color.GREEN.value", static_cast<int>(Color::GREEN));
+    EXPECT_EVAL_EQ("Color.BLUE.value", static_cast<int>(Color::BLUE));
+
+    color.export_values();
+    EXPECT_EVAL_EQ("RED", Color::RED);
+    EXPECT_EVAL_EQ("Yellow", Color::Yellow);
+    EXPECT_EVAL_EQ("GREEN", Color::GREEN);
+    EXPECT_EVAL_EQ("BLUE", Color::BLUE);
+}
+
+}  // namespace
+

+ 50 - 0
include/pybind11/tests/error.cpp

@@ -0,0 +1,50 @@
+#include "test.h"
+
+TEST_F(PYBIND11_TEST, exception_python_to_cpp) {
+    auto test = [](const char* code, py_Type type) {
+        try {
+            py::exec(code);
+        } catch(py::python_error& e) {
+            EXPECT_TRUE(e.match(type));
+        }
+    };
+
+    test("raise ValueError()", tp_ValueError);
+    test("raise KeyError()", tp_KeyError);
+    test("raise IndexError()", tp_IndexError);
+    test("raise StopIteration()", tp_StopIteration);
+    test("raise Exception()", tp_Exception);
+}
+
+TEST_F(PYBIND11_TEST, exception_cpp_to_python) {
+    auto m = py::module_::__main__();
+
+#define TEST_EXCEPTION(cppe, pye)                                                                                      \
+    m.def("test_" #cppe, []() {                                                                                        \
+        throw cppe(#cppe);                                                                                             \
+    });                                                                                                                \
+    py::exec("try:\n    test_" #cppe "()\nexcept Exception as e:\n    assert isinstance(e, " #pye                      \
+             ")\n    assert str(e) == '" #cppe "'")
+
+    using namespace std;
+    TEST_EXCEPTION(runtime_error, RuntimeError);
+    TEST_EXCEPTION(domain_error, ValueError);
+    TEST_EXCEPTION(invalid_argument, ValueError);
+    TEST_EXCEPTION(length_error, ValueError);
+    TEST_EXCEPTION(out_of_range, IndexError);
+    TEST_EXCEPTION(range_error, ValueError);
+
+    using namespace py;
+
+    m.def("test_stop_iteration", []() {
+        throw py::stop_iteration();
+    });
+    py::exec("try:\n    test_stop_iteration()\nexcept StopIteration as e:\n    pass");
+    TEST_EXCEPTION(index_error, IndexError);
+    // FIXME: TEST_EXCEPTION(key_error, KeyError);
+    TEST_EXCEPTION(value_error, ValueError);
+    TEST_EXCEPTION(type_error, TypeError);
+    TEST_EXCEPTION(import_error, ImportError);
+    TEST_EXCEPTION(attribute_error, AttributeError);
+    TEST_EXCEPTION(runtime_error, RuntimeError);
+}

+ 402 - 0
include/pybind11/tests/function.cpp

@@ -0,0 +1,402 @@
+#include "test.h"
+
+namespace {
+
+TEST_F(PYBIND11_TEST, vectorcall) {
+    auto m = py::module_::__main__();
+
+    py::exec(R"(
+def add(a, b):
+    return a + b
+)");
+    // position only
+    EXPECT_CAST_EQ(m.attr("add")(1, 2), 3);
+    // FIXME: pkpy does not support such calling.
+    // keyword only
+    // EXPECT_CAST_EQ(m.attr("add")(py::arg("a") = 1, py::arg("b") = 2), 3);
+    // mix
+    // EXPECT_CAST_EQ(m.attr("add")(1, py::arg("b") = 2), 3);
+
+    py::exec(R"(
+def add2(a, *args):
+    return a + sum(args)
+)");
+    EXPECT_CAST_EQ(m.attr("add2")(1, 2, 3, 4), 10);
+
+    EXPECT_EQ(py::type::of<py::tuple>()(py::eval("[1, 2, 3]")), py::eval("(1, 2, 3)"));
+    EXPECT_EQ(py::type::of<py::list>()(py::eval("(1, 2, 3)")), py::eval("[1, 2, 3]"));
+}
+
+TEST_F(PYBIND11_TEST, constructor) {
+    auto m = py::module_::__main__();
+
+    struct Point {
+        int x, y;
+
+        Point(int x, int y) : x(x), y(y) {}
+
+        bool operator== (const Point& p) const { return x == p.x && y == p.y; }
+    };
+
+    py::class_<Point>(m, "Point")
+        .def(py::init<int, int>(), py::arg("x") = 1, py::arg("y") = 2)
+        .def(py::init([](py::tuple tuple) {
+            return Point(tuple[0].cast<int>(), tuple[1].cast<int>());
+        }))
+        .def("__eq__", &Point::operator==);
+
+    EXPECT_EVAL_EQ("Point()", Point(1, 2));
+    EXPECT_EVAL_EQ("Point(3)", Point(3, 2));
+    EXPECT_EVAL_EQ("Point(3, 4)", Point(3, 4));
+    EXPECT_EVAL_EQ("Point((3, 4))", Point(3, 4));
+}
+
+TEST_F(PYBIND11_TEST, args) {
+    auto m = py::module_::__main__();
+
+    // test for binding function with args
+    m.def("sum", [](py::args args) {
+        int sum = 0;
+        for(auto arg: args) {
+            sum += arg.cast<int>();
+        }
+        return sum;
+    });
+
+    EXPECT_EVAL_EQ("sum(1, 2, 3)", 6);
+}
+
+TEST_F(PYBIND11_TEST, kwargs) {
+    auto m = py::module_::__main__();
+
+    // test for binding function with kwargs
+    m.def("cal", [](py::kwargs kwargs) {
+        int sum = kwargs["a"].cast<int>() + kwargs["b"].cast<int>() * kwargs["c"].cast<int>();
+        return sum;
+    });
+
+    EXPECT_EVAL_EQ("cal(a=1, b=2, c=3)", 7);
+}
+
+TEST_F(PYBIND11_TEST, defaults) {
+    auto m = py::module_::__main__();
+
+    // test for binding function with defaults
+    m.def(
+        "cal",
+        [](int a, int b = 2, int c = 3) {
+            return a + b * c;
+        },
+        py::arg("a"),
+        py::arg("b") = 2,
+        py::arg("c") = 3);
+
+    EXPECT_EVAL_EQ("cal(1)", 7);             // a = 1, b = 2, c = 3
+    EXPECT_EVAL_EQ("cal(1, 4)", 13);         // a = 1, b = 4, c = 3
+    EXPECT_EVAL_EQ("cal(1, 4, 5)", 21);      // a = 1, b = 4, c = 5
+    EXPECT_EVAL_EQ("cal(2, c=6)", 14);       // a = 2, b = 2, c = 6
+    EXPECT_EVAL_EQ("cal(2, b=4, c=6)", 26);  // a = 2, b = 4, c = 6
+}
+
+TEST_F(PYBIND11_TEST, defaults_with_args) {
+    auto m = py::module_::__main__();
+
+    // test for binding function with defaults
+    m.def(
+        "cal",
+        [](int a, int b, int c, py::args args) {
+            int sum = a + b + c;
+            for(auto arg: args) {
+                sum += arg.cast<int>();
+            }
+            return sum;
+        },
+        py::arg("a"),
+        py::arg("b") = 2,
+        py::arg("c") = 3);
+
+    EXPECT_EVAL_EQ("cal(1)", 6);               // a = 1, b = 2, c = 3
+    EXPECT_EVAL_EQ("cal(1, 4)", 8);            // a = 1, b = 4, c = 3
+    EXPECT_EVAL_EQ("cal(1, 4, 5)", 10);        // a = 1, b = 4, c = 5
+    EXPECT_EVAL_EQ("cal(1, 4, 5, 6)", 16);     // a = 1, b = 4, c = 5, args = (6)
+    EXPECT_EVAL_EQ("cal(1, 4, 5, 6, 7)", 23);  // a = 1, b = 4, c = 5, args = (6, 7)
+}
+
+TEST_F(PYBIND11_TEST, default_with_args_and_kwargs) {
+    auto m = py::module_::__main__();
+
+    // test for binding function with defaults
+    m.def(
+        "cal",
+        [](int a, int b, int c, py::args args, py::kwargs kwargs) {
+            int sum = a + b + c;
+            for(auto arg: args) {
+                sum += arg.cast<int>();
+            }
+            for(auto item: kwargs) {
+                sum += item.second.cast<int>();
+            }
+            return sum;
+        },
+        py::arg("a"),
+        py::arg("b") = 2,
+        py::arg("c") = 3);
+
+    EXPECT_EVAL_EQ("cal(1)", 6);               // a = 1, b = 2, c = 3
+    EXPECT_EVAL_EQ("cal(1, 4)", 8);            // a = 1, b = 4, c = 3
+    EXPECT_EVAL_EQ("cal(1, 4, 5)", 10);        // a = 1, b = 4, c = 5
+    EXPECT_EVAL_EQ("cal(1, 4, 5, 6)", 16);     // a = 1, b = 4, c = 5, args = (6)
+    EXPECT_EVAL_EQ("cal(1, 4, 5, 6, 7)", 23);  // a = 1, b = 4, c = 5, args = (6, 7)
+
+    EXPECT_EVAL_EQ("cal(1, 4, 5, d=6, e=7)", 23);  // a = 1, b = 4, c = 5, kwargs = {d=6, e=7}
+    EXPECT_EVAL_EQ("cal(1, d=6, e=7)", 19);        // a = 1, b = 2, c = 3, kwargs = {d=6, e=7}
+
+    EXPECT_EVAL_EQ("cal(1)", 6);       // a = 1, b = 2, c = 3
+    EXPECT_EVAL_EQ("cal(1, b=4)", 8);  // a = 1, b = 4, c = 3
+    EXPECT_EVAL_EQ("cal(1, c=5)", 8);  // a = 1, b = 2, c = 5
+
+    EXPECT_EVAL_EQ("cal(1, 4, d=6)", 14);     // a = 1, b = 4, c = 3, kwargs = {d=6}
+    EXPECT_EVAL_EQ("cal(1, c=5, d=6)", 14);   // a = 1, b = 2, c = 5, kwargs = {d=6}
+    EXPECT_EVAL_EQ("cal(1, 4, 5, d=6)", 16);  // a = 1, b = 4, c = 5, kwargs = {d=6}
+
+    EXPECT_EVAL_EQ("cal(1, 4, 5)", 10);             // a = 1, b = 4, c = 5, args = (), kwargs = {}
+    EXPECT_EVAL_EQ("cal(1, 4, 5, *[], **{})", 10);  // a = 1, b = 4, c = 5, args = (), kwargs = {}
+
+    EXPECT_EVAL_EQ("cal(1, 4, 5, 6, 7, d=8, e=9)", 40);  // a = 1, b = 4, c = 5, args = (6, 7), kwargs = {d=8, e=9}
+
+    struct Point {
+        int x, y;
+
+        Point(int x, int y) : x(x), y(y) {}
+
+        int sum(int x = 1, int y = 2) { return this->x + this->y + x + y; }
+    };
+
+    py::class_<Point>(m, "Point")
+        .def(py::init<int, int>())  //
+        .def("sum", &Point::sum, py::arg("x") = 1, py::arg("y") = 2);
+
+    EXPECT_EVAL_EQ("Point(1, 2).sum()", 6);       // x = 1, y = 2
+    EXPECT_EVAL_EQ("Point(1, 2).sum(3)", 8);      // x = 3, y = 2
+    EXPECT_EVAL_EQ("Point(1, 2).sum(3, 4)", 10);  // x = 3, y = 4
+}
+
+TEST_F(PYBIND11_TEST, overload) {
+    auto m = py::module_::__main__();
+
+    // test for binding function with overloads
+    m.def("cal", [](int a, int b) {
+        return a + b;
+    });
+
+    m.def("cal", [](int a, int b, int c) {
+        return a + b + c;
+    });
+
+    EXPECT_EVAL_EQ("cal(1, 2)", 3);
+    EXPECT_EVAL_EQ("cal(1, 2, 3)", 6);
+}
+
+TEST_F(PYBIND11_TEST, return_value_policy) {
+    static int copy_constructor_calls = 0;
+    static int move_constructor_calls = 0;
+    static int destructor_calls = 0;
+
+    struct Point {
+        int x, y;
+
+        Point(int x, int y) : x(x), y(y) {}
+
+        Point(const Point& p) : x(p.x), y(p.y) { copy_constructor_calls++; }
+
+        Point(Point&& p) : x(p.x), y(p.y) { move_constructor_calls++; }
+
+        ~Point() { destructor_calls++; }
+
+        static Point& make_point() {
+            static Point p(1, 2);
+            return p;
+        }
+
+        static Point& new_point() { return *new Point(1, 2); }
+
+        bool operator== (const Point& p) const { return x == p.x && y == p.y; }
+    };
+
+    py::finalize(true);
+
+    auto test = [](py::return_value_policy policy, auto bound_fn, auto fn) {
+        py::initialize();
+        copy_constructor_calls = 0;
+        move_constructor_calls = 0;
+        destructor_calls = 0;
+
+        auto m = py::module_::__main__();
+
+        py::class_<Point>(m, "Point")
+            .def(py::init<int, int>())
+            .def_readwrite("x", &Point::x)
+            .def_readwrite("y", &Point::y)
+            .def("__eq__", &Point::operator==);
+
+        m.def("make_point", bound_fn, policy);
+
+        EXPECT_EVAL_EQ("make_point()", Point::make_point());
+
+        py::finalize(true);
+
+        fn();
+    };
+
+    test(py::return_value_policy::reference, &Point::make_point, []() {
+        EXPECT_EQ(copy_constructor_calls, 0);
+        EXPECT_EQ(move_constructor_calls, 0);
+        EXPECT_EQ(destructor_calls, 0);
+    });
+
+    test(py::return_value_policy::copy, &Point::make_point, []() {
+        EXPECT_EQ(copy_constructor_calls, 1);
+        EXPECT_EQ(move_constructor_calls, 0);
+        EXPECT_EQ(destructor_calls, 1);
+    });
+
+    test(py::return_value_policy::move, &Point::make_point, []() {
+        EXPECT_EQ(copy_constructor_calls, 0);
+        EXPECT_EQ(move_constructor_calls, 1);
+        EXPECT_EQ(destructor_calls, 1);
+    });
+
+    test(py::return_value_policy::take_ownership, &Point::new_point, []() {
+        EXPECT_EQ(copy_constructor_calls, 0);
+        EXPECT_EQ(move_constructor_calls, 0);
+        EXPECT_EQ(destructor_calls, 1);
+    });
+}
+
+TEST_F(PYBIND11_TEST, default_return_value_policy) {
+    static int copy_constructor_calls = 0;
+    static int move_constructor_calls = 0;
+    static int destructor_calls = 0;
+
+    struct Point {
+        int x, y;
+
+        Point(int x, int y) : x(x), y(y) {}
+
+        Point(const Point& p) : x(p.x), y(p.y) { copy_constructor_calls++; }
+
+        Point(Point&& p) : x(p.x), y(p.y) { move_constructor_calls++; }
+
+        ~Point() { destructor_calls++; }
+
+        bool operator== (const Point& p) const { return x == p.x && y == p.y; }
+    };
+
+    auto m = py::module_::__main__();
+
+    py::class_<Point>(m, "Point")
+        .def(py::init<int, int>())
+        .def_readwrite("x", &Point::x)
+        .def_readwrite("y", &Point::y)
+        .def("__eq__", &Point::operator==);
+
+    // for function return value policy
+
+    // if return type is lvalue reference, policy is copy
+    m.def("make_point2", []() -> Point& {
+        static Point p(1, 2);
+        return p;
+    });
+    py::exec("p2 = make_point2()");
+    EXPECT_EQ(copy_constructor_calls, 1);
+    EXPECT_EQ(move_constructor_calls, 0);
+
+    // if return type is rvalue reference, policy is move
+    m.def("make_point3", []() -> Point&& {
+        static Point p(1, 2);
+        return std::move(p);
+    });
+    py::exec("p3 = make_point3()");
+    EXPECT_EQ(copy_constructor_calls, 1);
+    EXPECT_EQ(move_constructor_calls, 1);
+
+    // if return type is pointer, policy is take_ownership
+    m.def("make_point4", []() -> Point* {
+        return new Point(1, 2);
+    });
+    py::exec("p4 = make_point4()");
+    EXPECT_EQ(copy_constructor_calls, 1);
+    EXPECT_EQ(move_constructor_calls, 1);
+
+    py::finalize(true);
+    EXPECT_EQ(destructor_calls, 3);
+}
+
+TEST_F(PYBIND11_TEST, lambda) {
+    auto m = py::module_::__main__();
+
+    static int destructor_calls = 0;
+
+    struct NotTrivial {
+        int data;
+
+        int operator() (int x, int y) { return x + y + data; }
+
+        ~NotTrivial() { destructor_calls++; }
+    };
+
+    struct NotSmall {
+        size_t a;
+        size_t b;
+        size_t c;
+        size_t d;
+
+        size_t operator() (size_t x, size_t y) { return x + y + a + b + c + d; }
+
+        ~NotSmall() { destructor_calls++; }
+    };
+
+    // test for binding lambda
+    m.def("cal", NotTrivial{3});
+    m.def("cal2", NotSmall{3, 4, 5, 6});
+
+    EXPECT_EVAL_EQ("cal(1, 2)", 6);
+    EXPECT_EVAL_EQ("cal2(1, 2)", 21);
+
+    py::finalize(true);
+
+    EXPECT_EQ(destructor_calls, 4);
+}
+
+int add(int a, int b) { return a + b; }
+
+int add(int a, int b, int c) { return a + b + c; }
+
+TEST_F(PYBIND11_TEST, overload_cast) {
+    auto m = py::module_::__main__();
+
+    m.def("add", py::overload_cast<int, int>(add));
+    m.def("add", py::overload_cast<int, int, int>(add));
+
+    EXPECT_EVAL_EQ("add(1, 2)", 3);
+    EXPECT_EVAL_EQ("add(1, 2, 3)", 6);
+
+    struct X {
+        X() {}
+
+        int add(int a, int b) { return a + b; }
+
+        int add(int a, int b, int c) { return a + b + c; }
+    };
+
+    py::class_<X>(m, "X")
+        .def(py::init<>())
+        .def("add", py::overload_cast<int, int>(&X::add))
+        .def("add", py::overload_cast<int, int, int>(&X::add));
+
+    EXPECT_EVAL_EQ("X().add(1, 2)", 3);
+    EXPECT_EVAL_EQ("X().add(1, 2, 3)", 6);
+}
+
+}  // namespace
+

+ 31 - 0
include/pybind11/tests/module.cpp

@@ -0,0 +1,31 @@
+#include "test.h"
+
+PYBIND11_EMBEDDED_MODULE(example, m) {
+    m.def("add", [](int a, int b) {
+        return a + b;
+    });
+
+    auto math = m.def_submodule("math");
+    math.def("sub", [](int a, int b) {
+        return a - b;
+    });
+}
+
+namespace {
+
+TEST_F(PYBIND11_TEST, module) {
+    py::exec("import example");
+    EXPECT_EVAL_EQ("example.add(1, 2)", 3);
+
+    py::exec("from example import math");
+    EXPECT_EVAL_EQ("math.sub(1, 2)", -1);
+
+    py::exec("from example.math import sub");
+    EXPECT_EVAL_EQ("sub(1, 2)", -1);
+
+    auto math = py::module_::import("example.math");
+    EXPECT_EQ(math.attr("sub")(4, 3).cast<int>(), 1);
+}
+
+}  // namespace
+

+ 98 - 0
include/pybind11/tests/object.cpp

@@ -0,0 +1,98 @@
+#include "test.h"
+
+namespace {
+
+const char* source = R"(
+class Point:
+    def __init__(self, x, y):
+        self.x = x
+        self.y = y
+
+    def __add__(self, other):
+        return Point(self.x + other.x, self.y + other.y)
+
+    def __sub__(self, other):
+        return Point(self.x - other.x, self.y - other.y)
+
+    def __mul__(self, other):
+        return Point(self.x * other.x, self.y * other.y)
+
+    def __truediv__(self, other):
+        return Point(self.x / other.x, self.y / other.y)
+
+    def __floordiv__(self, other):
+        return Point(self.x // other.x, self.y // other.y)
+
+    def __mod__(self, other):
+        return Point(self.x % other.x, self.y % other.y)
+
+    def __pow__(self, other):
+        return Point(self.x ** other.x, self.y ** other.y)
+
+    def __lshift__(self, other):
+        return Point(self.x << other.x, self.y << other.y)
+
+    def __rshift__(self, other):
+        return Point(self.x >> other.x, self.y >> other.y)
+
+    def __eq__(self, other):
+        return self.x == other.x and self.y == other.y
+
+    def __ne__(self, other) -> bool:
+        return not self.__eq__(other)
+
+    def __lt__(self, other) -> bool:
+        return self.x < other.x and self.y < other.y
+
+    def __le__(self, other) -> bool:
+        return self.x <= other.x and self.y <= other.y
+
+    def __gt__(self, other) -> bool:
+        return self.x > other.x and self.y > other.y
+
+    def __ge__(self, other) -> bool:
+        return self.x >= other.x and self.y >= other.y
+
+    def __repr__(self):
+        return f'Point({self.x}, {self.y})'
+)";
+
+TEST_F(PYBIND11_TEST, object) {
+    py::module_ m = py::module_::import("__main__");
+    py::exec(source);
+    py::exec("p = Point(3, 4)");
+    py::object p = py::eval("p");
+
+    //  is
+    EXPECT_FALSE(p.is_none());
+    EXPECT_TRUE(p.is(p));
+
+    //  attrs
+    EXPECT_EQ(p.attr("x").cast<int>(), 3);
+    EXPECT_EQ(p.attr("y").cast<int>(), 4);
+
+    p.attr("x") = py::int_(5);
+    p.attr("y") = py::int_(6);
+
+    EXPECT_EQ(p.attr("x").cast<int>(), 5);
+    EXPECT_EQ(p.attr("y").cast<int>(), 6);
+    EXPECT_EXEC_EQ("p", "Point(5, 6)");
+
+    //  operators
+    EXPECT_EVAL_EQ("Point(10, 12)", p + p);
+    EXPECT_EVAL_EQ("Point(0, 0)", p - p);
+    EXPECT_EVAL_EQ("Point(25, 36)", p * p);
+    EXPECT_EVAL_EQ("Point(1, 1)", p / p);
+    // EXPECT_EVAL_EQ("Point(0, 0)", p // p);
+    EXPECT_EVAL_EQ("Point(0, 0)", p % p);
+
+    // iterators
+    py::object l = py::eval("[1, 2, 3]");
+    int index = 0;
+    for(auto item: l) {
+        EXPECT_EQ(item.cast<int>(), index + 1);
+        index++;
+    }
+}
+
+}  // namespace

+ 150 - 0
include/pybind11/tests/operators.cpp

@@ -0,0 +1,150 @@
+#include "test.h"
+#include <pybind11/operators.h>
+
+namespace {
+
+struct Int {
+    int x;
+
+    Int(int x) : x(x) {}
+
+#define OPERATOR_IMPL(op)                                                                                              \
+    template <typename LHS, typename RHS>                                                                              \
+    friend int operator op (const LHS& lhs, const RHS& rhs) {                                                          \
+        int l, r;                                                                                                      \
+        if constexpr(std::is_same_v<LHS, Int>)                                                                         \
+            l = lhs.x;                                                                                                 \
+        else                                                                                                           \
+            l = lhs;                                                                                                   \
+        if constexpr(std::is_same_v<RHS, Int>)                                                                         \
+            r = rhs.x;                                                                                                 \
+        else                                                                                                           \
+            r = rhs;                                                                                                   \
+        return l op r;                                                                                                 \
+    }
+
+    OPERATOR_IMPL(+)
+    OPERATOR_IMPL(-)
+    OPERATOR_IMPL(*)
+    OPERATOR_IMPL(/)
+    OPERATOR_IMPL(%)
+    OPERATOR_IMPL(&)
+    OPERATOR_IMPL(|)
+    OPERATOR_IMPL(^)
+    OPERATOR_IMPL(<<)
+    OPERATOR_IMPL(>>)
+
+#undef OPERATOR_IMPL
+
+    bool operator== (const Int& other) const { return x == other.x; }
+
+    bool operator!= (const Int& other) const { return x != other.x; }
+
+    bool operator< (const Int& other) const { return x < other.x; }
+
+    bool operator<= (const Int& other) const { return x <= other.x; }
+
+    bool operator> (const Int& other) const { return x > other.x; }
+
+    bool operator>= (const Int& other) const { return x >= other.x; }
+
+    bool operator!() const { return !x; }
+};
+
+}  // namespace
+
+TEST_F(PYBIND11_TEST, arithmetic_operators) {
+    py::module_ m = py::module_::import("__main__");
+    py::class_<Int>(m, "Int")
+        .def(py::init<int>())
+        .def(py::self + py::self)
+        .def(py::self + int())
+        .def(int() + py::self)
+        .def(py::self - py::self)
+        .def(py::self - int())
+        .def(int() - py::self)
+        .def(py::self * py::self)
+        .def(py::self * int())
+        .def(int() * py::self)
+        .def(py::self / py::self)
+        .def(py::self / int())
+        .def(int() / py::self)
+        .def(py::self % py::self)
+        .def(py::self % int())
+        .def(int() % py::self)
+        .def(py::self & py::self)
+        .def(py::self & int())
+        .def(int() & py::self)
+        .def(py::self | py::self)
+        .def(py::self | int())
+        .def(int() | py::self)
+        .def(py::self ^ py::self)
+        .def(py::self ^ int())
+        .def(int() ^ py::self)
+        .def(py::self << py::self)
+        .def(py::self << int())
+        .def(int() << py::self)
+        .def(py::self >> py::self)
+        .def(py::self >> int())
+        .def(int() >> py::self);
+
+    auto a = py::cast(Int(1));
+    auto ai = py::cast(1);
+    auto b = py::cast(Int(2));
+    auto bi = py::cast(2);
+
+    EXPECT_CAST_EQ(a + b, 3);
+    EXPECT_CAST_EQ(a + bi, 3);
+    EXPECT_CAST_EQ(ai + b, 3);
+    EXPECT_CAST_EQ(a - b, -1);
+    EXPECT_CAST_EQ(a - bi, -1);
+    EXPECT_CAST_EQ(ai - b, -1);
+    EXPECT_CAST_EQ(a * b, 2);
+    EXPECT_CAST_EQ(a * bi, 2);
+    EXPECT_CAST_EQ(ai * b, 2);
+    EXPECT_CAST_EQ(a / b, 0);
+    EXPECT_CAST_EQ(a / bi, 0);
+    // EXPECT_CAST_EQ(ai / b, 0);
+    EXPECT_CAST_EQ(a % b, 1);
+    EXPECT_CAST_EQ(a % bi, 1);
+    // EXPECT_CAST_EQ(ai % b, 1);
+    EXPECT_CAST_EQ(a & b, 0);
+    EXPECT_CAST_EQ(a & bi, 0);
+    // EXPECT_CAST_EQ(ai & b, 0);
+    EXPECT_CAST_EQ(a | b, 3);
+    EXPECT_CAST_EQ(a | bi, 3);
+    // EXPECT_CAST_EQ(ai | b, 3);
+    EXPECT_CAST_EQ(a ^ b, 3);
+    EXPECT_CAST_EQ(a ^ bi, 3);
+    // EXPECT_CAST_EQ(ai ^ b, 3);
+    EXPECT_CAST_EQ(a << b, 4);
+    EXPECT_CAST_EQ(a << bi, 4);
+    // EXPECT_CAST_EQ(ai << b, 4);
+    EXPECT_CAST_EQ(a >> b, 0);
+    EXPECT_CAST_EQ(a >> bi, 0);
+    // EXPECT_CAST_EQ(ai >> b, 0);
+}
+
+TEST_F(PYBIND11_TEST, logic_operators) {
+    py::module_ m = py::module_::import("__main__");
+    py::class_<Int>(m, "Int")
+        .def(py::init<int>())
+        .def_readwrite("x", &Int::x)
+        .def(py::self == py::self)
+        .def(py::self != py::self)
+        .def(py::self < py::self)
+        .def(py::self <= py::self)
+        .def(py::self > py::self)
+        .def(py::self >= py::self);
+
+    auto a = py::cast(Int(1));
+    auto b = py::cast(Int(2));
+
+    EXPECT_FALSE(a == b);
+    EXPECT_TRUE(a != b);
+    EXPECT_TRUE(a < b);
+    EXPECT_TRUE(a <= b);
+    EXPECT_FALSE(a > b);
+    EXPECT_FALSE(a >= b);
+}
+

+ 125 - 0
include/pybind11/tests/stl.cpp

@@ -0,0 +1,125 @@
+#include "test.h"
+#include <pybind11/stl.h>
+
+namespace {
+
+int constructor_calls = 0;
+int destructor_calls = 0;
+
+struct Point {
+    int x;
+    int y;
+
+    Point() : x(0), y(0) { constructor_calls++; }
+
+    Point(int x, int y) : x(x), y(y) { constructor_calls++; }
+
+    Point(const Point& p) : x(p.x), y(p.y) { constructor_calls++; }
+
+    Point(Point&& p) noexcept : x(p.x), y(p.y) { constructor_calls++; }
+
+    Point& operator= (const Point& p) {
+        x = p.x;
+        y = p.y;
+        return *this;
+    }
+
+    ~Point() { destructor_calls++; }
+
+    bool operator== (const Point& p) const { return x == p.x && y == p.y; }
+};
+
+}  // namespace
+
+TEST_F(PYBIND11_TEST, vector_bool) {
+    std::vector<bool> v = {true, false, true};
+    py::object obj = py::cast(v);
+    EXPECT_EVAL_EQ("[True, False, True]", obj);
+
+    std::vector<bool> v2 = obj.cast<std::vector<bool>>();
+    EXPECT_EQ(v, v2);
+}
+
+TEST_F(PYBIND11_TEST, list_like) {
+    py::class_<Point>(py::module_::__main__(), "Point")
+        .def(py::init<int, int>())
+        .def_readwrite("x", &Point::x)
+        .def_readwrite("y", &Point::y)
+        .def("__eq__", &Point::operator==);
+
+    // array
+    {
+        std::array<Point, 2> a = {Point(1, 2), Point(3, 4)};
+        py::object obj = py::eval("[Point(1, 2), Point(3, 4)]");
+        EXPECT_EVAL_EQ("[Point(1, 2), Point(3, 4)]", obj);
+
+        std::array<Point, 2> a2 = obj.cast<std::array<Point, 2>>();
+        EXPECT_EQ(a, a2);
+    }
+
+    // vector
+    {
+        std::vector<Point> v = {Point(1, 2), Point(3, 4)};
+        py::object obj = py::cast(v);
+        EXPECT_EVAL_EQ("[Point(1, 2), Point(3, 4)]", obj);
+
+        std::vector<Point> v2 = obj.cast<std::vector<Point>>();
+        EXPECT_EQ(v, v2);
+    }
+
+    // list
+    {
+        std::list<Point> l = {Point(1, 2), Point(3, 4)};
+        py::object obj = py::cast(l);
+        EXPECT_EVAL_EQ("[Point(1, 2), Point(3, 4)]", obj);
+
+        std::list<Point> l2 = obj.cast<std::list<Point>>();
+        EXPECT_EQ(l, l2);
+    }
+
+    // deque
+    {
+        std::deque<Point> d = {Point(1, 2), Point(3, 4)};
+        py::object obj = py::cast(d);
+        EXPECT_EVAL_EQ("[Point(1, 2), Point(3, 4)]", obj);
+
+        std::deque<Point> d2 = obj.cast<std::deque<Point>>();
+        EXPECT_EQ(d, d2);
+    }
+}
+
+TEST_F(PYBIND11_TEST, dict_like) {
+    py::class_<Point>(py::module_::__main__(), "Point")
+        .def(py::init<int, int>())
+        .def_readwrite("x", &Point::x)
+        .def_readwrite("y", &Point::y)
+        .def("__eq__", &Point::operator==);
+
+    // map
+    {
+        std::map<std::string, Point> m = {
+            {"a", Point(1, 2)},
+            {"b", Point(3, 4)}
+        };
+
+        py::object obj = py::cast(m);
+        EXPECT_EVAL_EQ("{'a': Point(1, 2), 'b': Point(3, 4)}", obj);
+
+        std::map<std::string, Point> m2 = obj.cast<std::map<std::string, Point>>();
+        EXPECT_EQ(m, m2);
+    }
+
+    // unordered_map
+    {
+        std::unordered_map<std::string, Point> m = {
+            {"a", Point(1, 2)},
+            {"b", Point(3, 4)}
+        };
+
+        py::object obj = py::cast(m);
+        EXPECT_EVAL_EQ("{'a': Point(1, 2), 'b': Point(3, 4)}", obj);
+
+        std::unordered_map<std::string, Point> m2 = obj.cast<std::unordered_map<std::string, Point>>();
+        EXPECT_EQ(m, m2);
+    }
+}

+ 16 - 0
include/pybind11/tests/test.h

@@ -0,0 +1,16 @@
+#include <gtest/gtest.h>
+#include <pybind11/pkbind.h>
+
+namespace py = pkbind;
+
+class PYBIND11_TEST : public ::testing::Test {
+protected:
+    void SetUp() override { py::initialize(); }
+
+    void TearDown() override { py::finalize(true); }
+};
+
+#define EXPECT_CAST_EQ(expr, expected) EXPECT_EQ(py::cast(expr), py::cast(expected))
+#define EXPECT_EVAL_EQ(expr, expected) EXPECT_EQ(py::eval(expr).cast<decltype(expected)>(), expected)
+#define EXPECT_EXEC_EQ(expr, expected) EXPECT_EQ(py::eval(expr), py::eval(expected))
+

+ 169 - 0
include/pybind11/tests/types.cpp

@@ -0,0 +1,169 @@
+#include "test.h"
+
+TEST_F(PYBIND11_TEST, int) {
+    py::object obj = py::int_(123);
+    py::object obj2 = py::eval("123");
+
+    EXPECT_EQ(obj, obj2);
+
+    EXPECT_EQ(obj.cast<int>(), 123);
+    EXPECT_EQ(obj.cast<long>(), 123);
+    EXPECT_EQ(obj.cast<long long>(), 123);
+}
+
+TEST_F(PYBIND11_TEST, float) {
+    py::object obj = py::float_(123.0);
+    py::object obj2 = py::eval("123.0");
+
+    EXPECT_EQ(obj, obj2);
+
+    EXPECT_EQ(obj.cast<float>(), 123.0);
+    EXPECT_EQ(obj.cast<double>(), 123.0);
+}
+
+TEST_F(PYBIND11_TEST, str) {
+    py::object obj = py::str("123");
+    py::object obj2 = py::eval("'123'");
+
+    EXPECT_EQ(obj, obj2);
+
+    EXPECT_STREQ(obj.cast<const char*>(), "123");
+    EXPECT_EQ(obj.cast<std::string>(), "123");
+    EXPECT_EQ(obj.cast<std::string_view>(), "123");
+
+    auto s = py::str("Hello, {}");
+    EXPECT_EQ(s.format("world").cast<std::string>(), "Hello, world");
+}
+
+TEST_F(PYBIND11_TEST, tuple) {
+    py::tuple tuple = py::tuple{
+        py::int_(1),
+        py::str("123"),
+        py::int_(3),
+    };
+    EXPECT_EQ(tuple, py::eval("(1, '123', 3)"));
+    EXPECT_EQ(tuple.size(), 3);
+    EXPECT_FALSE(tuple.empty());
+
+    tuple[0] = py::int_(3);
+    tuple[2] = py::int_(1);
+    EXPECT_EQ(tuple, py::eval("(3, '123', 1)"));
+
+    // iterators.
+    int index = 0;
+    for(auto item: tuple) {
+        if(index == 0) {
+            EXPECT_EQ(item, py::int_(3));
+        } else if(index == 1) {
+            EXPECT_EQ(item, py::str("123"));
+        } else if(index == 2) {
+            EXPECT_EQ(item, py::int_(1));
+        }
+        index++;
+    }
+}
+
+TEST_F(PYBIND11_TEST, list) {
+    // constructors
+    py::list list = py::list();
+    EXPECT_EQ(list, py::eval("[]"));
+    EXPECT_EQ(list.size(), 0);
+    EXPECT_TRUE(list.empty());
+
+    list = py::list{
+        py::int_(1),
+        py::int_(2),
+        py::int_(3),
+    };
+    EXPECT_EQ(list, py::eval("[1, 2, 3]"));
+    EXPECT_EQ(list.size(), 3);
+    EXPECT_FALSE(list.empty());
+
+    // accessor
+    list[0] = py::int_(3);
+    list[2] = py::int_(1);
+    EXPECT_EQ(list, py::eval("[3, 2, 1]"));
+
+    // iterators
+    int index = 0;
+    for(auto item: list) {
+        if(index == 0) {
+            EXPECT_EQ(item, py::int_(3));
+        } else if(index == 1) {
+            EXPECT_EQ(item, py::int_(2));
+        } else if(index == 2) {
+            EXPECT_EQ(item, py::int_(1));
+        }
+        index++;
+    }
+
+    // others
+    list.append(py::int_(4));
+    EXPECT_EQ(list, py::eval("[3, 2, 1, 4]"));
+
+    list.insert(0, py::int_(7));
+    EXPECT_EQ(list, py::eval("[7, 3, 2, 1, 4]"));
+}
+
+TEST_F(PYBIND11_TEST, dict) {
+    // constructors
+    py::dict dict = py::dict();
+    EXPECT_EQ(dict, py::eval("{}"));
+    EXPECT_EQ(dict.size(), 0);
+    EXPECT_TRUE(dict.empty());
+
+    // accessor
+    dict["a"] = py::int_(1);
+    dict["b"] = py::int_(2);
+    dict["c"] = py::int_(3);
+    EXPECT_EQ(dict, py::eval("{'a': 1, 'b': 2, 'c': 3}"));
+    EXPECT_EQ(dict,
+              py::dict({
+                  {"a", py::int_(1)},
+                  {"b", py::int_(2)},
+                  {"c", py::int_(3)},
+    }));
+
+    // iterators
+    int index = 0;
+    for(auto item: dict) {
+        if(index == 0) {
+            EXPECT_EQ(item.first.cast<std::string>(), "a");
+            EXPECT_EQ(item.second, py::int_(1));
+        } else if(index == 1) {
+            EXPECT_EQ(item.first.cast<std::string>(), "b");
+            EXPECT_EQ(item.second, py::int_(2));
+        } else if(index == 2) {
+            EXPECT_EQ(item.first.cast<std::string>(), "c");
+            EXPECT_EQ(item.second, py::int_(3));
+        }
+        index++;
+    }
+}
+
+TEST_F(PYBIND11_TEST, capsule) {
+    static int times = 0;
+
+    struct NotTrivial {
+        ~NotTrivial() { times++; }
+    };
+
+    py::handle x = py::capsule(new NotTrivial(), [](void* ptr) {
+        delete static_cast<NotTrivial*>(ptr);
+    });
+
+    auto m = py::module_::__main__();
+
+    m.def("foo", [](int x) {
+        return py::capsule(new int(x));
+    });
+
+    m.def("bar", [](py::capsule x, int y) {
+        EXPECT_EQ(x.cast<int>(), y);
+        delete (int*)x.data();
+    });
+
+    py::exec("bar(foo(123), 123)");
+    py::finalize(true);
+    EXPECT_EQ(times, 1);
+}