From 1fa6d62f17b6da906d8fde0958e4c7ea73fbd784 Mon Sep 17 00:00:00 2001 From: King_DuckZ Date: Wed, 18 May 2022 18:48:18 +0200 Subject: [PATCH] Add support for registering foreign methods that return foreign objects Still a bit work in progress but functioning --- examples/calendar/main.cpp | 2 +- examples/math_vector/main.cpp | 2 +- include/wrenpp/class_manager.hpp | 63 ++++++++++++++++- .../wrenpp/detail/construct_foreign_class.hpp | 67 +++++++++++-------- include/wrenpp/detail/meson.build | 1 + include/wrenpp/detail/module_and_name.hpp | 5 ++ include/wrenpp/detail/setters_getters.hpp | 2 +- include/wrenpp/detail/strings_in_vector.hpp | 14 ++++ .../detail/wren_class_name_from_type.hpp | 51 ++++++++++++++ include/wrenpp/vm.hpp | 2 + include/wrenpp/vm_fun.hpp | 10 ++- meson.build | 1 + src/class_manager.cpp | 66 ++++++++++++++---- src/vm.cpp | 9 +++ src/wren_class_name_from_type.cpp | 14 ++++ 15 files changed, 264 insertions(+), 45 deletions(-) create mode 100644 include/wrenpp/detail/wren_class_name_from_type.hpp create mode 100644 src/wren_class_name_from_type.cpp diff --git a/examples/calendar/main.cpp b/examples/calendar/main.cpp index 9d39058..ce05c68 100644 --- a/examples/calendar/main.cpp +++ b/examples/calendar/main.cpp @@ -123,7 +123,7 @@ int main() { vm.callback_manager().add_callback(true, "calendar", "Calendar", "today()", &Calendar::today) .add_callback(false, "calendar", "Calendar", "add_appointment(_)", wren::make_method_bindable<&Calendar::add_appointment>()) .add_callback(false, "calendar", "Calendar", "appointment_count()", wren::make_method_bindable<&Calendar::appointment_count>()); - vm.class_manager().add_class_maker("calendar", "Calendar", &wren::make_foreign_class); + vm.class_manager().add_class_maker("calendar", "Calendar", wren::make_foreign_class); vm.interpret("main", g_test_script); diff --git a/examples/math_vector/main.cpp b/examples/math_vector/main.cpp index 1755578..5648035 100644 --- a/examples/math_vector/main.cpp +++ b/examples/math_vector/main.cpp @@ -112,7 +112,7 @@ int main() { .add_callback(false, "math_vector", "MathVector", "z", make_method_bindable<&Vector::z>()); vm.class_manager() - .add_class_maker("math_vector", "MathVector", &make_foreign_class, + .add_class_maker("math_vector", "MathVector", make_foreign_class, double, //single value constructor std::tuple, //x,y,z constructor std::tuple<> //default constructor diff --git a/include/wrenpp/class_manager.hpp b/include/wrenpp/class_manager.hpp index d2a3f59..42be406 100644 --- a/include/wrenpp/class_manager.hpp +++ b/include/wrenpp/class_manager.hpp @@ -19,10 +19,13 @@ #include "detail/wren_types.hpp" #include "detail/strings_in_vector.hpp" +#include "detail/construct_foreign_class.hpp" +#include "detail/wren_class_name_from_type.hpp" #include #include #include #include +#include namespace wren { namespace detail { @@ -47,6 +50,7 @@ namespace wren { std::string_view module_name() const { return this->as_string_view<0>(); } std::string_view class_name() const { return this->as_string_view<1>(); } + operator ModuleAndName() const; using StringsInVector<2>::operator<; bool operator== (const ClassNameOwning& other) const; @@ -58,6 +62,9 @@ namespace wren { TempClassName (const ClassNameOwning& model); bool operator== (const TempClassName& other) const; + std::string_view module_name() const { return m_module_name; } + std::string_view class_name() const { return m_class_name; } + private: std::string_view m_module_name, m_class_name; }; @@ -71,19 +78,73 @@ namespace wren { using is_transparent = std::true_type; std::size_t operator()(TempClassName value) const; }; + + template + struct CppClassTypeHelper { + static void register_ifp(ClassManager&, const ClassNameOwning* name); + }; + + template + struct CppClassTypeHelper> { + static void register_ifp(ClassManager& classman, const ClassNameOwning* name); + }; } //namespace detail class ClassManager { + template friend struct detail::CppClassTypeHelper; typedef std::function make_foreign_class_t; typedef std::unordered_map ClassMap; + typedef std::unordered_map TypeMap; public: ClassManager(); ~ClassManager() noexcept; - ClassManager& add_class_maker (std::string_view module_name, std::string_view class_name, make_foreign_class_t); + template + ClassManager& add_class_maker (std::string module_name, std::string_view class_name, F&& maker); foreign_class_t make_class(std::string_view module_name, std::string_view class_name); + ModuleAndName wren_class_name_from_hash(ClassManagerNameHashType hash) const; + template + ModuleAndName wren_class_name_from_type() const; private: + ClassMap::const_iterator add_class_maker_implem ( + std::string_view module_name, + std::string_view class_name, + make_foreign_class_t + ); + + void add_type (ClassManagerNameHashType hash, const detail::ClassNameOwning* name); + ClassMap m_classes; + TypeMap m_types; //refers to memory in m_classes }; + + namespace detail { + template + inline void CppClassTypeHelper::register_ifp(ClassManager&, const ClassNameOwning*) { + //Nothing to do + } + + template + inline void CppClassTypeHelper>::register_ifp( + ClassManager& classman, + const ClassNameOwning* name + ) { + const auto new_hash = type_hash(); + classman.add_type(new_hash, name); + } + } //namespace detail + + template + inline ClassManager& ClassManager::add_class_maker (std::string module_name, std::string_view class_name, F&& maker) { + auto it_new = this->add_class_maker_implem(module_name, class_name, std::forward(maker)); + const auto& owning_name = it_new->first; + detail::CppClassTypeHelper>::register_ifp(*this, &owning_name); + return *this; + } + + template + inline ModuleAndName ClassManager::wren_class_name_from_type() const { + return detail::wren_class_name_from_type(*this); + } } //namespace wren diff --git a/include/wrenpp/detail/construct_foreign_class.hpp b/include/wrenpp/detail/construct_foreign_class.hpp index 59a6c3c..e82a3c8 100644 --- a/include/wrenpp/detail/construct_foreign_class.hpp +++ b/include/wrenpp/detail/construct_foreign_class.hpp @@ -17,7 +17,7 @@ #pragma once -#include "wrenpp/vm.hpp" +#include "../vm.hpp" #include "setters_getters.hpp" #if defined(WRENPP_WITH_NAME_GUESSING) # include "guess_class_name.hpp" @@ -123,33 +123,46 @@ namespace wren { static_assert(dummy == 1); //always true } } + + //This is named from the perspective of the user - they say + //MakeForeignClass and it will "produce" object of type X. In + //reality it produces a functor that does that, and it's that functor + //that gets registered into wren, so from the library perspective it + //should be called MakeForeignClassMaker + template + class MakeForeignClass { + public: + MakeForeignClass() = default; + + foreign_class_t operator()() const { + detail::static_assert_all_packs_are_unique(); + + foreign_class_t ret; + ret.allocate = [](VM& vm, ModuleAndName mn) { + void* const mem = vm.set_slot_new_foreign(0, 0, sizeof(T)); + + const auto result = detail::ForeignClassHelper::construct(vm, vm.slot_count() - 1, mem); + if (not result.success) { + set(vm, 1, std::string{"No registered c++ constructor "} + +#if defined(WRENPP_WITH_NAME_GUESSING) + "for class " + guess_class_name() + " " + +#endif + "takes " + std::to_string(result.parameter_count) + + " parameter" + (result.parameter_count == 1 ? "" : "s") + ); + vm.abort_fiber(1); + } + }; + ret.finalize = [](void* mem) { + const auto cale = static_cast(mem); + cale->~T(); + }; + + return ret; + } + }; } //namespace detail template - inline foreign_class_t make_foreign_class() { - detail::static_assert_all_packs_are_unique(); - - foreign_class_t ret; - ret.allocate = [](VM& vm, ModuleAndName mn) { - void* const mem = vm.set_slot_new_foreign(0, 0, sizeof(T)); - - const auto result = detail::ForeignClassHelper::construct(vm, vm.slot_count() - 1, mem); - if (not result.success) { - set(vm, 1, std::string{"No registered c++ constructor "} + -#if defined(WRENPP_WITH_NAME_GUESSING) - "for class " + guess_class_name() + " " + -#endif - "takes " + std::to_string(result.parameter_count) + - " parameter" + (result.parameter_count == 1 ? "" : "s") - ); - vm.abort_fiber(1); - } - }; - ret.finalize = [](void* mem) { - const auto cale = static_cast(mem); - cale->~T(); - }; - - return ret; - } + constexpr detail::MakeForeignClass make_foreign_class; } //namespace wren diff --git a/include/wrenpp/detail/meson.build b/include/wrenpp/detail/meson.build index 832864c..2e0b131 100644 --- a/include/wrenpp/detail/meson.build +++ b/include/wrenpp/detail/meson.build @@ -8,6 +8,7 @@ include_files = [ 'string_bt.hpp', 'StringCRC32.hpp', 'strings_in_vector.hpp', + 'wren_class_name_from_type.hpp', 'wren_types.hpp', ] diff --git a/include/wrenpp/detail/module_and_name.hpp b/include/wrenpp/detail/module_and_name.hpp index 7999b24..a497c31 100644 --- a/include/wrenpp/detail/module_and_name.hpp +++ b/include/wrenpp/detail/module_and_name.hpp @@ -41,6 +41,7 @@ namespace wren { //spits out. class ModuleAndName { public: + constexpr ModuleAndName(); constexpr ModuleAndName (const char* base, std::uint32_t hash, std::size_t mod_name_len, std::size_t class_name_len); constexpr const char* module_name_char() const noexcept { return m_base; } @@ -73,6 +74,10 @@ namespace wren { static_assert(sizeof(raw) == sizeof(module_and_name)); }; + constexpr ModuleAndName::ModuleAndName() : + ModuleAndName(nullptr, 0, 0, 0) + {} + constexpr ModuleAndName::ModuleAndName (const char* base, std::uint32_t hash, std::size_t mod_name_len, std::size_t class_name_len) : m_base(base), m_hash(hash), diff --git a/include/wrenpp/detail/setters_getters.hpp b/include/wrenpp/detail/setters_getters.hpp index bbe17ce..22336d9 100644 --- a/include/wrenpp/detail/setters_getters.hpp +++ b/include/wrenpp/detail/setters_getters.hpp @@ -17,7 +17,7 @@ #pragma once -#include "wrenpp/vm.hpp" +#include "../vm.hpp" #include #include #include diff --git a/include/wrenpp/detail/strings_in_vector.hpp b/include/wrenpp/detail/strings_in_vector.hpp index 7a7a22f..e4da8ba 100644 --- a/include/wrenpp/detail/strings_in_vector.hpp +++ b/include/wrenpp/detail/strings_in_vector.hpp @@ -81,6 +81,10 @@ namespace wren { bool operator== (const StringsInVectorImplem& other) const; bool operator< (const StringsInVectorImplem& other) const; + protected: + const char* raw_buffer() const; + std::size_t raw_buffer_size() const; + private: std::vector m_memory; }; @@ -132,6 +136,16 @@ namespace wren { return this_memory < other_memory; } + template + inline const char* StringsInVectorImplem>::raw_buffer() const { + return m_memory.data(); + } + + template + inline std::size_t StringsInVectorImplem>::raw_buffer_size() const { + return m_memory.size(); + } + template using StringsInVector = StringsInVectorImplem())>; diff --git a/include/wrenpp/detail/wren_class_name_from_type.hpp b/include/wrenpp/detail/wren_class_name_from_type.hpp new file mode 100644 index 0000000..d0d8869 --- /dev/null +++ b/include/wrenpp/detail/wren_class_name_from_type.hpp @@ -0,0 +1,51 @@ +/* Copyright 2020-2022, Michele Santullo + * This file is part of wrenpp. + * + * Wrenpp is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Wrenpp is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with wrenpp. If not, see . + */ + +#pragma once + +#include "module_and_name.hpp" +#include +#include +#include + +namespace wren { + typedef std::size_t ClassManagerNameHashType; + class VM; + + namespace detail { + //TODO: work in progress, make it take a type T and hash that for real + template + inline ClassManagerNameHashType type_hash() { + static const ClassManagerNameHashType my_hash = 0; + return static_cast(reinterpret_cast(&my_hash)); + } + + //Just an utility function to call a method on the class manager in vm + //and return its return value without including the header for the + //class manager. Useful in inline functions in header files where + //including class_manager.hpp would propagate it everywhere. + ModuleAndName fetch_class_name_from_manager ( + const VM& vm, + ClassManagerNameHashType hash + ); + + template + inline ModuleAndName wren_class_name_from_type (const VM& vm) { + return fetch_class_name_from_manager(vm, type_hash()); + } + } //namespace detail +} //namespace wren diff --git a/include/wrenpp/vm.hpp b/include/wrenpp/vm.hpp index dcd5f45..70f4674 100644 --- a/include/wrenpp/vm.hpp +++ b/include/wrenpp/vm.hpp @@ -109,7 +109,9 @@ namespace wren { void reset (Configuration* conf); CallbackManager& callback_manager(); + const CallbackManager& callback_manager() const; ClassManager& class_manager(); + const ClassManager& class_manager() const; private: struct LocalData; diff --git a/include/wrenpp/vm_fun.hpp b/include/wrenpp/vm_fun.hpp index 09e77f1..e7ce3a0 100644 --- a/include/wrenpp/vm_fun.hpp +++ b/include/wrenpp/vm_fun.hpp @@ -25,6 +25,7 @@ #include "detail/wren_types.hpp" #include "detail/construct_foreign_class.hpp" #include "detail/setters_getters.hpp" +#include "detail/wren_class_name_from_type.hpp" #include #include #include @@ -103,8 +104,15 @@ namespace wren { } else { auto ret = call_implem(std::make_integer_sequence(), vm); + //TODO: check for errors vm.ensure_slots(1); - set(vm, 0, ret); + if constexpr (std::is_fundamental::value) { + set(vm, 0, ret); + } + else { + ModuleAndName wren_name = wren_class_name_from_type(vm); + R* const new_object = make_wren_object(vm, wren_name, std::move(ret)); + } } }; } diff --git a/meson.build b/meson.build index cb8900f..d1315e1 100644 --- a/meson.build +++ b/meson.build @@ -68,6 +68,7 @@ wrenpp = library(meson.project_name(), 'src/vm_fun.cpp', 'src/callback_manager.cpp', 'src/class_manager.cpp', + 'src/wren_class_name_from_type.cpp', dependencies: [wren_dep], include_directories: public_incl, install: (not meson.is_subproject() or get_option('default_library')=='shared'), diff --git a/src/class_manager.cpp b/src/class_manager.cpp index 2bf6fe0..624c51a 100644 --- a/src/class_manager.cpp +++ b/src/class_manager.cpp @@ -16,6 +16,7 @@ */ #include "wrenpp/class_manager.hpp" +#include namespace wren { namespace detail { @@ -36,6 +37,15 @@ namespace wren { { } + ClassNameOwning::operator ModuleAndName() const { + return ModuleAndName{ + this->raw_buffer(), + 0, //TODO: replace with hash + this->module_name().size(), + this->class_name().size() + }; + } + bool ClassNameOwning::operator== (const ClassNameOwning& other) const { return *static_cast(this) == static_cast(other) or @@ -74,19 +84,6 @@ namespace wren { ClassManager::ClassManager() = default; ClassManager::~ClassManager() noexcept = default; - ClassManager& ClassManager::add_class_maker (std::string_view module_name, std::string_view class_name, make_foreign_class_t maker) { - using detail::TempClassName; - using detail::ClassNameOwning; - - auto it_found = m_classes.find(TempClassName{module_name, class_name}); - if (m_classes.cend() == it_found) - m_classes.insert(it_found, std::make_pair(ClassNameOwning{module_name, class_name}, maker)); - else - it_found->second = maker; - - return *this; - } - foreign_class_t ClassManager::make_class (std::string_view module_name, std::string_view class_name) { using detail::TempClassName; @@ -96,4 +93,47 @@ namespace wren { else return {nullptr, nullptr}; } + + auto ClassManager::add_class_maker_implem ( + std::string_view module_name, + std::string_view class_name, + make_foreign_class_t maker + ) -> ClassMap::const_iterator { + using detail::TempClassName; + using detail::ClassNameOwning; + + auto it_found = m_classes.find(TempClassName{module_name, class_name}); + ClassMap::const_iterator retval; + if (m_classes.cend() == it_found) { + retval = m_classes.insert( + it_found, + std::make_pair(ClassNameOwning{module_name, class_name}, maker) + ); + } + else { + it_found->second = maker; + retval = it_found; + } + + return retval; + } + + void ClassManager::add_type (ClassManagerNameHashType hash, const detail::ClassNameOwning* name) { + using detail::TempClassName; + + assert(m_types.count(hash) == 0 or m_types.at(hash) == name); + //insert if not present, leave old value if hash is already there + m_types.insert(std::make_pair(hash, name)); + } + + ModuleAndName ClassManager::wren_class_name_from_hash(ClassManagerNameHashType hash) const { + const auto it_found = m_types.find(hash); + if (m_types.cend() != it_found) { + const detail::ClassNameOwning* const name = it_found->second; + return *name; + } + else { + return {}; + } + } } //namespace wren diff --git a/src/vm.cpp b/src/vm.cpp index 9f3d59a..f6173d0 100644 --- a/src/vm.cpp +++ b/src/vm.cpp @@ -289,6 +289,7 @@ namespace wren { } bool VM::has_module(const char* module) const noexcept { + assert(module); return wrenHasModule(m_local->wvm, module); } @@ -348,10 +349,18 @@ namespace wren { return m_local->callback_manager; } + const CallbackManager& VM::callback_manager() const { + return m_local->callback_manager; + } + ClassManager& VM::class_manager() { return m_local->class_manager; } + const ClassManager& VM::class_manager() const { + return m_local->class_manager; + } + void VM::ensure_slots (int num_slots) { wrenEnsureSlots(m_local->wvm, num_slots); } diff --git a/src/wren_class_name_from_type.cpp b/src/wren_class_name_from_type.cpp new file mode 100644 index 0000000..a0cdb66 --- /dev/null +++ b/src/wren_class_name_from_type.cpp @@ -0,0 +1,14 @@ +#include "wrenpp/detail/wren_class_name_from_type.hpp" +#include "wrenpp/vm.hpp" +#include "wrenpp/class_manager.hpp" + +namespace wren { + namespace detail { + ModuleAndName fetch_class_name_from_manager ( + const VM& vm, + ClassManagerNameHashType hash + ) { + return vm.class_manager().wren_class_name_from_hash(hash); + } + } //namespace detail +} //namespace wren