/* 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) } )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(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 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) .add_class_maker("module_a", "ClassA", 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); std::cout << "Quitting in 1 sec" << std::endl; std::this_thread::sleep_for(std::chrono::milliseconds(1000)); return 0; }