King_DuckZ
2c99251b27
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.
219 lines
6.7 KiB
C++
219 lines
6.7 KiB
C++
/* 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)
|
|
foreign set_obj_c (obj)
|
|
foreign get_obj_c()
|
|
}
|
|
)script";
|
|
|
|
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
|
|
// 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
|
|
//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 {
|
|
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));
|
|
else if (module_name == "module_c")
|
|
return copied(g_module_c, sizeof(g_module_c));
|
|
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 ClassC;
|
|
|
|
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';
|
|
}
|
|
|
|
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
|
|
|
|
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>)
|
|
.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
|
|
//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);
|
|
|
|
//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;
|
|
}
|