/* 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 . */ #include "wrenpp/vm_fun.hpp" #include "wrenpp/def_configuration.hpp" #include "wrenpp/callback_manager.hpp" #include "wrenpp/class_manager.hpp" #include #include #include 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(vm); static_cast(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 obj) { m_obj_c = std::move(obj); } wren::ForeignObject* get_obj_c() { return &m_obj_c; } private: std::string m_nickname; wren::ForeignObject 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) .add_class_maker("module_a", "ClassA", make_foreign_class) .add_class_maker("module_c", "ClassC", make_foreign_class) ; //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 obj_a = make_wren_object(vm, wren::MN<"module_a", "ClassA">, "instantiated from c++"); wren::call(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(vm, wren::MN<"module_b", "ClassB">, "TheCpp"); wren::call(vm, obj_b, "do_action", obj_a, 4); //Example 4: set obj_c on obj_c and query it back wren::ForeignObject obj_c = make_wren_object(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; }