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:
parent
d2a1187ad5
commit
2c99251b27
13 changed files with 223 additions and 17 deletions
|
@ -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;
|
||||
|
|
|
@ -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;
|
|
@ -1,4 +1,4 @@
|
|||
executable('call_cpp',
|
||||
executable('cpp_calls',
|
||||
'main.cpp',
|
||||
dependencies: wrenpp_dep,
|
||||
install: false,
|
|
@ -2,4 +2,4 @@ subdir('dieroll')
|
|||
subdir('greet')
|
||||
subdir('calendar')
|
||||
subdir('math_vector')
|
||||
subdir('call_cpp')
|
||||
subdir('cpp_calls')
|
||||
|
|
42
include/wrenpp/detail/deref_ifn.hpp
Normal file
42
include/wrenpp/detail/deref_ifn.hpp
Normal 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
|
|
@ -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);
|
||||
|
|
41
include/wrenpp/detail/is_kindof_handle.hpp
Normal file
41
include/wrenpp/detail/is_kindof_handle.hpp
Normal 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
|
|
@ -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,16 +174,16 @@ 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);
|
||||
template <> double get<double> (VM& vm, int slot_num);
|
||||
template <> bool get<bool> (VM& vm, int slot_num);
|
||||
template <> std::pair<const char*, int> get<std::pair<const char*,int>> (VM& vm, int slot_num);
|
||||
template<> double get<double> (VM& vm, int slot_num);
|
||||
template<> bool get<bool> (VM& vm, int slot_num);
|
||||
template<> std::pair<const char*, int> get<std::pair<const char*,int>> (VM& vm, int slot_num);
|
||||
template<> int get<int> (VM& vm, int slot_num);
|
||||
template<> std::size_t get<std::size_t> (VM& vm, int slot_num);
|
||||
template<> std::string get<std::string> (VM& vm, int slot_num);
|
||||
template <> std::string_view get<std::string_view> (VM& vm, int slot_num);
|
||||
template <> std::vector<char> get<std::vector<char>> (VM& vm, int slot_num);
|
||||
template<> std::string_view get<std::string_view> (VM& vm, int slot_num);
|
||||
template<> std::vector<char> get<std::vector<char>> (VM& vm, int slot_num);
|
||||
} //namespace wren
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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
21
src/setters_getters.cpp
Normal 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
|
Loading…
Reference in a new issue