From 2c99251b27a949e12ddeeed98cf93ec80d57e305 Mon Sep 17 00:00:00 2001 From: King_DuckZ Date: Tue, 28 May 2024 08:25:19 +0200 Subject: [PATCH] Add support for returning a pre-existing foreign object from a foreign method This is potentially subject to change in the near future. This commit adds support for returning an instance (currently wrapped in ForeignObject<>) of a foreign object that is already instantiated. The object returned this way simply gets selected by handle into slot 0 of C Wren. Conversely, returning just a naked object causes wrenpp to make a Wren-accessible copy of it. --- examples/calendar/main.cpp | 2 +- examples/{call_cpp => cpp_calls}/main.cpp | 46 +++++++++++++++++++- examples/{call_cpp => cpp_calls}/meson.build | 2 +- examples/meson.build | 2 +- include/wrenpp/detail/deref_ifn.hpp | 42 ++++++++++++++++++ include/wrenpp/detail/foreign_object.hpp | 31 +++++++++++++ include/wrenpp/detail/is_kindof_handle.hpp | 41 +++++++++++++++++ include/wrenpp/detail/setters_getters.hpp | 22 +++++++--- include/wrenpp/handle.hpp | 1 + include/wrenpp/vm_fun.hpp | 24 +++++++--- src/handle.cpp | 5 +++ src/meson.build | 1 + src/setters_getters.cpp | 21 +++++++++ 13 files changed, 223 insertions(+), 17 deletions(-) rename examples/{call_cpp => cpp_calls}/main.cpp (76%) rename examples/{call_cpp => cpp_calls}/meson.build (72%) create mode 100644 include/wrenpp/detail/deref_ifn.hpp create mode 100644 include/wrenpp/detail/is_kindof_handle.hpp create mode 100644 src/setters_getters.cpp diff --git a/examples/calendar/main.cpp b/examples/calendar/main.cpp index b8f6ef1..12a21ad 100644 --- a/examples/calendar/main.cpp +++ b/examples/calendar/main.cpp @@ -128,7 +128,7 @@ int main() { vm.interpret("main", g_test_script); - Calendar* const cale = std::get<0>(wren::variables(vm, MN<"main", "cale">)); + Calendar* const cale = std::get<0>(wren::variables(vm, MN<"main", "cale">)).data(); cale->print_appointments(); return 0; diff --git a/examples/call_cpp/main.cpp b/examples/cpp_calls/main.cpp similarity index 76% rename from examples/call_cpp/main.cpp rename to examples/cpp_calls/main.cpp index 5870a35..69fd59f 100644 --- a/examples/call_cpp/main.cpp +++ b/examples/cpp_calls/main.cpp @@ -52,10 +52,19 @@ foreign class ClassB { } foreign greeting_message(first_name, family_name, emoji) + foreign set_obj_c (obj) + foreign get_obj_c() } )script"; -//This is three examples in one: +constexpr char g_module_c[] = +"foreign class ClassC {" R"script( + construct new (msg) {} + foreign print_message() +} +)script"; + +//This is several examples in one: //1. instantiate ClassA in Wren and pass it to do_action() which is a // non-foreign method in wren //2. instantiate ClassA in C++, have C++ invoke the non-foreign method @@ -64,14 +73,21 @@ foreign class ClassB { //3. instantiate ClassB in C++, have C++ invoke the non-foreign method // do_action() on it and pass the same ClassA instance from the previous // step to it +//4. show how to return a pre-existing instance of ClassC from ClassB both in +// C++ and Wren constexpr char g_script[] = "" R"script( import "module_a" for ClassA import "module_b" for ClassB +import "module_c" for ClassC var obj_b = ClassB.new("TheWren") obj_b.do_action(ClassA.new("instanciated from script"), 2) obj_b.greeting_message("Jane", "Doe", "ʕ·͡ᴥ·ʔ") + +var obj_c = ClassC.new("Message of ClassC object instantiated in Wren") +obj_b.set_obj_c(obj_c) +obj_b.get_obj_c().print_message() )script"; class MyWrenConfig : public wren::DefConfiguration { @@ -81,6 +97,8 @@ public: return copied(g_module_a, sizeof(g_module_a)); else if (module_name == "module_b") return copied(g_module_b, sizeof(g_module_b)); + else if (module_name == "module_c") + return copied(g_module_c, sizeof(g_module_c)); return nullptr; } @@ -112,6 +130,8 @@ struct ClassA { std::string m_message; }; +class ClassC; + class ClassB { public: explicit ClassB (std::string nickname) : m_nickname(nickname) { @@ -130,8 +150,23 @@ public: m_nickname << "' " << family_name << "\" " << emoji << '\n'; } + void set_obj_c (wren::ForeignObject obj) { + m_obj_c = std::move(obj); + } + wren::ForeignObject* get_obj_c() { return &m_obj_c; } + private: std::string m_nickname; + wren::ForeignObject m_obj_c; +}; + +class ClassC { +public: + ClassC (std::string msg) : m_message(msg) {} + void print_message() const { std::cout << m_message << '\n'; } + +private: + std::string m_message; }; } //unnamed namespace @@ -149,10 +184,14 @@ int main() { vm.callback_manager() .add_callback(false, "module_a", "ClassA", "say_hi()", make_function_bindable<&ClassA::say_hi>) .add_callback(false, "module_b", "ClassB", "greeting_message(_,_,_)", make_function_bindable<&ClassB::greeting_message>) + .add_callback(false, "module_b", "ClassB", "set_obj_c(_)", make_function_bindable<&ClassB::set_obj_c>) + .add_callback(false, "module_b", "ClassB", "get_obj_c()", make_function_bindable<&ClassB::get_obj_c>) + .add_callback(false, "module_c", "ClassC", "print_message()", make_function_bindable<&ClassC::print_message>) ; vm.class_manager() .add_class_maker("module_b", "ClassB", make_foreign_class) .add_class_maker("module_a", "ClassA", make_foreign_class) + .add_class_maker("module_c", "ClassC", make_foreign_class) ; //Example 1: invoke obj_b.do_action() from Wren passing an obj_a that's @@ -169,6 +208,11 @@ int main() { auto obj_b = make_wren_object(vm, wren::MN<"module_b", "ClassB">, "TheCpp"); wren::call(vm, obj_b, "do_action", obj_a, 4); + //Example 4: set obj_c on obj_c and query it back + wren::ForeignObject obj_c = make_wren_object(vm, wren::MN<"module_c", "ClassC">, "Message of ClassC object instantiated in C++"); + obj_b.object().set_obj_c(std::move(obj_c)); + obj_b.object().get_obj_c()->object().print_message(); + std::cout << "Quitting in 1 sec" << std::endl; std::this_thread::sleep_for(std::chrono::milliseconds(1000)); return 0; diff --git a/examples/call_cpp/meson.build b/examples/cpp_calls/meson.build similarity index 72% rename from examples/call_cpp/meson.build rename to examples/cpp_calls/meson.build index fc7a39a..d933478 100644 --- a/examples/call_cpp/meson.build +++ b/examples/cpp_calls/meson.build @@ -1,4 +1,4 @@ -executable('call_cpp', +executable('cpp_calls', 'main.cpp', dependencies: wrenpp_dep, install: false, diff --git a/examples/meson.build b/examples/meson.build index 4d7752e..4db7a01 100644 --- a/examples/meson.build +++ b/examples/meson.build @@ -2,4 +2,4 @@ subdir('dieroll') subdir('greet') subdir('calendar') subdir('math_vector') -subdir('call_cpp') +subdir('cpp_calls') diff --git a/include/wrenpp/detail/deref_ifn.hpp b/include/wrenpp/detail/deref_ifn.hpp new file mode 100644 index 0000000..efb5a74 --- /dev/null +++ b/include/wrenpp/detail/deref_ifn.hpp @@ -0,0 +1,42 @@ +/* Copyright 2020-2024, 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 + +namespace wren::detail { + +//Dereferences the given parameter if necessary, always returning an optionally +//cv-qualified ref +template +struct DerefIFN { + static typename std::remove_reference::type& deref ( + typename std::remove_reference::type& param + ) { + return param; + } +}; + +template +struct DerefIFN { + static T& deref (T* param) { return *param; } +}; + +//Add specialisations for std::unique_ptr & co? + +} //namespace wren::detail diff --git a/include/wrenpp/detail/foreign_object.hpp b/include/wrenpp/detail/foreign_object.hpp index 898fcaa..711d69b 100644 --- a/include/wrenpp/detail/foreign_object.hpp +++ b/include/wrenpp/detail/foreign_object.hpp @@ -22,6 +22,30 @@ namespace wren { +class VM; + +//This is meant to be a short lived object representing a still-in-a-slot +//foreign object. Upon casting it to ForeignObject it will create a handle to +//the data in given slot +template +class TempForeignObject { +public: + TempForeignObject (VM* vm, T* object, int slot_num) : + m_vm(vm), + m_data(object), + m_slot_num(slot_num) + { } + + VM* vm() const { return m_vm; } + T* data() const { return m_data; } + int slot_num() const { return m_slot_num; } + +private: + VM* m_vm; + T* m_data; + int m_slot_num; +}; + template class ForeignObject { public: @@ -29,6 +53,7 @@ public: ForeignObject (std::nullptr_t) : ForeignObject() {} ForeignObject (T* obj, Handle&& handle); ForeignObject (ForeignObject&&) = default; + ForeignObject (const TempForeignObject& temp_object); ~ForeignObject() noexcept = default; ForeignObject& operator= (ForeignObject&&) = default; @@ -58,6 +83,12 @@ inline ForeignObject::ForeignObject (T* obj, Handle&& handle) : m_object(obj) { } +template +inline ForeignObject::ForeignObject (const TempForeignObject& temp_object) : + m_handle(temp_object.vm(), temp_object.slot_num()), + m_object(temp_object.data()) +{ } + template inline void ForeignObject::set_to_slot (int num) { m_handle.set_to_slot(num); diff --git a/include/wrenpp/detail/is_kindof_handle.hpp b/include/wrenpp/detail/is_kindof_handle.hpp new file mode 100644 index 0000000..87cd4cf --- /dev/null +++ b/include/wrenpp/detail/is_kindof_handle.hpp @@ -0,0 +1,41 @@ +/* Copyright 2020-2024, 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 "deref_ifn.hpp" + +namespace wren::detail { + +template +struct IsKindOfHandleHelper { + static constexpr bool value = false; +}; +template +struct IsKindOfHandleHelper { + static constexpr bool value = true; + static void set_to_slot (int slot_num, TRaw obj) {DerefIFN::deref(obj).set_to_slot(slot_num);} +}; +template +struct IsKindOfHandleHelper, TRaw> { + static constexpr bool value = true; + static void set_to_slot (int slot_num, TRaw obj) {DerefIFN::deref(obj).set_to_slot(slot_num);} +}; +template +using IsKindOfHandle = IsKindOfHandleHelper::type>::type, T>; + +} //namespace wren::detail diff --git a/include/wrenpp/detail/setters_getters.hpp b/include/wrenpp/detail/setters_getters.hpp index 2e0c82c..c8657fe 100644 --- a/include/wrenpp/detail/setters_getters.hpp +++ b/include/wrenpp/detail/setters_getters.hpp @@ -19,7 +19,9 @@ #include "../vm.hpp" #include "../handle.hpp" +#include "foreign_object.hpp" #include "module_and_name.hpp" +#include "is_kindof_handle.hpp" #include #include #include @@ -34,8 +36,14 @@ namespace wren { templatestruct GetTypeToRetType{ static_assert(not std::is_fundamental_v, "User type expected"); static_assert(not std::is_pointer_v, "Unexpected pointer type"); - typedef std::remove_cv_t* type; + typedef TempForeignObject< std::remove_cv_t > type; }; + templatestruct GetTypeToRetType> { + //This doesn't make much sense, essentially you're asking wrenpp to + //give you a foreign object by vm/slot when you already have its + //handle + }; + templatestruct GetTypeToRetType> : TType>{}; template<>struct GetTypeToRetType : TType{}; template<>struct GetTypeToRetType : TType{}; template<>struct GetTypeToRetType : TType{}; @@ -166,16 +174,16 @@ namespace wren { template inline detail::GetTypeToRetType_t get (VM& vm, int slot_num) { - return foreign(vm, slot_num); + return {&vm, foreign(vm, slot_num), slot_num}; } template<> const char* get (VM& vm, int slot_num); - template <> double get (VM& vm, int slot_num); - template <> bool get (VM& vm, int slot_num); - template <> std::pair get> (VM& vm, int slot_num); + template<> double get (VM& vm, int slot_num); + template<> bool get (VM& vm, int slot_num); + template<> std::pair get> (VM& vm, int slot_num); template<> int get (VM& vm, int slot_num); template<> std::size_t get (VM& vm, int slot_num); template<> std::string get (VM& vm, int slot_num); - template <> std::string_view get (VM& vm, int slot_num); - template <> std::vector get> (VM& vm, int slot_num); + template<> std::string_view get (VM& vm, int slot_num); + template<> std::vector get> (VM& vm, int slot_num); } //namespace wren diff --git a/include/wrenpp/handle.hpp b/include/wrenpp/handle.hpp index 91deae5..d5c3039 100644 --- a/include/wrenpp/handle.hpp +++ b/include/wrenpp/handle.hpp @@ -27,6 +27,7 @@ namespace wren { Handle() noexcept; Handle (const Handle&) = delete; Handle (Handle&& other) noexcept; + Handle (VM* vm, int slot_num); Handle (VM* vm, WrenHandle* handle) noexcept : m_handle(handle), m_vm(vm) diff --git a/include/wrenpp/vm_fun.hpp b/include/wrenpp/vm_fun.hpp index c28bf38..3e38fed 100644 --- a/include/wrenpp/vm_fun.hpp +++ b/include/wrenpp/vm_fun.hpp @@ -26,6 +26,7 @@ #include "detail/construct_foreign_class.hpp" #include "detail/setters_getters.hpp" #include "detail/wren_class_name_from_type.hpp" +#include "detail/is_kindof_handle.hpp" #include "detail/foreign_object.hpp" #include #include @@ -62,6 +63,13 @@ namespace wren { void abort_fiber_with_error (VM& vm, int slot, std::string_view message); namespace detail { + template + struct CleanParamForGet { typedef typename std::decay::type type; }; + template + struct CleanParamForGet> { typedef T type; }; + template + using CleanParamForGetT = typename CleanParamForGet::type; + //Sets arguments so wren is ready to perform a function call. It doesn't //touch at slot 0, so eventual objects for method calls must be set //manually @@ -82,8 +90,8 @@ namespace wren { template static R call_implem (std::integer_sequence, VM& vm) { static_assert(sizeof...(Indices) == sizeof...(Args), "Mismatching argument count"); - T* obj = foreign(vm, 0); - return (obj->*Method)(get>(vm, Indices + 1)...); + T* const obj = foreign(vm, 0); + return (obj->*Method)(get>(vm, Indices + 1)...); } }; template @@ -95,8 +103,8 @@ namespace wren { template static R call_implem (std::integer_sequence, VM& vm) { static_assert(sizeof...(Indices) == sizeof...(Args), "Mismatching argument count"); - T* obj = foreign(vm, 0); - return (obj->*ConstMethod)(get>(vm, Indices + 1)...); + T* const obj = foreign(vm, 0); + return (obj->*ConstMethod)(get>(vm, Indices + 1)...); } }; template @@ -108,7 +116,7 @@ namespace wren { template static R call_implem (std::integer_sequence, VM& vm) { static_assert(sizeof...(Indices) == sizeof...(Args), "Mismatching argument count"); - return (*Function)(get>(vm, Indices + 1)...); + return (*Function)(get>(vm, Indices + 1)...); } }; @@ -125,10 +133,14 @@ namespace wren { else { auto ret = CallImplemProvider::call_implem(std::make_integer_sequence(), vm); //TODO: check for errors - vm.ensure_slots(1); + assert(vm.slot_count() >= 1); //vm.ensure_slots(1); if constexpr (std::is_fundamental::value) { set(vm, 0, ret); } + else if constexpr (IsKindOfHandle::value) { + IsKindOfHandle::set_to_slot(0, ret); + assert(vm.slot_type(0) == SlotType::Foreign); + } else { ModuleAndName wren_name = wren_class_name_from_type(vm); ForeignObject new_object = make_wren_object(vm, wren_name, std::move(ret)); diff --git a/src/handle.cpp b/src/handle.cpp index 210a6bc..8a7f8b8 100644 --- a/src/handle.cpp +++ b/src/handle.cpp @@ -21,6 +21,11 @@ #include namespace wren { + Handle::Handle (VM* vm, int slot_num) : + Handle(vm->slot_handle(slot_num)) + { + } + Handle::~Handle() noexcept { release(); } diff --git a/src/meson.build b/src/meson.build index fc11b51..a879dbd 100644 --- a/src/meson.build +++ b/src/meson.build @@ -19,6 +19,7 @@ wrenpp = library(meson.project_name(), 'class_manager.cpp', 'wren_class_name_from_type.cpp', 'module_and_name.cpp', + 'setters_getters.cpp', dependencies: [wren_dep, crc32_dep], include_directories: public_incl, install: (not meson.is_subproject() or get_option('default_library')=='shared'), diff --git a/src/setters_getters.cpp b/src/setters_getters.cpp new file mode 100644 index 0000000..f7147e9 --- /dev/null +++ b/src/setters_getters.cpp @@ -0,0 +1,21 @@ +/* Copyright 2020-2024, 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/detail/setters_getters.hpp" + +namespace wren { +} //namespace wren