Add support for object parameters to Wren calls
This commit is contained in:
parent
b00bd59027
commit
05298b6111
9 changed files with 282 additions and 37 deletions
175
examples/call_cpp/main.cpp
Normal file
175
examples/call_cpp/main.cpp
Normal file
|
@ -0,0 +1,175 @@
|
|||
/* 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/vm_fun.hpp"
|
||||
#include "wrenpp/def_configuration.hpp"
|
||||
#include "wrenpp/callback_manager.hpp"
|
||||
#include "wrenpp/class_manager.hpp"
|
||||
#include <chrono>
|
||||
#include <thread>
|
||||
#include <iostream>
|
||||
|
||||
namespace {
|
||||
constexpr char g_module_a[] =
|
||||
"foreign class ClassA {" R"script(
|
||||
construct new(msg) {
|
||||
System.print("Wren ClassA constructed")
|
||||
}
|
||||
|
||||
foreign say_hi()
|
||||
}
|
||||
)script";
|
||||
|
||||
constexpr char g_module_b[] = ""
|
||||
R"script(
|
||||
import "module_a" for ClassA
|
||||
|
||||
foreign class ClassB {
|
||||
construct new(nickname) {
|
||||
System.print("Wren ClassB constructed")
|
||||
}
|
||||
|
||||
do_action(obj_a, times) {
|
||||
System.print("Wren ClassB is doing its action %(times) times...")
|
||||
for (z in 1..times) {
|
||||
obj_a.say_hi()
|
||||
}
|
||||
greeting_message("John", "Doe", "\\(O.O)/")
|
||||
}
|
||||
|
||||
foreign greeting_message(first_name, family_name, emoji)
|
||||
}
|
||||
)script";
|
||||
|
||||
//This is three 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
|
||||
// do_action() on the object obj_b from the previous execution and pass the
|
||||
// C++ instance of ClassA to it
|
||||
//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
|
||||
constexpr char g_script[] = ""
|
||||
R"script(
|
||||
import "module_a" for ClassA
|
||||
import "module_b" for ClassB
|
||||
|
||||
var obj_b = ClassB.new("TheWren")
|
||||
obj_b.do_action(ClassA.new("instanciated from script"), 2)
|
||||
obj_b.greeting_message("Jane", "Doe", "ʕ·͡ᴥ·ʔ")
|
||||
)script";
|
||||
|
||||
class MyWrenConfig : public wren::DefConfiguration {
|
||||
public:
|
||||
char* load_module_fn (wren::VM* vm, std::string_view module_name) {
|
||||
if (module_name == "module_a")
|
||||
return copied(g_module_a, sizeof(g_module_a));
|
||||
else if (module_name == "module_b")
|
||||
return copied(g_module_b, sizeof(g_module_b));
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void load_module_complete_fn (wren::VM* vm, std::string_view module_name, char* buff) {
|
||||
static_cast<void>(vm);
|
||||
static_cast<void>(module_name);
|
||||
delete[] buff;
|
||||
}
|
||||
|
||||
private:
|
||||
char* copied (const char* source, std::size_t len) {
|
||||
char* const buff = new char[len];
|
||||
std::copy(source, source + len / sizeof(source[0]), buff);
|
||||
return buff;
|
||||
}
|
||||
};
|
||||
|
||||
struct ClassA {
|
||||
explicit ClassA (std::string msg) : m_message(msg) {
|
||||
std::cout << "C++ ClassA constructed\n";
|
||||
}
|
||||
ClassA (const ClassA&) = delete;
|
||||
ClassA (ClassA&&) = delete;
|
||||
|
||||
void say_hi() {
|
||||
std::cout << "C++ ClassA says \"" << m_message << "\"!\n";
|
||||
}
|
||||
|
||||
std::string m_message;
|
||||
};
|
||||
|
||||
class ClassB {
|
||||
public:
|
||||
explicit ClassB (std::string nickname) : m_nickname(nickname) {
|
||||
std::cout << "C++ ClassB constructed\n";
|
||||
}
|
||||
|
||||
ClassB (const ClassB&) = delete;
|
||||
ClassB (ClassB&&) = delete;
|
||||
|
||||
void greeting_message (
|
||||
std::string_view first_name,
|
||||
const char* family_name,
|
||||
const std::string& emoji
|
||||
) {
|
||||
std::cout << "C++ ClassB says \"hello " << first_name << " '" <<
|
||||
m_nickname << "' " << family_name << "\" " << emoji << '\n';
|
||||
}
|
||||
|
||||
private:
|
||||
std::string m_nickname;
|
||||
};
|
||||
} //unnamed namespace
|
||||
|
||||
int main() {
|
||||
using wren::make_function_bindable;
|
||||
using wren::make_foreign_class;
|
||||
using wren::make_wren_object;
|
||||
|
||||
//We need a custom one to deal with custom module loading for
|
||||
//module_a and module_b
|
||||
MyWrenConfig config;
|
||||
wren::VM vm(&config, nullptr);
|
||||
|
||||
|
||||
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>)
|
||||
;
|
||||
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>)
|
||||
;
|
||||
|
||||
//Example 1: invoke obj_b.do_action() from Wren passing an obj_a that's
|
||||
//also instantiated from within the Wren script
|
||||
vm.interpret("main", g_script);
|
||||
|
||||
//Example 2: invoke obj_b.do_action() from C++ passing a new obj_a which
|
||||
//is instantiated from C++
|
||||
wren::ForeignObject<ClassA> obj_a = make_wren_object<ClassA>(vm, wren::MN<"module_a", "ClassA">, "instantiated from c++");
|
||||
wren::call<void>(vm, wren::MN<"main", "obj_b">, "do_action", obj_a, 3);
|
||||
|
||||
//Example 3: invoke obj_b.do_action() from C++, where obj_b is also
|
||||
//instantiated from C++, and pass the C++ obj_a instance to it
|
||||
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);
|
||||
|
||||
std::cout << "Quitting in 1 sec" << std::endl;
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
|
||||
return 0;
|
||||
}
|
5
examples/call_cpp/meson.build
Normal file
5
examples/call_cpp/meson.build
Normal file
|
@ -0,0 +1,5 @@
|
|||
executable('call_cpp',
|
||||
'main.cpp',
|
||||
dependencies: wrenpp_dep,
|
||||
install: false,
|
||||
)
|
|
@ -1,4 +1,4 @@
|
|||
/* Copyright 2020-2022, Michele Santullo
|
||||
/* Copyright 2020-2024, Michele Santullo
|
||||
* This file is part of wrenpp.
|
||||
*
|
||||
* Wrenpp is free software: you can redistribute it and/or modify
|
||||
|
@ -63,8 +63,6 @@ void user_get_env (wren::VM& vm, wren::ModuleAndName) {
|
|||
} //unnamed namespace
|
||||
|
||||
int main() {
|
||||
//typedef wren::ModuleAndName MN;
|
||||
|
||||
wren::DefConfiguration config;
|
||||
wren::VM vm(&config, nullptr);
|
||||
vm.callback_manager().add_callback(true, "main", "User", "get_env(_)", [](){return &user_get_env;});
|
||||
|
|
|
@ -2,3 +2,4 @@ subdir('dieroll')
|
|||
subdir('greet')
|
||||
subdir('calendar')
|
||||
subdir('math_vector')
|
||||
subdir('call_cpp')
|
||||
|
|
63
include/wrenpp/detail/foreign_object.hpp
Normal file
63
include/wrenpp/detail/foreign_object.hpp
Normal file
|
@ -0,0 +1,63 @@
|
|||
/* 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 "wrenpp/handle.hpp"
|
||||
#include <utility>
|
||||
|
||||
namespace wren {
|
||||
|
||||
template <typename T>
|
||||
class ForeignObject {
|
||||
public:
|
||||
ForeignObject() = default;
|
||||
ForeignObject (std::nullptr_t) : ForeignObject() {}
|
||||
ForeignObject (T* obj, Handle&& handle);
|
||||
~ForeignObject() noexcept = default;
|
||||
|
||||
void set_to_slot (int num);
|
||||
T& object() { return *m_object; }
|
||||
const T& object() const { return *m_object; }
|
||||
Handle& handle() { return m_handle; }
|
||||
const Handle& handle() const { return m_handle; }
|
||||
bool is_valid() const { return nullptr != m_object; }
|
||||
|
||||
operator T&() { return object(); }
|
||||
operator const T&() const { return object(); }
|
||||
operator Handle&() { return handle(); }
|
||||
operator const Handle&() const { return handle(); }
|
||||
bool operator!= (std::nullptr_t) const { return nullptr != m_object; }
|
||||
bool operator== (std::nullptr_t) const { return nullptr == m_object; }
|
||||
|
||||
private:
|
||||
Handle m_handle;
|
||||
T* m_object{nullptr};
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
inline ForeignObject<T>::ForeignObject (T* obj, Handle&& handle) :
|
||||
m_handle(std::move(handle)),
|
||||
m_object(obj)
|
||||
{ }
|
||||
|
||||
template <typename T>
|
||||
inline void ForeignObject<T>::set_to_slot (int num) {
|
||||
m_handle.set_to_slot(num);
|
||||
}
|
||||
|
||||
} //namespace wren
|
|
@ -1,4 +1,4 @@
|
|||
/* Copyright 2020-2022, Michele Santullo
|
||||
/* Copyright 2020-2024, Michele Santullo
|
||||
* This file is part of wrenpp.
|
||||
*
|
||||
* Wrenpp is free software: you can redistribute it and/or modify
|
||||
|
@ -18,6 +18,7 @@
|
|||
#pragma once
|
||||
|
||||
#include "../vm.hpp"
|
||||
#include "../handle.hpp"
|
||||
#include "module_and_name.hpp"
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
@ -87,6 +88,8 @@ namespace wren {
|
|||
void set (VM& vm, int slot_num, const char* beg, const char* end);
|
||||
void set (VM& vm, int slot_num, int value);
|
||||
void set (VM& vm, int slot_num, std::size_t value);
|
||||
void set (VM& vm, int slot_num, const Handle& handle);
|
||||
void set (VM& vm, int slot_num, const ModuleAndName& name);
|
||||
std::string_view slot_string_view (VM& vm, int slot_num);
|
||||
template <typename T> detail::GetTypeToRetType_t<T> get (VM& vm, int slot_num);
|
||||
template <typename T> T* foreign (VM& vm, int slot_num);
|
||||
|
@ -138,6 +141,14 @@ namespace wren {
|
|||
vm.set_slot_double(slot_num, static_cast<double>(value));
|
||||
}
|
||||
|
||||
inline void set (VM& vm, int slot_num, const Handle& handle) {
|
||||
vm.set_slot_handle(handle, slot_num);
|
||||
}
|
||||
|
||||
inline void set (VM& vm, int slot_num, const ModuleAndName& name) {
|
||||
vm.variable_or_throw(name.module_name_char(), name.class_name_char(), slot_num);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline T* foreign (VM& vm, int slot_num) {
|
||||
T* obj = static_cast<T*>(vm.slot_foreign(slot_num));
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* Copyright 2020-2022, Michele Santullo
|
||||
/* Copyright 2020-2024, Michele Santullo
|
||||
* This file is part of wrenpp.
|
||||
*
|
||||
* Wrenpp is free software: you can redistribute it and/or modify
|
||||
|
@ -42,6 +42,8 @@ namespace wren {
|
|||
operator WrenHandle*() const { return m_handle; }
|
||||
operator bool() const { return nullptr != m_handle; }
|
||||
|
||||
void set_to_slot (int num);
|
||||
|
||||
private:
|
||||
WrenHandle* m_handle;
|
||||
VM* m_vm;
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* Copyright 2020-2022, Michele Santullo
|
||||
/* Copyright 2020-2024, Michele Santullo
|
||||
* This file is part of wrenpp.
|
||||
*
|
||||
* Wrenpp is free software: you can redistribute it and/or modify
|
||||
|
@ -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/foreign_object.hpp"
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <type_traits>
|
||||
|
@ -46,11 +47,11 @@ namespace wren {
|
|||
R call (VM& vm, ModuleAndName object, const char (&method)[N], const Args&... args);
|
||||
|
||||
template <typename T, typename... Args>
|
||||
T* make_wren_object(VM& vm, ModuleAndName mn, Args&&... args);
|
||||
ForeignObject<T> make_wren_object(VM& vm, ModuleAndName mn, Args&&... args);
|
||||
|
||||
#if defined(WRENPP_WITH_NAME_GUESSING)
|
||||
template <typename T, dhandy::bt::string Mod, typename... Args>
|
||||
T* make_wren_object(VM& vm, Args&&... args);
|
||||
ForeignObject<T> make_wren_object(VM& vm, Args&&... args);
|
||||
#endif
|
||||
|
||||
void interpret (VM& vm, const std::string& module_name, const std::string& script);
|
||||
|
@ -61,28 +62,13 @@ namespace wren {
|
|||
void abort_fiber_with_error (VM& vm, int slot, std::string_view message);
|
||||
|
||||
namespace detail {
|
||||
template <typename T>
|
||||
inline void set_single_for_call (VM& vm, int slot, const T& value) {
|
||||
set(vm, slot, value);
|
||||
}
|
||||
|
||||
template <>
|
||||
inline void set_single_for_call (VM& vm, int slot_num, const Handle& handle) {
|
||||
vm.set_slot_handle(handle, slot_num);
|
||||
}
|
||||
|
||||
template <>
|
||||
inline void set_single_for_call (VM& vm, int slot_num, const ModuleAndName& name) {
|
||||
variable(vm, name, slot_num);
|
||||
}
|
||||
|
||||
//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
|
||||
template <typename... Args, int... Indices>
|
||||
inline void set_for_call (std::integer_sequence<int, Indices...>, VM& vm, const Args&... args) {
|
||||
vm.ensure_slots(sizeof...(Args));
|
||||
(set_single_for_call(vm, Indices + 1, args), ...);
|
||||
(set(vm, Indices + 1, args), ...);
|
||||
}
|
||||
|
||||
template <auto V>
|
||||
|
@ -145,7 +131,7 @@ namespace wren {
|
|||
}
|
||||
else {
|
||||
ModuleAndName wren_name = wren_class_name_from_type<R>(vm);
|
||||
R* const new_object = make_wren_object<R>(vm, wren_name, std::move(ret));
|
||||
ForeignObject<R> new_object = make_wren_object<R>(vm, wren_name, std::move(ret));
|
||||
static_cast<void>(new_object);
|
||||
}
|
||||
}
|
||||
|
@ -197,15 +183,13 @@ namespace wren {
|
|||
inline R call (VM& vm, const Handle& object, const char (&method)[N], const Args&... args) {
|
||||
using dhandy::bt::string;
|
||||
|
||||
const constexpr char dummy_name_buff[N] = {0};
|
||||
const constexpr auto params = string<N, char>(dummy_name_buff) +
|
||||
string("(") +
|
||||
const constexpr auto params = string("(") +
|
||||
((static_cast<void>(args), string(",_")) + ... + string("")).template substr<1>() +
|
||||
string(")");
|
||||
;
|
||||
char cat_buff[params.size() + 1];
|
||||
char cat_buff[params.size() + 1 + N - 1];
|
||||
std::copy(method, method + N - 1, cat_buff);
|
||||
std::copy(params.data() + N - 1, params.data() + params.size() + 1, cat_buff + N - 1);
|
||||
std::copy(params.data(), params.data() + params.size() + 1, cat_buff + N - 1);
|
||||
return call<R, Args...>(vm, object, vm.make_call_handle(cat_buff), args...);
|
||||
}
|
||||
|
||||
|
@ -218,12 +202,12 @@ namespace wren {
|
|||
}
|
||||
|
||||
template <typename T, typename... Args>
|
||||
inline T* make_wren_object(VM& vm, ModuleAndName mn, Args&&... args) {
|
||||
variable(vm, mn, 0);
|
||||
inline ForeignObject<T> make_wren_object(VM& vm, ModuleAndName mn, Args&&... args) {
|
||||
variable_ensure_slot(vm, mn, 0);
|
||||
void* const mem = vm.set_slot_new_foreign(0, 0, sizeof(T));
|
||||
try {
|
||||
T* const obj = new(mem) T{std::forward<Args>(args)...};
|
||||
return obj;
|
||||
return {obj, vm.slot_handle(0)};
|
||||
}
|
||||
catch (const std::runtime_error& err) {
|
||||
abort_fiber_with_error(vm, 1, err.what());
|
||||
|
@ -233,7 +217,7 @@ namespace wren {
|
|||
|
||||
#if defined(WRENPP_WITH_NAME_GUESSING)
|
||||
template <typename T, dhandy::bt::string Mod, typename... Args>
|
||||
inline T* make_wren_object(VM& vm, Args&&... args) {
|
||||
inline ForeignObject<T> make_wren_object(VM& vm, Args&&... args) {
|
||||
return make_wren_object<T>(
|
||||
vm,
|
||||
MN<Mod, g_guessed_class_name<T>>,
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* Copyright 2020-2022, Michele Santullo
|
||||
/* Copyright 2020-2024, Michele Santullo
|
||||
* This file is part of wrenpp.
|
||||
*
|
||||
* Wrenpp is free software: you can redistribute it and/or modify
|
||||
|
@ -18,6 +18,7 @@
|
|||
#include "wrenpp/handle.hpp"
|
||||
#include "wrenpp/vm.hpp"
|
||||
#include <cassert>
|
||||
#include <ciso646>
|
||||
|
||||
namespace wren {
|
||||
Handle::~Handle() noexcept {
|
||||
|
@ -25,7 +26,12 @@ namespace wren {
|
|||
}
|
||||
|
||||
void Handle::release() noexcept {
|
||||
assert(m_vm);
|
||||
m_vm->release_handle(*this);
|
||||
assert(m_vm or not m_handle);
|
||||
if (m_vm)
|
||||
m_vm->release_handle(*this);
|
||||
}
|
||||
|
||||
void Handle::set_to_slot (int slot) {
|
||||
m_vm->set_slot_handle(*this, slot);
|
||||
}
|
||||
} //namespace wren
|
||||
|
|
Loading…
Reference in a new issue