wrenpp/examples/cpp_calls/main.cpp
King_DuckZ 2c99251b27 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.
2024-05-28 08:25:19 +02:00

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;
}