From fa0183a3bf9daf6d7042ac0af00350496f9f90e5 Mon Sep 17 00:00:00 2001 From: King_DuckZ Date: Fri, 29 Apr 2022 18:16:57 +0200 Subject: [PATCH] Implement CallbackManager With it users don't have to provide a foreign_method_fn() function anymore, it's become optional. --- examples/greet/main.cpp | 25 +----- include/wrenpp/callback_manager.hpp | 129 +++++++++++++++++++++++++++ include/wrenpp/def_configuration.hpp | 3 +- include/wrenpp/vm.hpp | 3 + meson.build | 1 + src/callback_manager.cpp | 121 +++++++++++++++++++++++++ src/def_configuration.cpp | 6 ++ src/vm.cpp | 6 ++ 8 files changed, 272 insertions(+), 22 deletions(-) create mode 100644 include/wrenpp/callback_manager.hpp create mode 100644 src/callback_manager.cpp diff --git a/examples/greet/main.cpp b/examples/greet/main.cpp index 11a52f4..dd6daa3 100644 --- a/examples/greet/main.cpp +++ b/examples/greet/main.cpp @@ -17,6 +17,7 @@ #include "wrenpp/vm_fun.hpp" #include "wrenpp/def_configuration.hpp" +#include "wrenpp/callback_manager.hpp" #include #include #include @@ -58,33 +59,15 @@ const char* raw_fetch_env (const char* name) noexcept { void user_get_env (wren::VM& vm) { set(vm, 0, raw_fetch_env(wren::get(vm, 1))); } - -class MyConfig : public wren::DefConfiguration { -public: - wren::foreign_method_t foreign_method_fn ( - wren::VM* vm, - std::string_view module, - std::string_view class_name, - bool is_static, - std::string_view signature - ) { - //std::cout << "VM " << vm << " requested foreign method " << - // class_name << "::" << signature << - // " in module " << module << "\n"; - - if ("User" == class_name and "main" == module and "get_env(_)" == signature) - return &user_get_env; - - return nullptr; - } -}; } //unnamed namespace int main() { //typedef wren::ModuleAndName MN; - MyConfig config; + wren::DefConfiguration config; wren::VM vm(&config, nullptr); + vm.callback_manager().add_callback(true, "main", "User", "get_env(_)", &user_get_env); + vm.interpret("main", g_script); wren::call(vm, {"main", "the_user"}, "greet"); diff --git a/include/wrenpp/callback_manager.hpp b/include/wrenpp/callback_manager.hpp new file mode 100644 index 0000000..450c9d5 --- /dev/null +++ b/include/wrenpp/callback_manager.hpp @@ -0,0 +1,129 @@ +/* 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 "wren_types.hpp" +#include +#include +#include + +namespace wren::detail { + class FullSignatureOwning; +} //namespace wren::detail + +namespace std { + template <> + struct hash { + std::size_t operator() (const wren::detail::FullSignatureOwning& key) const; + }; +}; + +namespace wren { + namespace detail { + class FullSignatureBase { + public: + FullSignatureBase (const FullSignatureBase& other) : + m_module_hash(other.m_module_hash), + m_class_name_hash(other.m_class_name_hash), + m_signature_hash(other.m_signature_hash) + { } + + std::size_t module_hash() const { return m_module_hash; } + std::size_t class_hash() const { return m_class_name_hash; } + std::size_t signature_hash() const { return m_signature_hash; } + + protected: + FullSignatureBase (std::size_t module_hash_v, std::size_t class_hash_v, std::size_t signature_hash_v) : + m_module_hash(module_hash_v), + m_class_name_hash(class_hash_v), + m_signature_hash(signature_hash_v) + { } + + private: + std::size_t m_module_hash; + std::size_t m_class_name_hash; + std::size_t m_signature_hash; + }; + + class FullSignatureOwning : public FullSignatureBase { + public: + FullSignatureOwning (std::string_view module_name, std::string_view class_name, std::string_view signature); + + std::string_view module_name() const { return make_string_view(m_module_range); } + std::string_view class_name() const { return make_string_view(m_class_range); } + std::string_view signature() const { return make_string_view(m_signature_range); } + + private: + struct WordRange { + std::size_t start, length; + }; + + std::string_view make_string_view(WordRange range) const; + + std::vector m_full_name; + WordRange m_module_range; + WordRange m_class_range; + WordRange m_signature_range; + }; + + //Temp because it's only supposed to be used to store temporaries + //It will contain a dangling reference if the FullSignatureOwning object + //used to initialise an object of this class gets destroyed + class TempFullSignature : public FullSignatureBase { + public: + TempFullSignature (const FullSignatureOwning& mn); + TempFullSignature (std::string_view module_name, std::string_view class_name, std::string_view signature); + + std::string_view module_name() const { return m_module_name; } + std::string_view class_name() const { return m_class_name; } + std::string_view signature() const { return m_signature; } + + private: + std::string_view m_module_name; + std::string_view m_class_name; + std::string_view m_signature; + }; + + struct FullSignatureEqual { + using is_transparent = std::true_type; + bool operator()(TempFullSignature left, TempFullSignature right) const; + }; + + struct FullSignatureHash { + using is_transparent = std::true_type; + std::size_t operator()(TempFullSignature value) const; + }; + } //namespace detail + + class CallbackManager { + typedef std::unordered_map MethodMap; + public: + CallbackManager(); + ~CallbackManager() noexcept; + + void add_callback (bool is_static, std::string_view module_name, std::string_view class_name, std::string_view signature, foreign_method_t cb); + foreign_method_t callback (bool is_static, std::string_view module_name, std::string_view class_name, std::string_view signature) const; + + private: + MethodMap* method_map (bool is_static) noexcept; + const MethodMap* method_map (bool is_static) const noexcept; + + MethodMap m_static_methods; + MethodMap m_normal_methods; + }; +} //namespace wren diff --git a/include/wrenpp/def_configuration.hpp b/include/wrenpp/def_configuration.hpp index a51fbb4..a9614d7 100644 --- a/include/wrenpp/def_configuration.hpp +++ b/include/wrenpp/def_configuration.hpp @@ -18,7 +18,7 @@ #pragma once #include "configuration.hpp" -#include "wrenpp/wren_types.hpp" +#include "wren_types.hpp" #include "error_type.hpp" namespace wren { @@ -27,5 +27,6 @@ namespace wren { static void write_fn (VM*, wren_string_t text); static void error_fn (VM*, ErrorType, wren_string_t module, int line, wren_string_t msg); static void* reallocate_fn(void* ptr, std::size_t size); + static foreign_method_t foreign_method_fn (VM* vm, wren_string_t module, wren_string_t class_name, bool is_static, wren_string_t signature); }; } //namespace wren diff --git a/include/wrenpp/vm.hpp b/include/wrenpp/vm.hpp index 0c24a6b..7891731 100644 --- a/include/wrenpp/vm.hpp +++ b/include/wrenpp/vm.hpp @@ -35,6 +35,7 @@ namespace wren { class Configuration; class VM; class DynafuncMaker; + class CallbackManager; typedef void(*foreign_method_t)(VM&); typedef void(*finalizer_t)(void*); @@ -112,6 +113,8 @@ namespace wren { std::size_t dynafunc_byte_size() const; void reset (Configuration* conf); + CallbackManager& callback_manager(); + private: struct LocalData; diff --git a/meson.build b/meson.build index 5ee357a..3ced8a8 100644 --- a/meson.build +++ b/meson.build @@ -61,6 +61,7 @@ wrenpp = library(meson.project_name(), 'src/dynafunc_' + arch + '_' + os + '.S', 'src/handle.cpp', 'src/vm_fun.cpp', + 'src/callback_manager.cpp', dependencies: [wren_dep], include_directories: public_incl, install: true, diff --git a/src/callback_manager.cpp b/src/callback_manager.cpp new file mode 100644 index 0000000..b63a1fe --- /dev/null +++ b/src/callback_manager.cpp @@ -0,0 +1,121 @@ +/* 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 . + */ + +#include "wrenpp/callback_manager.hpp" +#include +#include + +namespace wren::detail { + FullSignatureOwning::FullSignatureOwning (std::string_view module_name, std::string_view class_name, std::string_view signature) : + FullSignatureBase(std::hash{}(module_name), std::hash{}(class_name), std::hash{}(signature)) + { + m_full_name.resize(module_name.size() + 1 + class_name.size() + 1 + signature.size() + 1); + char* buff = m_full_name.data(); + std::size_t buff_used = 0; + + std::copy(module_name.cbegin(), module_name.cend(), buff + buff_used); + m_module_range.start = buff_used; + m_module_range.length = module_name.size(); + buff[buff_used + module_name.size()] = '\0'; + buff_used += module_name.size() + 1; + + std::copy(class_name.cbegin(), class_name.cend(), buff + buff_used); + m_class_range.start = buff_used; + m_class_range.length = class_name.size(); + buff[buff_used + class_name.size()] = '\0'; + buff_used += class_name.size() + 1; + + std::copy(signature.cbegin(), signature.cend(), buff + buff_used); + m_signature_range.start = buff_used; + m_signature_range.length = signature.size(); + buff[buff_used + signature.size()] = '\0'; + buff_used += signature.size() + 1; + + assert(m_full_name[m_module_range.start + m_module_range.length] == '\0'); + assert(m_full_name[m_class_range.start + m_module_range.length] == '\0'); + assert(m_full_name[m_signature_range.start + m_signature_range.length] == '\0'); + assert(this->module_name() == module_name); + assert(this->class_name() == class_name); + assert(this->signature() == signature); + } + + std::string_view FullSignatureOwning::make_string_view(WordRange range) const { + return {m_full_name.data() + range.start, range.length}; + } + + TempFullSignature::TempFullSignature (const FullSignatureOwning& full_sig) : + FullSignatureBase(full_sig), + m_module_name(full_sig.module_name()), + m_class_name(full_sig.class_name()) + { } + + TempFullSignature::TempFullSignature (std::string_view module_name, std::string_view class_name, std::string_view signature) : + FullSignatureBase(std::hash{}(module_name), std::hash{}(class_name), std::hash{}(signature)), + m_module_name(module_name), + m_class_name(class_name), + m_signature(signature) + { } + + bool FullSignatureEqual::operator()(TempFullSignature left, TempFullSignature right) const { + return left.class_name() == right.class_name() and + left.module_name() == right.module_name(); + } + + std::size_t FullSignatureHash::operator() (TempFullSignature key) const { + //see example at https://en.cppreference.com/w/cpp/utility/hash + const std::size_t h1 = key.module_hash(); + const std::size_t h2 = key.class_hash(); + const std::size_t h3 = key.signature_hash(); + return h1 ^ ((h2 ^ (h3 << 1)) << 1); + } +} //namespace wren::detail + +namespace wren { + CallbackManager::CallbackManager() = default; + CallbackManager::~CallbackManager() noexcept = default; + + void CallbackManager::add_callback (bool is_static, std::string_view module_name, std::string_view class_name, std::string_view signature, foreign_method_t cb) { + using detail::FullSignatureOwning; + using detail::TempFullSignature; + + MethodMap& map = *method_map(is_static); + auto it_found = map.find(TempFullSignature{module_name, class_name, signature}); + if (map.cend() == it_found) + map.insert(it_found, std::make_pair(FullSignatureOwning{module_name, class_name, signature}, cb)); + else + it_found->second = cb; + } + + foreign_method_t CallbackManager::callback (bool is_static, std::string_view module_name, std::string_view class_name, std::string_view signature) const { + using detail::TempFullSignature; + + const MethodMap* const map = method_map(is_static); + const auto it_found = map->find(TempFullSignature{module_name, class_name, signature}); + if (map->cend() != it_found) + return it_found->second; + else + return nullptr; + } + + auto CallbackManager::method_map(bool is_static) noexcept -> MethodMap* { + return (is_static ? &m_static_methods : &m_normal_methods); + } + + auto CallbackManager::method_map(bool is_static) const noexcept -> const MethodMap* { + return const_cast(this)->method_map(is_static); + } +} //namespace wren diff --git a/src/def_configuration.cpp b/src/def_configuration.cpp index 00e97d8..a050f82 100644 --- a/src/def_configuration.cpp +++ b/src/def_configuration.cpp @@ -16,6 +16,8 @@ */ #include "wrenpp/def_configuration.hpp" +#include "wrenpp/callback_manager.hpp" +#include "wrenpp/vm.hpp" #include #include @@ -65,4 +67,8 @@ namespace wren { } return nullptr; } + + foreign_method_t DefConfiguration::foreign_method_fn (VM* vm, wren_string_t module, wren_string_t class_name, bool is_static, wren_string_t signature) { + return vm->callback_manager().callback(is_static, module, class_name, signature); + } } //namespace wren diff --git a/src/vm.cpp b/src/vm.cpp index 2874dd9..3b3a942 100644 --- a/src/vm.cpp +++ b/src/vm.cpp @@ -17,6 +17,7 @@ #include "wrenpp/vm.hpp" #include "wrenpp/configuration.hpp" +#include "wrenpp/callback_manager.hpp" #include "dynafunc_maker.hpp" #include #include @@ -152,6 +153,7 @@ namespace wren { #endif } + CallbackManager callback_manager; detail::Callbacks callbacks; DynafuncMaker dynafunc; WrenVM* wvm; @@ -316,6 +318,10 @@ namespace wren { m_local->dynafunc.clear(); } + CallbackManager& VM::callback_manager() { + return m_local->callback_manager; + } + void VM::ensure_slots (int num_slots) { wrenEnsureSlots(m_local->wvm, num_slots); }