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.
This commit is contained in:
King_DuckZ 2024-05-28 08:25:19 +02:00
parent d2a1187ad5
commit 2c99251b27
13 changed files with 223 additions and 17 deletions

View file

@ -128,7 +128,7 @@ int main() {
vm.interpret("main", g_test_script);
Calendar* const cale = std::get<0>(wren::variables<Calendar>(vm, MN<"main", "cale">));
Calendar* const cale = std::get<0>(wren::variables<Calendar>(vm, MN<"main", "cale">)).data();
cale->print_appointments();
return 0;

View file

@ -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<ClassC> obj) {
m_obj_c = std::move(obj);
}
wren::ForeignObject<ClassC>* get_obj_c() { return &m_obj_c; }
private:
std::string m_nickname;
wren::ForeignObject<ClassC> 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<ClassB, std::string>)
.add_class_maker("module_a", "ClassA", make_foreign_class<ClassA, std::string>)
.add_class_maker("module_c", "ClassC", make_foreign_class<ClassC, std::string>)
;
//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<ClassB>(vm, wren::MN<"module_b", "ClassB">, "TheCpp");
wren::call<void>(vm, obj_b, "do_action", obj_a, 4);
//Example 4: set obj_c on obj_c and query it back
wren::ForeignObject<ClassC> obj_c = make_wren_object<ClassC>(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;

View file

@ -1,4 +1,4 @@
executable('call_cpp',
executable('cpp_calls',
'main.cpp',
dependencies: wrenpp_dep,
install: false,

View file

@ -2,4 +2,4 @@ subdir('dieroll')
subdir('greet')
subdir('calendar')
subdir('math_vector')
subdir('call_cpp')
subdir('cpp_calls')

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <type_traits>
namespace wren::detail {
//Dereferences the given parameter if necessary, always returning an optionally
//cv-qualified ref
template <typename T>
struct DerefIFN {
static typename std::remove_reference<T>::type& deref (
typename std::remove_reference<T>::type& param
) {
return param;
}
};
template <typename T>
struct DerefIFN<T*> {
static T& deref (T* param) { return *param; }
};
//Add specialisations for std::unique_ptr & co?
} //namespace wren::detail

View file

@ -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 <typename T>
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 <typename T>
class ForeignObject {
public:
@ -29,6 +53,7 @@ public:
ForeignObject (std::nullptr_t) : ForeignObject() {}
ForeignObject (T* obj, Handle&& handle);
ForeignObject (ForeignObject&&) = default;
ForeignObject (const TempForeignObject<T>& temp_object);
~ForeignObject() noexcept = default;
ForeignObject& operator= (ForeignObject&&) = default;
@ -58,6 +83,12 @@ inline ForeignObject<T>::ForeignObject (T* obj, Handle&& handle) :
m_object(obj)
{ }
template <typename T>
inline ForeignObject<T>::ForeignObject (const TempForeignObject<T>& temp_object) :
m_handle(temp_object.vm(), temp_object.slot_num()),
m_object(temp_object.data())
{ }
template <typename T>
inline void ForeignObject<T>::set_to_slot (int num) {
m_handle.set_to_slot(num);

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "deref_ifn.hpp"
namespace wren::detail {
template <typename T, typename TRaw>
struct IsKindOfHandleHelper {
static constexpr bool value = false;
};
template <typename TRaw>
struct IsKindOfHandleHelper<Handle, TRaw> {
static constexpr bool value = true;
static void set_to_slot (int slot_num, TRaw obj) {DerefIFN<TRaw>::deref(obj).set_to_slot(slot_num);}
};
template <typename U, typename TRaw>
struct IsKindOfHandleHelper<ForeignObject<U>, TRaw> {
static constexpr bool value = true;
static void set_to_slot (int slot_num, TRaw obj) {DerefIFN<TRaw>::deref(obj).set_to_slot(slot_num);}
};
template <typename T>
using IsKindOfHandle = IsKindOfHandleHelper<typename std::remove_pointer<typename std::remove_cvref<T>::type>::type, T>;
} //namespace wren::detail

View file

@ -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 <string_view>
#include <vector>
#include <tuple>
@ -34,8 +36,14 @@ namespace wren {
template<typename T>struct GetTypeToRetType{
static_assert(not std::is_fundamental_v<T>, "User type expected");
static_assert(not std::is_pointer_v<T>, "Unexpected pointer type");
typedef std::remove_cv_t<T>* type;
typedef TempForeignObject< std::remove_cv_t<T> > type;
};
template<typename T>struct GetTypeToRetType<ForeignObject<T>> {
//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
};
template<typename T>struct GetTypeToRetType<TempForeignObject<T>> : TType<TempForeignObject<T>>{};
template<>struct GetTypeToRetType<const char*> : TType<const char*>{};
template<>struct GetTypeToRetType<double> : TType<double>{};
template<>struct GetTypeToRetType<bool> : TType<bool>{};
@ -166,7 +174,7 @@ namespace wren {
template <typename T>
inline detail::GetTypeToRetType_t<T> get (VM& vm, int slot_num) {
return foreign<T>(vm, slot_num);
return {&vm, foreign<T>(vm, slot_num), slot_num};
}
template<> const char* get<const char*> (VM& vm, int slot_num);

View file

@ -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)

View file

@ -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 <string>
#include <string_view>
@ -62,6 +63,13 @@ namespace wren {
void abort_fiber_with_error (VM& vm, int slot, std::string_view message);
namespace detail {
template <typename T>
struct CleanParamForGet { typedef typename std::decay<T>::type type; };
template <typename T>
struct CleanParamForGet<ForeignObject<T>> { typedef T type; };
template <typename T>
using CleanParamForGetT = typename CleanParamForGet<T>::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 <int... Indices>
static R call_implem (std::integer_sequence<int, Indices...>, VM& vm) {
static_assert(sizeof...(Indices) == sizeof...(Args), "Mismatching argument count");
T* obj = foreign<T>(vm, 0);
return (obj->*Method)(get<std::decay_t<Args>>(vm, Indices + 1)...);
T* const obj = foreign<T>(vm, 0);
return (obj->*Method)(get<CleanParamForGetT<Args>>(vm, Indices + 1)...);
}
};
template <typename T, typename R, typename... Args, R(T::*ConstMethod)(Args...)const>
@ -95,8 +103,8 @@ namespace wren {
template <int... Indices>
static R call_implem (std::integer_sequence<int, Indices...>, VM& vm) {
static_assert(sizeof...(Indices) == sizeof...(Args), "Mismatching argument count");
T* obj = foreign<T>(vm, 0);
return (obj->*ConstMethod)(get<std::decay_t<Args>>(vm, Indices + 1)...);
T* const obj = foreign<T>(vm, 0);
return (obj->*ConstMethod)(get<CleanParamForGetT<Args>>(vm, Indices + 1)...);
}
};
template <typename R, typename... Args, R(*Function)(Args...)>
@ -108,7 +116,7 @@ namespace wren {
template <int... Indices>
static R call_implem (std::integer_sequence<int, Indices...>, VM& vm) {
static_assert(sizeof...(Indices) == sizeof...(Args), "Mismatching argument count");
return (*Function)(get<std::decay_t<Args>>(vm, Indices + 1)...);
return (*Function)(get<CleanParamForGetT<Args>>(vm, Indices + 1)...);
}
};
@ -125,10 +133,14 @@ namespace wren {
else {
auto ret = CallImplemProvider<V>::call_implem(std::make_integer_sequence<int, argument_count>(), vm);
//TODO: check for errors
vm.ensure_slots(1);
assert(vm.slot_count() >= 1); //vm.ensure_slots(1);
if constexpr (std::is_fundamental<R>::value) {
set(vm, 0, ret);
}
else if constexpr (IsKindOfHandle<R>::value) {
IsKindOfHandle<R>::set_to_slot(0, ret);
assert(vm.slot_type(0) == SlotType::Foreign);
}
else {
ModuleAndName wren_name = wren_class_name_from_type<R>(vm);
ForeignObject<R> new_object = make_wren_object<R>(vm, wren_name, std::move(ret));

View file

@ -21,6 +21,11 @@
#include <ciso646>
namespace wren {
Handle::Handle (VM* vm, int slot_num) :
Handle(vm->slot_handle(slot_num))
{
}
Handle::~Handle() noexcept {
release();
}

View file

@ -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'),

21
src/setters_getters.cpp Normal file
View file

@ -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 <http://www.gnu.org/licenses/>.
*/
#include "wrenpp/detail/setters_getters.hpp"
namespace wren {
} //namespace wren