Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- Вам нужно отключить /permissive-флаг, который включен по умолчанию, но все еще глючит. Это потому, что вы используете /permissive-флаг компилятора, который не установлен, но по умолчанию для MSVC.
- Если вы получаете лавину ошибок (особенно в отношении auto), возможно, вы не включили режим C ++ 14 / C ++ 17 для своего компилятора. Добавьте один из std=c++17, std=c++1zили std=c++1yк вашим опциям компилятора. По умолчанию это всегда включено для компиляторов VC ++ в Visual Studio и в друзьях, но для g ++ и clang ++ требуется флаг (если вы не используете GCC 6.0 или выше).
- _CRT_SECURE_NO_WARNINGS /Zc:twoPhase-
- _CRT_SECURE_NO_DEPRECATE
- _CRT_NONSTDC_NO_DEPRECATE
- #define SOL_ALL_SAFETIES_ON 1
- #pragma warning (disable : 4996).
- #pragma warning (disable : 26439).
- #include<iostream>
- #include"include/lua.hpp"
- #include <sol/sol.hpp>
- #include <cassert>
- using namespace std;
- using namespace sol;
- //Стек(англ.stack — стопка; читается стэк) — абстрактный тип данных, представляющий собой список элементов,
- //организованных по принципу LIFO(англ.last in — first out, «последний вошёл — первым вышел»)
- int main(int argc, char* argv[]) {
- cout << "=== opening a state ===" << endl;
- state lua; // Lua состояние.
- lua.open_libraries(lib::base, lib::package); // открыть доп.библиотеки.
- lua.script("print('bark bark bark!')");
- cout <<endl;
- return 0;
- };
- const char* LUA = R"(
- print('Hello world!')
- )";
- int main(int argc, char* argv[]) {
- state lua;// Lua состояние.
- lua.open_libraries(lib::base, lib::package); // открыть доп.библиотеки.
- lua.script(LUA);// запуск Lua скрипта.
- return 0;
- };
- Если это работает, вы готовы начать! Первая строка создает lua_State и будет удерживать ее в течение всей области, в которой она объявлена (например, от открытия {до закрытия }). Он автоматически закроет / очистит это состояние lua, когда оно будет уничтожено.
- Вторая строка открывает одну предоставленную lua библиотеку, «base». Есть несколько других библиотек, которые поставляются с lua, которые вы можете открыть по умолчанию, и они включены в перечисление sol :: lib . Вы можете открыть несколько базовых библиотек, указав несколько sol::lib аргументов:
- int main(int argc, char* argv[]) {
- cout << "=== basic ===" << endl;
- // создаем пустое состояние lua
- state lua;
- // по умолчанию библиотеки не открываются
- // вы можете открывать библиотеки, используя open_libraries
- // библиотеки находятся в классе en en sol :: lib
- lua.open_libraries(lib::base);
- // вы можете открыть все библиотеки, не передавая аргументы
- // lua.open_libraries ();
- // вызвать код lua напрямую
- lua.script("print('hello world')");
- // вызываем код lua и проверяем, правильно ли он загружен и работает:
- auto handler = &script_default_on_error;
- lua.script("print('hello again, world')", handler);
- // Используем пользовательский обработчик ошибок, если он вам нужен
- // Это вызывается, когда результат плохой
- auto simple_handler = [](lua_State*, protected_function_result result) {
- // Вы можете просто пропустить это, чтобы сайт вызова справился с этим
- return result;
- };
- // вышеуказанная лямбда идентична sol :: simple_on_error, но это
- // показано здесь, чтобы показать, что вы можете написать что угодно
- {
- auto result = lua.script("print('hello hello again, world') \n return 24", simple_handler);
- if (result.valid()) {
- cout << "the third script worked, and a double-hello statement should appear above this one!" << endl;
- int value = result;
- //c_assert(value == 24);
- }
- else {
- cout << "the third script failed, check the result type for more information!" << endl;
- }
- }
- {
- auto result = lua.script("does.not.exist", simple_handler);
- if (result.valid()) {
- cout << "the fourth script worked, which it wasn't supposed to! Panic!" << endl;
- int value = result;
- //c_assert(value == 24);
- }
- else {
- error err = result;
- cout << "the fourth script failed, which was intentional!\t\nError: " << err.what() << endl;
- }
- }
- cout << endl;
- return 0;
- };
- Еще пример.
- int main(int argc, char* argv[]) {
- const char* LUA = R"(
- print('hello world')
- )";
- state lua;// Lua состояние.
- lua.open_libraries(lib::base, lib::package); /* открыть доп.библиотеки.
- //запуск lua скрипта. Без пользовательского обработчика ошибок.
- //lua.script(LUA);
- // вызываем код lua и проверяем, правильно ли он загружен и работает:
- auto handler = &script_default_on_error;
- lua.script("print('hello again, world')", handler);
- /* Используем пользовательский обработчик ошибок, если он вам нужен это вызывается, когда результат плохой.
- Вы можете просто пропустить это, чтобы скрипт вызова справился с этим. Эта лямбда идентична
- sol :: simple_on_error, но это показано здесь, чтобы показать, что вы можете написать что угодно.*/
- typedef protected_function_result result, pro_func_res;
- auto simple_handler = [](lua_State*, pro_func_res result) {return result; }; {
- auto result = lua.script("print('hello hello again') \n return 24", simple_handler);
- if (result.valid()) { cout << "the script is working" << endl;// если ошибок нет.
- int value = result;} //c_assert(value == 24);
- else { cout << "script call error, check the result type for more info!" << endl; }
- }
- return 0;
- };
- Вариант 2.
- const char* LUA = R"(
- print('Hello world!')
- )";
- int main(int argc, char* argv[]) {
- state lua;// Lua состояние.
- lua.open_libraries(lib::base, lib::package); // открыть доп.библиотеки.
- typedef protected_function_result result, pfres;// Назвать структуру короче.
- auto simple_handler = [](lua_State*, pfres res) {return res; }; {
- auto res = lua.script(LUA, simple_handler);
- if (result.valid()) {
- cout << "the script is good working" << endl;}// если ошибок нет.
- else { cout << "script call error, check the result type for more info!" << endl; }
- }
- return 0;
- };
- Если вы уже используете lua и просто хотите использовать его sol в некоторых местах, вы можете использовать state_view:
- используя state_view.
- sol::state_view точно такой же sol::state, но он не управляет временем жизни lua_State*. Таким образом, вы получаете все вкусности, которые идут с sol::state без каких-либо последствий собственности.
- Sol не имеет компонентов инициализации, которые должны намеренно оставаться живыми в течение всей программы. Он полностью самодостаточен и использует сборщики мусора lua и различные методы реализации, не требующие состояния C ++. После того, как вы это сделаете, вам доступны все возможности sol , а затем и некоторые!
- sol::state_view также полезно, когда вы хотите создать DLL, которая загружает некоторый модуль Lua через require.
- Вы также можете вызвать require и предоставить строку файла сценария или что-то, что возвращает объект, который вы установили равным чему-то в C ++. Для этого вы можете использовать требуемые функции.
- Помните, что sol может быть настолько легким, насколько вам нужно: почти все типы Lua sol принимают lua_State* аргумент, а затем второй аргумент индекса стека, то есть вы можете использовать таблицы , функции lua , сопрограммы и другие объекты, полученные из ссылок, которые предоставляют надлежащие Конструктор для вашего использования. Вы также можете установить типы пользователей и другие нужные вам вещи, не меняя всю архитектуру за один раз. int index
- Вы даже можете настроить его для работы с внешней оболочкой / фреймворком / библиотекой Lua .
- Обратите внимание, что вы также можете создавать нестандартные указатели и ссылочные типы с настраиваемым подсчетом ссылок, что также хорошо работает с системой. Смотрите unique_usertype_traits <T>, чтобы увидеть как! Пользовательские типы также упоминаются в руководстве по настройке.
- Есть несколько вещей, которые создают sol::state для вас. Вы можете прочитать об этом в документации sol :: state и вызывать эти функции напрямую, если они вам нужны.
- int something_in_my_system(lua_State* L) {
- // // начните использовать sol с уже существующей системой
- state_view lua(L); // non-owning
- lua.script("print('bark bark bark!')");
- // get the table off the top of the stack
- //table expected_table(L, -1);
- // start using it...
- return 0; // or whatever you require of working with a raw function
- };
- int main(int argc, char* argv[]) {
- lua_State* L = luaL_newstate();/*Функция создает новое Lua состояние. Она вызывает lua_newstate с функцией-*/
- luaL_openlibs(L);
- something_in_my_system(L);
- lua_close(L);// закрыть состояние
- return 0;
- };
- Еще пример.
- const char* LUA = R"(
- print("state view")
- )";
- int embeds_sol(lua_State* L) {
- // start using sol with a pre-existing system
- state_view lua(L); // non-owning
- lua.script(LUA);
- return 0; // все, что требуется для работы с необработанной функцией.
- };
- int main(int argc, char* argv[]) {
- lua_State* L = luaL_newstate();/*Функция создает новое Lua состояние.*/
- luaL_openlibs(L);
- embeds_sol(L);// функция использующая SOL.
- lua_close(L);// закрыть состояние
- return 0;
- };
- Переменные.
- Работать с переменными легко с sol, и ведет себя почти как любая ассоциативная структура массива / map, с которой вы, возможно, имели дело ранее.
- чтение
- Учитывая этот файл lua, который загружается в sol:
- config = {
- fullscreen = false,
- resolution = { x = 1024, y = 768 }
- }
- Вы можете взаимодействовать с виртуальной машиной Lua следующим образом:
- #define SOL_ALL_SAFETIES_ON 1
- #include <sol/sol.hpp>
- #include <tuple>
- #include <assert.hpp>
- #include <utility> // for pair
- int main() {
- sol::state lua;
- lua.script_file("variables.lua");
- // the type "sol::state" behaves
- // exactly like a table!
- bool isfullscreen = lua["config"]["fullscreen"]; // can get nested variables
- sol::table config = lua["config"];
- c_assert(!isfullscreen);
- return 0;
- };
- Еще пример.
- const char* LUA = R"(
- x= 20
- )";
- int main(int argc, char* argv[]) {
- state lua;// Lua состояние.
- lua.open_libraries(lib::base, lib::package); // открыть доп.библиотеки.
- lua.script(LUA);
- int full = lua["x"];/* Получить переменную x.*/
- cout << full << endl;
- return 0;
- };
- const char* LUA = R"(
- x= 20
- )";
- int main(int argc, char* argv[]) {
- state lua;// Lua состояние.
- lua.open_libraries(lib::base, lib::package); // открыть доп.библиотеки.
- lua.script(LUA);
- sol::optional<int> int_x = lua["x"];
- if (int_x) {
- cout << " var this is int " << endl;
- int x1 = lua["x"];
- cout << x1 << endl;
- return 0;
- };
- sol::optional<bool> boolean_x = lua["x"];
- if (boolean_x) {
- cout << " var this is bool " << endl;
- bool x2 = lua["x"];
- cout << x2 << endl;
- return 0;
- };
- sol::optional<double> double_x = lua["x"];
- if (double_x) {
- cout << " var this is double " << endl;
- double x3 = lua["x"];
- cout << x3 << endl;
- };
- return 0;
- };
- int main(int argc, char* argv[]) {
- state lua;// Lua состояние.
- lua.open_libraries(lib::base, lib::package); // открыть доп.библиотеки.
- lua.script_file("variables.lua");
- // the type "sol::state" behaves
- // exactly like a table!
- bool isfullscreen = lua["config"]["fullscreen"]; // can get nested variables
- cout << isfullscreen << endl;
- table config = lua["config"];
- for each (auto var in config)
- {
- cout << &var << endl;
- }
- return 0;
- };
- Из этого примера вы можете увидеть, что есть много способов извлечь нужные вам переменные. Например, чтобы определить, существует ли вложенная переменная или нет, вы можете использовать ее autoдля захвата значения table[key]поиска, а затем использовать .valid()метод:
- #define SOL_ALL_SAFETIES_ON 1
- #include <sol/sol.hpp>
- #include <tuple>
- #include <assert.hpp>
- #include <utility> // for pair
- int main(int argc, char* argv[]) {
- state lua;// Lua состояние.
- lua.open_libraries(lib::base, lib::package); // открыть доп.библиотеки.
- lua.script_file("variables.lua");
- // test variable
- auto bark = lua["config"]["bark"];
- if (bark.valid()) { cout << "Branch taken: config and bark are existing variables " << endl;
- // Branch taken: config и bark-это существующие переменные.
- }
- else {cout << "config and/or bark are not variables " << endl;
- // config и / или bark не являются переменными.
- }
- return 0;
- };
- Это удобно, когда вы хотите проверить, существует ли вложенная переменная. Вы также можете проверить, присутствует ли переменная верхнего уровня или нет, с помощью sol::optional которой также проверяется, существуют ли A) ключи, в которые вы собираетесь войти, и B) тип, который вы пытаетесь получить, определенного типа:
- #define SOL_ALL_SAFETIES_ON 1
- #include <sol/sol.hpp>
- #include <tuple>
- #include <assert.hpp>
- #include <utility> // for pair
- };
- int main(int argc, char* argv[]) {
- state lua;// Lua состояние.
- lua.open_libraries(lib::base, lib::package); // открыть доп.библиотеки.
- lua.script_file("variables.lua");
- // can also use optional
- sol::optional<int> not_an_integer = lua["config"]["fullscreen"];
- if (not_an_integer) {
- cout << " var this is int " << endl;}
- sol::optional<bool> is_a_boolean = lua["config"]["fullscreen"];
- if (is_a_boolean) {
- cout << " var this is bool " << endl;}
- sol::optional<double> is_a_double = lua["not_a_variable"];
- if (is_a_double) {cout << " var this is double " << endl;}
- return 0;
- };
- const char* LUA = R"(
- config = {
- fullscreen = false,
- resolution = { x = 1024, y = 768 }
- }
- )";
- int main(int argc, char* argv[]) {
- state lua;// Lua состояние.
- lua.open_libraries(lib::base, lib::package); // открыть доп.библиотеки.
- lua.script(LUA);
- bool isfullscreen = lua["config"]["fullscreen"]; // can get nested variables
- cout << isfullscreen << endl;
- sol::optional<int> x = lua["config"]["resolution"]["x"];
- if (x) {
- int x1 = lua["config"]["resolution"]["x"];
- cout << x1 << endl;
- }
- return 0;
- };
- Это может пригодиться, когда даже в оптимизированном или выпускном режимах вам все еще нужна безопасность проверки. Вы также можете использовать методы get_or , если определенное значение может присутствовать, но вы просто хотите установить значение по умолчанию в другом месте:
- #define SOL_ALL_SAFETIES_ON 1
- #include <sol/sol.hpp>
- #include <tuple>
- #include <assert.hpp>
- #include <utility> // for pair
- int main() {
- sol::state lua;
- lua.script_file("variables.lua");
- // this will result in a value of '24'
- // (it tries to get a number, and fullscreen is
- // not a number
- int is_defaulted = lua["config"]["fullscreen"].get_or(24);
- c_assert(is_defaulted == 24);
- // This will result in the value of the config, which is 'false'
- bool is_not_defaulted = lua["config"]["fullscreen"];
- c_assert(!is_not_defaulted);
- return 0;
- };
- const char* LUA = R"(
- config = {
- fullscreen = 32
- }
- )";
- int main(int argc, char* argv[]) {
- state lua;// Lua состояние.
- lua.open_libraries(lib::base, lib::package); // открыть доп.библиотеки.
- lua.script(LUA);
- int full = lua["config"]["fullscreen"].get_or(24);/*Если переменная не int,
- то равна по умолчанию 24.*/
- cout << full << endl;
- return 0;
- };
- Это все, что нужно для чтения переменных!
- написание
- Написание становится намного проще. Даже без написания скрипта для файла или строки вы можете читать и записывать переменные в lua по своему усмотрению:
- #define SOL_ALL_SAFETIES_ON 1
- #include <sol/sol.hpp>
- #include <iostream>
- int main() {
- sol::state lua;
- // open those basic lua libraries
- // again, for print() and other basic utilities
- lua.open_libraries(sol::lib::base);
- // value in the global table
- lua["bark"] = 50;
- // a table being created in the global table
- lua["some_table"] = lua.create_table_with(
- "key0", 24,
- "key1", 25,
- lua["bark"], "the key is 50 and this string is its value!");
- // Run a plain ol' string of lua code
- // Note you can interact with things set through sol in C++ with lua!
- // Using a "Raw String Literal" to have multi-line goodness:
- // http://en.cppreference.com/w/cpp/language/string_literal
- lua.script(R"(
- print(some_table[50])
- print(some_table["key0"])
- print(some_table["key1"])
- -- a lua comment: access a global in a lua script with the _G table
- print(_G["bark"])
- )");
- return 0;
- };
- Еще пример.
- const char* LUA = R"(
- print(some_table[50])--"the key is 50 and this string is its value!"
- print(some_table["key0"])--24
- print(some_table["key1"])--25
- -- a lua comment: access a global in a lua script with the _G table
- print(_G["bark"])-- 50
- )";
- int main(int argc, char* argv[]) {
- state lua;// Lua состояние.
- lua.open_libraries(lib::base, lib::package); // открыть доп.библиотеки.
- //значение в глобальной таблице
- lua["bark"] = 50;
- // таблица, создаваемая в глобальной таблице.
- lua["some_table"] = lua.create_table_with(
- "key0", 24,"key1", 25,
- lua["bark"], "the key is 50 and this string is its value!");
- /* Запуск простой строчки кода lua, Примечание Вы можете взаимодействовать с вещами,
- установленными через sol в C++ с lua! Использование "необработанного строкового литерала"
- для многострочной доброты:*/
- lua.script(LUA);
- return 0;
- };
- Этот пример в значительной степени подводит итог того, что можно сделать. Обратите внимание, что синтаксис сделает эту переменную, но если вы туннелируете слишком глубоко без предварительного создания таблицы, Lua API вызовет панику (например, вызовет панику). Вы также можете лениться с чтением / записью значений: lua["non_existing_key_1"] = 1lua["does_not_exist"]["b"] = 20
- #define SOL_ALL_SAFETIES_ON 1
- #include <sol/sol.hpp>
- #include <iostream>
- int main() {
- sol::state lua;
- auto barkkey = lua["bark"];
- if (barkkey.valid()) {
- // Branch not taken: doesn't exist yet
- cout << "How did you get in here, arf?!" << endl;
- }
- barkkey = 50;
- if (barkkey.valid()) {
- // Branch taken: value exists!
- cout << "Bark Bjork Wan Wan Wan" << endl;
- }
- return 0;
- };
- Еще пример.
- int main(int argc, char* argv[]) {
- state lua;// Lua состояние.
- lua.open_libraries(lib::base, lib::package); // открыть доп.библиотеки.
- auto barkkey = lua["bark"]; //уст значение в глобальной таблице.
- if (barkkey.valid()) {// значение не существует!
- cout << "value nil" << endl;}
- barkkey = 50;
- if (barkkey.valid()) { // значение существует!
- cout << "barkkey = 50" << endl; }
- return 0;
- };
- Наконец, можно удалить ссылку / переменную, установив для нее значение с nil использованием константы sol::lua_nil в C ++:
- #define SOL_ALL_SAFETIES_ON 1
- #include <sol/sol.hpp>
- #include <iostream>
- int main() {
- sol::state lua;
- lua["bark"] = 50;
- sol::optional<int> x = lua["bark"];
- // x will have a value
- if (x) {
- cout << "x has no value, as expected" << endl;
- }
- else {
- return -1;
- }
- lua["bark"] = sol::lua_nil;
- sol::optional<int> y = lua["bark"];
- // y will not have a value
- if (y) {
- return -1;
- }
- else {
- cout << "y has no value, as expected" << endl;
- }
- return 0;
- };
- Еще пример.
- int main(int argc, char* argv[]) {
- state lua;// Lua состояние.
- lua.open_libraries(lib::base, lib::package); // открыть доп.библиотеки.
- lua["bark"] = 50;//уст значение в глобальной таблице.
- sol::optional<int> x = lua["bark"]; // x имеет значение, получим его.
- if (x) {cout << "x has value "<< endl;
- }
- else {return -1;}
- lua["bark"] = sol::lua_nil;
- sol::optional<int> y = lua["bark"]; // y не имеет значение.
- if (y) { return -1;
- }
- else { cout << "y has no value " << endl; }
- return 0;
- };
- Легко видеть, что здесь есть много вариантов сделать то, что вы хотите. Но это просто традиционные числа и строки. Что если нам нужно больше мощности, больше возможностей, чем то, что могут предложить нам эти ограниченные типы? Давайте добавим некоторые функции в классы C ++ !
- Sol может регистрировать все виды функций. Многие из них показаны в быстром 'n' грязном, но здесь мы обсудим множество дополнительных способов регистрации функций в системе Lua, обернутой в sol.
- Установка новой функции
- Имея функцию C ++, вы можете поместить ее в sol несколькими эквивалентными способами, работая аналогично тому, как работает установка переменных :
- Регистрация функций C ++
- #include <sol/sol.hpp>
- string my_function( int a, string b ) {
- // Create a string with the letter 'D' "a" times,
- // append it to 'b'
- return b + string( 'D', a );
- }
- int main () {
- sol::state lua;
- lua["my_func"] = my_function; // way 1
- lua.set("my_func", my_function); // way 2
- lua.set_function("my_func", my_function); // way 3
- // This function is now accessible as 'my_func' in
- // lua scripts / code run on this state:
- lua.script("some_str = my_func(1, 'Da')");
- // Read out the global variable we stored in 'some_str' in the
- // quick lua code we just executed
- string some_str = lua["some_str"];
- // some_str == "DaD"
- };
- Еще пример.
- string my_function(int a, string b) {
- return b + string('D', a);
- }
- int main(int argc, char* argv[]) {
- state lua;// Lua состояние.
- lua.open_libraries(lib::base, lib::package); // открыть доп.библиотеки.
- lua["my_func"] = my_function; // way 1
- lua.set("my_func", my_function); // way 2
- lua.set_function("my_func", my_function); // way 3
- // Эта функция теперь доступна как' my_func ' в
- // lua скрипты / код выполняется в этом состоянии:
- lua.script("some_str = my_func(1, 'Da')");
- // Прочитайте глобальную переменную, которую мы сохранили в' some_str
- // быстрый lua код, который мы только что выполнили
- string some_str = lua["some_str"];
- cout << some_str << endl;
- return 0;
- };
- 2
- const char* LUA = R"(
- foo(1, 3)
- )";
- void foo(int a, int b) {
- cout << a+b << endl;
- }
- int main(int argc, char* argv[]) {
- state lua;// Lua состояние.
- lua.open_libraries(lib::base, lib::package); // открыть доп.библиотеки.
- lua.set("foo", foo); // вариант 2.
- lua.script(LUA);// lua код, который мы только что выполнили.
- return 0;
- };
- Один и тот же код работает со всеми видами функций, начиная с указателей на функции / переменные-члены, которые есть у вас в классе, а также с лямбдами:
- Регистрация функций-членов C ++
- struct my_class {
- int a = 0;
- my_class(int x) : a(x) {
- }
- int func() {
- ++a; // increment a by 1
- return a;
- }
- };
- int main () {
- sol::state lua;
- // Here, we are binding the member function and a class instance: it will call the function on
- // the given class instance
- lua.set_function("my_class_func", &my_class::func, my_class());
- // We do not pass a class instance here:
- // the function will need you to pass an instance of "my_class" to it
- // in lua to work, as shown below
- lua.set_function("my_class_func_2", &my_class::func);
- // With a pre-bound instance:
- lua.script(R"(
- first_value = my_class_func()
- second_value = my_class_func()
- )");
- // first_value == 1
- // second_value == 2
- // With no bound instance:
- lua.set("obj", my_class(24));
- // Calls "func" on the class instance
- // referenced by "obj" in Lua
- lua.script(R"(
- third_value = my_class_func_2(obj)
- fourth_value = my_class_func_2(obj)
- )");
- // first_value == 25
- // second_value == 26
- };
- Пример.
- int main(int argc, char* argv[]) {
- struct my_class {
- int b = 24;
- int f() const {
- return 24;
- }
- void g() {
- ++b;
- }
- };
- state lua;
- lua.open_libraries();
- table bark = lua["bark"] = lua.get_or("bark", lua.create_table());
- bark.new_usertype<my_class>("my_class",
- "f", &my_class::f,
- "g", &my_class::g); // the usual
- // can add functions, as well (just like the global table)
- bark.set_function("print_my_class", [](my_class& self) {
- cout << "my_class { b: " << self.b << " }\n"; });
- // this works
- lua.script("obj = bark.my_class.new()");
- lua.script("obj:g()");
- // calling this function also works
- lua.script("bark.print_my_class(obj)");
- my_class& obj = lua["obj"];
- cout <<"\n";
- return 0;
- };
- Вариант 2.
- const char* LUA = R"(
- obj = t.class.new()
- n= obj:f()
- print(n)--24
- obj:g()
- n= obj:f()
- print(n)--25
- t.print_class(obj)
- )";
- int main(int argc, char* argv[]) {
- struct my_class { int b = 24;
- int f() const { return b; }
- void g() { ++b;}
- };
- state lua;
- lua.open_libraries();
- table bark = lua["t"] = lua.get_or("t", lua.create_table());
- bark.new_usertype<my_class>("class", "f", &my_class::f, "g", &my_class::g);
- //может добавить функции, классы в таблицу, использовать как глобальную таблицую)
- bark.set_function("print_class", [](my_class& self) {
- cout << "class { b: " << self.b << " }\n"; });// функцию выводит атрибут.
- lua.script(LUA);
- cout <<"\n";
- return 0;
- };
- Вариант 3.
- const char* LUA = R"(
- obj = t.a.new()
- obj:show()
- t.print_class(obj)
- )";
- int main(int argc, char* argv[]) {
- struct a { int x = 10;
- a() { cout << "create object " << this << endl; }//конструктор по умолчанию
- void show() { cout << "\nobject - " << this << "\tx = " << x << "\n\n"; }//вывести значение x на экран.
- ~a() { cout << "destory object " << this << endl << endl; }//вызов деструктора.
- };
- state lua;
- lua.open_libraries();
- table bark = lua["t"] = lua.get_or("t", lua.create_table());// Глоб таблица.
- bark.new_usertype<a>("a", "show", &a::show); /*может добавить функции, классы в таблицу,
- использовать как глобальную таблицую*/
- bark.set_function("print_class", [](a& self) {cout << "class { b: " << self.x << " }\n"; });// функцию выводит атрибут.
- lua.script(LUA);
- cout <<"\n";
- return 0;
- };
- Функции класса-члена и переменные класса-члена будут превращены в функции при установке таким образом. Вы можете получить интуитивно понятную переменную с доступом после этого раздела, когда узнаете о пользовательских типах для C ++ в Lua , но сейчас мы просто имеем дело с функциями!obj.a = value
- Другой вопрос, который возникает у многих людей, касается шаблонов функций. Шаблоны функций - функции-члены или свободные функции - не могут быть зарегистрированы, потому что они не существуют, пока вы не создадите их в C ++. Поэтому, учитывая шаблонную функцию, такую как:
- Шаблонная функция C ++
- template <typename A, typename B>
- auto my_add( A a, B b ) {
- return a + b;
- }
- Вы должны указать все аргументы шаблона, чтобы связать и использовать его, вот так:
- Регистрация инстанциации шаблона функции
- int main () {
- sol::state lua;
- // adds 2 integers
- lua["my_int_add"] = my_add<int, int>;
- // concatenates 2 strings
- lua["my_string_combine"] = my_add<string, string>;
- lua.script("my_num = my_int_add(1, 2)");
- int my_num = lua["my_num"];
- // my_num == 3
- lua.script("my_str = my_string_combine('bark bark', ' woof woof')");
- string my_str = lua["my_str"];
- // my_str == "bark bark woof woof"
- };
- 2.
- const char* LUA = R"(
- my_num = my_combine(1, 2)
- )";
- template <class t1, class t2>
- auto my_add(t1 a, t1 b) { return a + b; };
- int main(int argc, char* argv[]) {
- state lua;// Lua состояние.
- lua.open_libraries(lib::base, lib::package); // открыть доп.библиотеки.
- lua["my_combine"] = sol::overload(my_add<int, int>, my_add<string, string>);
- lua.script(LUA);
- int num = lua["my_num"];
- cout << num << endl;
- lua.script("my_str = my_combine('my', ' work')");
- string str = lua["my_str"];
- cout << str << endl;
- return 0;
- };
- Обратите внимание, что мы связываем две отдельные функции. Что если мы хотим связать только одну функцию, но она будет вести себя по-разному в зависимости от того, с какими аргументами она вызывается? Это называется перегрузкой, и это можно сделать с помощью sol :: overload следующим образом:
- Регистрация экземпляров шаблона функции C ++
- int main () {
- sol::state lua;
- // adds 2 integers
- lua["my_combine"] = sol::overload( my_add<int, int>, my_add<string, string> );
- lua.script("my_num = my_combine(1, 2)");
- lua.script("my_str = my_combine('bark bark', ' woof woof')");
- int my_num = lua["my_num"];
- string my_str = lua["my_str"];
- // my_num == 3
- // my_str == "bark bark woof woof"
- };
- В качестве перегрузке шаблонных методов.
- int main(int argc, char* argv[]) {
- state lua;// Lua состояние.
- lua.open_libraries(lib::base, lib::package); // открыть доп.библиотеки.
- lua["my_combine"] = sol::overload( my_add<int, int>, my_add<string, string> );
- lua.script("my_num = my_combine(1, 2)");
- int my_num = lua["my_num"];
- cout << my_num << endl;
- lua.script("my_str = my_combine('my', ' work')");
- string my_str = lua["my_str"];
- cout << my_str << endl;
- return 0;
- };
- Это полезно для функций, которые могут принимать несколько типов и должны вести себя по-разному в зависимости от этих типов. Вы можете установить столько перегрузок, сколько хотите, и они могут быть разных типов.
- В качестве примечания, связывание функций с параметрами по умолчанию не связывает по волшебству несколько версий функции, вызываемой с параметрами по умолчанию. Вместо этого вы должны использовать sol :: overload .
- В качестве примечания, пожалуйста, убедитесь, что понимаете Убедитесь, что вы понимаете последствия привязки лямбда / вызываемой структуры различными способами и что это значит для вашего кода!
- Получение функции от Lua.
- Есть 2 способа получить функцию от Lua. Один с sol :: function, а другой - более продвинутая оболочка с sol :: protected_function . Используйте их для извлечения вызываемых из Lua и вызова основной функции двумя способами:
- Получение sol :: function
- int main () {
- sol::state lua;
- lua.script(R"(
- function f (a)
- return a + 5
- end
- )");
- // Get and immediately call
- int x = lua["f"](30);
- // x == 35
- // Store it into a variable first, then call
- sol::function f = lua["f"];
- int y = f(20);
- // y == 25
- };
- Вариант 2.
- const char* LUA = R"(
- function foo (a)
- return a + 5
- end
- )";
- int main(int argc, char* argv[]) {
- state lua;// Lua состояние.
- lua.open_libraries(lib::base, lib::package); // открыть доп.библиотеки.
- lua.script(LUA);// Вызвать функцию и получить результат.
- int x = lua["foo"](30);
- cout << x << endl;// x == 35
- lua.script(LUA);// Сначала сохранить результат в переменной, затем вызвать.
- sol::function f = lua["foo"];
- int y = f(20);
- cout << y << endl; // y == 25
- return 0;
- };
- Вариант 3.
- const char* LUA = R"(
- function foo ()
- print(223)
- end
- function f()
- print("func f")
- end
- )";
- int main(int argc, char* argv[]) {
- state lua;// Lua состояние.
- lua.open_libraries(lib::base, lib::package); // открыть доп.библиотеки.
- lua.script(LUA);// Вызвать функцию и получить результат.
- lua["foo"]();
- lua.script(LUA);// Сначала сохранить результат в переменной, затем вызвать.
- sol::function f = lua["f"];
- f();
- return 0;
- };
- В Lua вы можете получить все, что можно вызвать, включая функции C ++, которые вы используете, set_function или аналогичные. sol::protected_function ведет себя аналогично sol::function, но имеет переменную error_handler, которую вы можете установить в функцию Lua. Это ловит все ошибки и запускает их через функцию обработки ошибок:
- Получение sol :: protected_function
- int main () {
- sol::state lua;
- lua.script(R"(
- function handler (message)
- return "Handled this message: " .. message
- end
- function f (a)
- if a < 0 then
- error("negative number detected")
- end
- return a + 5
- end
- )");
- sol::protected_function f = lua["f"];
- f.error_handler = lua["handler"];
- sol::protected_function_result result = f(-500);
- if (result.valid()) {
- // Call succeeded
- int x = result;
- }
- else {
- // Call failed
- sol::error err = result;
- string what = err.what();
- // 'what' Should read
- // "Handled this message: negative number detected"
- }
- };
- Вариант 2.
- const char* LUA = R"(
- function handler (message)
- return "Handled this message: " .. message
- end
- function f (a)
- if a < 0 then
- error("negative number detected")
- end
- return a + 5
- end
- )";
- int main(int argc, char* argv[]) {
- state lua;// Lua состояние.
- lua.open_libraries(lib::base, lib::package); // открыть доп.библиотеки.
- lua.script(LUA);
- typedef sol::protected_function profunc;
- typedef sol::protected_function_result profunc_res;
- profunc f = lua["f"];
- f.error_handler = lua["handler"];
- lua.script(LUA);
- profunc_res result = f(-500);
- if (result.valid()) { // вызов успешен.
- int x = result;
- cout << x << endl;
- }
- else {// Вызов функции с ошибкой.
- sol::error err = result;// 'what' Should read
- string what = err.what(); // "Handled this message: negative number detected"
- cout << what << endl;// вывести ошибку.
- }
- return 0;
- };
- Несколько возвратов в и из Lua Вы можете вернуть несколько параметров в Lua и из него, используя tuple/ pair классы, предоставляемые C ++. Это позволяет вам также использовать sol::tie для установки возвращаемых значений в предварительно объявленные элементы. Чтобы получить несколько возвратов, просто запросите tuple тип из результата вычисления функции или sol::tie связку предварительно объявленных переменных вместе и установите результат, равный этому:
- Несколько возвратов от Lua
- int main () {
- sol::state lua;
- lua.script("function f (a, b, c) return a, b, c end");
- tuple<int, int, int> result;
- result = lua["f"](1, 2, 3);
- // result == { 1, 2, 3 }
- int a, int b;
- string c;
- sol::tie( a, b, c ) = lua["f"](1, 2, "bark");
- // a == 1
- // b == 2
- // c == "bark"
- }
- Вы также можете вернуть несколько элементов самостоятельно из связанной с C ++ функции. Здесь мы собираемся связать лямбду C ++ с Lua, а затем вызвать ее через Lua и получить tuple выход с другой стороны:
- Вариант 2.
- const char* LUA = R"(
- function f (a, b, c) return a, b, c end
- )";
- int main(int argc, char* argv[]) {
- state lua;// Lua состояние.
- lua.open_libraries(lib::base, lib::package); // открыть доп.библиотеки.
- lua.script(LUA);
- tuple<int, int, int> result;
- result = lua["f"](10, 20, 30); // result == { 10, 20, 30 }
- cout << get<0>(result) << endl;// 10
- return 0;
- };
- Вариант 3.
- template<size_t I = 0, typename... Tp>
- inline typename enable_if<I == sizeof...(Tp), void>::type
- print(tuple<Tp...>& t)
- { }
- template<size_t I = 0, typename... Tp>
- inline typename enable_if < I < sizeof...(Tp), void>::type
- print(tuple<Tp...>& t)
- {
- cout <<get<I>(t)<< " ";
- print<I + 1, Tp...>(t);
- }
- const char* LUA = R"(
- function f (a, b, c) return a+1, b+2, c+3 end
- )";
- int main(int argc, char* argv[]) {
- state lua;// Lua состояние.
- lua.open_libraries(lib::base, lib::package); // открыть доп.библиотеки.
- lua.script(LUA);
- tuple<int, int, int> res;
- res = lua["f"](10, 20, 30); // result == { 10, 20, 30 }
- print(res);
- return 0;
- };
- Многократное возвращение в Lua
- int main () {
- sol::state lua;
- lua["f"] = [](int a, int b, sol::object c) {
- // sol::object can be anything here: just pass it through
- return make_tuple( a, b, c );
- };
- tuple<int, int, int> result = lua["f"](1, 2, 3);
- // result == { 1, 2, 3 }
- tuple<int, int, string> result2;
- result2 = lua["f"](1, 2, "Arf?")
- // result2 == { 1, 2, "Arf?" }
- int a, int b;
- string c;
- sol::tie( a, b, c ) = lua["f"](1, 2, "meow");
- // a == 1
- // b == 2
- // c == "meow"
- }
- Обратите внимание, что мы используем sol :: object для переноса через «любое значение» из Lua. Вы также можете использовать sol::make_object для создания объекта из некоторого значения, чтобы он также мог быть возвращен в Lua.
- Любое возвращение в и из Lua
- На это намекали в предыдущем примере кода, но sol::object это хороший способ передать «любой тип» обратно в Lua (пока мы все ждем, variant<...>чтобы его реализовали и отправили разработчики компилятора / библиотеки C ++).
- Он может быть использован как так, в сочетании с sol::this_state:
- Верни что-нибудь в Луа
- sol::object fancy_func (sol::object a, sol::object b, sol::this_state s) {
- sol::state_view lua(s);
- if (a.is<int>() && b.is<int>()) {
- return sol::make_object(lua, a.as<int>() + b.as<int>());
- }
- else if (a.is<bool>()) {
- bool do_triple = a.as<bool>();
- return sol::make_object(lua, b.as<double>() * ( do_triple ? 3 : 1 ) );
- }
- return sol::make_object(lua, sol::lua_nil);
- }
- int main () {
- sol::state lua;
- lua["f"] = fancy_func;
- int result = lua["f"](1, 2);
- // result == 3
- double result2 = lua["f"](false, 2.5);
- // result2 == 2.5
- // call in Lua, get result
- lua.script("result3 = f(true, 5.5)");
- double result3 = lua["result3"];
- // result3 == 16.5
- };
- Вариант 2.
- sol::object fancy_func(sol::object a, sol::object b, sol::this_state s) {
- sol::state_view lua(s);
- if (a.is<int>() && b.is<int>()) {
- return sol::make_object(lua, a.as<int>() + b.as<int>());
- }
- else if (a.is<bool>()) {
- bool do_triple = a.as<bool>();
- return sol::make_object(lua, b.as<double>() * (do_triple ? 3 : 1));
- }
- return sol::make_object(lua, sol::lua_nil);
- };
- int main(int argc, char* argv[]) {
- state lua;// Lua состояние.
- lua.open_libraries(lib::base, lib::package); // открыть доп.библиотеки.
- lua["f"] = fancy_func;
- int res = lua["f"](1, 2);
- cout << res << endl;// 3
- double res1 = lua["f"](false, 2.5);
- cout << res1 << endl;// 2.5
- lua.script("result3 = f(true, 5.5)");
- double res3 = lua["result3"];
- cout << res3 << endl;// res3 == 16.5
- return 0;
- };
- Вариант 3.
- const char* LUA = R"(
- result3 = f(65)
- )";
- object fancy_func(object a, this_state lua) {
- if (a.is<int>()) {
- return make_object(lua, a.as<int>());
- }
- else if (a.is<bool>()) {
- bool do_bool = a.as<bool>();
- return make_object(lua, (do_bool ? "true" : "false"));
- }
- return make_object(lua, sol::lua_nil);
- };
- int main(int argc, char* argv[]) {
- state lua;// Lua состояние.
- lua.open_libraries(lib::base, lib::package); // открыть доп.библиотеки.
- lua["f"] = fancy_func;
- lua.script(LUA);
- double res3 = lua["result3"];
- cout << res3 << endl;// res3 == 16.5
- return 0;
- };
- Это охватывает почти все, что вам нужно знать о функциях и о том, как они взаимодействуют с sol. Для некоторых продвинутых уловок и изящных вещей, проверьте sol :: this_state и sol :: variadic_args . Следующая остановка в этом уроке о типах C ++ (usertypes) в Lua ! Если вам нужно немного больше информации о функциях на стороне C ++ и о том, как наилучшим образом использовать аргументы из C ++ .
- C ++ в Lua
- Использование пользовательских типов («usertype» или просто «udt») просто с sol. Если вы не вызываете какие-либо переменные или функции-члены, вам даже не нужно «регистрировать» тип пользователя: просто пропустите его. Но если вам нужны переменные и функции для вашего пользовательского типа внутри Lua, вам нужно зарегистрировать его. Мы собираемся привести короткий пример, который включает в себя кучу информации о том, как работать с вещами.
- Возьмите эту player структуру в C ++ в заголовочном файле:
- player.hpp
- #include <iostream>
- struct player {
- public:
- int bullets;
- int speed;
- player()
- : player(3, 100) {
- }
- player(int ammo)
- : player(ammo, 100) {
- }
- player(int ammo, int hitpoints)
- : bullets(ammo), hp(hitpoints) {
- }
- void boost() {
- speed += 10;
- }
- bool shoot() {
- if (bullets < 1)
- return false;
- --bullets;
- return true;
- }
- void set_hp(int value) {
- hp = value;
- }
- int get_hp() const {
- return hp;
- }
- private:
- int hp;
- };
- Это довольно минимальный класс, но мы не хотим переписывать это с помощью metatables в Lua. Мы хотим, чтобы это было частью Lua легко. Ниже приведен код Lua, который мы хотели бы иметь для правильной работы:
- player_script.lua
- p1 = player.new(2)
- -- p2 is still here from being
- -- set with lua["p2"] = player(0); below
- local p2shoots = p2:shoot()
- assert(not p2shoots)
- -- had 0 ammo
- -- set variable property setter
- p1.hp = 545
- -- get variable through property unqualified_getter
- print(p1.hp)
- assert(p1.hp == 545)
- local did_shoot_1 = p1:shoot()
- print(did_shoot_1)
- print(p1.bullets)
- local did_shoot_2 = p1:shoot()
- print(did_shoot_2)
- print(p1.bullets)
- local did_shoot_3 = p1:shoot()
- print(did_shoot_3)
- -- can read
- print(p1.bullets)
- -- would error: is a readonly variable, cannot write
- -- p1.bullets = 20
- p1:boost()
- -- call the function we define at runtime from a Lua script
- p1:brake()
- вариант 1.
- struct player {
- player() {this->hp = 10;}
- void boost() { cout << hp << endl; }
- private:
- int hp;
- };
- const char* LUA = R"(
- p1 = player.new()
- p1:boost()
- )";
- int main(int argc, char* argv[]) {
- state lua;// Lua состояние.
- lua.open_libraries(lib::base, lib::package); // открыть доп.библиотеки.
- // сделать usertype метастабильным
- sol::usertype<player> player_type = lua.new_usertype<player>("player",
- sol::constructors<player()>()); // 1 конструктор.
- player_type["boost"] = &player::boost;// типичная функция-член.
- lua.script(LUA);
- return 0;
- };
- Вариант 2.
- struct a {
- int x = 10;
- a() { cout << "create object " << this << endl; }//конструктор по умолчанию
- void show() { cout << "\nobject - " << this << "\tx = " << x << "\n\n"; }//вывести значение x на экран.
- ~a() { cout << "destory object " << this << endl << endl; }//вызов деструктора.
- };
- const char* LUA = R"(
- p1 = a.new()
- p1:show()
- p2 = a.new()
- p2:show()
- )";
- int main(int argc, char* argv[]) {
- state lua;// Lua состояние.
- lua.open_libraries(lib::base, lib::package); // открыть доп.библиотеки.
- // сделать usertype метастабильным
- usertype<a> player_type = lua.new_usertype<a>("a",constructors<a()>()); // 1 конструктор.
- player_type["show"] = &a::show;// типичная функция-член.
- lua.script(LUA);
- return 0;
- };
- Для этого вы связываете вещи, используя new_usertype метод и, как показано ниже. Эти методы используются как для таблицы, так и для состояния (_view) , но мы собираемся просто использовать его для state:
- main.cpp
- #define SOL_ALL_SAFETIES_ON 1
- #include <sol/sol.hpp>
- #include "player.hpp"
- #include <iostream>
- int main() {
- sol::state lua;
- lua.open_libraries(sol::lib::base);
- // note that you can set a
- // userdata before you register a usertype,
- // and it will still carry
- // the right metatable if you register it later
- // set a variable "p2" of type "player" with 0 ammo
- lua["p2"] = player(0);
- // make usertype metatable
- sol::usertype<player> player_type = lua.new_usertype<player>("player",
- // 3 constructors
- sol::constructors<player(), player(int), player(int, int)>());
- // typical member function that returns a variable
- player_type["shoot"] = &player::shoot;
- // typical member function
- player_type["boost"] = &player::boost;
- // gets or set the value using member variable syntax
- player_type["hp"] = sol::property(&player::get_hp, &player::set_hp);
- // read and write variable
- player_type["speed"] = &player::speed;
- // can only read from, not write to
- // .set(foo, bar) is the same as [foo] = bar;
- player_type.set("bullets", sol::readonly(&player::bullets));
- lua.script_file("prelude_script.lua");
- lua.script_file("player_script.lua");
- return 0;
- }
- В сценарии используется еще один метод, которого нет в C ++ или который не определен в коде C ++ для привязки пользовательского типа, называется brake. Даже если метод не существует в C ++, вы можете добавить методы в таблицу классов в Lua:
- prelude_script.lua
- function player:brake ()
- self.speed = 0
- print("we hit the brakes!")
- end
- Теперь этот сценарий должен работать нормально, и вы можете наблюдать и играть со значениями. Еще больше вещей, которые вы можете сделать , описано в другом месте, например, функции инициализатора (поддержка частных конструкторов / деструкторов), «статические» функции, которые можно вызывать , и перегруженные функции-члены. Вы можете даже связать глобальные переменные (даже по ссылке с ) с помощью. Есть много чего попробовать!
- name.my_function( ... ) refsol::var
- Это мощный способ разрешить повторное использование кода C ++ из Lua, помимо простой регистрации функций, и должен помочь вам получить более сложные классы и структуры данных! Однако в случае, когда вам нужно больше настроек, чем просто пользовательских типов, вы можете настроить sol так, чтобы он в большей степени соответствовал вашим желаниям, используя нужные структуры настройки и расширения.
- Вы можете проверить этот код и более сложный код в каталоге примеров, посмотрев usertype_примеры с префиксом.
- собственность
- Владение важно при управлении ресурсами в C ++. У sol есть много семантик владения, которые по умолчанию безопасны. Ниже приведены правила.
- владение объектом
- Вы можете взять ссылку на что-то, что существует в Lua, вытащив sol :: reference или sol :: object :
- object_lifetime.cpp
- #define SOL_ALL_SAFETIES_ON 1
- #include <sol/sol.hpp>
- #include <string>
- #include <iostream>
- int main () {
- sol::state lua;
- lua.open_libraries(sol::lib::base);
- lua.script(R"(
- obj = "please don't let me die";
- )");
- sol::object keep_alive = lua["obj"];
- lua.script(R"(
- obj = nil;
- function say(msg)
- print(msg)
- end
- )");
- lua.collect_garbage();
- lua["say"](lua["obj"]);
- // still accessible here and still alive in Lua
- // even though the name was cleared
- string message = keep_alive.as<string>();
- cout << message << endl;
- // Can be pushed back into Lua as an argument
- // or set to a new name,
- // whatever you like!
- lua["say"](keep_alive);
- return 0;
- };
- Вариант 2.
- int main(int argc, char* argv[]) {
- state lua;// Lua состояние.
- lua.open_libraries(lib::base, lib::package); // открыть доп.библиотеки.
- lua.script(R"(
- obj = "i object";
- )");
- object keep_alive = lua["obj"];/*Вы можете взять ссылку на что - то,
- что существует в Lua, вытащив reference или object*/
- lua.script(R"(
- obj = nil;
- function say(msg)
- print(msg)
- end
- )");
- lua.collect_garbage();
- lua["say"](lua["obj"]);// вызвать функцию say. nil.
- // все еще доступен здесь и все еще жив в Lua
- // даже если имя было очищено
- string msg = keep_alive.as<string>();// Сохраним значение значение obj в строке.
- cout << msg << endl;
- // Может быть возвращен в Lua в качестве аргумента
- // или установить новое имя,
- // что вам нравится!
- lua["say"](keep_alive);
- return 0;
- };
- Все объекты должны быть уничтожены до того, как sol :: state будет уничтожен, в противном случае вы получите висячие ссылки на состояние Lua, и вещи будут взрываться ужасным, ужасным образом.
- Это относится не только к sol :: object : все типы, производные от sol :: reference и sol :: object ( sol :: table sol :: userdata и т. Д.), Должны быть очищены до того, как состояние выйдет из области видимости.
- владение указателем.
- Sol не будет владеть необработанными указателями: необработанные указатели не владеют ничем. sol не будет удалять необработанные указатели, потому что они не имеют (и не должны) ничего:
- pointer_lifetime.cpp
- #define SOL_ALL_SAFETIES_ON 1
- #include <sol/sol.hpp>
- struct my_type {
- void stuff() {
- }
- };
- int main () {
- sol::state lua;
- // AAAHHH BAD
- // dangling pointer!
- lua["my_func"] = []() -> my_type* { return new my_type(); };
- // AAAHHH!
- lua.set("something", new my_type());
- // AAAAAAHHH!!!
- lua["something_else"] = new my_type();
- return 0;
- }
- Используйте / верните unique_ptr или shared_ptr вместо или просто верните значение:
- (умные указатели) pointer_lifetime.cpp
- #define SOL_ALL_SAFETIES_ON 1
- #include <sol/sol.hpp>
- struct my_type {
- void stuff() {
- }
- };
- int main () {
- sol::state lua;
- // AAAHHH BAD
- // dangling pointer!
- lua["my_func"] = []() -> my_type* { return new my_type(); };
- // AAAHHH!
- lua.set("something", new my_type());
- // AAAAAAHHH!!!
- lua["something_else"] = new my_type();
- return 0;
- }
- Если у вас есть что-то, что, как вы знаете, будет длиться долго, и вы просто хотите передать это Lua в качестве справки, то это тоже хорошо
- (статический) pointer_lifetime.cpp
- #define SOL_ALL_SAFETIES_ON 1
- #include <sol/sol.hpp>
- struct my_type {
- void stuff() {
- }
- };
- int main () {
- sol::state lua;
- lua["my_func5"] = []() -> my_type* {
- static my_type mt;
- return &mt;
- };
- return 0;
- }
- Sol может обнаружить nullptr, поэтому, если вы вернете его, не будет никакого зависания, потому что sol::lua_nil будет нажат. Но если вы знаете, что это nil заранее, пожалуйста, верните nullptr_tили sol::lua_nil:
- (nil / nullptr) pointer_lifetime.cpp
- #define SOL_ALL_SAFETIES_ON 1
- #include <sol/sol.hpp>
- struct my_type {
- void stuff() {
- }
- };
- int main () {
- sol::state lua;
- // THIS IS STILL BAD DON'T DO IT AAAHHH BAD
- // return a unique_ptr that's empty instead
- // or be explicit!
- lua["my_func6"] = []() -> my_type* { return nullptr; };
- // :ok:
- lua["my_func7"] = []() -> nullptr_t { return nullptr; };
- // :ok:
- lua["my_func8"] = []() -> unique_ptr<my_type> {
- // default-constructs as a nullptr,
- // gets pushed as nil to Lua
- return unique_ptr<my_type>();
- // same happens for shared_ptr
- };
- // Acceptable, it will set 'something' to nil
- // (and delete it on next GC if there's no more references)
- lua.set("something", nullptr);
- // Also fine
- lua["something_else"] = nullptr;
- return 0;
- }
- эфермальные (прокси) объекты
- Прокси и типы результатов являются однодневными. Они полагаются на стек Lua, а их конструкторы / деструкторы взаимодействуют со стеком Lua. Это означает, что они совершенно небезопасны, чтобы возвращаться из функций в C ++, без особого внимания к тому, как они используются, что часто требует полагаться на поведение, определяемое реализацией.
- Пожалуйста, будьте осторожны при использовании (protected_) function_result , load_result (особенно многократная загрузка / функция приводит к одной функции C ++!) Stack_reference и подобных основанных на стеке вещах. Если вы хотите вернуть эти вещи, подумайте
- добавляя свои собственные типы
- Иногда, перекрывая золя, чтобы сделать его обрабатывать некоторые struct«S и class» эс как нечто иное , чем просто UserData желательно. Способ сделать это состоит в том, чтобы воспользоваться преимуществами 4 пунктов настройки для Sol. Это sol_lua_check, sol_lua_get, sol_lua_push, и sol_lua_check_get.
- Это шаблон класса / структуры, поэтому вы будете переопределять их, используя технику C ++, которая вызывает специализацию класса / структуры . Ниже приведен пример структуры, которая разбивается на две части при движении в направлении C ++ -> Lua, а затем возвращается в структуру при переходе в Lua -> C ++:
- #define SOL_ALL_SAFETIES_ON 1
- #include <sol/sol.hpp>
- #include <iostream>
- #include "assert.hpp"
- struct two_things {
- int a;
- bool b;
- };
- template <typename Handler>
- bool sol_lua_check(sol::types<two_things>, lua_State* L, int index, Handler&& handler, sol::stack::record& tracking) {
- // indices can be negative to count backwards from the top of the stack,
- // rather than the bottom up
- // to deal with this, we adjust the index to
- // its absolute position using the lua_absindex function
- int absolute_index = lua_absindex(L, index);
- // Check first and second second index for being the proper types
- bool success = sol::stack::check<int>(L, absolute_index, handler) && sol::stack::check<bool>(L, absolute_index + 1, handler);
- tracking.use(2);
- return success;
- }
- two_things sol_lua_get(sol::types<two_things>, lua_State* L, int index, sol::stack::record& tracking) {
- int absolute_index = lua_absindex(L, index);
- // Get the first element
- int a = sol::stack::get<int>(L, absolute_index);
- // Get the second element,
- // in the +1 position from the first
- bool b = sol::stack::get<bool>(L, absolute_index + 1);
- // we use 2 slots, each of the previous takes 1
- tracking.use(2);
- return two_things{ a, b };
- }
- int sol_lua_push(sol::types<two_things>, lua_State* L, const two_things& things) {
- int amount = sol::stack::push(L, things.a);
- // amount will be 1: int pushes 1 item
- amount += sol::stack::push(L, things.b);
- // amount 2 now, since bool pushes a single item
- // Return 2 things
- return amount;
- };
- int main() {
- cout << "=== customization ===" << endl;
- cout << boolalpha;
- sol::state lua;
- lua.open_libraries(sol::lib::base);
- //Это базовая формула, которой вы можете следовать, чтобы распространить ее на свои собственные классы. Использование его в остальной части библиотеки должно быть беспроблемным:
- // Create a pass-through style of function
- lua.script("function f ( a, b ) print(a, b) return a, b end");
- // get the function out of Lua
- sol::function f = lua["f"];
- two_things things = f(two_things{ 24, false });
- c_assert(things.a == 24);
- c_assert(things.b == false);
- // things.a == 24
- // things.b == true
- cout << "things.a: " << things.a << endl;
- cout << "things.b: " << things.b << endl;
- cout << endl;
- return 0;
- }
- Вариант 1.
- // индексы могут быть отрицательными для обратного отсчета от вершины стека,
- // а не снизу вверх
- // чтобы справиться с этим, мы настраиваем индекс на
- // / / его абсолютное положение с помощью функции индекса lua_abs
- // Проверьте первый и второй второй индекс на наличие правильных типов
- // Получить первый элемент
- // Получить второй элемент,
- // в положении +1 от первого
- // мы используем 2 слота, каждый из предыдущих занимает 1
- // сумма будет 1: int толкает 1 пункт
- // сумма 2 теперь, так как bool толкает один элемент
- // Вернуть 2 вещи / / создать сквозной стиль функции
- // получить функцию из Lua
- struct two_things {
- int a;
- bool b;
- };
- template <typename Handler>
- bool sol_lua_check(types<two_things>, lua_State* L, int index, Handler&& handler, stack::record& tracking) {
- int absolute_index = lua_absindex(L, index);
- bool success = stack::check<int>(L, absolute_index, handler) && stack::check<bool>(L, absolute_index + 1, handler);
- tracking.use(2);
- return success;
- }
- two_things sol_lua_get(types<two_things>, lua_State* L, int index, stack::record& tracking) {
- int absolute_index = lua_absindex(L, index);
- int a = stack::get<int>(L, absolute_index);
- bool b = stack::get<bool>(L, absolute_index + 1);
- tracking.use(2);
- return two_things{ a, b };
- }
- int sol_lua_push(types<two_things>, lua_State* L, const two_things& things) {
- int amount = stack::push(L, things.a);
- amount += stack::push(L, things.b);
- return amount;
- };
- int main(int argc, char* argv[]) {
- state lua;// Lua состояние.
- lua.open_libraries(lib::base, lib::package); // открыть доп.библиотеки.
- //Это базовая формула, которой вы можете следовать, чтобы распространить ее на свои собственные классы. Использование его в остальной части библиотеки должно быть беспроблемным:
- lua.script("function f ( a, b ) print(a, b) return a, b end");
- sol::function f = lua["f"];
- two_things things = f(two_things{ 24, false });
- // things.a == 24
- // things.b == true
- cout << "things.a: " << things.a << endl;
- cout << "things.b: " << things.b << endl;
- cout << endl;
- return 0;
- };
- Вариант 1.
- struct t {
- int a;
- };
- template <typename Handler>
- bool sol_lua_check(types<t>, lua_State* L, int index, Handler&& handler, stack::record& tracking) {
- int absolute_index = lua_absindex(L, index);/* индексы могут быть отрицательными для обратного отсчета от вершины
- стека, а не снизу вверх чтобы справиться с этим, мы настраиваем индекс на его абсолютное
- положение с помощью функции индекса lua_abs*/
- bool success = stack::check<int>(L, absolute_index, handler);/* Проверить первый индекс на правильный тип.*/
- tracking.use(1);// сколько вы используете.
- return success;
- };
- t sol_lua_get(types<t>, lua_State* L, int index, stack::record& tracking) {
- int absolute_index = lua_absindex(L, index);// Получить элемент из стека.
- int a = stack::get<int>(L, absolute_index);
- tracking.use(1);
- return t{ a };
- };
- int sol_lua_push(types<t>, lua_State* L, const t& things) {
- int amount = stack::push(L, things.a);// Отравить в стек int.
- return amount;
- };
- int main(int argc, char* argv[]) {
- state lua;// Lua состояние.
- lua.open_libraries(lib::base, lib::package); // открыть доп.библиотеки.
- /*Это базовая формула, которой вы можете следовать, чтобы распространить ее на свои собственные классы.
- Использование его в остальной части библиотеки должно быть беспроблемным:*/
- lua.script("function f (a) print(a) return a end");// создать сквозной стиль функции получить функцию из Lua.
- sol::function f = lua["f"];
- t things = f(t{ 24 });
- cout << "things.a: " << things.a << endl;// things.a == 24
- return 0;
- };
- И это все!
- Несколько замечаний о реализации: во-первых, есть вспомогательный параметр типа sol :: stack :: record для методов получения и проверки. Это отслеживает, что было выполнено последней завершенной операцией. Так как мы получили 2 члена, мы используем, tracking.use(2);чтобы указать, что мы использовали 2 стековых позиции (одна для bool, одна для int). Второе, на что следует обратить внимание, это то, что мы убедились, что использовали index параметр, а затем приступили к добавлению 1 к нему для следующего.
- Вы можете сделать что-то подходящее для Lua, но не получить его таким же образом, если специализируете только одну часть системы. Если вам нужно получить его (как возврат, используя одно или несколько значений из Lua), вам следует специализировать sol::stack::getter класс sol::stack::checker шаблона и класс шаблона. Если вам нужно вставить его в Lua в какой-то момент, вам нужно будет специализировать sol::stack::pusher класс шаблона. sol::lua_size Шаблонный класс признак должен быть специализированы для обоих случаев, если это только не отталкивает 1 пункт, в этом случае реализация по умолчанию будет предполагать 1.
- Заметка
- Важно отметить , что gett, push и check различие между типом Tи указателем на тип T*. Это означает, что если вы хотите работать чисто, скажем, с T*дескриптором, который не имеет такой же семантики, как просто T, вам может потребоваться указать checkers / getters / pushers для обоих T*и T. Checkers for T*forward to the checkers for T, но получатель for T*не пересылает получателю for T(например, из-за int*того , что он не совсем совпадает с int).
- В общем, это нормально, так как большинство получателей / контролеров используют только 1 элемента стека. Но, если вы делаете более сложные вложенные классы, было бы полезно использовать, tracking.last чтобы понять, сколько индексов стека выполнила последняя операция gett / check, и увеличить его на единицу после использования вызова. index + tracking.last stack::check<..>( L, index, tracking)
- Вы можете прочитать больше о самих точках расширения на странице API для стека , и если что-то идет не так или у вас больше есть вопросы, пожалуйста, не стесняйтесь, напишите на странице Github Issues или отправьте электронное письмо!
- Ошибки.
- как обрабатывать исключения или другие ошибки
- Вот несколько советов и приемов для распространенных ошибок об итерации, ошибках компиляции / времени компоновки и других подводных камнях, особенно при работе с исключениями, условиями ошибок и т.п. в sol.
- Сценарии выполнения.
- Скрипты могут иметь синтаксические ошибки, могут загружаться из файловой системы неправильно или иметь проблемы во время выполнения. Зная, какой из них может быть неприятным. Существуют различные небольшие строительные блоки для загрузки и запуска кода, но для проверки ошибок вы можете использовать перегруженные функции script / script_file в sol :: state / sol :: state_view , особенно в safe_script вариантах. Они также принимают обратный вызов ошибки, который вызывается только тогда, когда что-то идет не так, и sol поставляется с некоторыми обработчиками ошибок по умолчанию в форме sol::script_default_on_error и sol::script_pass_on_error.
- Ошибки / предупреждения компилятора.
- Множество ошибок компиляции может произойти, когда что-то пойдет не так. Вот несколько основных советов по работе с этими типами:
- Если существует множество ошибок, связанных с index_sequence типами, чертами типов и другими членами, скорее всего, вы не включили свой переключатель C ++ 14 для своего компилятора. Visual Studio 2015 превращает их по умолчанию, но г ++ и лязг ++ не имеют их как значения по умолчанию , и вы должны передать флаг --std=c++1yили --std=c++14, или аналогичный для компилятора.
- Если вы добавляете в Lua не примитивный тип, вы можете получить странные ошибки в списках инициализаторов или невозможности инициализировать a luaL_Reg. Это может быть связано с автоматической функцией и регистрацией оператора . Отключение может помочь.
- Иногда сгенерированный тип пользователя может быть очень длинным, если вы связываете много функций-членов. Вы можете получить множество предупреждений об отключении отладочных символов или __LINE_VARпревышении максимальной длины. Вы можете безопасно отключить эти предупреждения для некоторых компиляторов.
- Ошибки глубины шаблона также могут быть проблемой в более ранних версиях clang ++ и g ++. Используйте -ftemplate-depthфлаг компилятора и укажите действительно большое число (например, 2048 или даже удвоите эту сумму), чтобы позволить компилятору работать свободно.
- При интенсивном использовании шаблонов пользовательских типов MSVC может вызвать ошибку компилятора C1128 , которая решается с помощью флага компиляции / bigobj .
- Если у вас есть тип только для перемещения, этот тип может потребоваться, readonlyесли он связан как переменная-член для пользовательского типа или связан с использованием state_view::set_function. Смотрите sol :: readonly для более подробной информации.
- Назначение stringили использование после его создания может привести к ошибкам компилятора при работе с ним и его результатам. Смотрите эту проблему для исправления этого поведения .pair<T1, T2>operator=sol::function
- Иногда использование __stdcallв 32-битной (x86) среде на VC ++ может вызвать проблемы с привязкой функций из-за ошибки компилятора. У нас есть исправление prelimanry, но если оно не работает и все еще есть проблемы: поместите функцию в a, functionчтобы ошибки компилятора и другие проблемы исчезли. Также см. Этот отчет о проблеме __stdcall для более подробной информации.
- Если вы используете /std:c++latestVC ++ и получаете ряд ошибок для noexceptспецификаторов функций, вам, возможно, придется подать проблему или дождаться следующего выпуска компилятора VC ++.
- Mac OSX Сбои
- На LuaJIT ваш код может зависать в случайных точках при использовании Mac OSX. Убедитесь, что ваша сборка имеет эти флаги, как рекомендовано на сайте LuaJIT:
- -pagezero_size 10000 -image_base 100000000
- Это позволит вашему коду работать правильно, без сбоев. Пожалуйста, прочитайте документацию LuaJIT по компиляции и запуску с LuaJIT для получения дополнительной информации.
- «Компилятор вне пространства кучи»
- Типичные Visual Studio, компилятор будет жаловаться , что из кучи пространства , потому что визуальные дефолтов студии с использованием x86 (32-разрядная версия ) версия сама по себе (он все равно будет компилировать x86 или x64 или ARM двоичные файлы, просто компилятор сам является 32-битный исполняемый файл). Чтобы обойти требования к пространству кучи, добавьте в свои .vcoxprojфайлы следующий оператор в соответствии с инструкциями OrfeasZ в этом выпуске :<Import .../>
- <PropertyGroup>
- <PreferredToolArchitecture>x64</PreferredToolArchitecture>
- </PropertyGroup>
- При этом следует использовать 64-битные инструменты по умолчанию и увеличить максимальное пространство кучи до того, что может обрабатывать машина с 64-битными окнами. Если у вас не более 4 ГБ ОЗУ или вы все еще сталкиваетесь с проблемами, вам следует изучить использование create_simple_usertypeи добавление функций 1 к 1 , как показано в простом примере типа пользователя здесь ..set( ... )
- Ошибки компоновщика
- Есть много причин для ошибок компилятора компилятора. Обычно не знают, что вы скомпилировали библиотеку Lua как C ++: при сборке с C ++ важно отметить, что каждая типичная (статическая или динамическая) библиотека ожидает использования соглашения о вызовах C, и что sol включает код используя, где это применимо.extern 'C'
- Однако, когда целевая библиотека Lua скомпилирована с C ++, необходимо изменить соглашение о вызовах и схему искажения имен, избавившись от блока. Этого можно достичь, добавив перед включением sol3, или добавив его в командную строку вашей компиляции. Если вы собираете LuaJIT в режиме C ++ (как бы вы этого не знали), то вам тоже нужно . Как правило, нет необходимости использовать этот последний.extern 'C'#defineSOL_USING_CXX_LUA#define SOL_USING_CXX_LUAJIT
- Обратите внимание, что вы не должны определять их стандартными сборками Lua или LuaJIT. Смотрите страницу конфигурации для более подробной информации.
- «Пойманные (…) исключения» ошибки
- Иногда вы ожидаете правильно написанные ошибки и вместо этого получаете ошибку о перехвате ...исключения. Это может означать, что вы либо построили Lua как C ++, либо используете такую среду, как LuaJIT, которая имеет полную поддержку взаимодействия для исключений в определенных типах систем (x64 для LuaJIT 2.0.5, x86 и x64 в LuaJIT 2.1.x-beta и более поздних версиях).
- Пожалуйста, убедитесь, что используете SOL_EXCEPTIONS_SAFE_PROPAGATIONопределение перед включением sol3, чтобы сделать это. Вы можете прочитать больше на странице исключений здесь .
- Лови и ломай!
- По умолчанию sol добавляет default_at_panicобработчик в состояния, открытые sol ( более подробно см. В автоматических обработчиках sol :: state ). Если исключения не отключены, этот обработчик сгенерирует, чтобы дать пользователю шанс на восстановление. Однако почти во всех случаях, когда Lua вызывает lua_atpanicи нажимает эту функцию, это означает, что в вашем коде или коде Lua произошла необратимая ошибка, и виртуальная машина находится в непредсказуемом или мертвом состоянии. Поймать ошибку, выдаваемую из обработчика по умолчанию, и затем действовать так, как будто все исправлено или не в порядке, НЕ лучшая идея. Неожиданные ошибки в оптимизированных сборках и сборках в режиме выпуска могут привести, помимо прочих серьезных проблем.
- Желательно, если вы поймаете ошибку, в которую войдете, что произошло, завершите работу виртуальной машины Lua как можно скорее, а затем произойдет сбой, если ваше приложение не сможет обработать раскрутку нового состояния Lua. Ловить можно, но вы должны понимать риски того, что вы делаете, когда вы это делаете. Для получения дополнительной информации о перехвате исключений, потенциалах, не отключая исключения и другие хитрости и предостережения, читайте об исключениях в sol здесь .
- Lua - это прежде всего API C: исключение, возникающее из него, по сути является последним, терминальным поведением, которого виртуальная машина не ожидает. Вы можете увидеть пример обработки паники на странице исключений здесь . Это означает , что создание вокруг незащищенной функции sol3 или вызова сценария является НЕ достаточно , чтобы сохранить виртуальную машину в чистом состоянии. Lua не понимает исключения, и их выдача приводит к неопределенному поведению, если они однажды всплывают через C API, а затем состояние используется снова. Пожалуйста, поймайте, и сбой.try { ... } catch (...) {}
- Кроме того, для вас было бы хорошей идеей использовать функции безопасности, о которых говорилось в разделе о безопасности , особенно для тех, которые связаны с функциями.
- Деструкторы и безопасность
- Другая проблема заключается в том, что Lua - это C API. Он использует setjmpи longjmpвыпрыгивать из кода при возникновении ошибки. Это означает, что он будет игнорировать деструкторы в вашем коде, если вы неправильно используете библиотеку или базовую виртуальную машину Lua. Чтобы решить эту проблему, соберите Lua как C ++. Когда ошибка Lua VM возникает и lua_errorзапускается, она вызывает ее как исключение, которое вызывает правильную семантику раскручивания.
- Сборка Lua как C ++ позволяет обойти эту проблему и позволяет ошибочно составлять ошибки, генерируемые lua.
- Защищенные функции и доступ
- По умолчанию sol :: function предполагает, что код работает нормально и проблем нет. sol :: state (_view) :: script (_file) также предполагает, что код работает нормально. Используйте sol :: protected_function, чтобы получить доступ к функции, где вы можете проверить, все ли работает. Используйте sol :: option для безопасного получения значения из Lua. Используйте sol :: state (_view) :: do_string / do_file / load / load_file для безопасной загрузки и получения результатов из скрипта. Предусмотрены простые и быстрые настройки по умолчанию с исключениями, которые могут привести к аварийному завершению работы виртуальной машины в случае сбоя.
- Защищенные функции не доступны всем.
- Иногда некоторые скрипты загружаются плохо. Даже если вы защитите вызов функции, фактическая загрузка файла или его выполнение будут плохими, и в этом случае sol :: protected_function не спасет вас. Убедитесь, что вы зарегистрировали свой собственный обработчик паники, чтобы вы могли отлавливать ошибки, или следуйте советам поведения catch + crash выше. Помните, что вы также можете связать свои собственные функции и отказаться от встроенной защиты sol3 для себя, связав необработанную функцию lua_CFunction
- Итерация.
- Таблицы могут иметь другой мусор, который затрудняет итерацию их числовой части при использовании мягкого for-eachцикла или при вызове for_eachфункции sol . Используйте числовой вид для перебора таблицы. Итерация также не повторяется в каком-либо определенном порядке: см. Это примечание в документации таблицы для более подробного объяснения .
- поддерживаемые компиляторы, двоичный размер, время компиляции получить хороший конечный продукт из соли
- поддерживаемые компиляторы
- GCC 7.x теперь отсутствует вместе с Visual Studio 2018. Это означает, что Sol-версия v2.20.1 - это текущая версия кода, предназначенная для более старых компиляторов, не перечисленных ниже. Более новый код будет нацелен на работу со следующими компиляторами и использование их возможностей, возможно, с использованием любых возможностей C ++ 17, предоставляемых компиляторами и стандартными библиотеками, связанными с ними по умолчанию.
- v2.20.1 поддерживает:
- VC ++
- Visual Studio 2018
- Visual Studio 2015 (последние обновления)
- GCC (включает MinGW)
- V7.x
- v6.x
- v5.x
- v4.8 +
- лязг
- v4.x
- v3.9.x
- v3.8.x
- v3.7.x
- v3.6.x
- Примечание: это относится и к Apple Clang в XCode, но этот компилятор также имеет свои недостатки и проблемы
- Это не значит, что мы немедленно отказываемся от старых компиляторов. Мы обновим эту страницу, так как соответствующие исправления перенесены в выпуски v2.xx. Помните, что sol3 является полнофункциональным: больше ничего мы не можем добавить в библиотеку на данный момент с поддержкой компилятора C ++ 11 / C ++ 14, так что ваш код будет покрыт еще долго.
- Новые функции будут нацелены на следующие компиляторы:
- VC ++
- Visual Studio vNext
- Visual Studio 2018
- GCC (включает MinGW)
- v8.x
- V7.x
- лязг
- V7.x
- v6.x
- v5.x
- v4.x
- v3.9.x
- Обратите внимание, что Visual Studio 2018 Community Edition теперь абсолютно бесплатен и устанавливается быстрее и проще, чем когда-либо прежде. Это также устраняет много хакерских обходных путей и формально поддерживает decltype SFINAE.
- Версия 7.x компилятора MinGW GCC исправляет давнишний срыв в заголовке <codecvt>, который заменяет порядковые номера строк utf16 и utf32.
- В Clang 3.4, 3.5 и 3.6 есть много ошибок, с которыми мы сталкивались при разработке sol3 и которые в течение долгого времени отрицательно влияли на пользователей.
- Мы рекомендуем всем пользователям обновиться немедленно. Если по какой-то причине вам нужен старый код, используйте Sol Release v2.20.1 : в противном случае всегда берите последнюю версию Sol3.
- поддержка функций
- Следите за будущим компилятором и поддержкой функций в этом выпуске здесь .
- поддерживаемая версия Lua
- Мы поддерживаем:
- Lua 5.3+
- Lua 5.2
- Lua 5.1
- LuaJIT 2.0.x +
- LuaJIT 2.1.x-beta3 +
- двоичные размеры
- Для индивидов, которые часто используют пользовательские типы, время компиляции может возрасти. Это происходит из-за того, что C ++ 11 и C ++ 14 не имеют очень хороших возможностей для обработки параметров шаблона и переменных параметров шаблона. В C ++ 17 и C ++ Next есть несколько вещей, которые может использовать sol, но проблема в том, что многие люди не могут работать с новейшими и лучшими: поэтому мы должны использовать более старые методы, которые приводят к честному количество избыточных специализаций функций, которые могут зависеть от выбора встраивания компилятора и других подобных методов.
- улучшения скорости компиляции
- Вот некоторые заметки о том, как добиться лучшего времени компиляции без ущерба для производительности:
- Когда вы связываете множество пользовательских типов, поместите их все в одну единицу перевода (один файл C ++), чтобы она не перекомпилировалась несколько раз, а затем будет отброшена компоновщиком позже.
- Помните, что привязка типа пользователя заканчивается сериализацией в состояние Lua, поэтому вам никогда не нужно, чтобы они появлялись в заголовке и вызывали одинаковые накладные расходы на компиляцию для каждого скомпилированного модуля в вашем проекте.
- Рассмотрите возможность размещения групп привязок в нескольких разных единицах перевода (нескольких исходных файлах C ++), чтобы при изменении привязок перекомпилировалась только часть привязок.
- Избегайте помещать привязки в заголовки: он будет замедлять ваш сборник
- Если вы разрабатываете совместно используемую библиотеку, ограничьте общую площадь поверхности, четко и явно помечая функции как видимые и экспортируемые, а все остальное по умолчанию оставляйте скрытыми или невидимыми.
- Для людей, у которых уже есть инструмент, который извлекает сигнатуры функций и аргументы, в ваших интересах было бы подключиться к этому инструменту или генератору и выгрузить информацию один раз, используя низкоуровневые абстракции sol3. Вопрос описания предварительных шагов можно найти здесь .
- следующие шаги
- Следующим шагом для sol с точки зрения разработчика является формальное превращение библиотеки в C ++ 17. Это будет означать использование выражений складывания и некоторых других вещей, которые значительно сократят время компиляции. К сожалению, это означает также повышение требований к компилятору. Большинству не все равно, другие очень медленно обновляются: найти баланс сложно, и часто нам приходится выбирать обратную совместимость и исправления для плохих / старых компиляторов (которых уже много в базе кода).
- Надеемся, что по мере развития событий мы продвигаемся вперед.
- особенности
- что поддерживает sol (и другие библиотеки)?
- Цель sol - предоставить невероятно чистый API, который обеспечивает высокую производительность (сравнимую или лучше, чем C, на которой он был написан) и чрезвычайно простую в использовании. То есть пользователи должны иметь возможность сказать: «это работает так, как я ожидал».
- Для сложных технических компонентов Lua и его экосистемы, которую мы поддерживаем, вот полное изложение:
- что поддерживает Sol
- Поддержка Lua 5.1, 5.2 и 5.3+ и LuaJIT 2.0.4 + 2.1.x-beta3 +. Мы достигаем этого через наш заголовок совместимости .
- Поддержка таблиц : установка значений, получение значений нескольких (разных) типов
- Ленивая оценка для вложенных / цепочек запросов
- table["a"]["b"]["c"] = 24;
- Неявное преобразование в типы, которые вы хотите
- double b = table["computed_value"];
- получение поддержки: пометьте функцию, чей возврат должен привести к сопрограмме
- Дополнительная поддержка: установка значений, получение значений нескольких (разных) типов
- Ленивая оценка для вложенных / цепочек запросов
- optional<int> maybe_number = table["a"]["b"]["invalid_key"];
- Включает безопасность, когда вы этого хотите: скорость, когда вы этого не делаете
- Поддержка вызовов (функции, лямбды, функции-члены)
- Вытащите любую функцию Lua с помощью sol :: function :sol::function fx = table["socket_send"];
- Можно также установить вызываемые элементы в прокси оператора [] :table["move_dude"] = &engine::move_dude;
- Безопасность: используйте sol :: protected_function, чтобы поймать любую ошибку
- ЛЮБОЙ вид: исключение C ++ или ошибки Lua перехвачены и проходят через необязательную error_handlerпеременную
- Дополнительно: перегрузка одного имени функции, поэтому вам не нужно делать скучные проверки типов
- Дополнительно: эффективная обработка и хорошо документированный способ работы с аргументами
- Поддержка пользовательского типа ( sol :: usertype в API):
- Установить функции-члены для вызова
- Установить переменные-члены
- Установите переменные в классе, которые основаны на функциях установки / получения, используя свойства
- Используйте свободные функции, которые принимают Тип в качестве первого аргумента (указатель или ссылка)
- Поддержка классов «Фабрика», которые не предоставляют конструктор или деструктор
- Изменение памяти пользовательских данных в C ++ напрямую влияет на Lua без копирования, и
- Изменение пользовательских данных в Lua напрямую влияет на ссылки / указатели C ++
- my_class& a = table["a"]; my_class* a_ptr = table["a"];
- Если вам нужна копия, просто используйте семантику значений и получите копии:
- my_class a = table["a"];
- Поддержка потоков / сопрограмм
- Используйте, возобновляйте и играйте с сопрограммами, как обычные функции
- Получить и использовать их даже в отдельном потоке Lua
- Мониторинг статуса и проверка ошибок
- Дополнительно: настраивается и расширяется под ваши собственные типы, если вы переопределяете определения структуры шаблона getter / pusher / checker .
- Feature Matrix ™
- Приведенная ниже таблица характеристик проверяет наличие чего-либо. Это, однако, фактически не учитывает трудоемкий синтаксис.
- ✔ полная поддержка: работает так, как вы ожидаете (оператор [] для таблиц и т. Д.)
- ~ частичная поддержка / поддержка wonky: это означает, что она либо поддерживается другим способом (не с желаемым синтаксисом, серьезными предостережениями и т. д.). Иногда означает отказ от использования простого C API (в какой момент, в чем смысл абстракции?).
- Support нет поддержки: функция не работает или, если она есть, она ДЕЙСТВИТЕЛЬНО не подходит
- Замечания по применению библиотек приведены ниже таблиц.
- объяснения категории
- Объяснения для нескольких категорий приведены ниже (остальные говорят сами за себя).
- необязательно: поддержка получения элемента или, возможно, нет (и не форсирование конструкции по умолчанию того, что составляет поддельный / мертвый объект). Обычно приходит с std(::experimental)::optional. Это довольно новый класс, поэтому допустим и внутренний класс внутри библиотеки с похожей семантикой.
- таблицы: своего рода абстракция для работы с таблицами. Идеальная поддержка есть , и все, что подразумевает синтаксис.mytable["some_key"] =value
- цепочка таблиц: в сочетании с таблицами, с возможностью глубокого запроса в таблицы mytable["key1"]["key2"]["key3"]. Обратите внимание, что это становится отправной точкой для некоторых библиотек: сбой, если "key1"при попытке доступа не существует "key2"(sol избегает этого специально при использовании sol::optional), и иногда это также является узким местом с высокой производительностью, поскольку выражения не лениво оцениваются библиотекой.
- произвольные ключи: разрешить коду C ++ использовать пользовательские данные, другие таблицы, целые числа и т. д. в качестве ключей для таблицы.
- определяемые пользователем типы (udts): типы C ++, данные формы и функции в коде Lua.
- udts - функции-члены: функции-члены C ++ для типа, обычно вызываемые my_object:foo(1)или похожие в Lua.
- udts - табличные переменные: переменные / свойства члена C ++, управляемые и в Luamy_object.var = 24
- Функция привязки: поддержка привязки всех типов функций. Лямбды, функции-члены, свободные функции, в разных контекстах и т. Д.
- защищенная функция: использование lua_pcallдля вызова функции, которая предлагает обработку ошибок и прыжок на батуте (а также возможность отказаться от этого поведения)
- multi-return: возвращение нескольких значений из и в Lua (обычно через tuple<...>или каким-либо другим способом)
- Вариантный / вариантный аргумент: возможность принимать «что угодно» от Lua и даже возвращать «что угодно» Lua ( objectабстракция, вариационные аргументы и т. д.)
- наследование: допускает некоторую степень подтипирования или наследования классов / пользовательских данных от Lua - это обычно означает, что вы можете извлечь базовый указатель из Lua, даже если вы передадите библиотеке производный указатель
- перегрузка: способность вызывать перегруженные функции, сопоставленные по арности или типу (тогда lua вызывает другую функцию ).foo( 1 )foo("bark" )
- Lua thread: базовая оболочка API lua thread; связывает с сопрограммой.
- сопрограммы: позволяет вызывать функцию несколько раз, возобновляя выполнение сопрограммы Lua каждый раз
- получение функций C ++: позволяет вызывать функцию из C ++ несколько раз и возвращать любые результаты, которые она возвращает, через C API или в Lua
- окружения: абстракция для получения, установки и управления средой с использованием табличных методов, функций или иным образом. Обычно для песочницы
- обычный C luawrapper Lua-INTF Luabind Селена Sol3 oolua Lua-апи-п.п. Кагуя SLB3 SWIG luacppinterface luwra
- необязательный ~ ✗ ✔ ✗ ✗ ✔ ✗ ✗ ✔ ✗ ✗ ✗ ✗
- таблицы ~ ~ ~ ✔ ✔ ✔ ~ ✔ ✔ ✗ ✗ ~ ✔
- цепочка таблиц ~ ~ ~ ✔ ✔ ✔ ✗ ✔ ✔ ✗ ✗ ~ ✔
- произвольные ключи ~ ✔ ✔ ✔ ✔ ✔ ✗ ~ ✔ ✗ ✗ ✗ ✗
- пользовательские типы (udts) ~ ✔ ✔ ✔ ✔ ✔ ~ ✔ ✔ ✔ ✔ ✔ ✔
- udts: функции-члены ~ ✔ ✔ ✔ ✔ ✔ ~ ✔ ✔ ✔ ✔ ✔ ✔
- udts: переменные таблицы ~ ~ ~ ~ ~ ✔ ~ ~ ~ ✗ ✔ ✗ ~
- абстракции стека ~ ✔ ✔ ✔ ✔ ✔ ✔ ✔ ✔ ~ ✗ ~ ✔
- Lua Callables из C (++) ~ ✔ ✔ ✔ ✔ ✔ ✔ ✔ ✔ ✔ ✔ ✔ ~
- привязка функции ~ ✔ ✔ ✔ ✔ ✔ ~ ~ ✔ ~ ~ ~ ✔
- защищенный вызов ~ ✗ ~ ~ ~ ✔ ~ ✔ ~ ~ ~ ~ ~
- мульти-возврат ~ ✗ ✔ ✔ ✔ ✔ ~ ✔ ✔ ~ ✔ ~ ✗
- вариационный / вариантный аргумент ~ ✔ ✔ ✔ ✔ ✔ ~ ✔ ✔ ~ ~ ~ ✗
- наследование ~ ✔ ✔ ✔ ✔ ✔ ~ ~ ✔ ~ ✔ ~ ✗
- перегрузка ~ ✗ ✔ ✗ ✗ ✔ ✗ ✗ ✔ ✔ ✔ ✗ ✗
- Луа нить ~ ✗ ~ ✗ ✗ ✔ ✔ ✗ ✔ ✗ ✗ ✔ ✗
- окружающая среда ✗ ✗ ✗ ✗ ✗ ✔ ✗ ✗ ✗ ✗ ✗ ✗ ✗
- сопрограммы ~ ✗ ~ ✔ ✔ ✔ ✗ ✗ ✔ ✗ ✗ ✔ ✗
- дающие функции C ++ ~ ✔ ✔ ✔ ~ ✔ ~ ✗ ✔ ✗ ~ ✔ ~
- поддержка no-rtti ✔ ✗ ✔ ✗ ✗ ✔ ✔ ✗ ✔ ✔ ~ ✔ ✔
- поддержка без исключений ✔ ✗ ✔ ~ ✗ ✔ ✔ ✗ ✔ ✔ ~ ✔ ✔
- Lua 5.1 ✔ ✔ ✔ ✔ ✗ ✔ ✔ ✔ ✔ ✔ ✔ ✗ ✔
- Lua 5.2 ✔ ✔ ✔ ✔ ✔ ✔ ✔ ✔ ✔ ✔ ✔ ✔ ✔
- Lua 5.3 ✔ ✔ ✔ ✔ ✔ ✔ ✔ ✔ ✔ ✔ ✔ ✔ ✔
- LuaJIT ✔ ✔ ✔ ✔ ~ ✔ ✔ ✔ ✔ ✔ ✔ ✗ ✔
- распределение компилировать заголовок и то и другое компилировать заголовок заголовок компилировать компилировать заголовок компилировать генерироваться компилировать заголовок
- замечания по реализации
- Обычный C -
- Очевидно, что вы можете делать все что угодно с Plain C, но прилагаемые усилия являются астрономическими по сравнению с тем, что предлагают другие оболочки, библиотеки и фреймворки.
- Не очень хорошо масштабируется (с точки зрения простоты использования разработчиком)
- Компиляция (или использование менеджера пакетов), очевидно, необходима для вашей платформы и требует использования ЛЮБЫХ из этих библиотек, но это нормально, потому что все библиотеки в любом случае нуждаются в некоторой версии Lua, так что у вас всегда есть это!
- кагуя -
- Табличные переменные / переменные-члены автоматически превращаются в набор и получениеobj:x( value )obj:x()
- Имеет дополнительную поддержку
- Вдохновленная поддержка сопрограмм для sol
- Автор библиотеки (саторен) хороший парень!
- C ++ 11/14, или Boosified (что делает его совместимым с C ++ 03)
- Регистрация классов немного многословна, но не так оскорбительна, как OOLua или lua-intf или другие
- Синтаксис настройки конструктора выглядит привлекательно
- соль -
- Одна из немногих библиотек с дополнительной поддержкой!
- В основном самый быстрый по всем параметрам: http://sol2.readthedocs.io/en/latest/benchmarks.html
- Поддержка перегрузки может запутаться с наследованием, см. Здесь
- Используются флаги C ++ 14 / «C ++ 1y» (-std = c ++ 14, -std = c ++ 1y, = std = c ++ 1z) (доступно с GCC 4.9 и Clang 3.5)
- Активные вопросы, активные люди
- Заслуживает много любви!
- lua-intf -
- Может быть как заголовочным, так и скомпилированным
- Имеет дополнительную поддержку
- C ++ 11
- Регистрация на основе макросов (странный псевдо-язык)
- Довольно быстро в большинстве случаев
- Регистрация классов / «модулей» при использовании кода на C ++ чрезвычайно многословна
- Чтобы связать поиски в цепочку, нужно склеить ключи (например, "mykey.mykey2") при operator[]поиске (например, вы не можете их произвольно вложить, вы должны предварительно составить правильную строку поиска) (с треском проваливается для поиска не в строке! ).
- Не так уж и плохо!
- Селена -
- Табличные переменные / переменные-члены автоматически превращаются в набор и получениеobj:set_x( value )obj:x()
- Регистрация классов / «модулей» с использованием кода C ++ очень многословна, похожа на стиль lua-intf
- Большую часть времени ест дерьмо, когда дело доходит до производительности (см. Тесты )
- Многие пользователи (посты в блогах и т. Д. Сделали его популярным), но репозиторий довольно застойный ...
- Luawrapper -
- Принимает подход написания и чтения таблиц с использованием readVariableи writeVariableфункций
- C ++ 11, без макросов!
- Интерфейс может быть неуклюжим (нет табличных структур данных: большинство вещей идет, хотя readVariable/ writeVariable)
- Внутренние ошибки компилятора в Visual Studio 2015 - отправлен PR, чтобы исправить это, надеюсь, он будет поднят
- SWIG (3.0) -
- Очень всеобъемлющий для привязки понятий C ++ (классы, переменные и т. Д.) К Lua
- Помогает буквально ни с чем другим (таблицы, потоки, абстракции стека и т. Д.)
- Не очень хорошая, полнофункциональная библиотека ...
- Требуется этап предварительной обработки (но это не… УЖАСНО сложный этап предварительной обработки); некоторый пример написания дополнительных классов, которые вы уже объявили
- luacppinterface -
- В ветке, которая исправляет предупреждения VC ++ и вводит новую работу, есть проблемы с проверкой типов, поэтому используйте только стабильную ветку
- Нет поддержки табличных переменных
- На самом деле есть таблицы (но без оператора [])
- Не поддерживает произвольные ключи
- Луабинд -
- Одна из старых фреймворков, но многие обновляют ее и предоставляют «дебошифицированные» версии
- Странные ключевые слова in-lua и синтаксический анализ, позволяющие писать классы на lua
- не уверен, что хорошая функция; поставщик привязан к этой библиотеке, чтобы зависеть от этого конкретного синтаксиса класса?
- Комплексные привязки lua (могут даже связывать «свойства»)
- Есть некоторый код, который создает ICE в Visual C ++: я отправил исправление в библиотеку в надежде, что оно будет принято
- Поддержка таблиц Wonky: без базовых функций преобразования luabind::object; нужно нажать объект, а затем использовать lua API, чтобы получить то, что вы хотите
- Луа-апи-пп -
- Скомпилировано, но рекомендуется добавлять исходные файлы непосредственно в ваш проект
- Регистрация пользовательских данных с помощью толстых макросов установки: LUAPP_USERDATA (…) плюс несколько бесплатных функций, которые принимают аргументT& self
- Вы можете связать функции-члены напрямую, но только если вы переопределяете метатируемые записи
- В противном случае, СЛОЖНАЯ саморегистрация, которая заставляет задуматься, зачем вы используете фреймворк
- Вы должны создать контекст, а затем вызвать его, чтобы начать доступ к состоянию lua (добавив больше шаблона… спасибо)
- К счастью, в отличие от многих библиотек, он на самом деле имеет тип Table, который можно использовать с легкостью. В КОНЦЕ КОНЦОВ
- C ++ 11-иш в некоторых отношениях
- Грустное лицо, благодаря способу регистрации пользовательских данных
- SLB3 -
- Старый код экспортируется в github из умирающего кода Google
- «.NET Style» - чтобы переопределить функциональность, наследовать от класса - шаблон (разве это не то, от чего мы пытаемся избавиться?)
- Указатели повсюду: семантика владения неясна
- Документация бедных мочой, тьфу!
- Наверное, наименее любимый для работы!
- уолу -
- Синтаксис этой библиотеки не мой любимый ... иди, прочитай документы , решай сам!
- Наихудшее с точки зрения того, как его использовать: может иметь документы, но DSL чрезвычайно дрянной с толстыми макросами, которые трудно отлаживать / проверять на наличие ошибок
- Та же проблема, что и у lua-api-pp: нигде не может быть макросов объявления, кроме пространства имен верхнего уровня из-за макроса объявления шаблона
- Поддерживает отсутствие исключений или RTT включен (блестящий!)
- Плохая поддержка RAII: стиль default-construct-and-get (требуется некоторая форма инициализации для выполнения getобъекта, и его трудно расширить)
- Автор библиотеки сообщил мне, что он лично советует людям не использовать Tableабстракцию в OOLua ... Я также советую людям считать ее абстракции таблиц несуществующими?
- Табличные переменные / переменные-члены из C ++ превращаются в вызовы функций ( get_xи set_xпо умолчанию)
- Люва -
- Как вы храните сохраняющие состояние функторы / лямбы? Пока что такой поддержки нет.
- Невозможно извлечь функции, не оставив их в стеке: ручная очистка становится делом
- Не понимает functionпреобразования и тому подобное (но с помощью дополнительного кода можно заставить его работать)
- В последнее время многое улучшено: можно объединять таблицы в таблицы и тому подобное, даже если производительность в этом случае немного огорчает
- Когда вам удастся установить вызовы функций с помощью макросов, они будут быстрыми (может ли шаблонное решение сделать то же самое? Соль узнает!)
- Отсутствие поддержки табличных переменных - получите функции getter / setter, похожие на kaguya
- Табличные переменные становятся классическими статиками (удивительно)
- Танки в более поздних MSVC
- compatibility.hpp
- Совместимость Lua 5.3 / 5.2 для Lua 5.1 / LuaJIT
- Это подробный заголовок, используемый для обеспечения совместимости с API 5.2 и 5.3+. Он содержит код из MIT-лицензированного кода Lua в некоторых местах, а также из репозитория lua- compat от KeplerProject.
- Он не полностью документирован, поскольку единственная цель этого заголовка - для внутреннего использования, чтобы убедиться, что sol компилируется на всех платформах / дистрибутивах без ошибок или отсутствующих функциональных возможностей Lua. Если вы считаете, что есть какие-то функции совместимости, которых нам не хватает, или если вы сталкиваетесь с ошибками переопределения, внесите ошибку в систему отслеживания ошибок .
- Если у вас это уже есть в вашем проекте или у вас есть собственный уровень совместимости, то перед включением или передачей этого флага в командной строке отключите упаковщик совместимости.#define SOL_NO_COMPAT 1sol.hpp
- Для лицензий, смотрите здесь
- легкий <T> / пользователь <T>
- служебный класс для самой дешевой формы (легких) пользовательских данных
- template <typename T>
- struct user;
- template <typename T>
- struct light;
- sol::user<T>и sol::light<T>два служебных класса, которые не участвуют в полной системе sol :: usertype <T> . Цель этих классов - обеспечить минимальный объем памяти и накладные расходы для размещения одного элемента и получения одного элемента из Lua. sol::user<T>при нажатии на Lua создаст тонкий, безымянный metatable для этого экземпляра, в частности, для вызова его деструктора. sol::light<T>специально толкает ссылку / указатель на Lua как sol::type::lightuserdata.
- Если вы чувствуете, что вам не нужно, чтобы что-то участвовало в полной системе пользовательских типов <T> , используйте служебные функции и для создания этих типов и сохранения их в Lua. Вы можете получить их стек Lua / из системы Lua, используя те же методы извлечения на и на столах и с операциями стека.sol::make_user( ... )sol::make_light( ... )getoperator[]
- Оба имеют неявные операторы преобразования в T*и T&, так что вы можете сразу установить для них соответствующие указатели и ссылочные типы, если они вам нужны.
- пространство имен стека
- слой абстракции нитро-песчаного ядра над Lua
- namespace stack
- Если вы обнаружите, что абстракции более высокого уровня не соответствуют вашим потребностям, вы, возможно, захотите углубиться в stack пространство имен, чтобы попытаться получить больше от sol. stack.hpp а stack пространство имен определяет несколько утилит для работы с Lua, включая утилиты push / popping, геттеры, средства проверки типов, помощники вызовов Lua и многое другое. Это пространство имен не документировано полностью, так как большая часть его интерфейса является ртутной и может меняться между выпусками, либо сильно повышать производительность, либо улучшать sol api .
- Работу на этом уровне стека можно улучшить, если понять, как работает стек Lua в целом, а затем дополнить его объектами и предметами.
- Однако есть несколько точек настройки ADL, которые вы можете использовать для своих целей, и несколько потенциально полезных функций. Это может помочь, если вы пытаетесь уменьшить объем кода, который вам нужно написать, или если вы хотите, чтобы ваши типы по-разному вели себя в стеке sol. Обратите внимание, что переопределение значений по умолчанию может привести к отказу от многих гарантий безопасности, которые предоставляет sol: поэтому изменяйте точки расширения по своему усмотрению.
- структуры
- структура: запись
- struct record {
- int last;
- int used;
- void use(int count);
- };
- Эта структура предназначена для расширенного использования с stack :: get и stack :: check_get . При переопределении точек настройки важно вызвать useфункцию-член этого класса с количеством вещей, которые вы вытаскиваете из стека. usedсодержит общее накопление произведенных предметов. lastэто количество элементов, полученных из стека с последней операцией (не обязательно извлеченных из стека). Во всех тривиальных случаях для типов и после операции; структуры, такие как и могут тянуть больше в зависимости от классов, которые он содержит.last == 1used == 1pairtuple
- При переопределении точек настройки обратите внимание, что эта структура должна позволять вам помещать несколько возвращаемых значений и получать несколько возвращаемых значений в стек, и, таким образом, иметь возможность беспрепятственно упаковать / распаковать возвращаемые значения из Lua в одну структуру C ++ и наоборот. наоборот. Эта функция рекомендуется только для людей, которым необходимо настроить библиотеку дальше, чем основы. Это также хороший способ добавить поддержку для типа и предложить его обратно в исходную библиотеку, чтобы другие могли получить пользу от вашей работы.
- Обратите внимание, что настройки также можно разместить здесь на отдельной странице, если отдельные лица решат сделать детальные настройки для своей структуры или других мест.
- структура: зонд
- struct probe {
- bool success;
- int levels;
- probe(bool s, int l);
- operator bool() const;
- };
- Эта структура используется для того, чтобы показать, было ли успешное исследование get_field или нет.
- члены
- функция: call_lua
- template<bool check_args = stack_detail::default_check_arguments, bool clean_stack = true, typename Fx, typename... FxArgs>
- inline int call_lua(lua_State* L, int start, Fx&& fx, FxArgs&&... fxargs);
- Эта функция полезна, когда вы связываете к сырому функции C , но нужны абстракции SOL, чтобы спасти вас агонию настройки аргументов и знать , как вызов функций C работает . startПараметр указывает функцию , где начать вытягивать аргументы. Параметр fx - это то, что должно называться. Дополнительные аргументы передаются функции напрямую. Существуют промежуточные версии этого ( sol::stack::call_into_luaи аналогичного) для более продвинутых пользователей, но они не документированы, поскольку они могут быть изменены для повышения производительности или соответствующей корректировки API в последующих итерациях sol3. Используйте более продвинутые версии на свой страх и риск.
- функция: получить
- template <typename T>
- auto get( lua_State* L, int index = -1 )
- template <typename T>
- auto get( lua_State* L, int index, record& tracking )
- Получает значение объекта indexв стеке. Тип возвращаемого значения зависит от T: с примитивными типами обычно это так T: для всех нераспознанных Tэто, как правило, T&точка расширения, возвращаемая реализацией sol_lua_get <T> . Тип Tпроверяется один раз, как есть (с constоставленными в покое и ссылочными квалификаторами), а затем еще раз, когда он удаляетconst квалификаторы верхнего уровня и модификаторы ссылок, прежде чем перенаправлять их в функцию sol_lua_get <T> точки расширения . stack::getпо умолчанию будет пересылать все аргументы в функцию stack :: check_get с обработчиком, type_panicчтобы сильно предупреждать об ошибках, если вы просите о безопасности,
- Вы также можете извлечь sol :: необязательный <T> из этого, чтобы он пытался не выдавать ошибки при выполнении get, а тип неверный.
- функция: проверить
- template <typename T>
- bool check( lua_State* L, int index = -1 )
- template <typename T, typename Handler>
- bool check( lua_State* L, int index, Handler&& handler )
- template <typename T, typename Handler>
- bool check( lua_State* L, int index, Handler&& handler, record& tracking )
- Проверяет, имеет ли объект indexтип T. Если это не так , он будет вызывать handlerфункцию с , , и в качестве аргументов (и , возможно , с 5 - го аргумента строки . Если вы не передаете свой собственный обработчик, обработчик будет пройдена.lua_State* Lint indexsol::type expectedsol::type actualsol::string_view messageno_panic
- функция: get_usertype
- template <typename T>
- auto get_usertype( lua_State* L, int index = -1 )
- template <typename T>
- auto get_usertype( lua_State* L, int index, record& tracking )
- Непосредственно пытается повторно получить тип, Tиспользуя механизмы пользовательского типа sol3. Аналогично обычному getдля определенного пользователем типа. Полезно, когда вам нужно получить доступ к механизму получения пользовательских типов в sol3 и в то же время обеспечить собственную настройку .
- функция: check_usertype
- template <typename T>
- bool check_usertype( lua_State* L, int index = -1 )
- template <typename T, typename Handler>
- bool check_usertype( lua_State* L, int index, Handler&& handler )
- template <typename T, typename Handler>
- bool check_usertype( lua_State* L, int index, Handler&& handler, record& tracking )
- Проверяет, имеет ли объект at indexтип Tи сохраняется ли он как пользовательский тип sol3. Полезно, когда вам нужно получить доступ к механизму проверки пользовательских типов sol3, в то же время предоставляя свои собственные настройки .
- функция: check_get
- template <typename T>
- auto check_get( lua_State* L, int index = -1 )
- template <typename T, typename Handler>
- auto check_get( lua_State* L, int index, Handler&& handler, record& tracking )
- Получает значение объекта indexв стеке, но делает это безопасно. Возвращает optional<U>, где Uв данном случае это тип возвращаемого значения stack::get<T>. Это позволяет человеку должным образом проверить, является ли тип, который он получает, тем, что он на самом деле хочет, и изящно обрабатывать ошибки при работе со стеком, если он того пожелает. Вы можете SOL_ALL_SAFETIES_ONвключить дополнительную безопасность , в которой по stack::getумолчанию будет вызываться эта версия функции с некоторым вариантом обработчика, sol::type_panic_stringчтобы сильно предупреждать об ошибках и помогать вам отслеживать ошибки, если вы подозреваете, что в вашей системе что-то идет не так.
- функция: нажать
- // push T inferred from call site, pass args... through to extension point
- template <typename T, typename... Args>
- int push( lua_State* L, T&& item, Args&&... args )
- // push T that is explicitly specified, pass args... through to extension point
- template <typename T, typename Arg, typename... Args>
- int push( lua_State* L, Arg&& arg, Args&&... args )
- // recursively call the the above "push" with T inferred, one for each argument
- template <typename... Args>
- int multi_push( lua_State* L, Args&&... args )
- Основываясь на том, как он вызывается, помещает в стек переменное количество объектов. в 99% случаев возвращает 1 объект, помещенный в стек. В случае a tuple<...>он рекурсивно выталкивает каждый объект, содержащийся в кортеже, слева направо, в результате чего в стек помещается переменное число вещей (это позволяет многозначные возвраты при привязке функции C ++ к Lua). Может вызываться с аргументами, отличающимися от типа, который хочет выдвинуть, или откуда будет выведен вывод . Окончательная форма этой функции , которая будет вызывать один для каждого аргумента. То, что описывает то, что нажать, сначала очищается путем удаления верхнего уровня.sol::stack::push<T>( L, args... )sol::stack::push( L, arg, args... )Targsol::stack::multi_pushsol::stack::pushTconstквалификаторы и эталонные квалификаторы перед отправкой в точку расширения sol_lua_push <T> .
- функция: push_reference
- // push T inferred from call site, pass args... through to extension point
- template <typename T, typename... Args>
- int push_reference( lua_State* L, T&& item, Args&&... args )
- // push T that is explicitly specified, pass args... through to extension point
- template <typename T, typename Arg, typename... Args>
- int push_reference( lua_State* L, Arg&& arg, Args&&... args )
- // recursively call the the above "push" with T inferred, one for each argument
- template <typename... Args>
- int multi_push_reference( lua_State* L, Args&&... args )
- Эти функции ведут себя аналогично приведенным выше, но они проверяют определенные критерии и вместо этого пытаются выдвинуть ссылку, а не принудительно копировать копию, если это необходимо. Используйте его осторожно, так как sol3 использует это главным образом как возврат из функций и переменных пользовательского типа, чтобы сохранить семантику цепочки / переменной от этого объекта класса. Его внутренние компоненты обновляются в соответствии с потребностями sol3, и, хотя он, как правило, делает «правильные вещи» и его не нужно менять какое-то время, sol3 оставляет за собой право изменять свои внутренние механизмы обнаружения в соответствии с потребностями своих пользователей в любое время, как правило, без нарушения обратной совместимости и ожиданий, но не совсем гарантировано.
- функция: поп
- template <typename... Args>
- auto pop( lua_State* L );
- Выталкивает объект из стека Удалит фиксированное количество объектов из стека, как правило, определяется sol::lua_size<T>чертами предоставленных аргументов. Обычно это функция простоты, используемая для удобства.
- функция: верх
- int top( lua_State* L );
- Возвращает количество значений в стеке.
- функция: set_field
- template <bool global = false, typename Key, typename Value>
- void set_field( lua_State* L, Key&& k, Value&& v );
- template <bool global = false, typename Key, typename Value>
- void set_field( lua_State* L, Key&& k, Value&& v, int objectindex);
- Устанавливает поле, на которое ссылается ключ, kна заданное значение v, помещая ключ в стек, помещая значение в стек, а затем выполняя эквивалент lua_setfieldобъекта для данного значения objectindex. Выполняет оптимизацию и вызывает более быстрые версии функции, если тип Keyсчитается строкой в стиле c и / или если он также помечен в шаблонном globalаргументе как глобальный.
- функция: get_field
- template <bool global = false, typename Key>
- void get_field( lua_State* L, Key&& k [, int objectindex] );
- Получает поле, на которое ссылается ключ k, путем нажатия ключа на стек и затем выполнения эквивалента lua_getfield. Выполняет оптимизацию и вызывает более быстрые версии функции, если тип Keyсчитается строкой в стиле c и / или если он также помечен в шаблонном globalаргументе как глобальный.
- Эта функция оставляет полученное значение в стеке.
- функция: probe_get_field
- template <bool global = false, typename Key>
- probe probe_get_field( lua_State* L, Key&& k [, int objectindex] );
- Получает поле, на которое ссылается ключ k, путем нажатия ключа на стек и затем выполнения эквивалента lua_getfield. Выполняет оптимизацию и вызывает более быстрые версии функции, если тип Keyсчитается строкой в стиле c и / или если он также помечен в шаблонном globalаргументе как глобальный. Кроме того, он делает это безопасно, входя только на столько уровней, насколько это возможно: если возвращаемое значение не является чем-то, что может быть проиндексировано, тогда запросы обхода с tuple/ pairпрекратят работу раньше и вернут зондирующую информацию со структурой зонда .
- Эта функция оставляет полученное значение в стеке.
- объекты (точки расширения)
- Вы можете настроить способ, которым sol обрабатывает различные структуры и классы, следуя информации, представленной в добавлении ваших собственных типов .
- Ниже приведена более обширная информация для любознательных.
- Точка расширения ADL sol_lua_get
- MyType sol_lua_get ( sol::types<MyType>, lua_State* L, int index, sol::stack::record& tracking ) {
- // do work
- // ...
- return MyType{}; // return value
- }
- Эта точка расширения относится к getобъекту (или ссылке, или указателю, или какому-либо другому) типа Tили к чему-то, что может быть преобразовано в него. Внутренняя реализация getter по умолчанию предполагает, Tчто это тип пользователя, и извлекает данные пользователя из Lua, прежде чем пытаться привести их к желаемому T.
- В целом, есть реализации для получения чисел (типа is_floating, is_integralсовпадающих по типу), получения stringи добавления широких строковых и юникодных вариантов, получения необработанных пользовательских данных с помощью userdata_value и чего угодно, как upvalues с upvalue_index , получения необработанных lua_CFunction s и, наконец, извлечения функций Lua в . Он также определен для всего, что происходит от sol :: reference . У этого также есть специальная реализация для 2 умных указателей стандартной библиотеки (см. Память типа пользователя ), которая может быть более конкретно расширена.const char*function<R(Args...)>
- Точка расширения ADL sol_lua_push
- int push ( sol::types<MyType>, lua_State* L, MyType&& value ) {
- // can optionally take more than just 1 argument
- // to "construct" in-place and similar
- // use them however you like!
- // ...
- return N; // number of things pushed onto the stack
- }
- Эта точка расширения является pushзначением в Lua. Возвращает количество вещей, помещенных в стек. Реализация по умолчанию предполагает, Tчто это пользовательский тип, и помещает пользовательские данные в Lua с привязкой к ним специфической для класса метатаблицы в масштабе штата. Есть реализации толкания чисел ( is_floating, is_integralСопоставления типов), получение stringи , получая сырой UserData с UserData и сырьем upvalues с повышать стоимость , получая сырой lua_CFunction с, и , наконец , вытаскивая функцию Lua в . Он также определен для всего, что происходит от sol :: reference . У этого также есть специальная реализация для 2 умных указателей стандартной библиотеки (см. Память типа пользователя)const char*sol::function).
- Точка расширения ADL sol_lua_check
- template <typename Handler>
- bool sol_lua_check ( sol::types<MyType>, lua_State* L, int index, Handler&& handler, sol::stack::record& tracking ) {
- // if the object in the Lua stack at index is a T, return true
- if ( ... ) {
- tracking.use(1); // or however many you use
- return true;
- }
- // otherwise, call the handler function,
- // with the required 4/5 arguments, then return false
- //
- handler(L, index, expected, indextype, "message");
- return false;
- }
- Эта точка расширения заключается в checkтом, является ли тип по данному индексу тем, чем он должен быть. Реализация по умолчанию просто проверяет, равен ли ожидаемый тип, переданный через шаблон, типу объекта по указанному индексу в стеке Lua. Реализация по умолчанию для типов, которые рассматриваются, userdataпроходит множество проверок, чтобы поддержать проверку, является ли тип действительно типом Tили является ли он базовым классом того, что он на самом деле хранит как пользовательские данные в этом индексе.
- Обратите внимание, что вы можете
- Точка расширения ADL sol_lua_interop_check
- template <typename T, typename Handler>
- bool sol_lua_interop_check(sol::types<T>, lua_State* L, int relindex, sol::type index_type, Handler&& handler, sol::stack::record& tracking) {
- // implement custom checking here for a userdata:
- // if it doesn't match, return "false" and regular
- // sol userdata checks will kick in
- return false;
- // returning true will skip sol's
- // default checks
- }
- Эта точка расширения предназначена для checkвнешних пользовательских данных. Он должен возвращаться, trueесли тип соответствует некоторой пользовательской спецификации пользовательских данных (скажем, из другой библиотеки или внутренней структуры), и falseесли нет. Реализация по умолчанию просто возвращается, falseчтобы позволить исходным обработчикам sol3 позаботиться обо всем. Если вы хотите реализовать свою собственную проверку пользовательских типов; например, для возни с toLuaили OOLuaили kaguyaили некоторыми другими библиотеками. Обратите внимание, что библиотека должна иметь макет с совместимой памятью, если вы хотите специализировать этот метод проверки, но не последующий метод получения . Вы можете специализировать его, как показано в примерах взаимодействия .
- Заметка
- Вы должны включить эту функцию с помощью SOL_ENABLE_INTEROP, как описано в разделе конфигурации и безопасности .
- Точка расширения ADL sol_lua_interop_get
- template <typename T>
- pair<bool, T*> sol_lua_interop_get(sol::types<T> t, lua_State* L, int relindex, void* unadjusted_pointer, sol::stack::record& tracking) {
- // implement custom getting here for non-sol3 userdatas:
- // if it doesn't match, return "false" and regular
- // sol userdata getters will kick in
- return { false, nullptr };
- }
- Эта точка расширения относится к getсторонним данным пользователя. Он должен возвращать оба trueи настроенный указатель, если тип соответствует некоторой пользовательской спецификации пользовательских данных (скажем, из другой библиотеки или внутренней структуры). Реализация по умолчанию просто возвращается, чтобы позволить стандартной реализации sol3 позаботиться обо всем. Вы можете использовать его для взаимодействия с другими платформами, которые не являются sol3, но все еще включают их силу; например, для возни с или некоторых других библиотек. Вы можете специализировать его, как показано в примерах взаимодействия .{ false, nullptr }kaguya
- Заметка
- Вы должны включить его с помощью SOL_ENABLE_INTEROP, как описано в разделе конфигурации и безопасности .
- Заметка
- Вам НЕ нужно использовать этот метод, в частности, если расположение памяти совместимо. (Например, toLuaхранит пользовательские данные в sol3-совместимом способе.)
- решить
- утилита для выбора перегруженных вызовов функций C ++
- функция: разрешить перегрузку C ++
- template <typename... Args, typename F>
- constexpr auto resolve( F f );
- resolveэто функция, которая предназначена для того, чтобы помочь пользователям выбрать одну функцию из группы перегруженных функций в C ++. Он работает как для членских, так и для свободных функций. Вы можете использовать его для выбора перегрузок, указав сигнатуру в качестве первого аргумента шаблона. Дан набор перегруженных функций:
- int overloaded(int x);
- int overloaded(int x, int y);
- int overloaded(int x, int y, int z);
- struct thing {
- int overloaded() const;
- int overloaded(int x);
- int overloaded(int x, int y);
- int overloaded(int x, int y, int z);
- };
- Вы можете устранить их неоднозначность, используя resolve:
- auto one_argument_func = resolve<int(int)>( overloaded );
- auto two_argument_func = resolve<int(int, int)>( overloaded );
- auto three_argument_func = resolve<int(int, int, int)>( overloaded );
- auto member_three_argument_func = resolve<int(int, int, int)>( &thing::overloaded );
- auto member_zero_argument_const_func = resolve<int() const>( &thing::overloaded );
- Это важно отметить , что constпомещается в конце для того, когда вы хотите константные перегрузки. Вы получите ошибки компилятора, если вы не конкретизируете и не устраните неоднозначность для функций-членов const. Это разрешение также становится полезным при установке функций для таблицы или state_view :
- sol::state lua;
- lua.set_function("a", resolve<int(int)>( overloaded ) );
- lua.set_function("b", resolve<int(int, int)>( overloaded ));
- lua.set_function("c", resolve<int(int, int, int)>( overloaded ));
- Его также можно использовать с sol :: c_call :
- sol::state lua;
- auto f = sol::c_call<
- decltype(sol::resolve<int(int, int)>(&overloaded)),
- sol::resolve<int(int, int)>(&overloaded)
- >;
- lua.set_function("f", f);
- lua.script("f(1, 2)");
- Заметка
- Вы не можете использовать, sol::resolve<...>(...)когда одна функция является шаблонной, и у нее есть перегрузка без шаблонов: в этом случае она всегда будет неудачной. Чтобы решить эту проблему, используйте руководство или (с необходимыми квалификаторами const -ess, volatile-ness и r-value / l-value, если необходимо).static_cast<R(Args...)>( &func )static_cast<R (T::*)(Args...)>( &T::overloaded_member_func )
- c_call
- шаблонный тип для передачи функций через шаблоны
- template <typename Function, Function f>
- int c_call (lua_State* L);
- template <typename... Functions>
- int c_call (lua_State* L);
- Цель sol::c_call<...>состоит в том, чтобы предоставить способ обернуть функцию и транспортировать ее в контексте времени компиляции. Это обеспечивает более быструю скорость за счет гораздо более сложного для чтения / ухудшения интерфейса и может уменьшить некоторые проблемы со скоростью компиляции шаблонов. sol::c_callожидает тип для своего первого аргумента шаблона и значение ранее предоставленного типа для второго аргумента шаблона. Чтобы сделать перегруженную функцию во время компиляции, укажите несколько функций в одной и той же паре, но поместите ее в a .type, valuesol::wrap
- Заметка
- Это также может быть помещено в список аргументов для пользовательского типа .
- Это выталкивает необработанные данные lua_CFunctionво все c_callобъекты, в которые вы передаете указатель результирующей функции, будь то таблица, пользовательские данные или что-либо еще, использующее API sol3. Полученный результат lua_CFunctionтакже можно использовать непосредственно с API lua, точно так же, как многие типы sol3 могут смешиваться с API Lua, если вы знаете, что делаете.
- Желательно, чтобы пользователь рассмотрел возможность создания макроса для выполнения необходимых действий . Sol не предоставляет один, потому что многие кодовые базы уже имеют один подобный этому .decltype( &function_name, ), function_name
- Ниже приведен пример различных способов использования sol::c_call:
- #define SOL_ALL_SAFETIES_ON 1
- #include <sol/sol.hpp>
- #include "assert.hpp"
- int f1(int) { return 32; }
- int f2(int, int) { return 1; }
- struct fer {
- double f3(int, int) {
- return 2.5;
- }
- };
- int main() {
- sol::state lua;
- // overloaded function f
- lua.set("f", sol::c_call<sol::wrap<decltype(&f1), &f1>, sol::wrap<decltype(&f2), &f2>, sol::wrap<decltype(&fer::f3), &fer::f3>>);
- // singly-wrapped function
- lua.set("g", sol::c_call<sol::wrap<decltype(&f1), &f1>>);
- // without the 'sol::wrap' boilerplate
- lua.set("h", sol::c_call<decltype(&f2), &f2>);
- // object used for the 'fer' member function call
- lua.set("obj", fer());
- // call them like any other bound function
- lua.script("r1 = f(1)");
- lua.script("r2 = f(1, 2)");
- lua.script("r3 = f(obj, 1, 2)");
- lua.script("r4 = g(1)");
- lua.script("r5 = h(1, 2)");
- // get the results and see
- // if it worked out
- int r1 = lua["r1"];
- c_assert(r1 == 32);
- int r2 = lua["r2"];
- c_assert(r2 == 1);
- double r3 = lua["r3"];
- c_assert(r3 == 2.5);
- int r4 = lua["r4"];
- c_assert(r4 == 32);
- int r5 = lua["r5"];
- c_assert(r5 == 1);
- return 0;
- }
- « As_function :: Содержание :: разрешение »
- as_function
- убедитесь, что объект выдвинут как функция
- template <typename Sig = sol::function_sig<>, typename... Args>
- function_argumants<Sig, Args...> as_function ( Args&& ... );
- Эта функция предназначена для того, чтобы гарантировать, что вызываемая структура (например, лямбда) может быть передана вызовам sol :: table и обрабатываться как привязка функции вместо пользовательских данных. Рекомендуется вместо этого использовать вызов sol :: table :: set_function , но если по какой-то причине его нужно использовать , то это поможет гарантировать, что вызываемая структура обрабатывается как лямбда / вызываемая, а не просто как структура пользовательских данных.set( key, value )setas_function
- Этот класс также может сделать так, чтобы пользовательские типы связывали типы переменных как функции для привязок пользовательских типов.
- #define SOL_ALL_SAFETIES_ON 1
- #include <sol/sol.hpp>
- int main () {
- struct callable {
- int operator()( int a, bool b ) {
- return a + (b ? 10 : 20);
- }
- };
- sol::state lua;
- // Binds struct as userdata
- // can still be callable, but beware
- // caveats
- lua.set( "not_func", callable() );
- // Binds struct as function
- lua.set( "func", sol::as_function( callable() ) );
- // equivalent: lua.set_function( "func", callable() );
- // equivalent: lua["func"] = callable();
- }
- Обратите внимание, что если вы действительно хотите, чтобы пользовательские данные были доступны для вызова , вам просто нужно создать sol :: table :: new_usertype и затем привязать "__call"метаметод (или просто использовать sol::meta_function::call перечисление ). Это может или не может быть сделано автоматически для вас, в зависимости от того, перегружен ли оператор вызова и тому подобное.
- Вот пример привязки переменной как функции к типу пользователя:
- #define SOL_ALL_SAFETIES_ON 1
- #include <sol/sol.hpp>
- int main () {
- class B {
- public:
- int bvar = 24;
- };
- sol::state lua;
- lua.open_libraries(sol::lib::base);
- lua.new_usertype<B>("B",
- // bind as variable
- "b", &B::bvar,
- // bind as function
- "f", sol::as_function(&B::bvar)
- );
- B b;
- lua.set("b", &b);
- lua.script(R"(x = b:f()
- y = b.b
- assert(x == 24)
- assert(y == 24)
- )");
- return 0;
- }
- « Только для чтения :: Содержание :: c_call »
- только для чтения
- подпрограмма, чтобы пометить переменную-член только для чтения
- template <typename T>
- auto readonly( T&& value );
- Цель только для чтения - защитить набор переменных для типа пользователя или функции. Просто оберните его вокруг переменной-члена, например, в соответствующем месте, чтобы использовать его. Если кто-то попытается установить его, он ошибется своим кодом.sol::readonly( &my_class::my_member_variable )
- sol::readonlyЭто особенно важно, когда вы работаете с типами, которые не имеют конструктора копирования. Lua не понимает семантику перемещения, и поэтому для установщиков пользовательских типов требуется конструктор копирования C ++. Контейнеры как переменные-члены, которые содержат типы, которые не являются копируемыми, но могут быть перемещены - например, vector<my_move_only_type>среди других - также могут ошибочно утверждать, что они являются копируемыми, но терпят неудачу с ошибками компилятора. Если ваш тип не соответствует определению контейнера как подлежащего копированию или просто не подлежит копированию в целом, и это переменная-член, пожалуйста, используйте sol::readonly.
- Если вы хотите создать таблицу только для чтения, вам нужно пройти через сложную песню и танец, переопределив __indexметаметод. Вот полный пример того, как это сделать, используя sol:
- read_only.cpp
- #define SOL_ALL_SAFETIES_ON 1
- #include <sol/sol.hpp>
- #include <iostream>
- struct object {
- void my_func() {
- cout << "hello\n";
- }
- };
- int deny(lua_State* L) {
- return luaL_error(L, "HAH! Deniiiiied!");
- }
- int main(int, char*[]) {
- sol::state lua;
- lua.open_libraries(sol::lib::base);
- object my_obj;
- sol::table obj_table = lua.create_named_table("object");
- sol::table obj_metatable = lua.create_table_with();
- obj_metatable.set_function("my_func", &object::my_func, &my_obj);
- // Set whatever else you need to
- // on the obj_metatable,
- // not on the obj_table itself!
- // Properly self-index metatable to block things
- obj_metatable[sol::meta_function::new_index] = deny;
- obj_metatable[sol::meta_function::index] = obj_metatable;
- // Set it on the actual table
- obj_table[sol::metatable_key] = obj_metatable;
- try {
- lua.script(R"(
- print(object.my_func)
- object["my_func"] = 24
- print(object.my_func)
- )");
- }
- catch (const exception& e) {
- cout << "an expected error occurred: " << e.what() << endl;
- }
- return 0;
- }
- Это подробный пример, но он все объясняет. Поскольку этот процесс немного сложен и может иметь неожиданные последствия для пользователей, которые создают свои собственные таблицы, создание таблиц только для чтения - это то, что мы просим пользователей сделать с помощью приведенного выше кода, так как семантика подходит для десятков использования. случаи были бы чрезвычайно сложными.
- политика
- изменение стека непосредственно перед возвратом вызова lua
- sol::policiesэто продвинутая функция низкоуровневой модификации, позволяющая вам использовать преимущества абстракций sol3 перед применением ваших собственных стековых модификаций в последний момент. Они охватывают ту же функциональность, что и типы luabind «return reference» и «зависимости» . Для вашего использования определены несколько предварительно настроенных политик:
- политика использование модификация
- sol::returns_self sol::policies( some_function, sol::returns_self() )
- принимает аргумент в стеке с индексом 1 ( selfв вызовах функций-членов и лямбда-выражениях, которые вначале принимают определенные пользовательские данные) и делает его возвращаемым значением
- вместо того, чтобы создавать новые пользовательские данные, которые ссылаются на ту же память C ++, он копирует пользовательские данные, подобно записи просто увеличивает счетчик ссылокobj2 = obj1
- экономит пространство памяти поверх сохранения оригинальной памяти
- sol::returns_self_with<int...> sol::policies( some_function, sol::returns_self_with<2, 3>() )
- то же самое, что и выше, с предупреждением, selfкоторое возвращается, в то же время помещая зависимости вself
- может поддерживать внешние зависимости
- sol::self_dependency sol::policies( some_function, sol::self_dependency() );
- это делает значение, возвращаемое связываемым объектом, зависимым от selfаргумента
- полезно для возврата ссылки на переменную-член и поддержания живого родительского класса этой переменной-члена
- sol::stack_dependencies sol::policies( some_function, sol::stack_dependencies( target_index, 2, 1, ... ) );
- все, что находится в target_indexстеке, получает специальную таблицу «keep alive» с элементами в стеке, указанными целочисленными индексами послеtarget_index
- позволяет поддерживать аргументы и другие вещи в течение всего времени существования класса
- обычай sol::policies( some_function, [](lua_State* L, int current_stack_return_count) -> int { ... } )
- все, что вы хотите, пока оно имеет форму int (lua_State*, int )
- работает с вызываемыми элементами (такими как лямбды), если они имеют правильную форму
- ожидается вернуть количество вещей в стеке, чтобы вернуться к Lua
- «Some_function» может быть любой вызываемой функцией, переменной-членом или подобным
- дополнения зависимостей работают только на пользовательских данных
- работает с , и на всех привязках пользовательских типовtable::set( ... )table::set_function( ... );
- Вы можете указать несколько политик для одного sol::policiesвызова, а также указать пользовательские политики, если подпись верна.
- « Protect :: Contents :: readonly »
- защитить
- подпрограмма для обозначения функции / переменной как требующей безопасности
- template <typename T>
- auto protect( T&& value );
- sol::protect( my_func )позволяет защитить вызов функции или переменную-член, когда она установлена в Lua. Его можно использовать с пользовательскими типами или просто при установке функции в sol. Ниже приведен пример, который демонстрирует, что вызов, который обычно не дает ошибок без включенных функций безопасности , вместо этого вызывает ошибки и приводит к pcallсбою оболочки безопасного вызова Lua :
- #define SOL_ALL_SAFETIES_ON 1
- #include <sol/sol.hpp>
- #include "assert.hpp"
- int main(int, char*[]) {
- struct protect_me {
- int gen(int x) {
- return x;
- }
- };
- sol::state lua;
- lua.open_libraries(sol::lib::base);
- lua.new_usertype<protect_me>("protect_me",
- "gen", sol::protect( &protect_me::gen )
- );
- lua.script(R"__(
- pm = protect_me.new()
- value = pcall(pm.gen,"wrong argument")
- )__");
- bool value = lua["value"];
- c_assert(!value);
- return 0;
- }
- вар
- Для подключения статических / глобальных переменных к пользовательским типам Lua
- Единственная цель этого типа тегов - работать с пользовательскими типами для предоставления my_class.my_static_var доступа, а также для предоставления доступа на основе ссылок.
- #define SOL_ALL_SAFETIES_ON 1
- #include <sol/sol.hpp>
- #include "assert.hpp"
- #include <iostream>
- struct test {
- static int number;
- };
- int test::number = 25;
- int main() {
- sol::state lua;
- lua.open_libraries();
- lua.new_usertype<test>("test",
- "direct", sol::var(2),
- "number", sol::var(test::number),
- "ref_number", sol::var(ref(test::number))
- );
- int direct_value = lua["test"]["direct"];
- c_assert(direct_value == 2);
- int number = lua["test"]["number"];
- c_assert(number == 25);
- int ref_number = lua["test"]["ref_number"];
- c_assert(ref_number == 25);
- test::number = 542;
- // number is its own memory: was passed by value
- // So does not change
- int number_again = lua["test"]["number"];
- c_assert(number_again == 25);
- // ref_number is just test::number
- // passed through ref
- // so, it holds a reference
- // which can be updated
- int ref_number_again = lua["test"]["ref_number"];
- c_assert(ref_number_again == 542);
- // be careful about referencing local variables,
- // if they go out of scope but are still reference
- // you'll suffer dangling reference bugs!
- return 0;
- }
- « Собственность :: Содержание :: защита »
- уступая
- говоря функции C ++, чтобы привести ее результаты в Lua
- template <typename F>
- yield_wrapper<F> yielding( F&& f )
- sol::yieldingполезен для вызова функций C ++, которые должны быть преобразованы в сопрограмму Lua. Это обертка вокруг одного аргумента, который, как ожидается, будет связан как функция. Вы можете передавать его везде, где может быть связана обычная функция, за исключением определений пользовательских типов .
- сопрограмма
- Возобновляемые / уступающие функции от Lua
- A coroutine- это ссылка на функцию в Lua, которую можно вызывать несколько раз, чтобы получить конкретный результат. Он запускается в lua_State, который использовался для его создания (см. Поток для примера того, как получить сопрограмму, которая работает в потоке, отдельном от вашего обычного «основного» lua_State ).
- Coroutine Объект полностью аналогичен protected_function объекта, с дополнительными функциями - членами , чтобы проверить , если сопрограмма дал ( call_status :: выданное ) , и, таким образом , снова работоспособной, была ли она завершена ( call_status :: ОК ) и , следовательно , не может дать больше значения, или произошла ошибка (см. коды ошибок status () и call_status ).
- Например, вы можете работать с сопрограммой следующим образом:
- co.lua
- function loop()
- while counter ~= 30
- do
- coroutine.yield(counter);
- counter = counter + 1;
- end
- return counter
- end
- Это функция, которая дает:
- main.cpp
- sol::state lua;
- lua.open_libraries(sol::lib::base, sol::lib::coroutine);
- lua.script_file("co.lua");
- sol::coroutine cr = lua["loop"];
- for (int counter = 0; // start from 0
- counter < 10 && cr; // we want 10 values, and we only want to run if the coroutine "cr" is valid
- // Alternative: counter < 10 && cr.valid()
- ++counter) {
- // Call the coroutine, does the computation and then suspends
- int value = cr();
- }
- Обратите внимание, что этот код не проверяет наличие ошибок: для этого вы можете вызвать функцию и назначить ее как , а затем проверить, как в случае с protected_function . Наконец, вы можете запустить эту сопрограмму в другом потоке, выполнив следующие действия:auto result = cr();result.valid()
- main_with_thread.cpp
- sol::state lua;
- lua.open_libraries(sol::lib::base, sol::lib::coroutine);
- lua.script_file("co.lua");
- sol::thread runner = sol::thread::create(lua.lua_state());
- sol::state_view runnerstate = runner.state();
- sol::coroutine cr = runnerstate["loop"];
- for (int counter = 0; counter < 10 && cr; ++counter) {
- // Call the coroutine, does the computation and then suspends
- int value = cr();
- }
- Вариант 2.
- const char* LUA = R"(
- function loop()
- counter = 0
- while counter ~= 3
- do
- counter = counter + 1;
- coroutine.yield(counter);
- end
- return 0
- end
- )";
- int main(int argc, char* argv[]) {
- state lua; // Lua состояние.
- lua.open_libraries(lib::base, lib::package, sol::lib::coroutine); // открыть доп.библиотеки.
- lua.script(LUA);
- // auto result = lua.safe_script(LUA, sol::script_pass_on_error);
- thread runner = sol::thread::create(lua.lua_state());//создает новой поток в новом состоянии.
- state_view runstate = runner.state();// получаем состояние для этого потока.
- coroutine cr = runstate["loop"];// получить типа wrap().
- //for (int counter = 0; counter < 3; ++counter) {
- // int value = cr();
- // cout << value << endl;
- //};
- /*int p = cr.call();
- cout << p << endl;*/
- while (call_status::yielded == cr.status()) {// если приостановлена, возобновить
- cout << " yield" << endl;
- int value = cr();
- cout << value << endl;
- }
- return 0;
- };
- Ниже приведены члены sol::coroutine:
- члены
- функция: конструктор
- coroutine(lua_State* L, int index = -1);
- Хватает сопрограмму по указанному индексу с учетом lua_State*.
- возвращение статуса сопрограммы
- call_status status() const noexcept;
- Возвращает статус сопрограммы.
- проверяет на ошибку
- bool error() const noexcept;
- Проверяет, произошла ли ошибка при запуске сопрограммы.
- выполняемый и явный оператор bool
- bool runnable () const noexcept;
- explicit operator bool() const noexcept;
- Эти функции позволяют проверить, можно ли еще вызывать сопрограмму (имеет больше значений для выдачи и не имеет ошибок). Если у вас есть объект сопрограммы , вы можете проверить или сделать .coroutine my_co = /*...*/runnable()if ( my_co ) { /* use coroutine */ }
- вызывая сопрограмму
- template<typename... Args>
- protected_function_result operator()( Args&&... args );
- template<typename... Ret, typename... Args>
- decltype(auto) call( Args&&... args );
- template<typename... Ret, typename... Args>
- decltype(auto) operator()( types<Ret...>, Args&&... args );
- Вызывает сопрограмму. Второй operator()позволяет вам указать тип возвращаемых шаблонов с использованием синтаксиса. Затем проверьте дополнительную информацию об успешном выполнении или просто проверьте объект сопрограммы в сообщении ifs, как показано выше .my_co(sol::types<int, string>, ...)status()
- защищенная
- Вызовы функций Lua, которые перехватывают ошибки и обеспечивают обработку ошибок
- class protected_function : public reference;
- typedef protected_function safe_function;
- Вдохновленный запросом starwing в старом репозитории sol , этот класс обеспечивает тот же интерфейс, что и функция, но с усиленной защитой и потенциальным обработчиком ошибок для любых ошибок Lua и исключений C ++. Вы можете получить функцию непосредственно из стека, используя конструктор, или передать ей две допустимые функции, которые мы продемонстрируем чуть позже.
- При вызове без указания типов возврата, указанных sol::types<...>списком или списком типов шаблонов, он генерирует класс protected_function_result, который неявно преобразуется в запрошенный тип возврата. Например:call<Ret...>( ... )
- function got_problems( error_msg )
- return "got_problems handler: " .. error_msg
- end
- function woof ( bark_energy )
- if bark_energy < 20 then
- error("*whine*")
- end
- return (bark_energy * (bark_power / 4))
- end
- function woofers ( bark_energy )
- if bark_energy < 10 then
- error("*whine*")
- end
- return (bark_energy * (bark_power / 4))
- end
- )";
- Следующий код C ++ будет вызывать эту функцию из этого файла и извлекать возвращаемое значение, если только не происходит ошибка, в этом случае вы можете связать функцию обработки ошибок следующим образом:
- #define SOL_ALL_SAFETIES_ON 1
- #include <sol/sol.hpp>
- #include <iostream>
- int main () {
- sol::state lua;
- lua.open_libraries(sol::lib::base);
- lua.script(code);
- sol::protected_function problematic_woof = lua["woof"];
- problematic_woof.error_handler = lua["got_problems"];
- auto firstwoof = problematic_woof(20);
- if ( firstwoof.valid() ) {
- // Can work with contents
- double numwoof = firstwoof;
- cout << "Got value: " << numwoof << endl;
- }
- else{
- // An error has occured
- sol::error err = firstwoof;
- string what = err.what();
- cout << what << endl;
- }
- // errors, calls handler and then returns a string error from Lua at the top of the stack
- auto secondwoof = problematic_woof(19);
- if (secondwoof.valid()) {
- // Call succeeded
- double numwoof = secondwoof;
- cout << "Got value: " << numwoof << endl;
- }
- else {
- // Call failed
- // Note that if the handler was successfully called, this will include
- // the additional appended error message information of
- // "got_problems handler: " ...
- sol::error err = secondwoof;
- string what = err.what();
- cout << what << endl;
- }
- Этот код намного длиннее, чем его аналог функции, но позволяет человеку проверять наличие ошибок. Тип здесь для autoявляются sol::protected_function_result. Они неявно преобразуются в типы результатов, как и все типы в стиле прокси .
- В качестве альтернативы, при плохом или хорошем вызове функции, вы можете использовать, sol::optionalчтобы проверить, успешен ли вызов или нет:
- // can also use optional to tell things
- sol::optional<double> maybevalue = problematic_woof(19);
- if (maybevalue) {
- // Have a value, use it
- double numwoof = maybevalue.value();
- cout << "Got value: " << numwoof << endl;
- }
- else {
- cout << "No value!" << endl;
- }
- cout << endl;
- return 0;
- }
- Это делает код немного более кратким и легким для размышления, если вы не хотите беспокоиться о прочтении ошибки. К счастью, в отличие от этого sol::unsafe_function_result, вы можете сохранять sol::protected_function_resultпеременные и помещать / помещать вещи над ней в стек, где находятся возвращаемые значения. Это делает его немного более гибким, чем жесткий, производительный sol::unsafe_function_resultтип, который получается при вызове sol :: unsafe_function .
- Если вы уверены, что результат был успешным, вы также можете просто указать нужный вам тип (например, doubleили string), и он получит его. Но, если это не сработает, sol может бросить и / или запаниковать, если у вас включены функции безопасности :
- // construct with function + error handler
- // shorter than old syntax
- sol::protected_function problematicwoof(lua["woof"], lua["got_problems"]);
- // dangerous if things go wrong!
- double value = problematicwoof(19);
- Наконец, важно отметить, что вы можете установить обработчик по умолчанию. Функция описана ниже: пожалуйста, используйте ее, чтобы избежать необходимости постоянно устанавливать обработчики ошибок:
- // sets got_problems as the default
- // handler for all protected_function errors
- sol::protected_function::set_default_handler(lua["got_problems"]);
- sol::protected_function problematicwoof = lua["woof"];
- sol::protected_function problematicwoofers = lua["woofers"];
- double value = problematicwoof(19);
- double value2 = problematicwoof(9);
- члены
- конструктор: protected_function
- template <typename T>
- protected_function( T&& func, reference handler = sol::protected_function::get_default_handler() );
- protected_function( lua_State* L, int index = -1, reference handler = sol::protected_function::get_default_handler() );
- Создаёт protected_function. Используйте версию с двумя аргументами, чтобы легче передавать пользовательскую функцию обработки ошибок. Вы также можете установить переменную-член error_handler после создания позже. protected_functionвсегда будет использовать последний обработчик ошибок, установленный для переменной, который является либо тем, что вы передали ей, либо значением по умолчанию во время создания .
- функция: вызов оператора / вызов защищенной функции
- template<typename... Args>
- protected_function_result operator()( Args&&... args );
- template<typename... Ret, typename... Args>
- decltype(auto) call( Args&&... args );
- template<typename... Ret, typename... Args>
- decltype(auto) operator()( types<Ret...>, Args&&... args );
- Вызывает функцию. Второй operator()позволяет вам указать тип возвращаемых шаблонов с использованием синтаксиса. Если вы не указали тип возвращаемого значения каким-либо образом, он выдаст s .my_func(sol::types<int, string>, ...)protected_function_result
- Заметка
- Все аргументы переданы. В отличие от get / set / operator [] для sol :: state или sol :: table , семантика значений здесь не используется. Это пересылка эталонной семантики, которая не копирует / не перемещает, если это не сделано специально для принимающих функций / специально для пользователя.
- обработчики по умолчанию
- static const reference& get_default_handler ();
- static void set_default_handler( reference& ref );
- Получите и установите объект Lua, который используется в качестве обработчика ошибок по умолчанию. По умолчанию используется обработчик ошибок no-ref. Вы можете изменить это, позвонив или подобный: все, что производит ссылку, должно быть хорошо.protected_function::set_default_handler( lua["my_handler"] );
- переменная: обработчик
- reference error_handler;
- Обработчик ошибок, который вызывается, если возникает ошибка времени выполнения, которую может обнаружить Lua. Функция обработчика ошибок должна принимать один строковый аргумент (используйте тип std :: string, если вы хотите использовать функцию C ++, связанную с lua в качестве обработчика ошибок) и возвращать единственный строковый аргумент (опять же, возвращать std :: string или строковый аргумент из функции C ++, если вы используете его в качестве обработчика ошибок). Если исключения включены, sol попытается преобразовать .what()аргумент исключения в строку, а затем вызовет функцию обработки ошибок. Это ссылка , так как она должна ссылаться на то, что существует в реестре lua или в стеке Lua. При создании автоматически устанавливается обработчик ошибок по умолчанию protected_function.
- Заметка
- protected_function_resultбезопасно вызывает его значения из стека при вызове его деструктора, отслеживая индекс и количество аргументов, которые должны были быть возвращены. lua_removeНапример, если вы удалите элементы, расположенные ниже, с помощью него, он будет работать не так, как ожидалось. Пожалуйста, не выполняйте фундаментальные операции по перестановке стека, пока не будет вызван деструктор (толкание / выталкивание над ним просто отлично).
- Чтобы узнать больше о том, как обрабатываются аргументы функции, см. Это примечание .
- функция
- вызов функций, связанных с Lua
- Заметка
- Эта абстракция предполагает, что функция работает безопасно. Если вы ожидаете, что в вашем коде есть ошибки (например, вы не всегда имеете явный контроль над ним или пытаетесь отлаживать ошибки), пожалуйста, явно используйте sol :: protected_function . Вы также можете сделать по sol::functionумолчанию sol::protected_function, включив функции безопасности .
- class unsafe_function : public reference;
- typedef unsafe_function function;
- Функция является верной версией защищенной функции , исключающей необходимость проверок типов и обработки ошибок (таким образом, в некоторых случаях незначительно увеличивает скорость). Это тип функции по умолчанию sol. Возьмите функцию прямо из стека, используя конструктор:
- конструктор: unsafe_function
- unsafe_function(lua_State* L, int index = -1);
- Вызывает конструктор и создает этот тип прямо из стека. Например:
- funcs.lua
- bark_power = 11;
- function woof ( bark_energy )
- return (bark_energy * (bark_power / 4))
- end
- Следующий код C ++ вызовет эту функцию из этого файла и получит возвращаемое значение:
- #define SOL_ALL_SAFETIES_ON 1
- #include <sol/sol.hpp>
- #include "assert.hpp"
- int main (int, char*[]) {
- sol::state lua;
- lua.script(code);
- sol::function woof = lua["woof"];
- double numwoof = woof(20);
- c_assert(numwoof == 55.0);
- Вызов woof(20)генерирует unsafe_function_result , который затем неявно преобразуется в doubleпосле вызова . Промежуточный временный function_resultобъект затем разрушается, выводя результаты вызова функции Lua из стека Lua.
- Вы также можете вернуть несколько значений с помощью tupleили, если вам нужно привязать их к существующим переменным, используйте sol::tie:
- lua.script( "function f () return 10, 11, 12 end" );
- sol::function f = lua["f"];
- tuple<int, int, int> abc = f();
- c_assert(get<0>(abc) == 10);
- c_assert(get<1>(abc) == 11);
- c_assert(get<2>(abc) == 12);
- // or
- int a, b, c;
- sol::tie(a, b, c) = f();
- c_assert(a == 10);
- c_assert(b == 11);
- c_assert(c == 12);
- return 0;
- }
- Это значительно облегчает работу с несколькими возвращаемыми значениями. Использование tieиз стандарта C ++ приведет к висящим ссылкам или плохому поведению из-за очень плохого способа, которым кортежи / C ++ tieбыли определены и реализованы: используйте вместо этого, чтобы удовлетворить любые требования множественного возврата.sol::tie( ... )
- Предупреждение
- НЕ сохраняйте возвращаемый тип unsafe_function_result ( function_resultкогда настройки безопасности не включены ) с помощью auto, как в , и НЕ храните его где-либо. В отличие от его аналога protected_function_result , хранить его НЕ безопасно, так как предполагается, что его возвращаемые типы все еще находятся на вершине стека, а при вызове деструктора выскакивает число результатов, которые функция должна была вернуть с вершины стека. Если вы возитесь со стеком Lua между сохранением и его уничтожением, вы будете подвержены невероятному количеству удивительных и трудно отслеживаемых ошибок. Не делай этого.auto numwoof = woof(20);function_resultfunction_result
- функция: оператор вызова / вызов функции
- template<typename... Args>
- unsafe_function_result operator()( Args&&... args );
- template<typename... Ret, typename... Args>
- decltype(auto) call( Args&&... args );
- template<typename... Ret, typename... Args>
- decltype(auto) operator()( types<Ret...>, Args&&... args );
- Вызывает функцию. Второй operator()позволяет вам указать тип возвращаемых шаблонов с использованием синтаксиса. Функция предполагает, что ошибок времени выполнения нет, и, таким образом, будет вызывать функцию, если обнаружится ошибка, и в противном случае может вернуть мусорные / поддельные значения, если пользователь не будет осторожен.my_func(sol::types<int, string>, ...)atpanic
- Чтобы узнать больше о том, как обрабатываются аргументы функции, см. Это примечание
- « Tie :: Contents :: protected_function »
- защищенная
- Вызовы функций Lua, которые перехватывают ошибки и обеспечивают обработку ошибок
- class protected_function : public reference;
- typedef protected_function safe_function;
- Вдохновленный запросом starwing в старом репозитории sol , этот класс обеспечивает тот же интерфейс, что и функция, но с усиленной защитой и потенциальным обработчиком ошибок для любых ошибок Lua и исключений C ++. Вы можете получить функцию непосредственно из стека, используя конструктор, или передать ей две допустимые функции, которые мы продемонстрируем чуть позже.
- При вызове без указания типов возврата, указанных sol::types<...>списком или списком типов шаблонов, он генерирует класс protected_function_result, который неявно преобразуется в запрошенный тип возврата. Например:call<Ret...>( ... )
- function got_problems( error_msg )
- return "got_problems handler: " .. error_msg
- end
- function woof ( bark_energy )
- if bark_energy < 20 then
- error("*whine*")
- end
- return (bark_energy * (bark_power / 4))
- end
- function woofers ( bark_energy )
- if bark_energy < 10 then
- error("*whine*")
- end
- return (bark_energy * (bark_power / 4))
- end
- )";
- Следующий код C ++ будет вызывать эту функцию из этого файла и извлекать возвращаемое значение, если только не происходит ошибка, в этом случае вы можете связать функцию обработки ошибок следующим образом:
- #define SOL_ALL_SAFETIES_ON 1
- #include <sol/sol.hpp>
- #include <iostream>
- int main () {
- sol::state lua;
- lua.open_libraries(sol::lib::base);
- lua.script(code);
- sol::protected_function problematic_woof = lua["woof"];
- problematic_woof.error_handler = lua["got_problems"];
- auto firstwoof = problematic_woof(20);
- if ( firstwoof.valid() ) {
- // Can work with contents
- double numwoof = firstwoof;
- cout << "Got value: " << numwoof << endl;
- }
- else{
- // An error has occured
- sol::error err = firstwoof;
- string what = err.what();
- cout << what << endl;
- }
- // errors, calls handler and then returns a string error from Lua at the top of the stack
- auto secondwoof = problematic_woof(19);
- if (secondwoof.valid()) {
- // Call succeeded
- double numwoof = secondwoof;
- cout << "Got value: " << numwoof << endl;
- }
- else {
- // Call failed
- // Note that if the handler was successfully called, this will include
- // the additional appended error message information of
- // "got_problems handler: " ...
- sol::error err = secondwoof;
- string what = err.what();
- cout << what << endl;
- }
- Этот код намного длиннее, чем его аналог функции, но позволяет человеку проверять наличие ошибок. Тип здесь для autoявляются sol::protected_function_result. Они неявно преобразуются в типы результатов, как и все типы в стиле прокси .
- В качестве альтернативы, при плохом или хорошем вызове функции, вы можете использовать, sol::optionalчтобы проверить, успешен ли вызов или нет:
- // can also use optional to tell things
- sol::optional<double> maybevalue = problematic_woof(19);
- if (maybevalue) {
- // Have a value, use it
- double numwoof = maybevalue.value();
- cout << "Got value: " << numwoof << endl;
- }
- else {
- cout << "No value!" << endl;
- }
- cout << endl;
- return 0;
- }
- Это делает код немного более кратким и легким для размышления, если вы не хотите беспокоиться о прочтении ошибки. К счастью, в отличие от этого sol::unsafe_function_result, вы можете сохранять sol::protected_function_resultпеременные и помещать / помещать вещи над ней в стек, где находятся возвращаемые значения. Это делает его немного более гибким, чем жесткий, производительный sol::unsafe_function_resultтип, который получается при вызове sol :: unsafe_function .
- Если вы уверены, что результат был успешным, вы также можете просто указать нужный вам тип (например, doubleили string), и он получит его. Но, если это не сработает, sol может бросить и / или запаниковать, если у вас включены функции безопасности :
- // construct with function + error handler
- // shorter than old syntax
- sol::protected_function problematicwoof(lua["woof"], lua["got_problems"]);
- // dangerous if things go wrong!
- double value = problematicwoof(19);
- Наконец, важно отметить, что вы можете установить обработчик по умолчанию. Функция описана ниже: пожалуйста, используйте ее, чтобы избежать необходимости постоянно устанавливать обработчики ошибок:
- // sets got_problems as the default
- // handler for all protected_function errors
- sol::protected_function::set_default_handler(lua["got_problems"]);
- sol::protected_function problematicwoof = lua["woof"];
- sol::protected_function problematicwoofers = lua["woofers"];
- double value = problematicwoof(19);
- double value2 = problematicwoof(9);
- члены
- конструктор: protected_function
- template <typename T>
- protected_function( T&& func, reference handler = sol::protected_function::get_default_handler() );
- protected_function( lua_State* L, int index = -1, reference handler = sol::protected_function::get_default_handler() );
- Создаёт protected_function. Используйте версию с двумя аргументами, чтобы легче передавать пользовательскую функцию обработки ошибок. Вы также можете установить переменную-член error_handler после создания позже. protected_functionвсегда будет использовать последний обработчик ошибок, установленный для переменной, который является либо тем, что вы передали ей, либо значением по умолчанию во время создания .
- функция: вызов оператора / вызов защищенной функции
- template<typename... Args>
- protected_function_result operator()( Args&&... args );
- template<typename... Ret, typename... Args>
- decltype(auto) call( Args&&... args );
- template<typename... Ret, typename... Args>
- decltype(auto) operator()( types<Ret...>, Args&&... args );
- Вызывает функцию. Второй operator()позволяет вам указать тип возвращаемых шаблонов с использованием синтаксиса. Если вы не указали тип возвращаемого значения каким-либо образом, он выдаст s .my_func(sol::types<int, string>, ...)protected_function_result
- Заметка
- Все аргументы переданы. В отличие от get / set / operator [] для sol :: state или sol :: table , семантика значений здесь не используется. Это пересылка эталонной семантики, которая не копирует / не перемещает, если это не сделано специально для принимающих функций / специально для пользователя.
- обработчики по умолчанию
- static const reference& get_default_handler ();
- static void set_default_handler( reference& ref );
- Получите и установите объект Lua, который используется в качестве обработчика ошибок по умолчанию. По умолчанию используется обработчик ошибок no-ref. Вы можете изменить это, позвонив или подобный: все, что производит ссылку, должно быть хорошо.protected_function::set_default_handler( lua["my_handler"] );
- переменная: обработчик
- reference error_handler;
- Обработчик ошибок, который вызывается, если возникает ошибка времени выполнения, которую может обнаружить Lua. Функция обработчика ошибок должна принимать один строковый аргумент (используйте тип std :: string, если вы хотите использовать функцию C ++, связанную с lua в качестве обработчика ошибок) и возвращать единственный строковый аргумент (опять же, возвращать std :: string или строковый аргумент из функции C ++, если вы используете его в качестве обработчика ошибок). Если исключения включены, sol попытается преобразовать .what()аргумент исключения в строку, а затем вызовет функцию обработки ошибок. Это ссылка , так как она должна ссылаться на то, что существует в реестре lua или в стеке Lua. При создании автоматически устанавливается обработчик ошибок по умолчанию protected_function.
- Заметка
- protected_function_resultбезопасно вызывает его значения из стека при вызове его деструктора, отслеживая индекс и количество аргументов, которые должны были быть возвращены. lua_removeНапример, если вы удалите элементы, расположенные ниже, с помощью него, он будет работать не так, как ожидалось. Пожалуйста, не выполняйте фундаментальные операции по перестановке стека, пока не будет вызван деструктор (толкание / выталкивание над ним просто отлично).
- Чтобы узнать больше о том, как обрабатываются аргументы функции, см. Это примечание .
- proxy, (protectedunsafe) _function_result - производные proxy_base
- `` table [x] `` и `` function (…) `` структура преобразования
- template <typename Recurring>
- struct proxy_base;
- template <typename Table, typename Key>
- struct proxy : proxy_base<...>;
- struct stack_proxy: proxy_base<...>;
- struct unsafe_function_result : proxy_base<...>;
- struct protected_function_result: proxy_base<...>;
- Эти классы предоставляют оператор неявного присваивания operator=(for set) и оператор неявного преобразования (for ) для поддержки элементов, извлеченных из базовой реализации Lua, в частности sol :: table и результатов вызовов функций для sol :: function и sol :: protected_function .operator Tget
- прокси
- proxyвозвращается поисками в sol :: table и табличные сущности. Поскольку он основан на ключе и типе таблицы, его было бы сложно записать по буквам: вы можете захватить его, используя слово, autoесли вам кажется, что вам нужно нести его по какой-то причине перед его использованием. proxyлениво оценивает свои аргументы, когда вы наконец звоните getили setпо нему. Вот несколько примеров с использованием следующего скрипта lua:
- bark = {
- woof = {
- [2] = "arf!"
- }
- }
- После загрузки этого файла или помещения его в строку и чтения строки непосредственно в lua (см. Состояние ), вы можете начать разбираться с ним в C ++ примерно так:
- #define SOL_ALL_SAFETIES_ON 1
- #include <sol/sol.hpp>
- #include "assert.hpp"
- #include <iostream>
- int main () {
- sol::state lua;
- lua.open_libraries(sol::lib::base);
- lua.script(code);
- // produces proxy, implicitly converts to string, quietly destroys proxy
- string arf_string = lua["bark"]["woof"][2];
- // lazy-evaluation of tables
- auto x = lua["bark"];
- auto y = x["woof"];
- auto z = y[2];
- // retrivies value inside of lua table above
- string value = z;
- c_assert(value == "arf!");
- // Can change the value later...
- z = 20;
- // Yay, lazy-evaluation!
- int changed_value = z; // now it's 20!
- c_assert(changed_value == 20);
- lua.script("assert(bark.woof[2] == 20)");
- Мы не рекомендуем использовать proxyленивое вычисление выше для использования между классами или между функциями: это больше, чем вы можете сделать, чтобы сохранить ссылку на значение, которое вам нравится, вызвать скрипт или запустить функцию lua, а затем получить его впоследствии , Вы также можете установить функции (и функциональные объекты) таким образом, а также получить их:
- lua["a_new_value"] = 24;
- lua["chase_tail"] = [](int chasing) {
- int r = 2;
- for (int i = 0; i < chasing; ++i) {
- r *= r;
- }
- return r;
- };
- lua.script("assert(a_new_value == 24)");
- lua.script("assert(chase_tail(2) == 16)");
- return 0;
- }
- члены
- функции: [перегружено] неявное преобразование get
- requires( sol::is_primitive_type<T>::value == true )
- template <typename T>
- operator T() const;
- requires( sol::is_primitive_type<T>::value == false )
- template <typename T>
- operator T&() const;
- Получает значение, связанное с ключами, сгенерированными прокси, и передает его типу T. Обратите внимание, что эта функция всегда будет возвращать T&неконстантную ссылку на типы, которые не основаны на sol :: reference и не являются примитивным типом lua.
- функция: получить значение
- template <typename T>
- decltype(auto) get( ) const;
- Получает значение, связанное с ключами, и преобразует его в тип T.
- функция: опционально получить значение
- template <typename T, typename Otherwise>
- optional<T> get_or( Otherwise&& otherise ) const;
- Получает значение, связанное с ключами, и преобразует его в тип T. Если это не правильный тип, он возвратит sol::nulloptвместо.
- функция: [перегружено] опционально получить или создать значение
- template <typename T>
- decltype(auto) get_or_create();
- template <typename T, typename Otherwise>
- decltype(auto) get_or_create( Otherwise&& other );
- Получает значение, связанное с ключами, если оно существует. Если это не так, он установит его со значением и вернет результат.
- operator[]прокси-только члены
- функция: действует
- bool valid () const;
- Возвращает, действительно ли этот прокси ссылается на действительный объект. Он использует sol :: stack :: probe_get_field, чтобы определить, действительно ли он действителен.
- функции: [перегружено] неявное множество
- requires( sol::detail::Function<Fx> == false )
- template <typename T>
- proxy& operator=( T&& value );
- requires( sol::detail::Function<Fx> == true )
- template <typename Fx>
- proxy& operator=( Fx&& function );
- Устанавливает значение, связанное с ключами, с которыми был сгенерирован прокси value. Если это функция, звонки set_function. Если это не так, просто звонки set. Не существует в unsage_function_result или protected_function_result .
- Функция: установить вызываемый
- template <typename Fx>
- proxy& set_function( Fx&& fx );
- Устанавливает значение, связанное с ключами, с которыми был создан прокси, для функции fx. Не существует в unsafe_function_result или protected_function_result .
- функция: установить значение
- template <typename T>
- proxy& set( T&& value );
- Устанавливает значение, связанное с ключами, с которыми был сгенерирован прокси value. Не существует в unsafe_function_result или protected_function_result .
- stack_proxy
- sol::stack_proxyэто то, что возвращается sol :: variadic_args и другими частями фреймворка. Он похож на прокси, но предназначен для псевдонима стекового индекса, а не именованной переменной.
- unsafe_function_result
- unsafe_function_resultявляется неявным рабочим преобразования только для промежуточного и только промежуточного времени, когда вызывается функция . Он НЕ предназначен для хранения или захвата auto. Это обеспечивает быстрый доступ к нужному базовому значению. Он не реализует set/ set_function/ templated operator=, так как присутствует на прокси .
- Этот тип, однако, разрешает доступ к нескольким базовым значениям. Используется result.get<Type>(index_offset)для получения объекта со Typeсмещением index_offsetв результатах. Смещение 0 основано. Не указав аргумент, по умолчанию значение равно 0.
- unsafe_function_resultтакже есть begin()и end()функции, которые возвращают (почти) случайные итераторы. Они возвращают тип прокси, который может быть неявно преобразован в stack_proxy .
- protected_function_result
- protected_function_resultэто более хорошая версия, unsafe_function_resultкоторая может быть использована для обнаружения ошибок. Это дает безопасный доступ к желаемой базовой стоимости. Он не реализует set// set_functionшаблон, operator=как присутствует на прокси .
- Этот тип, однако, разрешает доступ к нескольким базовым значениям. Используется result.get<Type>(index_offset)для получения объекта со Typeсмещением index_offsetв результатах. Смещение 0 основано. Не указав аргумент, по умолчанию значение равно 0.
- unsafe_function_resultтакже есть begin()и end()функции, которые возвращают (почти) случайные итераторы. Они возвращают тип прокси, который может быть неявно преобразован в stack_proxy .
- на функциональные объекты и прокси
- Заметка
- Начиная с последних версий sol3 (2.18.2 и выше), это больше не проблема, поскольку даже связанные классы будут иметь любой обнаруживаемый оператор вызова функции, автоматически связанный с объектом, чтобы позволить этому работать без необходимости использовать .setили .set_function. Примечание здесь сохранено для потомков и информации для более старых версий. Есть только несколько небольших предостережений, см. Эту заметку здесь .
- « This_environment :: Contents :: as_container »
- variadic_args
- прозрачный аргумент для работы с несколькими параметрами функции
- struct variadic_args;
- Этот класс предназначен для представления каждого отдельного аргумента в его текущем индексе и за его пределами в списке функций. Он не увеличивает количество аргументов и поэтому прозрачен. Вы можете разместить его в любом месте списка аргументов, и он будет представлять все объекты в вызове функции, которые идут после него, независимо от того, перечислены они явно или нет.
- variadic_argsтакже есть begin()и end()функции, которые возвращают (почти) итераторы со случайным доступом. Они возвращают тип прокси, который может быть неявно преобразован в тип, который вы хотите, очень похожий на тип прокси таблицы .
- #define SOL_ALL_SAFETIES_ON 1
- #include <sol/sol.hpp>
- #include <iostream>
- int main() {
- cout << "=== variadic_args ===" << endl;
- sol::state lua;
- lua.open_libraries(sol::lib::base);
- // Function requires 2 arguments
- // rest can be variadic, but:
- // va will include everything after "a" argument,
- // which means "b" will be part of the varaidic_args list too
- // at position 0
- lua.set_function("v", [](int a, sol::variadic_args va, int /*b*/) {
- int r = 0;
- for (auto v : va) {
- int value = v; // get argument out (implicit conversion)
- // can also do int v = v.as<int>();
- // can also do int v = va.get<int>(i); with index i
- r += value;
- }
- // Only have to add a, b was included from variadic_args and beyond
- return r + a;
- });
- lua.script("x = v(25, 25)");
- lua.script("x2 = v(25, 25, 100, 50, 250, 150)");
- lua.script("x3 = v(1, 2, 3, 4, 5, 6)");
- // will error: not enough arguments
- //lua.script("x4 = v(1)");
- lua.script("assert(x == 50)");
- lua.script("assert(x2 == 600)");
- lua.script("assert(x3 == 21)");
- lua.script("print(x)"); // 50
- lua.script("print(x2)"); // 600
- lua.script("print(x3)"); // 21
- cout << endl;
- return 0;
- }
- Вы также можете «сохранить» аргументы и т.п. позже, вставив их в vector<sol::object>или что-то подобное, что сериализует их в реестр. Ниже приведен пример сохранения всех аргументов, предоставленных sol::variadic_argsв лямбда-переменной захвата с именем args.
- #define SOL_ALL_SAFETIES_ON 1
- #include <sol/sol.hpp>
- #include <iostream>
- #include <functional>
- int main() {
- cout << "=== variadic_args serialization/storage ===" << endl;
- sol::state lua;
- lua.open_libraries(sol::lib::base);
- function<void()> function_storage;
- auto store_routine = [&function_storage] (sol::function f, sol::variadic_args va) {
- function_storage = [f, args = vector<sol::object>(va.begin(), va.end())]() {
- f(sol::as_args(args));
- };
- };
- lua.set_function("store_routine", store_routine);
- lua.script(R"(
- function a(name)
- print(name)
- end
- store_routine(a, "some name")
- )");
- function_storage();
- lua.script(R"(
- function b(number, text)
- print(number, "of", text)
- end
- store_routine(b, 20, "these apples")
- )");
- function_storage();
- cout << endl;
- return 0;
- }
- Наконец, обратите внимание, что вы можете использовать sol::variadic_argsконструктор для «смещения» / «смещения» просматриваемых аргументов:
- #define SOL_ALL_SAFETIES_ON 1
- #include <sol/sol.hpp>
- #include <iostream>
- int main () {
- cout << "=== variadic_args shifting constructor ===" << endl;
- sol::state lua;
- lua.open_libraries(sol::lib::base);
- lua.set_function("f", [](sol::variadic_args va) {
- int r = 0;
- sol::variadic_args shifted_va(va.lua_state(), 3);
- for (auto v : shifted_va) {
- int value = v;
- r += value;
- }
- return r;
- });
- lua.script("x = f(1, 2, 3, 4)");
- lua.script("x2 = f(8, 200, 3, 4)");
- lua.script("x3 = f(1, 2, 3, 4, 5, 6)");
- lua.script("print(x)"); // 7
- lua.script("print(x2)"); // 7
- lua.script("print(x3)"); // 18
- cout << endl;
- return 0;
- }
- « Необязательный <T> :: Contents :: variadic_results »
- перегрузка
- вызов различных функций в зависимости от номера / типа аргумента
- функция: создать перегруженный набор
- 1
- 2
- 3
- 4
- 5
- template <typename... Args>
- struct overloaded_set : tuple<Args...> { /* ... */ };
- template <typename... Args>
- overloaded_set<Args...> overload( Args&&... args );
- Фактически созданный класс по sol::overloadсути является оберткой типов, tupleкоторая сообщает библиотеке, что создается перегрузка. Функция помогает пользователям создавать перегруженные функции, которые можно вызывать из Lua, используя 1 имя, но несколько аргументов. Он предназначен для замены спагетти кода, где пользователи макетируют это, делая странные операторы if и переключая, какую версию функции вызывать на основе luaL_check {number / udata / string} .
- Заметка
- Обратите внимание, что параметры по умолчанию в функции (например, ) не существуют вне забавы C ++ во время компиляции. Когда эта функция связывается или сериализуется в инфраструктуру Lua, она связывается как функция, принимающая 1 аргумент, а не 2 функции, принимающие либо 0, либо 1 аргумент. Если вы хотите добиться того же эффекта, вам нужно использовать перегрузку и явно вызывать ту версию функции, которую вы хотите. В C ++ нет магии, которая позволяла бы мне получать параметры по умолчанию и устанавливать их автоматически.int func(int a = 20)
- Заметка
- Разрешение перегрузки может зависеть от настроек конфигурации на страницах безопасности . Например, невозможно отличить целые числа (uint8_t, in32_t и т. Д.) От типов с плавающей запятой (float, double, half), когда они SOL_SAFE_NUMERICSне включены.
- Его использование простое: везде, где вы можете передать тип функции в Lua, будь то пользовательский тип или вы просто устанавливаете какую-либо функцию с помощью setили set_function(для таблицы или состояния (_view) ), просто оберните функции, которые вы хотите Рассмотрим разрешение перегрузки для одной функции следующим образом:
- sol::overload( func1, func2, ... funcN );
- Функции могут быть любым видом функции / функционального объекта (лямбда). Учитывая эти функции и структуру:
- #define SOL_ALL_SAFETIES_ON 1
- #include <sol/sol.hpp>
- #include "assert.hpp"
- #include <iostream>
- struct pup {
- int barks = 0;
- void bark () {
- ++barks; // bark!
- }
- bool is_cute () const {
- return true;
- }
- };
- void ultra_bark( pup& p, int barks) {
- for (; barks --> 0;) p.bark();
- }
- void picky_bark( pup& p, string s) {
- if ( s == "bark" )
- p.bark();
- }
- Затем вы используете его так же, как и для любой другой части API:
- int main () {
- cout << "=== overloading with members ===" << endl;
- sol::state lua;
- lua.open_libraries(sol::lib::base);
- lua.set_function( "bark", sol::overload(
- ultra_bark,
- []() { return "the bark from nowhere"; }
- ) );
- lua.new_usertype<pup>( "pup",
- // regular function
- "is_cute", &pup::is_cute,
- // overloaded function
- "bark", sol::overload( &pup::bark, &picky_bark )
- );
- Выполнение следующих действий в Lua вызовет определенные выбранные перегрузки и связанные с ними функции:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- const auto& code = R"(
- barker = pup.new()
- print(barker:is_cute())
- barker:bark() -- calls member function pup::bark
- barker:bark("meow") -- picky_bark, no bark
- barker:bark("bark") -- picky_bark, bark
- bark(barker, 20) -- calls ultra_bark
- print(bark()) -- calls lambda which returns that string
- )";
- lua.script(code);
- pup& barker = lua["barker"];
- cout << barker.barks << endl;
- c_assert(barker.barks == 22);
- cout << endl;
- return 0;
- }
- Заметка
- Перегрузка выполняется в системе «первым пришел - первым обслужен». Это означает, что если две перегрузки являются совместимыми, работоспособными перегрузками, он выберет первую в списке.
- Обратите внимание, что из-за этой системы вы можете использовать sol :: variadic_args, чтобы сделать функцию, которая служит «запасным вариантом». Убедитесь, что это последняя указанная функция в списке функций для . Этот пример показывает, как .sol::overload( ... )
- Заметка
- Пожалуйста, имейте в виду, что выполнение этого требует затрат времени выполнения, чтобы найти правильную перегрузку. Стоимость напрямую зависит не от количества перегрузок, а от количества функций с одинаковым количеством аргументов (sol будет заблаговременно исключать все функции, не соответствующие количеству аргументов).
- « As_returns :: Содержание :: собственность »
- пространство имен стека
- слой абстракции нитро-песчаного ядра над Lua
- namespace stack
- Если вы обнаружите, что абстракции более высокого уровня не соответствуют вашим потребностям, вы, возможно, захотите углубиться в stackпространство имен, чтобы попытаться получить больше от sol. stack.hppа stackпространство имен определяет несколько утилит для работы с Lua, включая утилиты push / popping, геттеры, средства проверки типов, помощники вызовов Lua и многое другое. Это пространство имен не документировано полностью, так как большая часть его интерфейса является ртутной и может меняться между выпусками, либо сильно повышать производительность, либо улучшать sol api .
- Работу на этом уровне стека можно улучшить, если понять, как работает стек Lua в целом, а затем дополнить его объектами и предметами.
- Однако есть несколько точек настройки ADL, которые вы можете использовать для своих целей, и несколько потенциально полезных функций. Это может помочь, если вы пытаетесь уменьшить объем кода, который вам нужно написать, или если вы хотите, чтобы ваши типы по-разному вели себя в стеке sol. Обратите внимание, что переопределение значений по умолчанию может привести к отказу от многих гарантий безопасности, которые предоставляет sol: поэтому изменяйте точки расширения по своему усмотрению.
- структуры
- структура: запись
- struct record {
- int last;
- int used;
- void use(int count);
- };
- Эта структура предназначена для расширенного использования с stack :: get и stack :: check_get . При переопределении точек настройки важно вызвать useфункцию-член этого класса с количеством вещей, которые вы вытаскиваете из стека. usedсодержит общее накопление произведенных предметов. lastэто количество элементов, полученных из стека с последней операцией (не обязательно извлеченных из стека). Во всех тривиальных случаях для типов и после операции; структуры, такие как и могут тянуть больше в зависимости от классов, которые он содержит.last == 1used == 1pairtuple
- При переопределении точек настройки обратите внимание, что эта структура должна позволять вам помещать несколько возвращаемых значений и получать несколько возвращаемых значений в стек, и, таким образом, иметь возможность беспрепятственно упаковать / распаковать возвращаемые значения из Lua в одну структуру C ++ и наоборот. наоборот. Эта функция рекомендуется только для людей, которым необходимо настроить библиотеку дальше, чем основы. Это также хороший способ добавить поддержку для типа и предложить его обратно в исходную библиотеку, чтобы другие могли получить пользу от вашей работы.
- Обратите внимание, что настройки также можно разместить здесь на отдельной странице, если отдельные лица решат сделать детальные настройки для своей структуры или других мест.
- структура: зонд
- struct probe {
- bool success;
- int levels;
- probe(bool s, int l);
- operator bool() const;
- };
- Эта структура используется для того, чтобы показать, было ли успешное исследование get_field или нет.
- члены
- функция: call_lua
- template<bool check_args = stack_detail::default_check_arguments, bool clean_stack = true, typename Fx, typename... FxArgs>
- inline int call_lua(lua_State* L, int start, Fx&& fx, FxArgs&&... fxargs);
- Эта функция полезна, когда вы связываете к сырому функции C , но нужны абстракции SOL, чтобы спасти вас агонию настройки аргументов и знать , как вызов функций C работает . startПараметр указывает функцию , где начать вытягивать аргументы. Параметр fx - это то, что должно называться. Дополнительные аргументы передаются функции напрямую. Существуют промежуточные версии этого ( sol::stack::call_into_luaи аналогичного) для более продвинутых пользователей, но они не документированы, поскольку они могут быть изменены для повышения производительности или соответствующей корректировки API в последующих итерациях sol3. Используйте более продвинутые версии на свой страх и риск.
- функция: получить
- template <typename T>
- auto get( lua_State* L, int index = -1 )
- template <typename T>
- auto get( lua_State* L, int index, record& tracking )
- Получает значение объекта indexв стеке. Тип возвращаемого значения зависит от T: с примитивными типами обычно это так T: для всех нераспознанных Tэто, как правило, T&точка расширения, возвращаемая реализацией sol_lua_get <T> . Тип Tпроверяется один раз, как есть (с constоставленными в покое и ссылочными квалификаторами), а затем еще раз, когда он удаляетconst квалификаторы верхнего уровня и модификаторы ссылок, прежде чем перенаправлять их в функцию sol_lua_get <T> точки расширения . stack::getпо умолчанию будет пересылать все аргументы в функцию stack :: check_get с обработчиком, type_panicчтобы сильно предупреждать об ошибках, если вы просите о безопасности,
- Вы также можете извлечь sol :: необязательный <T> из этого, чтобы он пытался не выдавать ошибки при выполнении get, а тип неверный.
- функция: проверить
- template <typename T>
- bool check( lua_State* L, int index = -1 )
- template <typename T, typename Handler>
- bool check( lua_State* L, int index, Handler&& handler )
- template <typename T, typename Handler>
- bool check( lua_State* L, int index, Handler&& handler, record& tracking )
- Проверяет, имеет ли объект indexтип T. Если это не так , он будет вызывать handlerфункцию с , , и в качестве аргументов (и , возможно , с 5 - го аргумента строки . Если вы не передаете свой собственный обработчик, обработчик будет пройдена.lua_State* Lint indexsol::type expectedsol::type actualsol::string_view messageno_panic
- функция: get_usertype
- template <typename T>
- auto get_usertype( lua_State* L, int index = -1 )
- template <typename T>
- auto get_usertype( lua_State* L, int index, record& tracking )
- Непосредственно пытается повторно получить тип, Tиспользуя механизмы пользовательского типа sol3. Аналогично обычному getдля определенного пользователем типа. Полезно, когда вам нужно получить доступ к механизму получения пользовательских типов в sol3 и в то же время обеспечить собственную настройку .
- функция: check_usertype
- template <typename T>
- bool check_usertype( lua_State* L, int index = -1 )
- template <typename T, typename Handler>
- bool check_usertype( lua_State* L, int index, Handler&& handler )
- template <typename T, typename Handler>
- bool check_usertype( lua_State* L, int index, Handler&& handler, record& tracking )
- Проверяет, имеет ли объект at indexтип Tи сохраняется ли он как пользовательский тип sol3. Полезно, когда вам нужно получить доступ к механизму проверки пользовательских типов sol3, в то же время предоставляя свои собственные настройки .
- функция: check_get
- template <typename T>
- auto check_get( lua_State* L, int index = -1 )
- template <typename T, typename Handler>
- auto check_get( lua_State* L, int index, Handler&& handler, record& tracking )
- Получает значение объекта indexв стеке, но делает это безопасно. Возвращает optional<U>, где Uв данном случае это тип возвращаемого значения stack::get<T>. Это позволяет человеку должным образом проверить, является ли тип, который он получает, тем, что он на самом деле хочет, и изящно обрабатывать ошибки при работе со стеком, если он того пожелает. Вы можете SOL_ALL_SAFETIES_ONвключить дополнительную безопасность , в которой по stack::getумолчанию будет вызываться эта версия функции с некоторым вариантом обработчика, sol::type_panic_stringчтобы сильно предупреждать об ошибках и помогать вам отслеживать ошибки, если вы подозреваете, что в вашей системе что-то идет не так.
- функция: нажать
- // push T inferred from call site, pass args... through to extension point
- template <typename T, typename... Args>
- int push( lua_State* L, T&& item, Args&&... args )
- // push T that is explicitly specified, pass args... through to extension point
- template <typename T, typename Arg, typename... Args>
- int push( lua_State* L, Arg&& arg, Args&&... args )
- // recursively call the the above "push" with T inferred, one for each argument
- template <typename... Args>
- int multi_push( lua_State* L, Args&&... args )
- Основываясь на том, как он вызывается, помещает в стек переменное количество объектов. в 99% случаев возвращает 1 объект, помещенный в стек. В случае a tuple<...>он рекурсивно выталкивает каждый объект, содержащийся в кортеже, слева направо, в результате чего в стек помещается переменное число вещей (это позволяет многозначные возвраты при привязке функции C ++ к Lua). Может вызываться с аргументами, отличающимися от типа, который хочет выдвинуть, или откуда будет выведен вывод . Окончательная форма этой функции , которая будет вызывать один для каждого аргумента. То, что описывает то, что нажать, сначала очищается путем удаления верхнего уровня.sol::stack::push<T>( L, args... )sol::stack::push( L, arg, args... )Targsol::stack::multi_pushsol::stack::pushTconstквалификаторы и эталонные квалификаторы перед отправкой в точку расширения sol_lua_push <T> .
- функция: push_reference
- // push T inferred from call site, pass args... through to extension point
- template <typename T, typename... Args>
- int push_reference( lua_State* L, T&& item, Args&&... args )
- // push T that is explicitly specified, pass args... through to extension point
- template <typename T, typename Arg, typename... Args>
- int push_reference( lua_State* L, Arg&& arg, Args&&... args )
- // recursively call the the above "push" with T inferred, one for each argument
- template <typename... Args>
- int multi_push_reference( lua_State* L, Args&&... args )
- Эти функции ведут себя аналогично приведенным выше, но они проверяют определенные критерии и вместо этого пытаются выдвинуть ссылку, а не принудительно копировать копию, если это необходимо. Используйте его осторожно, так как sol3 использует это главным образом как возврат из функций и переменных пользовательского типа, чтобы сохранить семантику цепочки / переменной от этого объекта класса. Его внутренние компоненты обновляются в соответствии с потребностями sol3, и, хотя он, как правило, делает «правильные вещи» и его не нужно менять какое-то время, sol3 оставляет за собой право изменять свои внутренние механизмы обнаружения в соответствии с потребностями своих пользователей в любое время, как правило, без нарушения обратной совместимости и ожиданий, но не совсем гарантировано.
- функция: поп
- template <typename... Args>
- auto pop( lua_State* L );
- Выталкивает объект из стека Удалит фиксированное количество объектов из стека, как правило, определяется sol::lua_size<T>чертами предоставленных аргументов. Обычно это функция простоты, используемая для удобства.
- функция: верх
- int top( lua_State* L );
- Возвращает количество значений в стеке.
- функция: set_field
- template <bool global = false, typename Key, typename Value>
- void set_field( lua_State* L, Key&& k, Value&& v );
- template <bool global = false, typename Key, typename Value>
- void set_field( lua_State* L, Key&& k, Value&& v, int objectindex);
- Устанавливает поле, на которое ссылается ключ, kна заданное значение v, помещая ключ в стек, помещая значение в стек, а затем выполняя эквивалент lua_setfieldобъекта для данного значения objectindex. Выполняет оптимизацию и вызывает более быстрые версии функции, если тип Keyсчитается строкой в стиле c и / или если он также помечен в шаблонном globalаргументе как глобальный.
- функция: get_field
- template <bool global = false, typename Key>
- void get_field( lua_State* L, Key&& k [, int objectindex] );
- Получает поле, на которое ссылается ключ k, путем нажатия ключа на стек и затем выполнения эквивалента lua_getfield. Выполняет оптимизацию и вызывает более быстрые версии функции, если тип Keyсчитается строкой в стиле c и / или если он также помечен в шаблонном globalаргументе как глобальный.
- Эта функция оставляет полученное значение в стеке.
- функция: probe_get_field
- template <bool global = false, typename Key>
- probe probe_get_field( lua_State* L, Key&& k [, int objectindex] );
- Получает поле, на которое ссылается ключ k, путем нажатия ключа на стек и затем выполнения эквивалента lua_getfield. Выполняет оптимизацию и вызывает более быстрые версии функции, если тип Keyсчитается строкой в стиле c и / или если он также помечен в шаблонном globalаргументе как глобальный. Кроме того, он делает это безопасно, входя только на столько уровней, насколько это возможно: если возвращаемое значение не является чем-то, что может быть проиндексировано, тогда запросы обхода с tuple/ pairпрекратят работу раньше и вернут зондирующую информацию со структурой зонда .
- Эта функция оставляет полученное значение в стеке.
- объекты (точки расширения)
- Вы можете настроить способ, которым sol обрабатывает различные структуры и классы, следуя информации, представленной в добавлении ваших собственных типов .
- Ниже приведена более обширная информация для любознательных.
- Точка расширения ADL sol_lua_get
- MyType sol_lua_get ( sol::types<MyType>, lua_State* L, int index, sol::stack::record& tracking ) {
- // do work
- // ...
- return MyType{}; // return value
- }
- Эта точка расширения относится к getобъекту (или ссылке, или указателю, или какому-либо другому) типа Tили к чему-то, что может быть преобразовано в него. Внутренняя реализация getter по умолчанию предполагает, Tчто это тип пользователя, и извлекает данные пользователя из Lua, прежде чем пытаться привести их к желаемому T.
- В целом, есть реализации для получения чисел (типа is_floating, is_integralсовпадающих по типу), получения stringи добавления широких строковых и юникодных вариантов, получения необработанных пользовательских данных с помощью userdata_value и чего угодно, как upvalues с upvalue_index , получения необработанных lua_CFunction s и, наконец, извлечения функций Lua в . Он также определен для всего, что происходит от sol :: reference . У этого также есть специальная реализация для 2 умных указателей стандартной библиотеки (см. Память типа пользователя ), которая может быть более конкретно расширена.const char*function<R(Args...)>
- Точка расширения ADL sol_lua_push
- int push ( sol::types<MyType>, lua_State* L, MyType&& value ) {
- // can optionally take more than just 1 argument
- // to "construct" in-place and similar
- // use them however you like!
- // ...
- return N; // number of things pushed onto the stack
- }
- Эта точка расширения является pushзначением в Lua. Возвращает количество вещей, помещенных в стек. Реализация по умолчанию предполагает, Tчто это пользовательский тип, и помещает пользовательские данные в Lua с привязкой к ним специфической для класса метатаблицы в масштабе штата. Есть реализации толкания чисел ( is_floating, is_integralСопоставления типов), получение stringи , получая сырой UserData с UserData и сырьем upvalues с повышать стоимость , получая сырой lua_CFunction с, и , наконец , вытаскивая функцию Lua в . Он также определен для всего, что происходит от sol :: reference . У этого также есть специальная реализация для 2 умных указателей стандартной библиотеки (см. Память типа пользователя)const char*sol::function).
- Точка расширения ADL sol_lua_check
- template <typename Handler>
- bool sol_lua_check ( sol::types<MyType>, lua_State* L, int index, Handler&& handler, sol::stack::record& tracking ) {
- // if the object in the Lua stack at index is a T, return true
- if ( ... ) {
- tracking.use(1); // or however many you use
- return true;
- }
- // otherwise, call the handler function,
- // with the required 4/5 arguments, then return false
- //
- handler(L, index, expected, indextype, "message");
- return false;
- }
- Эта точка расширения заключается в checkтом, является ли тип по данному индексу тем, чем он должен быть. Реализация по умолчанию просто проверяет, равен ли ожидаемый тип, переданный через шаблон, типу объекта по указанному индексу в стеке Lua. Реализация по умолчанию для типов, которые рассматриваются, userdataпроходит множество проверок, чтобы поддержать проверку, является ли тип действительно типом Tили является ли он базовым классом того, что он на самом деле хранит как пользовательские данные в этом индексе.
- Обратите внимание, что вы можете
- Точка расширения ADL sol_lua_interop_check
- template <typename T, typename Handler>
- bool sol_lua_interop_check(sol::types<T>, lua_State* L, int relindex, sol::type index_type, Handler&& handler, sol::stack::record& tracking) {
- // implement custom checking here for a userdata:
- // if it doesn't match, return "false" and regular
- // sol userdata checks will kick in
- return false;
- // returning true will skip sol's
- // default checks
- }
- Эта точка расширения относится к checkсторонним данным пользователя. Он должен возвращаться, trueесли тип соответствует некоторой пользовательской спецификации пользовательских данных (скажем, из другой библиотеки или внутренней структуры), и falseесли нет. Реализация по умолчанию просто возвращается, falseчтобы позволить исходным обработчикам sol3 позаботиться обо всем. Если вы хотите реализовать свою собственную проверку пользовательских типов; например, для возни с toLuaили OOLuaили kaguyaили некоторыми другими библиотеками. Обратите внимание, что библиотека должна иметь макет с совместимой памятью, если вы хотите специализировать этот метод проверки, но не последующий метод получения . Вы можете специализировать его, как показано в примерах взаимодействия .
- Заметка
- Вы должны включить эту функцию с помощью SOL_ENABLE_INTEROP, как описано в разделе конфигурации и безопасности .
- Точка расширения ADL sol_lua_interop_get
- template <typename T>
- pair<bool, T*> sol_lua_interop_get(sol::types<T> t, lua_State* L, int relindex, void* unadjusted_pointer, sol::stack::record& tracking) {
- // implement custom getting here for non-sol3 userdatas:
- // if it doesn't match, return "false" and regular
- // sol userdata getters will kick in
- return { false, nullptr };
- }
- Эта точка расширения относится к getсторонним данным пользователя. Он должен возвращать оба trueи настроенный указатель, если тип соответствует некоторой пользовательской спецификации пользовательских данных (скажем, из другой библиотеки или внутренней структуры). Реализация по умолчанию просто возвращается, чтобы позволить стандартной реализации sol3 позаботиться обо всем. Вы можете использовать его для взаимодействия с другими платформами, которые не являются sol3, но все еще включают их силу; например, для возни с или некоторых других библиотек. Вы можете специализировать его, как показано в примерах взаимодействия .{ false, nullptr }kaguya
- Заметка
- Вы должны включить его с помощью SOL_ENABLE_INTEROP, как описано в разделе конфигурации и безопасности .
- Заметка
- Вам НЕ нужно использовать этот метод, в частности, если расположение памяти совместимо. (Например, toLuaхранит пользовательские данные в sol3-совместимом способе.)
- нить
- отдельное состояние, которое может содержать и запускать функции
- class thread : public reference { /* ... */ };
- sol::threadэто отдельная исполняемая часть виртуальной машины Lua, которую можно использовать для выполнения работы отдельно от основного потока, например, с сопрограммами . Чтобы взять таблицу или сопрограмму и запустить ее специально на том, что sol::threadвы извлекли из lua или создали, просто получите эту функцию через состояние потока
- Заметка
- Поток ЦП не всегда эквивалентен новому потоку в Lua: this_thread::get_id()может быть одинаковым для 2 обратных вызовов, которые имеют 2 различных потока Lua. Чтобы узнать, из какого потока был вызван обратный вызов, подключитесь к sol :: this_state из своего обратного вызова Lua, а затем создайте a sol::thread, передавая sol::this_stateдля первого и последнего аргументов. Затем изучите результаты статуса и is_...звонков ниже.
- свободная функция
- функция: main_thread
- main_thread(lua_State* current, lua_State* backup_if_bad_platform = nullptr);
- Функция извлекает основной поток приложения на Lua 5.2 и выше только . Он предназначен для кода, который должен учитывать многопоточность (например, использует несколько потоков и сопрограмм ).sol::main_thread( ... )
- Предупреждение
- Эта кодовая функция будет присутствовать в Lua 5.1 / LuaJIT, но будет иметь правильное поведение только при наличии одного аргумента в Lua 5.2 и выше. Lua 5.1 не поддерживает извлечение основного потока из его реестра, и поэтому полностью рекомендуется, если вы пишете кросс-платформенный код Lua, что вы должны хранить основной поток вашего приложения в некотором глобальном хранилище, доступном где-либо. Затем передайте этот элемент в и он будет выбирать его каждый раз. Если вы не собираетесь использовать Lua 5.1 / LuaJIT, вы можете игнорировать последний параметр.sol::main_thread( possibly_thread_state, my_actual_main_state )my_actual_main_state
- члены
- конструктор: резьба
- thread(stack_reference r);
- thread(lua_State* L, int index = -1);
- thread(lua_State* L, lua_State* actual_thread);
- Извлекает поток из стека Lua по указанному индексу и позволяет человеку использовать все содержащиеся в нем абстракции. Также может потребоваться фактическое состояние потока, чтобы создать поток из этого.
- функция: просмотр состояния thread_state ()
- state_view state() const;
- Это извлекает текущее состояние потока, создавая представление state_view, которым можно манипулировать, как и любым другим. Сопрограммы, извлеченные из Lua с использованием состояния потока, будут запускаться именно для этого потока.
- функция: получить объект состояния потока
- lua_State* thread_state () const;
- Эта функция извлекает тот, lua_State*который представляет поток.
- текущий статус потока
- thread_status status () const;
- Извлекает состояние потока, которое описывает текущее состояние потока.
- статус основного потока
- bool is_main_thread () const;
- Проверяет, является ли поток основным потоком Lua.
- функция: создание потока
- thread create();
- static thread create (lua_State* L);
- Создает новый поток из данного а lua_State*.
- « Object :: Contents :: необязательный <T> »
- сопрограмма
- Возобновляемые / уступающие функции от Lua
- A coroutine- это ссылка на функцию в Lua, которую можно вызывать несколько раз, чтобы получить конкретный результат. Он запускается в lua_State, который использовался для его создания (см. Поток для примера того, как получить сопрограмму, которая работает в потоке, отдельном от вашего обычного «основного» lua_State ).
- coroutineОбъект полностью аналогичен protected_function объекта, с дополнительными функциями - членами , чтобы проверить , если сопрограмма дал ( call_status :: выданное ) , и, таким образом , снова работоспособной, была ли она завершена ( call_status :: ОК ) и , следовательно , не может дать больше значения, или произошла ошибка (см. коды ошибок status () и call_status ).
- Например, вы можете работать с сопрограммой следующим образом:
- co.lua
- function loop()
- while counter ~= 30
- do
- coroutine.yield(counter);
- counter = counter + 1;
- end
- return counter
- end
- Это функция, которая дает:
- main.cpp
- sol::state lua;
- lua.open_libraries(sol::lib::base, sol::lib::coroutine);
- lua.script_file("co.lua");
- sol::coroutine cr = lua["loop"];
- for (int counter = 0; // start from 0
- counter < 10 && cr; // we want 10 values, and we only want to run if the coroutine "cr" is valid
- // Alternative: counter < 10 && cr.valid()
- ++counter) {
- // Call the coroutine, does the computation and then suspends
- int value = cr();
- }
- Обратите внимание, что этот код не проверяет наличие ошибок: для этого вы можете вызвать функцию и назначить ее как , а затем проверить, как в случае с protected_function . Наконец, вы можете запустить эту сопрограмму в другом потоке, выполнив следующие действия:auto result = cr();result.valid()
- main_with_thread.cpp
- sol::state lua;
- lua.open_libraries(sol::lib::base, sol::lib::coroutine);
- lua.script_file("co.lua");
- sol::thread runner = sol::thread::create(lua.lua_state());
- sol::state_view runnerstate = runner.state();
- sol::coroutine cr = runnerstate["loop"];
- for (int counter = 0; counter < 10 && cr; ++counter) {
- // Call the coroutine, does the computation and then suspends
- int value = cr();
- }
- Ниже приведены члены sol::coroutine:
- члены
- функция: конструктор
- coroutine(lua_State* L, int index = -1);
- Хватает сопрограмму по указанному индексу с учетом lua_State*.
- возвращение статуса сопрограммы
- call_status status() const noexcept;
- Возвращает статус сопрограммы.
- проверяет на ошибку
- bool error() const noexcept;
- Проверяет, произошла ли ошибка при запуске сопрограммы.
- выполняемый и явный оператор bool
- bool runnable () const noexcept;
- explicit operator bool() const noexcept;
- Эти функции позволяют проверить, можно ли еще вызывать сопрограмму (имеет больше значений для выдачи и не имеет ошибок). Если у вас есть объект сопрограммы , вы можете проверить или сделать .coroutine my_co = /*...*/runnable()if ( my_co ) { /* use coroutine */ }
- вызывая сопрограмму
- template<typename... Args>
- protected_function_result operator()( Args&&... args );
- template<typename... Ret, typename... Args>
- decltype(auto) call( Args&&... args );
- template<typename... Ret, typename... Args>
- decltype(auto) operator()( types<Ret...>, Args&&... args );
- Вызывает сопрограмму. Второй operator()позволяет вам указать тип возвращаемых шаблонов с использованием синтаксиса. Затем проверьте дополнительную информацию об успешном выполнении или просто проверьте объект сопрограммы в сообщении ifs, как показано выше .my_co(sol::types<int, string>, ...)status()
- « Protected_function :: Contents :: yielding »
- функции и вы
- Sol может регистрировать все виды функций. Многие из них показаны в быстром 'n' грязном , но здесь мы обсудим множество дополнительных способов регистрации функций в системе Lua, обернутой в sol.
- Установка новой функции
- Имея функцию C ++, вы можете поместить ее в sol несколькими эквивалентными способами, работая аналогично тому, как работает установка переменных :
- Регистрация функций C ++
- #include <sol/sol.hpp>
- string my_function( int a, string b ) {
- // Create a string with the letter 'D' "a" times,
- // append it to 'b'
- return b + string( 'D', a );
- }
- int main () {
- sol::state lua;
- lua["my_func"] = my_function; // way 1
- lua.set("my_func", my_function); // way 2
- lua.set_function("my_func", my_function); // way 3
- // This function is now accessible as 'my_func' in
- // lua scripts / code run on this state:
- lua.script("some_str = my_func(1, 'Da')");
- // Read out the global variable we stored in 'some_str' in the
- // quick lua code we just executed
- string some_str = lua["some_str"];
- // some_str == "DaD"
- }
- Один и тот же код работает со всеми видами функций, начиная с указателей на функции / переменные-члены, которые есть у вас в классе, а также с лямбдами:
- Регистрация функций-членов C ++
- struct my_class {
- int a = 0;
- my_class(int x) : a(x) {
- }
- int func() {
- ++a; // increment a by 1
- return a;
- }
- };
- int main () {
- sol::state lua;
- // Here, we are binding the member function and a class instance: it will call the function on
- // the given class instance
- lua.set_function("my_class_func", &my_class::func, my_class());
- // We do not pass a class instance here:
- // the function will need you to pass an instance of "my_class" to it
- // in lua to work, as shown below
- lua.set_function("my_class_func_2", &my_class::func);
- // With a pre-bound instance:
- lua.script(R"(
- first_value = my_class_func()
- second_value = my_class_func()
- )");
- // first_value == 1
- // second_value == 2
- // With no bound instance:
- lua.set("obj", my_class(24));
- // Calls "func" on the class instance
- // referenced by "obj" in Lua
- lua.script(R"(
- third_value = my_class_func_2(obj)
- fourth_value = my_class_func_2(obj)
- )");
- // first_value == 25
- // second_value == 26
- }
- Функции класса-члена и переменные класса-члена будут превращены в функции при установке таким образом. Вы можете получить интуитивно понятную переменную с доступом после этого раздела, когда узнаете о пользовательских типах для C ++ в Lua , но сейчас мы просто имеем дело с функциями!obj.a = value
- Другой вопрос, который возникает у многих людей, касается шаблонов функций. Шаблоны функций - функции-члены или свободные функции - не могут быть зарегистрированы, потому что они не существуют, пока вы не создадите их в C ++. Поэтому, учитывая шаблонную функцию, такую как:
- Шаблонная функция C ++
- 1
- 2
- 3
- 4
- template <typename A, typename B>
- auto my_add( A a, B b ) {
- return a + b;
- }
- Вы должны указать все аргументы шаблона, чтобы связать и использовать его, вот так:
- Регистрация инстанциации шаблона функции
- int main () {
- sol::state lua;
- // adds 2 integers
- lua["my_int_add"] = my_add<int, int>;
- // concatenates 2 strings
- lua["my_string_combine"] = my_add<string, string>;
- lua.script("my_num = my_int_add(1, 2)");
- int my_num = lua["my_num"];
- // my_num == 3
- lua.script("my_str = my_string_combine('bark bark', ' woof woof')");
- string my_str = lua["my_str"];
- // my_str == "bark bark woof woof"
- }
- Обратите внимание, что мы связываем две отдельные функции. Что если мы хотим связать только одну функцию, но она будет вести себя по-разному в зависимости от того, с какими аргументами она вызывается? Это называется перегрузкой, и это можно сделать с помощью sol :: overload следующим образом:
- Регистрация экземпляров шаблона функции C ++
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- int main () {
- sol::state lua;
- // adds 2 integers
- lua["my_combine"] = sol::overload( my_add<int, int>, my_add<string, string> );
- lua.script("my_num = my_combine(1, 2)");
- lua.script("my_str = my_combine('bark bark', ' woof woof')");
- int my_num = lua["my_num"];
- string my_str = lua["my_str"];
- // my_num == 3
- // my_str == "bark bark woof woof"
- }
- Это полезно для функций, которые могут принимать несколько типов и должны вести себя по-разному в зависимости от этих типов. Вы можете установить столько перегрузок, сколько хотите, и они могут быть разных типов.
- В качестве примечания, связывание функций с параметрами по умолчанию не связывает по волшебству несколько версий функции, вызываемой с параметрами по умолчанию. Вместо этого вы должны использовать sol :: overload .
- В качестве примечания, пожалуйста, убедитесь, что понимаете Убедитесь, что вы понимаете последствия привязки лямбда / вызываемой структуры различными способами и что это значит для вашего кода!
- Получение функции от Lua
- Есть 2 способа получить функцию от Lua. Один с sol :: function, а другой - более продвинутая оболочка с sol :: protected_function . Используйте их для извлечения вызываемых из Lua и вызова основной функции двумя способами:
- Получение sol :: function
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- int main () {
- sol::state lua;
- lua.script(R"(
- function f (a)
- return a + 5
- end
- )");
- // Get and immediately call
- int x = lua["f"](30);
- // x == 35
- // Store it into a variable first, then call
- sol::function f = lua["f"];
- int y = f(20);
- // y == 25
- }
- В Lua вы можете получить все, что можно вызвать, включая функции C ++, которые вы используете, set_functionили аналогичные. sol::protected_functionведет себя аналогично sol::function, но имеет переменную error_handler, которую вы можете установить в функцию Lua. Это ловит все ошибки и запускает их через функцию обработки ошибок:
- Получение sol :: protected_function
- int main () {
- sol::state lua;
- lua.script(R"(
- function handler (message)
- return "Handled this message: " .. message
- end
- function f (a)
- if a < 0 then
- error("negative number detected")
- end
- return a + 5
- end
- )");
- sol::protected_function f = lua["f"];
- f.error_handler = lua["handler"];
- sol::protected_function_result result = f(-500);
- if (result.valid()) {
- // Call succeeded
- int x = result;
- }
- else {
- // Call failed
- sol::error err = result;
- string what = err.what();
- // 'what' Should read
- // "Handled this message: negative number detected"
- }
- }
- Несколько возвратов в и из Lua
- Вы можете вернуть несколько предметов в Lua и из него, используя tuple/ pairклассы, предоставляемые C ++. Это позволяет вам также использовать sol :: tie для установки возвращаемых значений в предварительно объявленные элементы. Чтобы получить несколько возвратов, просто запросите tupleтип из результата вычисления функции или sol::tieсвязку предварительно объявленных переменных вместе и установите результат, равный этому:
- Несколько возвратов от Lua
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- int main () {
- sol::state lua;
- lua.script("function f (a, b, c) return a, b, c end");
- tuple<int, int, int> result;
- result = lua["f"](1, 2, 3);
- // result == { 1, 2, 3 }
- int a, int b;
- string c;
- sol::tie( a, b, c ) = lua["f"](1, 2, "bark");
- // a == 1
- // b == 2
- // c == "bark"
- }
- Вы также можете вернуть несколько элементов самостоятельно из связанной с C ++ функции. Здесь мы собираемся связать лямбду C ++ с Lua, а затем вызвать ее через Lua и получить tupleвыход с другой стороны:
- Многократное возвращение в Lua
- int main () {
- sol::state lua;
- lua["f"] = [](int a, int b, sol::object c) {
- // sol::object can be anything here: just pass it through
- return make_tuple( a, b, c );
- };
- tuple<int, int, int> result = lua["f"](1, 2, 3);
- // result == { 1, 2, 3 }
- tuple<int, int, string> result2;
- result2 = lua["f"](1, 2, "Arf?")
- // result2 == { 1, 2, "Arf?" }
- int a, int b;
- string c;
- sol::tie( a, b, c ) = lua["f"](1, 2, "meow");
- // a == 1
- // b == 2
- // c == "meow"
- }
- Обратите внимание, что мы используем sol :: object для переноса через «любое значение» из Lua. Вы также можете использовать sol::make_objectдля создания объекта из некоторого значения, чтобы он также мог быть возвращен в Lua.
- Любое возвращение в и из Lua
- На это намекали в предыдущем примере кода, но sol::objectэто хороший способ передать «любой тип» обратно в Lua (пока мы все ждем, variant<...>чтобы его реализовали и отправили разработчики компилятора / библиотеки C ++).
- Он может быть использован как так, в сочетании с sol::this_state:
- Верни что-нибудь в Луа
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- sol::object fancy_func (sol::object a, sol::object b, sol::this_state s) {
- sol::state_view lua(s);
- if (a.is<int>() && b.is<int>()) {
- return sol::make_object(lua, a.as<int>() + b.as<int>());
- }
- else if (a.is<bool>()) {
- bool do_triple = a.as<bool>();
- return sol::make_object(lua, b.as<double>() * ( do_triple ? 3 : 1 ) );
- }
- return sol::make_object(lua, sol::lua_nil);
- }
- int main () {
- sol::state lua;
- lua["f"] = fancy_func;
- int result = lua["f"](1, 2);
- // result == 3
- double result2 = lua["f"](false, 2.5);
- // result2 == 2.5
- // call in Lua, get result
- lua.script("result3 = f(true, 5.5)");
- double result3 = lua["result3"];
- // result3 == 16.5
- }
- Это охватывает почти все, что вам нужно знать о функциях и о том, как они взаимодействуют с sol. Для некоторых продвинутых уловок и изящных вещей, проверьте sol :: this_state и sol :: variadic_args . Следующая остановка в этом уроке о типах C ++ (usertypes) в Lua ! Если вам нужно немного больше информации о функциях на стороне C ++ и о том, как наилучшим образом использовать аргументы из C ++, см. Это примечание .
- « Переменные :: содержание
- перегрузка
- вызов различных функций в зависимости от номера / типа аргумента
- функция: создать перегруженный набор
- 1
- 2
- 3
- 4
- 5
- template <typename... Args>
- struct overloaded_set : tuple<Args...> { /* ... */ };
- template <typename... Args>
- overloaded_set<Args...> overload( Args&&... args );
- Фактически созданный класс по sol::overloadсути является оберткой типов, tupleкоторая сообщает библиотеке, что создается перегрузка. Функция помогает пользователям создавать перегруженные функции, которые можно вызывать из Lua, используя 1 имя, но несколько аргументов. Он предназначен для замены спагетти кода, где пользователи макетируют это, делая странные операторы if и переключая, какую версию функции вызывать на основе luaL_check {number / udata / string} .
- Заметка
- Обратите внимание, что параметры по умолчанию в функции (например, ) не существуют вне забавы C ++ во время компиляции. Когда эта функция связывается или сериализуется в инфраструктуру Lua, она связывается как функция, принимающая 1 аргумент, а не 2 функции, принимающие либо 0, либо 1 аргумент. Если вы хотите добиться того же эффекта, вам нужно использовать перегрузку и явно вызывать ту версию функции, которую вы хотите. В C ++ нет магии, которая позволяла бы мне получать параметры по умолчанию и устанавливать их автоматически.int func(int a = 20)
- Заметка
- Разрешение перегрузки может зависеть от настроек конфигурации на страницах безопасности . Например, невозможно отличить целые числа (uint8_t, in32_t и т. Д.) От типов с плавающей запятой (float, double, half), когда они SOL_SAFE_NUMERICSне включены.
- Его использование простое: везде, где вы можете передать тип функции в Lua, будь то пользовательский тип или вы просто устанавливаете какую-либо функцию с помощью setили set_function(для таблицы или состояния (_view) ), просто оберните функции, которые вы хотите Рассмотрим разрешение перегрузки для одной функции следующим образом:
- sol::overload( func1, func2, ... funcN );
- Функции могут быть любым видом функции / функционального объекта (лямбда). Учитывая эти функции и структуру:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- #define SOL_ALL_SAFETIES_ON 1
- #include <sol/sol.hpp>
- #include "assert.hpp"
- #include <iostream>
- struct pup {
- int barks = 0;
- void bark () {
- ++barks; // bark!
- }
- bool is_cute () const {
- return true;
- }
- };
- void ultra_bark( pup& p, int barks) {
- for (; barks --> 0;) p.bark();
- }
- void picky_bark( pup& p, string s) {
- if ( s == "bark" )
- p.bark();
- }
- Затем вы используете его так же, как и для любой другой части API:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- int main () {
- cout << "=== overloading with members ===" << endl;
- sol::state lua;
- lua.open_libraries(sol::lib::base);
- lua.set_function( "bark", sol::overload(
- ultra_bark,
- []() { return "the bark from nowhere"; }
- ) );
- lua.new_usertype<pup>( "pup",
- // regular function
- "is_cute", &pup::is_cute,
- // overloaded function
- "bark", sol::overload( &pup::bark, &picky_bark )
- );
- Выполнение следующих действий в Lua вызовет определенные выбранные перегрузки и связанные с ними функции:
- const auto& code = R"(
- barker = pup.new()
- print(barker:is_cute())
- barker:bark() -- calls member function pup::bark
- barker:bark("meow") -- picky_bark, no bark
- barker:bark("bark") -- picky_bark, bark
- bark(barker, 20) -- calls ultra_bark
- print(bark()) -- calls lambda which returns that string
- )";
- lua.script(code);
- pup& barker = lua["barker"];
- cout << barker.barks << endl;
- c_assert(barker.barks == 22);
- cout << endl;
- return 0;
- }
- Заметка
- Перегрузка выполняется в системе «первым пришел - первым обслужен». Это означает, что если две перегрузки являются совместимыми, работоспособными перегрузками, он выберет первую в списке.
- Обратите внимание, что из-за этой системы вы можете использовать sol :: variadic_args, чтобы сделать функцию, которая служит «запасным вариантом». Убедитесь, что это последняя указанная функция в списке функций для . Этот пример показывает, как .sol::overload( ... )
- Заметка
- Пожалуйста, имейте в виду, что выполнение этого требует затрат времени выполнения, чтобы найти правильную перегрузку. Стоимость напрямую зависит не от количества перегрузок, а от количества функций с одинаковым количеством аргументов (sol будет заблаговременно исключать все функции, не соответствующие количеству аргументов).
- « As_returns :: Содержание :: собственность »
- функции
- работа с функциями в sol3
- Есть ряд примеров, касающихся функций и того, как их можно связать с sol3:
- Для более быстрого прохождения, которое демонстрирует почти все, посмотрите примеры и быстрый и грязный учебник
- Для полного объяснения, прочитайте учебник и проконсультируйтесь с предметами ниже
- Если у вас есть привязки и настройки, которые хотят использовать C API без вмешательства sol3, вы можете использовать необработанную функцию, которая имеет определенные последствия ( см. Ниже )
- Вернуть несколько значений в Lua:
- возвращая tuple
- используя sol :: variadic_results
- Вызовы функций перегрузки с различными типами аргументов и рассчитывают на одно имя (перегрузка с первым связыванием, пересылка с первым обслуживанием)
- Примечание: из-за этой функции автоматическое преобразование числа в строку из Lua не разрешено для перегрузок и не работает, когда включены функции безопасности.
- Перегрузки int / float должны быть SOL_SAFE_NUMERICSвключены, чтобы различать два
- Используйте C ++ захваты и лямбды для привязки функций-членов, привязанных к одному объекту /
- Вы можете работать с прозрачными аргументами, которые предоставляют вам специальную информацию, такую как
- sol :: variadic_args , для обработки переменного количества аргументов во время выполнения
- sol :: this_state , для получения текущего состояния Lua
- sol :: this_environment , для потенциального извлечения текущей среды Lua
- Управляйте сериализацией аргументов и возвращаемых типов с помощью sol :: nested , sol :: as_table , sol :: as_args и sol :: as_function
- Установите среды для функций и скриптов Lua с помощью sol :: environment
- Вы можете использовать политики, чтобы управлять зависимостями и оптимизировать возвращаемые значения, а также применять настраиваемое поведение к возвращаемым функциям.
- работа с callables / lambdas
- Чтобы явно указать, что структура должна интерпретироваться как функция, используйте . Вы также можете использовать вызов sol :: as_function , который обернет и идентифицирует ваш тип как функцию.my_table.set_function( key, func_value );
- Заметка
- Когда вы устанавливаете lambdas / callables, используя одну и ту же сигнатуру функции, вы можете страдать от данных (таких как строковые литералы) от «неправильного поведения». Это связано с тем, что некоторые компиляторы не предоставляют уникальные имена типов, которые мы можем получить в C ++ с отключенным RTTI, и, таким образом, он будет регистрировать первую лямбду определенной сигнатуры как ту, которая будет вызвана. В результате строковые литералы и другие данные, хранимые способом, определяемым реализацией компилятора, могут быть свернуты и запускать неправильную подпрограмму, даже если другие наблюдаемые побочные эффекты хороши.my_table.set( ... )const static
- Чтобы избежать этой проблемы, зарегистрируйте все свои лямбды с помощью my_table.set_function и вообще избегайте кошмаров .
- Кроме того, важно знать, что лямбда-выражения без указанного возвращаемого типа (и неконстантного, не ссылочного auto) будут возвращать возвращаемые значения. Чтобы явно захватить или вернуть ссылки, используйте decltype(auto)или укажите тип возвращаемого значения точно так, как вам нужно:
- #define SOL_ALL_SAFETIES_ON 1
- #include <sol/sol.hpp>
- #include <assert.hpp>
- int main(int, char*[]) {
- struct test {
- int blah = 0;
- };
- test t;
- sol::state lua;
- lua.set_function("f", [&t]() {
- return t;
- });
- lua.set_function("g", [&t]() -> test& {
- return t;
- });
- lua.script("t1 = f()");
- lua.script("t2 = g()");
- test& from_lua_t1 = lua["t1"];
- test& from_lua_t2 = lua["t2"];
- // not the same: 'f' lambda copied
- c_assert(&from_lua_t1 != &t);
- // the same: 'g' lambda returned reference
- c_assert(&from_lua_t2 == &t);
- return 0;
- }
- исключительная безопасность / обработка
- Все функции, связанные с sol3, устанавливают батут исключений вокруг функции (если вы не работаете с необработанной функцией lua_CF, которую вы выдвинули сами ). protected_function также имеет элемент-обработчик ошибок и батут исключений вокруг своих внутренних объектов, но он не гарантированно безопасен, если исключение выходит за его пределы. Поймать это исключение также небезопасно: если исключение возникло из API-интерфейса sol3, вы должны предположить, что виртуальная машина находится в неопределенном и / или сбитом состоянии.
- Пожалуйста , прочтите страницу ошибки и страницу исключения для получения более подробной информации о том , что делать с исключениями , которые взрываются из API.
- функции и передача аргументов
- Все аргументы переданы. В отличие от get / set / operator [] для sol :: state или sol :: table , семантика значений здесь не используется. Это пересылка эталонной семантики, которая не копирует / не перемещает, если это не сделано специально для принимающих функций / специально для пользователя.
- Заметка
- Это также означает, что вы должны передавать и получать аргументы определенными способами, чтобы максимизировать эффективность. Так , например, sol::table, sol::object, sol::userdataи друзья дешевы копировать, и следует просто принимать в качестве значений. Это включает в себя примитивные типы, такие как intи double. Однако типы C ++ - если вы не хотите копировать - следует использовать как или , чтобы сохранить копии, если это важно. Обратите внимание, что получение ссылок из Lua также означает, что вы можете напрямую изменять данные внутри Lua, поэтому будьте осторожны. Lua по умолчанию имеет дело с вещами в основном по ссылке (за исключением примитивных типов).const type&type&
- Когда вы связываете функцию с Lua, пожалуйста, примите любые аргументы указателя как T*, если вы точно не знаете, что будете соответствовать точному типу уникального / общего указателя и классу, который он переносит. sol3 не может поддерживать «неявное приведение обернутых указателей», например, shared_ptr<MySecondBaseClass>когда передается функция a shared_ptr<MyDerivedClass>. Иногда это может сработать, потому что компилятор может выстроить ваши классы таким образом, чтобы сработало непосредственное приведение, но это неопределенное поведение насквозь, и sol3 не имеет механизмов, с помощью которых он может сделать это безопасно и не взорваться в лицо пользователя.
- Заметка
- Пожалуйста, избегайте использования специальных аргументов unique_usertype, либо по ссылке, либо по значению. Во многих случаях по значению не работает (например, с unique_ptr), потому что многие типы только для перемещения, и Lua не имеет понятия семантики «перемещения». Ссылка на ссылку опасна, потому что sol3 передаст вам ссылку на исходные данные: но любые указатели, хранящиеся в Lua, могут быть признаны недействительными, если вы вызовете .reset()или схоже с указателем ядра. Пожалуйста, возьмите указатель ( T*), если вы ожидаете nil/ будете nullptrпереданы вашей функции, или ссылку ( илиconst T&T&) Если вы этого не сделаете. В качестве примечания: если вы пишете небольшой класс-обертку, который содержит базовый тип указателя, и взаимодействуете с помощью обертки, то, когда вы получаете обертку в качестве аргумента в C ++ -функции, связанной с Lua, вы можете свободно приводить внутренний объект. Это просто прямое приведение в качестве аргумента к функции, которая является проблемой.
- Заметка
- Вы можете получить даже больше скорости из sol::objectстиля типов путем осуществления sol::stack_object(или sol::stack_..., где ...это userdata, reference, tableи т.д.). Они ссылаются непосредственно на позицию стека, а не дешево / безопасно, на внутреннюю ссылку Lua, чтобы быть уверенным, что она не может быть выметена из-под вас. Обратите внимание, что если вы манипулируете стеком из-под этих объектов, они могут плохо себя вести, поэтому, пожалуйста, не взрывайте свой стек Lua при работе с этими типами.
- string(и wstring) являются особенными. Lua хранит строки как строки с нулевым символом в конце. будет копировать, поэтому взятие по значению или по константной ссылке все равно вызывает операцию копирования. Вы можете взять a , но это будет означать, что вы знакомы с тем, что происходит в стеке Lua (если вы измените его и начнете отбирать аргументы функций из него в вызовах функций и т. Д., О чем ранее было предупреждено).const char*stringstringconst char*
- безопасность вызова функции
- У вас могут быть функции здесь и при проверке пользовательских типов, чтобы определенно убедиться, что типы, передаваемые в функции C ++, являются такими, какими они должны быть, добавив перед включением sol или передавая его в командной строке. В противном случае, по соображениям скорости, эти проверки используются только там, где это абсолютно необходимо (например, различение перегрузок ). Смотрите безопасность для получения дополнительной информации.#define SOL_ALL_SAFETIES_ON
- необработанные функции ( lua_CFunction)
- Когда вы помещаете функцию в Lua, используя sol, используя любые методы, и эта функция точно соответствует сигнатуре , она будет рассматриваться как необработанная функция C (a ). Это означает, что обычный исключительный батутный соль оборачивает вызовы других ваших функций, не будет присутствовать. Вы будете нести ответственность за перехват исключений и их обработку до того, как они взорвутся в C API (и, возможно, уничтожат ваш код). Sol во всех других случаях добавляет батут для обработки исключений, который превращает исключения в ошибки Lua, которые могут быть обнаружены вышеупомянутыми защищенными функциями и средствами доступа.int( lua_State* );lua_CFunction
- Обратите внимание, что лямбды без сохранения состояния можно преобразовать в указатель на функцию, поэтому лямбды без сохранения состояния, аналогичные форме , также будут выдаваться как необработанные функции. Если вам нужно получить состояние Lua, вызывающее функцию, используйте sol :: this_state .[](lua_State*) -> int { ... }
- Предупреждение
- НЕ предполагайте, что сборка Lua как C ++ позволит вам бросать напрямую из необработанной функции. Если возбуждается исключение и оно всплывает в инфраструктуру Lua, даже если вы компилируете как C ++, Lua не распознает исключения, кроме тех, которые он использует lua_error. Другими словами, он вернет какой-то полностью ложный результат, потенциально оставит ваш стек Lua перебитым, а остальная часть вашей виртуальной машины может находиться в полуразрушенном состоянии. Пожалуйста, избегайте этого!
- « Особенности :: Содержание :: Типы пользователей »
- функция
- вызов функций, связанных с Lua
- Заметка
- Эта абстракция предполагает, что функция работает безопасно. Если вы ожидаете, что в вашем коде есть ошибки (например, вы не всегда имеете явный контроль над ним или пытаетесь отлаживать ошибки), пожалуйста, явно используйте sol :: protected_function . Вы также можете сделать по sol::functionумолчанию sol::protected_function, включив функции безопасности .
- class unsafe_function : public reference;
- typedef unsafe_function function;
- Функция является верной версией защищенной функции , исключающей необходимость проверок типов и обработки ошибок (таким образом, в некоторых случаях незначительно увеличивает скорость). Это тип функции по умолчанию sol. Возьмите функцию прямо из стека, используя конструктор:
- конструктор: unsafe_function
- unsafe_function(lua_State* L, int index = -1);
- Вызывает конструктор и создает этот тип прямо из стека. Например:
- funcs.lua
- bark_power = 11;
- function woof ( bark_energy )
- return (bark_energy * (bark_power / 4))
- end
- Следующий код C ++ вызовет эту функцию из этого файла и получит возвращаемое значение:
- #define SOL_ALL_SAFETIES_ON 1
- #include <sol/sol.hpp>
- #include "assert.hpp"
- int main (int, char*[]) {
- sol::state lua;
- lua.script(code);
- sol::function woof = lua["woof"];
- double numwoof = woof(20);
- c_assert(numwoof == 55.0);
- Вызов woof(20)генерирует unsafe_function_result , который затем неявно преобразуется в doubleпосле вызова . Промежуточный временный function_resultобъект затем разрушается, выводя результаты вызова функции Lua из стека Lua.
- Вы также можете вернуть несколько значений с помощью tupleили, если вам нужно привязать их к существующим переменным, используйте sol::tie:
- lua.script( "function f () return 10, 11, 12 end" );
- sol::function f = lua["f"];
- tuple<int, int, int> abc = f();
- c_assert(get<0>(abc) == 10);
- c_assert(get<1>(abc) == 11);
- c_assert(get<2>(abc) == 12);
- // or
- int a, b, c;
- sol::tie(a, b, c) = f();
- c_assert(a == 10);
- c_assert(b == 11);
- c_assert(c == 12);
- return 0;
- }
- Это значительно облегчает работу с несколькими возвращаемыми значениями. Использование tieиз стандарта C ++ приведет к висящим ссылкам или плохому поведению из-за очень плохого способа, которым кортежи / C ++ tieбыли определены и реализованы: используйте вместо этого, чтобы удовлетворить любые требования множественного возврата.sol::tie( ... )
- Предупреждение
- НЕ сохраняйте возвращаемый тип unsafe_function_result ( function_resultкогда настройки безопасности не включены ) с помощью auto, как в , и НЕ храните его где-либо. В отличие от его аналога protected_function_result , хранить его НЕ безопасно, так как предполагается, что его возвращаемые типы все еще находятся на вершине стека, а при вызове деструктора выскакивает число результатов, которые функция должна была вернуть с вершины стека. Если вы возитесь со стеком Lua между сохранением и его уничтожением, вы будете подвержены невероятному количеству удивительных и трудно отслеживаемых ошибок. Не делай этого.auto numwoof = woof(20);function_resultfunction_result
- функция: оператор вызова / вызов функции
- template<typename... Args>
- unsafe_function_result operator()( Args&&... args );
- template<typename... Ret, typename... Args>
- decltype(auto) call( Args&&... args );
- template<typename... Ret, typename... Args>
- decltype(auto) operator()( types<Ret...>, Args&&... args );
- Вызывает функцию. Второй operator()позволяет вам указать тип возвращаемых шаблонов с использованием синтаксиса. Функция предполагает, что ошибок времени выполнения нет, и, таким образом, будет вызывать функцию, если обнаружится ошибка, и в противном случае может вернуть мусорные / поддельные значения, если пользователь не будет осторожен.my_func(sol::types<int, string>, ...)atpanic
- Чтобы узнать больше о том, как обрабатываются аргументы функции, см. Это примечание
- « Tie :: Contents :: protected_function »
- контейнеры
- работа с контейнерами в sol3
- Контейнеры - это объекты, которые должны проверяться и повторяться, и задача которых обычно заключается в предоставлении хранилища для коллекции элементов. Стандартная библиотека имеет несколько контейнеров разных типов, и у всех них есть begin()и end()методы, которые возвращают итераторы. Массивы в стиле C также являются контейнерами, и sol3 обнаружит их все для использования и присвоит им специальные свойства и функции.
- Контейнеры из C ++ хранятся как userdataсо специальными usertypeметатаблицами со специальными операциями
- В Lua 5.1 это означает, что контейнеры, помещенные без оболочек, таких как as_table и nested , не будут работать с pairsдругими встроенными итерационными функциями Lua или другими встроенными функциями
- Lua 5.2+ будет вести себя нормально (не включает LuaJIT 2.0.x)
- Вы должны помещать контейнеры в C ++, возвращая их напрямую и получая / устанавливая их напрямую, и они будут иметь тип sol::type::userdataи обрабатываться как пользовательский тип
- Контейнером можно манипулировать как из C ++, так и из Lua, и, как и пользовательские данные, будут отражать изменения, если вы используете ссылку на данные.
- Это означает, что контейнеры автоматически не сериализуются как таблицы Lua
- Если вам нужны таблицы, рассмотрите возможность использования sol::as_tableиsol::nested
- Смотрите этот пример сериализации таблицы для более подробной информации
- Lua 5.1 имеет различную семантику для pairsи ipairs: будьте осторожны. Смотрите примеры ниже для более подробной информации
- Вы можете переопределить поведение контейнера, переопределив черту обнаружения и специализировав шаблон usertype_container
- Вы можете связать типичные массивы в стиле C, но должны следовать правилам
- Заметка
- Обратите внимание, что массивы c-style должны быть добавлены в Lua с помощью или для передачи этих свойств. Нет, простой указатель не считается массивом. Это важно, потому что также типизируется как array ( ), и, таким образом, мы можем использовать s или указатели на фактические типы массивов, чтобы работать для этой цели.lua["my_arr"] = &my_c_array;lua["my_arr"] = ref(my_c_array);T*lua["my_string"] = "some string";const char[n]reference_wrapper
- обнаружение контейнера
- Контейнеры обнаруживаются по типу признака sol::is_container<T>. Если это окажется правдой, sol3 попытается отправить пользовательские данные в Lua для указанного типа Tи наделить их некоторыми функциями и свойствами, перечисленными ниже. Эти функции и свойства обеспечиваются шаблонной структурой sol::usertype_container<T>, в которой есть ряд статических функций Lua C, связанных с метастабильностью безопасности. Если вы хотите переопределить поведение для конкретного контейнера, вы должны сначала специализироваться sol::is_container<T>для извлечения true_type, а затем переопределить функции, которые вы хотите изменить. Любая функция, которую вы не переопределите, будет вызывать реализацию по умолчанию или эквивалентную ей. Реализация по умолчанию для нераспознанных контейнеров - просто ошибки.
- Вы также можете специализироваться на том, sol::is_container<T>чтобы отключить обнаружение контейнера, если вы находите его слишком нетерпеливым для типа, который, как оказалось, имеет функции beginи endфункционирует, например, так:
- not_container.hpp
- struct not_container {
- void begin() {
- }
- void end() {
- }
- };
- namespace sol {
- template <>
- struct is_container<not_container> : false_type {};
- }
- Это позволит типу выдвигаться как обычные данные пользователя.
- Заметка
- Нажатие на новый тип пользователя предотвратит обработку соответствующего типа контейнера C ++ как контейнера. Чтобы заставить тип, который вы зарегистрировали / связали как тип пользователя, используя new_usertypeили new_simple_usertypeобрабатывать как контейнер, используйте sol :: as_container .
- переопределение контейнера
- Если вы хотите , чтобы это участие в качестве таблицы, используйте true_typeвместо false_typeот обнаружения containter примера. и предоставить соответствующие iteratorи value_typeопределения типа. Невыполнение этого требования приведет к контейнеру, чьи операции по умолчанию завершаются неудачно (или компиляция завершится неудачей).
- Если вам нужен тип, объявление и определение которого вы не можете контролировать как контейнер, то вы должны переопределить поведение по умолчанию, специализируя свойства контейнера, например:
- specializing.hpp
- struct not_my_type { ... };
- namespace sol {
- template <>
- struct is_container<not_my_type> : true_type {};
- template <>
- struct usertype_container<not_my_type> {
- ...
- // see below for implemetation details
- };
- }
- Предполагается, что различные операции, представленные ниже usertype_container<T>, будут такими. Возможность их переопределения требует знания стека Lua и того, как он работает, а также знания необработанных C-функций Lua . Вы можете прочитать о сырых функциях Си, взглянув на книгу «Программирование на Lua». Информация онлайн-версии о стеке и о том, как вернуть информацию, по-прежнему актуальна, и вы можете комбинировать ее, используя также низкоуровневый API стека sol для достижения желаемого поведения.
- Предупреждение
- Обработка исключений БУДЕТ быть предусмотрена вокруг этих конкретных исходных функций C, так что вам не нужно беспокоиться об исключениях или ошибках кипящих до конца и обработок этой части. Это специально обрабатывается для вас в этом конкретном случае, и ТОЛЬКО в этом конкретном случае. Необработанная заметка по-прежнему применяется ко всем другим необработанным функциям C, которые вы делаете вручную.
- контейнерные операции
- Ниже приведены многие контейнерные операции и их точки переопределения для usertype_container<T>. Пожалуйста, используйте их, чтобы понять, как использовать любую часть реализации.
- операция синтаксис lua usertype_container <T> точка расширения порядок аргументов стека Заметки / предостережения
- задавать c:set(key, value) static int set(lua_State*); 1 self 2 key 3 value
- Если valueэто ноль, он выполняет стирание в реализации по умолчанию
- если это контейнер, который поддерживает вставку и `` ключ`` является индексом, равным (размер контейнера) + 1, он вставит значение в конец контейнера (это идиома Lua)
- реализация по умолчанию использует .insertили оператор [] для массивов c
- index_set c[key] = value static int index_set(lua_State*); 1 self 2 key 3 value
- реализация по умолчанию откладывает работу на «set»
- если это контейнер последовательности и он поддерживает вставку и keyявляется индексом, равным размеру контейнера +1, он будет вставлен в конец контейнера (это идиома Lua)
- в v = c:at(key) static int at(lua_State*); 1 self 2 index
- может вернуть несколько значений
- реализация по умолчанию увеличивает итераторы линейно для не случайного доступа; использует, .find()если доступно
- если тип не имеет итераторов с произвольным доступом, не используйте это в цикле for !
- получить v = c:get(key) static int get(lua_State*); 1 сам 2 ключа
- может вернуть несколько значений
- реализация по умолчанию увеличивает итераторы линейно для не случайного доступа
- index_get v = c[key] static int index_get(lua_State*); 1 сам 2 ключа
- может вернуть только 1 значение
- реализация по умолчанию откладывает работу, чтобы "получить"
- Если keyэто строка и keyявляется одной из других функций-членов, она вернет эту функцию, а не выполнит поиск / индекс
- находить c:find(target) static int find(lua_State*); 1 само 2 цель
- target значение для контейнеров без поиска (фиксированные контейнеры, контейнеры последовательностей, неассоциативные и неупорядоченные контейнеры)
- стирать c:erase(target) static int erase(lua_State*); 1 само 2 цель
- для контейнеров последовательности, targetэто индекс для удаления
- для контейнеров поиска, targetявляется типом ключа
- использует линейное приращение , чтобы место для контейнеров последовательности , которые не имеют случайные итераторы доступа ( list, forward_listи тому подобные)
- может сделать недействительной итерацию
- вставить c:insert(target, value) 1 self 2 target 3 key
- для контейнеров последовательности target- это индекс, в противном случае это тип ключа
- вставляет в контейнер по возможности в указанном месте
- добавлять c:add(key, value) или же c:add(value) static int add(lua_State*); 1 self 2 key / value 3 value
- 2-й аргумент (3-й в стеке) предоставляется для добавления ассоциативных контейнеров
- заказанные контейнеры будут вставлены в соответствующее место, не обязательно в конце (использует .insert(...)или .find(...)с назначением
- может лишить законной силы итерацию
- размер #c static int size(lua_State*); 1 себя
- вызовы реализации по умолчанию, .size()если присутствуют
- в противном случае реализация по умолчанию использует distance(begin(L, self), end(L, self))
- Чисто c:clear() static int clear(lua_State*); 1 себя
- реализация по умолчанию не обеспечивает отступления, если нет clearоперации
- смещение н / static int index_adjustment(lua_State*, T&); н /
- возвращает индекс, который добавляет к переданному числовому индексу для доступа к массиву (реализация по умолчанию - моделирование индексации на основе 1 из Lua)return -1
- начать н / static iterator begin(lua_State*, T&); н /
- вызывается по умолчанию реализация в вышеуказанных функциях
- не является обычной необработанной функцией: должен возвращать итератор из второго аргумента «T &», который является self
- конец н / static iterator end(lua_State*, T&); н /
- вызывается по умолчанию реализация в вышеуказанных функциях
- не является обычной необработанной функцией: должен возвращать итератор из второго аргумента «T &», который является self
- следующий static int next(lua_State*); 1 себя
- реализовать только для опытных пользователей, которые понимают предостережения по написанию nextфункции для обхода Lua
- используется как 'итерационная функция', отправляемая с вызовом pair ()
- пары static int pairs(lua_State*); 1 себя
- внедрить, если продвинутый пользователь только то, что понимает предостережения
- Вместо этого переопределите начало и конец и оставьте это для реализации по умолчанию, если вы не знаете, для чего __pairsили как реализовать это и nextфункцию
- работает только в Lua 5.2+
- вызов в Lua 5.1 / LuaJIT завершится с ошибкой подтверждения (Lua ожидает быть таблицей); может использоваться как обычная функция, чтобы обойти ограничениеpairs( c )cc:pairs()
- ipairs static int ipairs(lua_State*); 1 себя
- внедрить, если продвинутый пользователь только то, что понимает предостережения
- Вместо этого переопределите начало и конец и оставьте это для реализации по умолчанию, если вы не знаете, для чего __ipairsили как реализовать это и nextфункцию
- работает только в Lua 5.2, устарел в Lua 5.3 (но все еще может вызываться в режимах совместимости)
- вызов в Lua 5.1 / LuaJIT завершится с ошибкой подтверждения (Lua ожидает быть таблицей)ipairs( c )c
- Заметка
- Если ваш тип не адекватная поддержки begin()и , end()и вы не можете изменить его, используйте sol::is_containerчерт переопределение вместе с пользовательской реализацией pairsна вашем UserType , чтобы заставить его работать , как вы хотите. Обратите внимание, что тип не имеет надлежащего типа begin()и end()не будет работать, если вы попытаетесь принудительно сериализовать его как таблицу (это означает, что следует избегать использования sol :: as_table и sol :: nested , в противном случае у вас будут ошибки компилятора). Просто установите его или получите напрямую, как показано в примерах, для работы с контейнерами C ++.
- Заметка
- Переопределение признаков обнаружения и рабочих характеристик, перечисленных выше, а затем попытка использовать sol::as_tableили подобное может привести к сбоям компиляции, если у вас нет свойства begin()или end()функции для типа. Если вы хотите, чтобы все происходило с особыми соображениями типа пользователя, не заключайте контейнер в одну из специальных абстракций преобразования / преобразования таблиц.
- контейнерные классификации
- Когда вы сериализуете контейнер в sol3, обработчик контейнера по умолчанию работает с контейнерами, проверяя их различные свойства, функции и определения типов. Вот общие значения контейнеров, которые по умолчанию распознает sol3, и какие уже известные контейнеры попадают в их категории:
- тип контейнера требования известные контейнеры Заметки / предостережения
- последовательность erase(iterator) push_back/insert(value_type) std :: vector std :: deque std :: list std :: forward_list
- find операция линейна по размеру списка (ищет все элементы)
- std :: forward_list имеет итераторы только для пересылки: set / find является линейной операцией
- std :: forward_list использует идиому «insert_after», требует специальной внутренней обработки
- фиксированный не хватает push_back/ insert не хватаетerase std :: array <T, n> T [n] (фиксированные массивы)
- обычные массивы в стиле c должны быть установлены или использоватьсяref( arr )&arr
- C ++ реализация вставляет с помощью оператора []
- приказал key_type ЬурейеЕ erase(key) find(key) insert(key) std :: set std :: multi_set
- container[key] = stuffоперация стирает, когда stuffноль, вставляет / устанавливает, когда нет
- container.get(key) возвращает сам ключ
- ассоциативный, упорядоченный key_type, mapped_typetypedefs erase(key) find(key) insert({ key, value }) std :: map std :: multi_map
- неупорядоченный так же, как приказано std :: unordered_set std :: unordered_multiset
- container[key] = stuffоперация стирает, когда stuffноль, вставляет / устанавливает, когда нет
- container.get(key) возвращает сам ключ
- итерация не гарантируется в порядке вставки, как в контейнере C ++
- неупорядоченный, ассоциативный так же, как приказано, ассоциативный std :: unordered_map std :: unordered_multimap
- итерация не гарантируется в порядке вставки, как в контейнере C ++
- полный пример
- Вот полный рабочий пример того, как это работает для Lua 5.3 и Lua 5.2, и как вы можете извлечь контейнер во всех версиях:
- #define SOL_ALL_SAFETIES_ON 1
- #include <sol/sol.hpp>
- #include <vector>
- #include <iostream>
- int main(int, char**) {
- cout << "=== containers ===" << endl;
- sol::state lua;
- lua.open_libraries();
- lua.script(R"(
- function f (x)
- print("container has:")
- for k=1,#x do
- v = x[k]
- print("\t", k, v)
- end
- print()
- end
- )");
- // Have the function we
- // just defined in Lua
- sol::function f = lua["f"];
- // Set a global variable called
- // "arr" to be a vector of 5 lements
- lua["arr"] = vector<int>{ 2, 4, 6, 8, 10 };
- // Call it, see 5 elements
- // printed out
- f(lua["arr"]);
- // Mess with it in C++
- // Containers are stored as userdata, unless you
- // use `sol::as_table()` and `sol::as_table_t`.
- vector<int>& reference_to_arr = lua["arr"];
- reference_to_arr.push_back(12);
- // Call it, see *6* elements
- // printed out
- f(lua["arr"]);
- lua.script(R"(
- arr:add(28)
- )");
- // Call it, see *7* elements
- // printed out
- f(lua["arr"]);
- lua.script(R"(
- arr:clear()
- )");
- // Now it's empty
- f(lua["arr"]);
- cout << endl;
- return 0;
- }
- Обратите внимание, что это не будет хорошо работать в Lua 5.1, так как он имеет явные проверки таблиц и не проверяет метаметоды, даже когда pairsили ipairsпередается таблица. В этом случае вам нужно будет использовать более ручную схему итерации или вам придется преобразовать ее в таблицу. В C ++ можно использовать золь :: as_table при прохождении что - то в библиотеку , чтобы получить таблицу из него: . Для ручной итерации в коде Lua без использования чего-либо с индексами попробуйте:lua["arr"] = as_table( vector<int>{ ... });as_table
- iteration.lua
- 1
- 2
- 3
- for i = 1, #vec do
- print(i, vec[i])
- end
- Есть и другие способы перебора ключей / значений, но они могут быть трудными и снижать производительность из-за отсутствия надлежащей поддержки в Lua 5.1. Мы рекомендуем вам перейти на Lua 5.2 или 5.3, если это является неотъемлемой частью вашей инфраструктуры.
- Если вы не можете my_container:pairs()выполнить обновление, используйте функцию «member» в Lua для выполнения итерации:
- #define SOL_ALL_SAFETIES_ON 1
- #include <sol/sol.hpp>
- #include "assert.hpp"
- #include <unordered_set>
- #include <iostream>
- int main() {
- struct hasher {
- typedef pair<string, string> argument_type;
- typedef size_t result_type;
- result_type operator()(const argument_type& p) const {
- return hash<string>()(p.first);
- }
- };
- using my_set = unordered_set<pair<string, string>, hasher>;
- cout << "=== containers with pair<> ===" << endl;
- sol::state lua;
- lua.open_libraries(sol::lib::base);
- lua.set_function("f", []() {
- return my_set{ { "key1", "value1" },{ "key2", "value2" },{ "key3", "value3" } };
- });
- lua.safe_script("v = f()");
- lua.safe_script("print('v:', v)");
- lua.safe_script("print('#v:', #v)");
- // note that using my_obj:pairs() is a
- // way around pairs(my_obj) not working in Lua 5.1/LuaJIT: try it!
- lua.safe_script("for k,v1,v2 in v:pairs() do print(k, v1, v2) end");
- cout << endl;
- return 0;
- }
- « Usertypes :: Contents :: threading »
- типы пользователей
- Возможно, наиболее мощная функция sol3 usertypes- это способ, с помощью которого sol3 и C ++ связывают ваши классы со средой исполнения Lua и связывают вещи между обеими таблицами и конкретными блоками памяти C ++, позволяя обрабатывать пользовательские данные Lua и другие вещи, такие как классы.
- Чтобы узнать больше о пользовательских типах, посетите:
- основной учебник
- учебник по настройке
- API документация
- документация памяти
- В папке примеров также есть несколько действительно хороших примеров для просмотра. Ниже также приведены некоторые примечания о гарантиях, которые вы можете найти о пользовательских типах и связанных с ними пользовательских данных:
- Контейнеры выдвигаются как специальные пользовательские типы, но могут быть отключены, если возникают проблемы, как подробно описано здесь .
- Определенные операторы обнаруживаются и автоматически связываются для пользовательских типов
- Вы можете использовать битовые поля, но это требует некоторой утонченности с вашей стороны. У нас есть пример, чтобы помочь вам начать здесь, который использует несколько хитростей .
- Все типы пользователей расширяются во время выполнения как в Lua, так и в C ++
- Если вам нужны динамические обратные вызовы или переопределяемые во время выполнения функции, используйте functionпеременную-член и получите / установите ее для объекта usertype
- functionработает как переменная-член или при передаче в качестве аргумента / возвращает значение (вы даже можете использовать его с sol::property)
- Вы также можете создать полностью динамический объект: см. Пример dynamic_object для более подробной информации.
- Вы можете использовать политики, чтобы управлять зависимостями и оптимизировать возвращаемые значения, а также применять настраиваемое поведение к возвращаемым функциям.
- Вы можете работать со специальными типами оболочек , таких как unique_ptr<T>, shared_ptr<T>и другие по умолчанию
- Расширьте их, используя черты sol :: unique_usertype <T>
- Это позволяет настраиваемым интеллектуальным указателям, специальным указателям, настраиваемым дескрипторам и другим указывать определенную семантику обработки для обеспечения надлежащего RAII с помощью сборки мусора Lua
- (Дополнительно) Вы можете переопределить функцию итерации для Lua 5.2 и выше (LuaJIT не имеет возможности), как показано в примере пар
- (Advanced) Interop с toLua, kaguya, OOLua, LuaBind, luwra, и всеми другими существующими библиотеками по использованию стека API по sol::stack::userdata_checkerи sol::stack::userdata_getter точке расширения
- Необходимо включить SOL_ENABLE_INTEROP, как определено в документации по конфигурации и безопасности , чтобы использовать
- Вот несколько других общих советов и советов для понимания и работы с типами пользователей:
- Обратите внимание, что двоеточие необходимо для «автоматической» передачи аргумента this/ selfв методы Lua
- obj:method_name() как вы называете «член» методы в Lua
- Это чисто синтаксический сахар, который передает имя объекта в качестве первого аргумента method_nameфункции
- my_obj:foo(bar, baz) такой же как my_obj.foo(my_obj, bar, baz)
- Обратите внимание, что один использует двоеточие, а другой использует точку, и если вы забудете сделать это правильно, ваш код будет зависать
- Есть определения безопасности, изложенные на странице безопасности здесь
- Вы можете нажать типы, классифицированные как userdata, прежде чем зарегистрировать usertype.
- Вы можете в любое время зарегистрировать тип пользователя в среде исполнения Lua.
- Вы можете получить пользовательский тип из среды выполнения Lua в любое время.
- Методы и свойства будут добавлены к типу только после регистрации пользовательского типа во время выполнения Lua
- Все методы и свойства будут отображаться во всех пользовательских данных, даже если этот объект был передан перед пользовательским типом (все пользовательские данные будут обновлены).
- Типы либо копируют один раз, либо перемещают один раз в область памяти, если это тип значения. Если это указатель, мы храним только ссылку
- Это означает получение типов классов (не примитивных типов, таких как строки или целые числа), T&или T*позволяет вам изменять данные в Lua напрямую
- Получить равнину, Tчтобы получить копию
- Типы возврата и передачи аргументов sol::function-типам используют идеальную семантику пересылки и ссылки, что означает, что копирование не происходит, если вы не укажете значение явно. Смотрите это примечание для деталей
- Вы можете установить indexи new_indexсвободно использовать любой пользовательский тип, который хотите переопределить, по умолчанию «если ключ отсутствует, найти его / установить его здесь» функциональность определенного объекта пользовательского типа.
- new_indexи indexне будет вызван, если вы попытаетесь манипулировать именованной таблицей пользовательских типов напрямую. Sol3 будут вызваны для добавления этой функции в таблицу поиска функций / переменных пользовательского типа.
- new_indexи indexбудет вызываться, если вы попытаетесь вызвать ключ, который не существует в самом объекте пользовательских данных (объект C ++)
- Если вы создали тип пользователя с именем test, это означает , что с вызовет вашу функцию, но вместо этого установит ключ для поиска этой функции для всех типов.t = test()t.hi = 54test.hi = function () print ("hi"); endhitest
- Первые байты всегда указатель на типизированную память C ++. То, что происходит после, зависит от того, что вы вставили в систему в соответствии со спецификацией памяти для пользовательских типов . Это совместимо с рядом систем, отличных от sol3, что упрощает взаимодействие с другими системами Lua.sizeof( void* )
- Методы, свойства, переменные и функции-члены, принимающие self&аргументы, изменяют данные напрямую
- Работайте над копией, принимая аргументы или возвращая по значению.
- Не используйте ссылки на r-значения: они ничего не значат в коде Lua.
- Типы только для перемещения могут быть взяты только по ссылке: sol3 не может знать, если / когда перемещать значение (кроме случаев, когда сериализация выполняется с идеальной пересылкой в Lua, но не вызывает функцию C ++ из Lua)
- Фактическая метатабель, связанная с типом пользователя, имеет длинное имя и определена как непрозрачная реализацией sol.
- Фактическая метатабельная внутренняя работа непрозрачна и определяется реализацией sol, и внутренних документов нет, поскольку оптимизация операций применяется на основе эвристики, которую мы обнаруживаем при тестировании производительности системы.
- « Функции ::
- тип пользователя <T> ¶
- структуры и классы из C ++, доступные для кода Lua
- Заметка
- T относится к типу, превращаемому в тип пользователя.
- class metatable : public table;
- template <typename T>
- class usertype : public metatable;
- В то время как другие платформы расширяют синтаксис lua или создают языки структуры данных (DSL) для создания классов в Lua, sol вместо этого предлагает возможность генерировать легкие привязки, которые снижают производительность. Вы можете увидеть небольшой стартовый пример здесь . Они используют метатаблицы и пользовательские данные в Lua для их реализации. Типы пользователей также могут быть расширены во время выполнения .
- Существуют более сложные варианты использования для создания и использования пользовательского типа, которые основаны на том, как использовать функцию .set () и ее первоначальную конструкцию (см. Ниже).
- перечисления
- перечисление meta_function для имен
- enum class meta_function {
- construct,
- index,
- new_index,
- mode,
- call,
- call_function = call,
- metatable,
- to_string,
- length,
- unary_minus,
- addition,
- subtraction,
- multiplication,
- division,
- modulus,
- power_of,
- involution = power_of,
- concatenation,
- equal_to,
- less_than,
- less_than_or_equal_to,
- garbage_collect,
- floor_division,
- bitwise_left_shift,
- bitwise_right_shift,
- bitwise_not,
- bitwise_and,
- bitwise_or,
- bitwise_xor,
- pairs,
- ipairs,
- next,
- type,
- type_info,
- call_construct,
- storage,
- gc_names,
- static_index,
- static_new_index,
- };
- typedef meta_function meta_method;
- Используйте это перечисление, чтобы указать имена более удобным способом, чем запоминание специальных имен мета-методов lua для каждого из них. Каждый связывается с определенной операцией, обозначенной описательным именем перечисления. Вы можете прочитать больше о метаметодах в руководстве Lua и узнать о том, как они работают и, как предполагается, будут реализованы там. Каждое из имен здесь (за исключением тех, которые используются в качестве ярлыков для других имен, таких как, meta_function::call_functionно meta_function::involutionне включая construct, которые просто отображаются на имя new), напрямую связаны с именем Lua для операции. meta_function::pairsдоступно только в Lua 5.2 и выше (не включает LuaJIT или Lua 5.1) и meta_function::ipairsдоступно только в Lua 5.2 (без учета флагов совместимости).
- Некоторые из них также sol2 конкретных, например meta_function::type_info, meta_function::call_construct, meta_function::static_indexи meta_function::static_new_indexявляются sol2 конкретными и полезными для пользователей. Записи meta_function::storageи meta_function::gc_namesявляются sol2-внутренними, но все еще в перечислении; пожалуйста , не используйте их.
- meta_function::indexи meta_function::new_indexприменяется строго к тому, когда объект в Lua вызывается с ключом, которого он еще не знает (например, он не был связан программистом C ++ с помощью .set(...)или .new_usertype<...>(...);. meta_function::static_indexи функции meta_function :: static_new_index` вызываются, когда ключ не найден и Пользователь вызывает новую функцию из названной метатаблицы.
- структуры
- automagic_enrollments для специальных членов, определенных в классе
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- struct automagic_enrollments {
- bool default_constructor = true;
- bool destructor = true;
- bool pairs_operator = true;
- bool to_string_operator = true;
- bool call_operator = true;
- bool less_than_operator = true;
- bool less_than_or_equal_to_operator = true;
- bool length_operator = true;
- bool equal_to_operator = true;
- };
- Эта структура используется new_usertypeдля специального назначения определенных специальных функций-членов, связанных с Lua, независимо от того, способна ли она на них или нет.
- new_usertype / set
- sol::usertype<T>это специализированная версия . требует, чтобы тип пользователя был определенным метатабельным для определенного класса. Оба они являются производными типами sol :: reference <reference> , то есть они принимают в . Например…sol::metatable``s, which are a specialized version of ``sol::tablesol::metatable``s attempt to treat the table like either a Lua or a sol2 metatable. ``sol::usertype<T>lua_State*
- new_usertype / set options
- Если типом является default_constructible , sol сгенерирует "new"член для типа пользователя для вас. В противном случае используйте для предотвращения конструктора или передайте при вызове . В противном случае ниже приведены специальные способы обработки конструкции пользовательского типа:my_table.new_usertype<MyType>("name", sol::no_constructor);sol::automagic_enrollments enrollments; /* modify members here */;.new_usertype<MyType>("name", enrollments);
- "{name}", constructors<T(), T(arg-1-0), T(arg-2-0, arg-2-1), ...>
- Укажите конструкторы, которые будут связаны в nameсписке конструкторов, указав их сигнатуру функции сclass_type(arg0, arg1, ... argN)
- Если вы передаете constructors<...>аргумент первый при построении UserType, то он автоматически будет дан "{name}"из"new"
- "{name}", constructors<Type-List-0, Type-List-1, ...>
- Этот синтаксис длиннее и предоставляется для обратной совместимости: приведенный выше синтаксис аргумента короче и чище
- Type-List-Nдолжен быть sol::types<Args...>, где Args...список типов, которые принимает конструктор. Поддерживает перегрузку по умолчанию
- Если вы передаете constructors<...>аргумент первый при построении UserType, то он автоматически будет дан "{name}"из"new"
- "{name}", sol::initializers( func1, func2, ... )
- Используется для обработки функций инициализатора, которые должны инициализировать саму память (но на самом деле не выделяют память, поскольку это поступает как блок пользовательских данных из Lua)
- Предоставляется одна или несколько функций, предоставляет перегруженную функцию Lua для создания указанного типа.
- Функция должна иметь сигнатуру аргумента или , где указатель или ссылка будут указывать на место выделенной памяти, которое неинициализировано . Обратите внимание, что Lua управляет памятью, поэтому выполнение а и установка его на или является плохой идеей: вместо этого используйте для вызова конструктора или работайте с памятью точно так, как считаете нужнымfunc( T*, Arguments... )func( T&, Arguments... )TnewT*T&placement new
- {anything}, sol::factories( func1, func2, ... )
- Используется для указания того, что функция завода (например, то , что производит , , или подобное) будет создавать тип объектаunique_ptr<T, ...>shared_ptr<T>T
- Предоставляется одна или несколько функций, предоставляет перегруженную функцию для вызова
- Функции могут принимать любую форму и возвращать что угодно, поскольку они просто считаются какой-то простой функцией и не требуют нового размещения или иного выполнения. Результаты этой функции будут добавлены в Lua по тем же правилам, что и все остальные.
- Может использоваться для остановки генерации конструктора по .new()умолчанию, поскольку sol::factoriesзапись будет распознаваться как конструктор для пользовательского типа
- Если этого недостаточно, посмотрите следующие 2 записи о том, как конкретно заблокировать конструктор
- {anything}, {some_factory_function}
- По сути, связывает то, что функция должна называть {anything}
- При использовании С sol::no_constructorопцией ниже (например, и после этого ) можно удалить типичные пути конструктора и затем предоставить только определенные фабричные функции. Обратите внимание, что эта комбинация похожа на использование метода, упомянутого ранее в этом списке. Чтобы контролировать деструктор, смотрите далее ниже"new", sol::no_constructor"create", &my_creation_funcsol::factories
- sol::call_constructor, {valid constructor / initializer / factory}
- Цель этого состоит в том, чтобы включить синтаксис и вызвать этот конструктор; у него нет другой целиlocal v = my_class( 24 )
- Это совместимо с luabind, kaguya и другими синтаксисами библиотеки Lua и выглядит аналогично синтаксису C ++, но общее согласие в программировании на Lua и других местах заключается в использовании функции с именем new
- Обратите внимание, что с sol::call_constructorключом должен быть указан тип конструкции выше. Свободная функция без нее передаст метатаблицу, описывающую этот объект как первый аргумент без этого различия, что может вызвать странные ошибки во время выполнения.
- {anything}, sol::no_constructor
- В частности, говорит Sol не создавать, .new()если один не указан, и тип является конструируемым по умолчанию
- Когда ключ {anything}вызывается на столе, это приведет к ошибке. Ошибка может быть в том, что тип неконструктивен.
- Используйте это плюс некоторые из вышеперечисленных, чтобы разрешить функцию фабрики для вашего типа функции, но запретить другие типы идиом конструктора в Lua
- {anything}, sol::no_constructor
- В частности, говорит Sol не создавать, .new()если один не указан, и тип является конструируемым по умолчанию
- Когда ключ {anything}вызывается на столе, это приведет к ошибке. Ошибка может быть в том, что тип неконструктивен.
- Используйте это плюс некоторые из вышеперечисленных, чтобы разрешить функцию фабрики для вашего типа функции, но запретить другие типы идиом конструктора в Lua
- параметры деструктора типа пользователя
- Если вы вообще ничего не указали и тип является разрушаемым , то деструктор будет привязан к метаметоду сбора мусора. В противном случае ниже приведены специальные способы обработки уничтожения пользовательского типа:
- "__gc", sol::destructor( func ) или же sol::meta_function::garbage_collect, sol::destructor( func )
- Создает пользовательский деструктор, который принимает аргумент T*или T&и ожидает, что он будет уничтожен / уничтожен. Обратите внимание, что lua контролирует память и, таким образом, освобождает необходимое пространство ПОСЛЕ того, как возвращается эта функция (например, не вызывайте, deleteпоскольку это попытается освободить память, которую вы не делали new)
- Если вы просто хотите использовать конструктор по умолчанию, вы можете заменить второй аргумент на sol::default_destructor
- Пользовательский тип выдаст ошибку / throw, если вы укажете деструктор, но не отобразите его sol::meta_function::gcили строку, эквивалентную"__gc"
- Заметка
- Вы ДОЛЖНЫ указать sol::destructorвокруг своей функции уничтожения, иначе она будет проигнорирована.
- пользовательские автоматические (автоматические) мета-функции
- Если вы не укажете sol::meta_functionимя (или эквивалентное имя Tметаметода строки) и этот тип поддерживает определенные операции, sol3 сгенерирует следующие операции, если сможет найти хорошую реализацию по умолчанию:
- для to_stringопераций, где , или (в пространстве имен) существует тип C ++ostream& operator<<( ostream&, const T& )obj.to_string()to_string( const T& )
- sol::meta_function::to_stringоператор будет создан
- запись делается либо
- ostringstreamдо базовой строки сериализации в Lua
- непосредственно сериализует возвращаемое значение obj.to_string()илиto_string( const T& )
- порядок предпочтений: затем функция-член , затем ADL-поиск на основеostream& operator<<obj.to_string()to_string( const T& )
- если вам нужно отключить это поведение для типа (например, чтобы избежать ошибок компилятора при конфликтах ADL), специализируйте sol::is_to_stringable<T>для своего типа false_type, например:
- namespace sol {
- template <>
- struct is_to_stringable<my_type> : false_type {};
- }
- для операций вызова, где существует тип C ++operator()( parameters ... )
- sol::meta_function::callоператор будет создан
- оператор вызова функции в C ++ не должен быть перегружен, иначе sol не сможет связать его автоматически
- оператор вызова функции в C ++ не должен быть шаблонным, иначе sol не сможет связать его автоматически
- если он перегружен или настроен, это ваша ответственность, чтобы связать его правильно
- для автоматической итерации, где begin()и end()существуют на типе C ++
- sol::meta_function::pairsоператор генерируется для вас
- Позволяет вам повторять использование в Luafor k, v in pairs( obj ) do ... end
- Lua 5.2 и только лучше: LuaJIT не позволяет этого, Lua 5.1 НЕ позволяет этого
- для случаев, когда .size()существует тип C ++
- оператор длины оператора Lua ( #my_obj) создан для вас
- для операций сравнения, где и существуют на типе C ++operator <operator <=
- Эти два sol::meta_function::less_than(_or_equal_to)созданы для вас
- >и >=операторы генерируются в Lua на основе <и <=операторов
- за operator==
- Оператор равенства всегда будет генерироваться, делая сравнение указателей, если operator==для двух типов значений не поддерживается, или делая сравнение ссылок и сравнение значений, если operator==поддерживается
- гетерогенные операторы не могут поддерживаться на равенство, так как Lua специально проверяет, используют ли они одну и ту же функцию для сравнения: если нет, то метод равенства не вызывается; Один из способов обойти это - написать один , вытащить аргументы 1 и 2 из стека для вашего типа, проверить все типы и затем вызвать себя после получения типов из Lua (возможно, используя sol :: stack :: get и sol :: stack :: check_get )int super_equality_function(lua_State* L) { ... }operator==
- пользовательские стандартные функции
- В противном случае, следующее используется для указания функций, для которых выполняется привязка к конкретному типу пользователя T.
- "{name}", &free_function
- Привязывает свободную функцию / функцию статического класса / объект функции (лямбда) к "{name}". Если первым аргументом является T*или T&, то он будет связывать его как функцию-член. Если это не так, он будет связан как «статическая» функция в таблице lua.
- "{name}", &type::function_name или же "{name}", &type::member_variable
- Связывает типичную функцию-член или переменную с "{name}". В случае переменной-члена или функции-члена typeдолжно быть Tили основаниеT
- "{name}", sol::readonly( &type::member_variable )
- Связывает типичную переменную с "{name}". Аналогично приведенному выше, но переменная будет доступна только для чтения, то есть будет сгенерирована ошибка, если что-либо попытается записать в эту переменную
- "{name}", sol::as_function( &type::member_variable )
- Связывает типичную переменную с, "{name}" но заставляет синтаксис вызываться как функция . Это производит геттер и сеттер, доступные obj:name()для получения и obj:name(value)установки.
- "{name}", sol::property( getter_func, setter_func )
- Связывает типичную переменную с "{name}", но получает и устанавливает с использованием указанных функций set и getter. Не то, чтобы, если вы не передадите функцию установки, переменная будет доступна только для чтения. Также не то, что если вы не передадите функцию получения, она будет только для записи
- "{name}", sol::var( some_value ) или же "{name}", sol::var( ref( some_value ) )
- Связывает типичную переменную "{name}", необязательно по ссылке (например, ссылается на ту же память в C ++). Это полезно для глобальных переменных / статических переменных класса и т.п.
- "{name}", sol::overload( Func1, Func2, ... )
- Создает функцию-член oveloaded, которая различает количество аргументов и типов
- Выгрузка нескольких функций с одним и тем же именем не создает перегрузки : вы должны использовать этот синтаксис, чтобы он работал
- sol::base_classes, sol::bases<Bases...>
- Сообщает типу пользователя, каковы его базовые классы. Это необходимо для правильной работы преобразований из базы в базу. См наследство
- регистрацию
- Вы можете разъединить и убить UserType и связанную с ним функциональность, позвонив .unregister()на sol::usertype<T>или sol::metatableуказал на соответствующую sol3 метатаблицы. Это полностью разъединит и очистит внутренние структуры поиска sol3 и ключевую информацию.
- функции времени выполнения
- Вы можете добавлять функции во время выполнения ко всему классу (не к отдельным объектам). Установите имя под метатабельным именем, которое вы привязали new_usertypeк объекту. Например:
- runtime_extension.cpp
- #define SOL_ALL_SAFETIES_ON 1
- #include <sol/sol.hpp>
- #include <iostream>
- struct object {
- int value = 0;
- };
- int main (int, char*[]) {
- cout << "==== runtime_extension =====" << endl;
- sol::state lua;
- lua.open_libraries(sol::lib::base);
- lua.new_usertype<object>( "object" );
- // runtime additions: through the sol API
- lua["object"]["func"] = [](object& o) { return o.value; };
- // runtime additions: through a lua script
- lua.script("function object:print () print(self:func()) end");
- // see it work
- lua.script("local obj = object.new() \n obj:print()");
- return 0;
- }
- Заметка
- Вы не можете добавлять функции к отдельному объекту. Вы можете добавлять функции только ко всему классу / типу пользователя.
- перегрузка
- Функции, установленные для пользовательского типа, поддерживают перегрузку. Смотрите здесь для примера.
- наследство
- Sol может корректировать указатели от производных классов до базовых классов во время выполнения, но у него есть некоторые предостережения, основанные на том, что вы компилируете:
- Если у вашего класса нет сложного виртуального наследования или множественного наследования, вы можете попытаться ускользнуть с повышением производительности, не указав какие-либо базовые классы и не выполнив какие-либо проверки приведения. (Что означает «complex ™»? Спросите документацию вашего компилятора, если вы в этом разбираетесь.)
- Для остальных из нас безопасных людей: Вы должны указать sol::base_classesтег с sol::bases<Types...>()аргументом, где Types...находятся все базовые классы одного типа T, из которого вы делаете пользовательский тип.
- Зарегистрируйте базовые классы явно.
- Заметка
- Всегда указывайте свои базы, если вы планируете извлекать базовый класс напрямую, используя абстракцию sol, а не сами себя.
- наследство.cpp
- #define SOL_ALL_SAFETIES_ON 1
- #include <sol/sol.hpp>
- struct A {
- int a = 10;
- virtual int call() { return 0; }
- virtual ~A(){}
- };
- struct B : A {
- int b = 11;
- virtual int call() override { return 20; }
- };
- int main (int, char*[]) {
- sol::state lua;
- lua.new_usertype<B>( "A",
- "call", &A::call
- );
- lua.new_usertype<B>( "B",
- "call", &B::call,
- sol::base_classes, sol::bases<A>()
- );
- return 0;
- }
- Заметка
- Вы должны перечислить ВСЕ базовые классы, включая (если они были) базовые классы A, а также базовые классы этих базовых классов и т. Д., Если вы хотите, чтобы sol / Lua обрабатывал их автоматически.
- Заметка
- Sol не поддерживает преобразование из базового класса в производный класс во время выполнения.
- Предупреждение
- Укажите все переменные-члены базового класса и функции-члены, чтобы избежать предостережений текущей реализации относительно автоматического поиска базового члена. В настоящее время sol пытается связать методы и переменные базового класса с их производными классами с помощью недокументированной неподдерживаемой функции, если вы укажете это sol::bases<...>. К сожалению, это может происходить за счет производительности, в зависимости от того, «далеко» ли база от производного класса в списке поиска баз. Если вы не хотите страдать от снижения производительности, пока мы сглаживаем изломы в реализации (и хотим, чтобы он оставался постоянным всегда), укажите все базовые методы для производного класса в списке методов, который вы пишете. В будущем мы надеемся, что с размышлениями нам не придется беспокоиться об этом.
- автоматические типы пользователей
- Типы пользователей автоматически регистрируют специальные функции, независимо от того, связаны они или нет с помощью new_usertype . Вы можете отключить это, специализируя sol::is_automagical<T>черту шаблона:
- struct my_strange_nonconfirming_type { /* ... */ };
- namespace sol {
- template <>
- struct is_automagical<my_strange_nonconforming_type> : false_type {};
- }
- наследование + перегрузка
- Несмотря на то, что перегрузка поддерживается независимо от предостережений наследования или нет, текущая версия sol имеет стиль перегрузки по первому совпадению при первом вызове, когда дело доходит до наследования. Сначала поместите функции с наиболее производными аргументами, чтобы получить ожидаемое совпадение или приведение внутри промежуточной функции C ++, и вызовите нужную функцию.
- скорость компиляции
- Заметка
- MSVC и clang / gcc могут понадобиться дополнительные флаги компилятора для обработки компиляции с широким использованием пользовательских типов. Смотрите: документацию по ошибкам для более подробной информации.
- Примечание о производительности
- Заметка
- Обратите внимание, что производительность для вызовов функций-членов снижается из-за фиксированных накладных расходов, если вы также связываете переменные и функции-члены. Это чисто ограничение реализации Lua, и, к сожалению, с этим ничего нельзя поделать. Однако если вы связываете только функции, а не переменные, sol автоматически оптимизирует время выполнения Lua и даст вам максимально возможную производительность. Пожалуйста, учтите простоту использования и обслуживания кода, прежде чем превращать все в функции.
- память типа пользователя
- расположение в памяти пользовательских типов
- Заметка
- Sol не берет на себя владение необработанными указателями, возвращенными из функций или установленными через setфункции. Верните значение, a unique_ptr, a shared_ptrнекоторого вида или подключите уникальные черты usertypes, чтобы работать для определенной структуры дескриптора, которую вы используете (AKA, для boost::shared_ptr).
- Пользовательские данные, сгенерированные sol, имеют определенную компоновку, в зависимости от того, как sol распознает переданные в него пользовательские данные. Все упомянутые метатируемые имена генерируются из имени самого класса. Обратите внимание, что мы используем 1 метатабель для 3 перечисленных ниже стилей, плюс 1 дополнительный метатабль, который используется для фактической таблицы, которую вы связываете с именем при вызове table::new/set_(simple_)usertype.
- В общем, мы всегда вставляем T*первые sizeof(T*)байты, поэтому любая инфраструктура, которая извлекает эти первые байты, ожидая, что указатель будет работать. Остальные данные имеют различные выравнивания и содержимое в зависимости от того, для чего они используются и как они используются.
- Предупреждение
- Расположение памяти, описанное ниже, не учитывает выравнивание. sol3 теперь учитывает выравнивание и выравнивает память, что важно для некорректно работающих распределителей и типов, которые плохо выровнены по размеру указателя в их системе. Если вам необходимо получить соответствующие выравнивания для usertypes , хранящегося в USERDATA указателей, пожалуйста , используйте функцию подробно с именем sol::detail::align_usertype_pointer, sol::detail::align_usertypeи sol::detail::align_usertype_unique. Это сместит void*указатель на соответствующую величину, чтобы достичь определенного раздела в памяти. Практически во всех других случаях использования используйте , а затем установите указатель в нужное место.void* memory = lua_touserdata(L, index);memory = sol::detail::align_usertype_pointer( memory );
- Предупреждение
- Диаграммы и пояснения ниже гарантированно будут работать 100% времени, если вы определите SOL_NO_MEMORY_ALIGNMENT . Имейте в виду, что это может привести к смещению операций чтения / записи, что может привести к сбою некоторых старых процессоров и вызвать предупреждения статического анализатора / инструментария, например Clang's Address Sanitizer (ASan).
- Чтобы получить T
- Если вы хотите получить T*указатель на данные, управляемые пользовательскими данными sol3, и не используете для этого абстракции sol3 (например, возиться с простым API Lua C), просто используйте lua_touserdataдля получения void*указателя. Затем выполните a . Каждый тип, помещенный в C ++, который классифицируется как пользовательские данные (например, все определяемые пользователем объекты, которые не охватываются основными типами абстракции стека), может быть получен в этом формате, независимо от того, являются ли они значениями, указателями или . Причины, по которым это работает, приведены ниже.T* object_pointer = *static_cast<T**>(the_void_pointer);unique_ptr
- Для T
- Они классифицируются с метатабельным именем, обычно полученным из самого имени класса.
- Расположение данных для ссылок выглядит следующим образом:
- | T* | T |
- ^-sizeof(T*) bytes-^-sizeof(T) bytes, actual data-^
- Lua очистит саму память, но не знает ни о какой семантике уничтожения, которую может навязать T, поэтому, когда мы уничтожаем эти данные, мы просто вызываем деструктор, чтобы уничтожить объект, и оставляем изменения памяти в lua для обработки после «__gc» метод выхода.
- Для T*
- Они классифицируются как отдельная T*метатабельная, по сути, «справочная» таблица. Вещи, передаваемые sol в качестве указателя или a reference<T>, считаются ссылками и, следовательно, __gcпо умолчанию не имеют метода (сборки мусора). Все необработанные указатели не принадлежат указателям в C ++. Если вы работаете с C API, обеспечьте обертку вокруг указателей, которые должны владеть данными, и используйте идиомы конструктора / деструктора (например, с внутренним unique_ptr) для поддержания чистоты.
- Структура данных для данных, которые относятся только к следующему:
- | T* |
- ^-sizeof(T*) bytes-^
- Вот и все. Не нужно называть семантику уничтожения.
- Для и unique_ptr<T, D>shared_ptr<T>
- Они классифицируются как «уникальные типы пользователей» , и для них также есть специальные метатаблицы. Специальный метатабель генерируется либо при добавлении пользовательского типа в Lua с помощью set_usertype, либо при первом нажатии одного из этих специальных типов. В дополнение к данным, функция удаления, которая понимает следующий макет, внедряется в макет пользовательских данных.
- Расположение данных для этих типов типов выглядит следующим образом:
- | T* | void(*)(void*) function_pointer | T |
- ^-sizeof(T*) bytes-^-sizeof(void(*)(void*)) bytes, deleter-^- sizeof(T) bytes, actal data -^
- Обратите внимание, что мы помещаем специальную функцию удаления перед фактическими данными. Это связано с тем, что пользовательское средство удаления должно знать, где находится смещение данных и где находится специальное средство удаления. Другими словами, поля фиксированного размера располагаются перед любыми данными переменного размера (T может быть известен во время компиляции, но при сериализации в Lua таким образом он становится объектом времени выполнения). Sol просто необходимо знать, T*как работают пользовательские данные (и метаданные пользователя), все остальное - для сохранения семантики построения / разрушения.
- Это наконец происходит. После переговоров C ++ Now и CppCon 2018, а также большого количества работы и обратной связи (спасибо всем, кто отправлял электронные письма, а также всем участникам Patron и Discord, которые участвовали), sol3 теперь полностью завершен.
- Что это значит?
- sol3 в основном совместим с предыдущим sol2. Это означает, что я больше не буду работать над sol2 (sol2 уже полностью готов к работе уже почти год и какое-то время получал в основном исправления ошибок). Все критические изменения идут в сторону sol3. Самые большие изменения:
- Для пользователей существует новое блестящее пространство точек настройки с явным обещанием, что как автор библиотеки я никогда не напишу ничего, что могло бы потенциально конкурировать с вашими точками настройки. Пользователям sol2 нужно будет перенести свои точки настройки на этот новый слой (и это нормально: исправление буквально удаляет кучу строк кода и больше не требует их записи в пространстве имен sol2).
- В настоящее время наблюдается огромный прирост производительности для всех людей, которые полагались на ранее рискованное наследование пользовательских типов. Это так быстро, как я мог сделать это без отражения, специфических для платформы хаков и сборочных уловок. После того, как Reflection завершит прохождение голосования по PD TS и будет одобрен для C ++ 20/23, и компиляторы его развернут, я могу реализовать это с новыми механизмами sol3 без нарушений обратной совместимости и получить последние падения производительности, которые позволят пользователям достичь уровней производительности наследования SWIG без какой-либо дополнительной разметки.
- Пользовательские типы контейнеров имеют лучшую обработку и значения по умолчанию в некоторых избранных случаях, а также немного лучшую производительность. Мы также исправили более старый класс ошибок, которые, тем не менее, ломали изменения, которые я не мог коснуться в sol2: теперь все чисто.
- Интеграция с CMake теперь является Modern Top Tier ™. Теперь вы можете получить sol2, сингл или сгенерированный сингл. Вы получаете цель sol2, делая sol2::sol2, sol2::sol2_singleпосле add_subdirectory(sol/single)или sol2::sol2_single_generatedпосле add_subdirectory(sol/single). Мы также значительно улучшили гигиену заголовков и аналогичные с новыми тестами компиляции и добавили целый новый набор тестов во время выполнения. Значение по умолчанию для включения sol2 также теперь #include <sol/sol.hpp>, следуя boost::стилю включения, чтобы помочь предотвратить столкновения.
- C ++ 17 включен. Определения, включающие возможности C ++ 17, все еще существуют из-за чистой лени: теперь это библиотека C ++ 17.
- Производительность хороша ™.
- Поскольку это значительное увеличение версии, часть кода, написанного людьми ранее, сломается. Вот хорошая новость: большая часть вашего кода не сломается, и ничего более фундаментального или ядра в sol2 на самом деле не сломалось. Просто вещи, которые были уже ужасны.
- Пользовательские удаления и очистка
- Это были первоочередные цели, которые нужно было исправить, но в конечном итоге это было легко, так как в основном это было связано с моим [delete]ключом и увлечением тем, как вещи исчезают.
- new_simple_usertypeи simple_usertypeобъекты больше не существуют. Они были хакерами для увеличения пропускной способности времени компиляции, в то же время заставляя пользователя оплачивать затраты времени выполнения в некоторых случаях Больше не нужно, и код работает так быстро, как может быть всегда: это было удалено .
- sol::usertype<T>«Я беру миллион вещей и создаю несколько слоев шаблонного дерьма и ставлю компилятор на колени с помощью конструктора AST OooOooh! объемом 12 ГБ!». sol::usertypeтеперь это фактический тип мета-таблицы, который ведет себя как обычный sol::table, где вы можете устанавливать / получать вещи. Вонкий конструктор с set_usertypeбольше не существует. Как и должно быть: я сожалею, что когда-либо писал это.
- Весь неправильный код приводит к разрыву сборки, так как я предпочитаю, чтобы я ломал вашу сборку, а не позволял вам обновляться и затем молча делал неправильные вещи. Крутая вещь в отношении поломок здесь состоит в том, что это не просто разрывы без передышки: у всего, что сломано, теперь есть лучшие способы сделать это, который компилируется быстрее (хотя я не обещаю, что у вас не будет кучи компилятора 12 ГБ, особенно если ты небрежен).
- Одна вещь, которую я не сломал, была lua.new_usertype<blah>( ctor, "key", value, "key2", sol::property(...), /* LITERALLY EVERYTHING */ );. Это будет продолжать работать. И это также поставит ваш компилятор на колени снова, что не очень приятно с вашей стороны. Пожалуйста, рассмотрите возможность использования
- struct blah {
- int a;
- bool b;
- char c;
- double d;
- /* ... */
- };
- constexr int some_static_class_value = 58;
- int main () {
- sol::state lua;
- sol::usertype<blah> metatable = lua.new_usertype<blah>(
- /* just some constructor and destructor stuff maybe*/
- );
- metatable["a"] = blah::a;
- metatable["b"] = sol::property(
- [](blah& obj) { return obj.b; },
- [](blah& obj, bool val) { obj.b = val; if (obj.b) { ++obj.a; } }
- );
- metatable["super_c"] = sol::var(some_static_class_value);
- }
- Скорость выполнения получающейся привязки остается той же, благодаря некоторым серьезным оптимизациям, которые я выполняю, чтобы убедиться, что мы сериализуем то, что максимально близко к оптимизированной карте, unique_ptr<char[]>которую callable or variableя мог бы создать. Пользовательские типы теперь также могут обрабатывать не строковые ключи, что позволяет кому-то индексировать в них пользовательские данные (указатели и тому подобное), целые числа и другие точки данных без необходимости переопределять методы __indexили __newindex.
- Количество улучшений действительно ошеломляет, правда ...! Я мог бы написать целую статью о детерминированных во время выполнения качествах, которые мы сейчас внедрили в sol2, благодаря таким вещам, как наличие объекта, unordered_mapкоторый больше не нужно выделять для поиска ключей без необходимости прибегать к boost::своим собственным или взламывать их unordered_map. Это означает, что поддержка людей, которые использовали sol2 для целей обработки в реальном времени, остается на месте, гарантируя, что у них есть путь времени исполнения с 0 выделениями, который они могут использовать для пользовательских типов и тому подобное.
- Новые настройки: Ваш собственный задний двор (и мой газон)
- Точки настройки также были плохими в sol2. Иногда пользователи хотели изменить, как система обрабатывает фундаментальные типы: они не могли без переписывания моего кода. Я хорошо говорю о том, почему точки специализации структуры как фундаментальная абстракция плохи для такой библиотеки, как sol2, в моем видео CppCon 2018 и о том, как мне приходится бороться, чтобы сохранить чистое разделение.
- sol::stack::pusher, sol::stack::checkerИ т.д. - борьба со мной , чтобы убедиться , что я не наступайте на ваши настройки точек специализации действительно отстой.
- Специализация этих старых имен сейчас не компилируется. Старые имена больше не существуют, чтобы убедиться, что это была серьезная ошибка компилятора, и что внесенные мной изменения не изменили время выполнения вашей программы и не дадут вам день отладочного приключения.
- Производительность не изменилась, но небрежный код наверняка изменился. Этот кусок кода, заполненного шаблонами, теперь можно заменить:
- struct two_things {
- int a;
- bool b;
- };
- namespace sol {
- // First, the expected size
- // Specialization of a struct
- template <>
- struct lua_size<two_things> : integral_constant<int, 2> {};
- // Then, specialize the type
- // this makes sure Sol can return it properly
- template <>
- struct lua_type_of<two_things> : integral_constant<sol::type, sol::type::poly> {};
- // Now, specialize various stack structures
- namespace stack {
- template <>
- struct checker<two_things> {
- template <typename Handler>
- static bool check(lua_State* L, int index, Handler&& handler, record& tracking) {
- int absolute_index = lua_absindex(L, index);
- bool success = stack::check<int>(L, absolute_index, handler)
- && stack::check<bool>(L, absolute_index + 1, handler);
- tracking.use(2);
- return success;
- }
- };
- template <>
- struct getter<two_things> {
- static two_things get(lua_State* L, int index, record& tracking) {
- int absolute_index = lua_absindex(L, index);
- int a = stack::get<int>(L, absolute_index);
- bool b = stack::get<bool>(L, absolute_index + 1);
- tracking.use(2);
- return two_things{ a, b };
- }
- };
- template <>
- struct pusher<two_things> {
- static int push(lua_State* L, const two_things& things) {
- int amount = stack::push(L, things.a);
- amount += stack::push(L, things.b);
- return amount;
- }
- };
- }
- }
- С этим намного более коротким и намного более замечательным фрагментом:
- struct two_things {
- int a;
- bool b;
- };
- template <typename Handler>
- bool sol_lua_check(sol::types<two_things>, lua_State* L, int index,
- Handler&& handler, sol::stack::record& tracking) {
- int absolute_index = lua_absindex(L, index);
- bool success = sol::stack::check<int>(L, absolute_index, handler)
- && sol::stack::check<bool>(L, absolute_index + 1, handler);
- tracking.use(2);
- return success;
- }
- two_things sol_lua_get(sol::types<two_things>, lua_State* L, int index,
- sol::stack::record& tracking) {
- int absolute_index = lua_absindex(L, index);
- int a = sol::stack::get<int>(L, absolute_index);
- bool b = sol::stack::get<bool>(L, absolute_index + 1);
- tracking.use(2);
- return two_things{ a, b };
- }
- int sol_lua_push(sol::types<two_things>, lua_State* L, const two_things& things) {
- int amount = sol::stack::push(L, things.a);
- amount += sol::stack::push(L, things.b);
- return amount;
- }
- С гарантией того, что любые будущие SFINAE и обработчики, которые я напишу, всегда будут «backend» и «default»: если вы напишите один в новой парадигме, это всегда переопределит мое поведение. Вы даже можете переопределить мое поведение для фундаментальных типов, таких как int64_t. У пользователей были проблемы с тем, как я работаю с целыми числами и другими вещами из-за того, что все числа Lua постоянно удваиваются: теперь им не нужно прибегать к значениям по умолчанию библиотеки или заставлять меня писать больше конфигурационных макросов. Они могут просто изменить его для своего приложения, что довольно здорово. Он также хранит всех вас, юных похитителей, у вас на заднем дворе, и с моего газона, да!
- Обратите внимание, что сокращение кода буквально отбирает пространства имен и кучу других грязных специализаций шаблонов. На самом деле, это просто написание ваших собственных функций сейчас. И эти функции не должны находиться в шаблонах: вы можете поместить объявление в заголовок, экспортировать их и многое другое! ( HandlerШаблон выше, потому что я ленивый.) В sol3 также есть полноценное обязательство и поддержка <sol/forward.hpp>заголовка, который заранее объявляет все необходимое, чтобы сработали вышеуказанные объявления (больше скорости компиляции, nyeeoooom 🏃💨!).
- Хорошо, но Feature Complete не означает, что выпущен ...?
- Ах, ты меня понял. Это функция завершена, но она не готова к выпуску. Код в порядке, но:
- Документация должна быть обновлена для того, как делается новый пользовательский тип. Это включает в себя учебные пособия и справочную документацию по API. Все примеры построены как часть тестов, которые проходят! Это означает, что примеры пока могут служить достаточно приличной документацией. Помните, что вы можете заглянуть в Sol2 Discord: мы очень полезны и помогаем людям не только начать, но и решать сложные проблемы! Вы также можете @thephantomderp в Твиттере, если вы придурок.
- Справочная документация по API содержит код для вставки или написания от руки, который правильно указывает семантику sol2. Вероятно, было бы лучше, если бы я мог просто ссылаться на фактический блок конструкторов в коде, а не создавать (заполненную опечаткой) разметку.
- Я хочу сделать блестящий логотип sol3. ... Но я отстой в рисовании. Итак, это вещь.
- Инфраструктура тестирования отстой. Потому что мои навыки CMake отстой.
- Мне нужно пометить vcpkg, Conan и, возможно, даже build2, чтобы убедиться, что у них всех есть пакеты, готовые к переходу на sol3 в какой-то момент в отдаленном будущем (совершенно необязательно, мне очень надоели менеджеры пакетов, системы мета-сборки и строить системы прямо сейчас).
- Это действительно так. Есть несколько небольших мест, где мне нужно изгнать демона-компилятора tupleи его множество грубых конструкторов, в основном, проверяя, находится ли sizeof...(Args) == 2в if constexprблоке, и немедленно отправляя его обработчику, не упаковывая вещи в forward_as_tupleпервый. Я также могу сэкономить массу откатов и разложений компилятора, создав частичные специализации с 1, 2 и 3 ключами для моих типов прокси-таблиц, поэтому мне больше не нужно рвать кортежи кортежей в этих вещах для хранения ленивых ключей: еще не видел 4 прокси-запросов. (И если они у вас есть без сохранения посредников, вы заслуживаете демонов-компиляторов, которые внедряются в ваш код и весь стыд, который с ним связан!).
- Существует также извивающийся левиафан, который я имею в виду для SFINAE-и-tag-dispatch-at-the-time, для оптимизации функций в sol2, который, вероятно, можно свести к некоторым, if constexprтак что .set_functionвызов не так уж и во время компиляции. дорогой тоже.
- if constexpr? Но Derpstorm, как насчет моего {старого компилятора здесь} ?!
- Я давным-давно высказал предположение, что sol3 будет использовать технологию C ++ 17 (например, почти 2 года назад?), В основном, чтобы сэкономить мне скорость компиляции и здравый смысл. Поскольку sol3 - это библиотека с открытым исходным кодом, я делаю это для удовольствия, а не для выгоды. Что переводится как:
- Requests Потяните запросы добро пожаловать! 🎉
- Я должен закончить школу, и я буду интернировать в течение лета. Sol2 уже получил широкое распространение в отрасли, сэкономил разработчикам миллионы на бесполезном оттоке Lua API и принес множество крупных стеков наличности в магазины по всему миру. Если вашей компании нужна специальная версия типа «снежинка» для C ++ 11 или - «Господь на небесах» - C ++ 03, пришлите мне электронное письмо, и мы сможем разработать контракт на перенос sol3 в вашу старую стабильную инфраструктуру. (Даже если это дряхлый, я не буду судить: я только что провел последнюю неделю, рассматривая различные варианты Ubuntu и Debian и CentOS и их компиляторов, а также мусор Travis и Appveyor. Я ненавижу мир, как есть, но я полностью понимаю, почему Вы придерживаетесь своей 4-летней машины без поддержки! ... Немного. Хорошо, я буду честен. Есть некоторое суждение. Но только немного, я клянусь!)
- Это все на данный момент. Я буду работать над документацией sol3, некоторыми усовершенствованиями инфраструктуры тестирования, так что она будет полезна для всех сумасшедших инструментов, которые я хочу сделать, и готовлюсь побеспокоить кресло SG16 - Тома Хонерманна - и легендарного создателя libogonek - Robot - с много вопросов по интерфейсу Unicode в ближайшее время! А потом это школа, и все может затихнуть.
- Или ... так ли это? 🏭
- Потоки
- int main(int argc, char* argv[]) {
- state lua;
- lua.open_libraries();
- sol::function transferred_into;
- lua["f"] = [&lua, &transferred_into](sol::object t, sol::this_state this_L) {
- cout << "state of main : " << (void*)lua.lua_state() << endl;// адрес в памяти.
- cout << "state of function : " << (void*)this_L.lua_state() << endl;
- // pass original lua_State* (or sol::state/sol::state_view)
- // transfers ownership from the state of "t",
- // to the "lua" sol::state
- transferred_into = sol::function(lua, t);
- };
- lua.script(R"(
- i = 0
- function test()
- co = coroutine.create(
- function()
- local g = function() i = i + 1 end
- f(g)
- g = nil
- collectgarbage()
- end
- )
- coroutine.resume(co)
- co = nil
- collectgarbage()
- end
- )");
- // give it a try
- lua.safe_script("test()");
- // should call 'g' from main thread, increment i by 1
- transferred_into();
- // check
- int i = lua["i"];
- cout << i << endl;
- return 0;
- };
- const char* LUA = R"(
- function loop()
- while counter ~= 30
- do
- coroutine.yield(counter);
- print(counter)
- counter = counter + 1;
- end
- return counter
- end
- )";
- int main(int argc, char* argv[]) {
- state lua;// Lua состояние.
- lua.open_libraries(lib::base, lib::package, lib::coroutine); // открыть доп.библиотеки.
- lua.script(LUA);
- coroutine cr = lua["loop"];
- // мы хотим 10 значений, и мы только хотим запустить, если сопрограмма "cr" действительна
- // Альтернатива: счетчик < 10 & & cr.действительный()
- //// Вызов сопрограммы, выполняет вычисления и затем приостанавливает
- //for (int counter = 0; counter < 10 && cr; ++counter) {
- // int value = cr();
- // cout << value << endl;
- //}
- return 0;
- };
- const char* LUA = R"(
- function loop()
- for i = 1, 10 do
- print(i)
- coroutine.yield()
- end
- end
- )";
- int main(int argc, char* argv[]) {
- state lua;// Lua состояние.
- lua.open_libraries(lib::base, lib::package, lib::coroutine); // открыть доп.библиотеки.
- lua.script(LUA);
- coroutine cr = lua["loop"];
- //мы хотим 10 значений, и мы только хотим запустить, если сопрограмма "cr" действительна
- //Альтернатива: счетчик < 10 & & cr.действительный()
- //Вызов сопрограммы, выполняет вычисления и затем приостанавливает
- for (int counter = 0; counter < 10 && cr; ++counter) {
- cr.call();
- }
- return 0;
- };
- const char* LUA = R"(
- function main(x, y, z)
- -- do something
- coroutine.yield(20)
- -- do something else
- -- do ...
- print(x, y, z)
- end
- function main2(x, y)
- coroutine.yield(10)
- print(x, y)
- end
- start_thread(main, 10, 12, 8)
- start_thread(main2, 1, 2)
- )";
- int main(int argc, char* argv[]) {
- state lua;
- vector<coroutine> threads;
- lua.open_libraries(lib::base, lib::coroutine);
- thread runner_thread = thread::create(lua);
- lua.set_function("start_thread",
- [&runner_thread, &threads](sol::function f, variadic_args va){
- // Вы всегда должны получить текущее состояние.
- state_view runner_thread_state = runner_thread.state();
- // Поместите задачу в наш список задач, чтобы сохранить ее и отслеживать ее.
- size_t threads_index = threads.size();
- threads.emplace_back(runner_thread_state, f);
- // создает объект непосредственно в конце вектора, т.е. без лишнего копирования.
- coroutine& run_thread = threads[threads_index];
- // вызов сопрограммы с аргументами, которые пришли.
- // из главного потока / другой поток.
- // толкатель для 'variadic_args' и других типов почвы перенесет
- // аргументы из вызывающего потока.
- // поток бегунка автоматически для вас.
- // используя lua_xmove` внутренне.
- int wait = run_thread(va);
- cout << "First thread " << wait << endl;// Когда вы вызываете его снова, вам не нужны новые аргументы
- // (они остаются неизменными с первого вызова)
- run_thread();
- cout << "Second thread " << wait << endl;}
- );
- lua.script(LUA);
- cout << endl;
- return 0;
- };
- using namespace sol; const char* LUA = R"(
- function main(x)
- -- do something
- coroutine.yield(20)
- -- do something else
- -- do ...
- print(x)
- end
- function main2(x)
- coroutine.yield(10)
- print(x)
- end
- start_thread(main, 10)
- start_thread(main2, 1)
- )";
- int main(int argc, char* argv[]) {
- state lua;
- vector<coroutine> threads;
- lua.open_libraries(lib::base, lib::coroutine);
- thread runner_thread = thread::create(lua);
- lua.set_function("start_thread", [&runner_thread, &threads](sol::function f, variadic_args va) {
- // Вы всегда должны получить текущее состояние.
- state_view runner_thread_state = runner_thread.state(); // Поместите задачу в наш список задач,
- //чтобы сохранить ее и отслеживать ее.
- size_t threads_index = threads.size();
- threads.emplace_back(runner_thread_state, f);// создает объект непосредственно в конце вектора, т.е. без лишнего копирования.
- coroutine& run_thread = threads[threads_index];
- // вызов сопрограммы с аргументами, которые пришли. из главного потока / другой поток.
- // толкатель для 'variadic_args' и других типов почвы перенесет аргументы из вызывающего потока.
- // поток бегунка автоматически для вас. используя lua_xmove` внутренне.
- int wait = run_thread(va);
- cout << "First thread " << wait << endl;// Когда вы вызываете его снова, вам не нужны новые аргументы
- // (они остаются неизменными с первого вызова)
- run_thread();
- cout << "Second thread " << wait << endl; }
- );
- lua.script(LUA);
- cout << endl;
- return 0;
- };
- const char* LUA = R"(
- function main(x)
- coroutine.yield(20)
- print(x)
- end
- function main2(x)
- coroutine.yield(10)
- print(x)
- end
- start_thread(main, 10)
- start_thread(main2, 1)
- )";
- int main(int argc, char* argv[]) {
- state lua;
- vector<coroutine> threads;
- lua.open_libraries(lib::base, lib::coroutine);
- thread runner_thread = thread::create(lua);
- lua.set_function("start_thread",[&runner_thread, &threads](sol::function f, variadic_args va) {
- // Вы всегда должны получить текущее состояние.
- state_view runner_thread_state = runner_thread.state(); // Поместите задачу в наш список задач,
- //чтобы сохранить ее и отслеживать ее.
- size_t threads_index = threads.size();
- threads.emplace_back(runner_thread_state, f);// создает объект непосредственно в конце вектора, т.е.
- //.без лишнего копирования.
- coroutine& run_thread = threads[threads_index];
- // вызов сопрограммы с аргументами, которые пришли. из главного потока / другой поток.
- // толкатель для 'variadic_args' и других типов почвы перенесет аргументы из вызывающего потока.
- // поток бегунка автоматически для вас. используя lua_xmove` внутренне.
- int wait = run_thread(va);
- //cout << "First thread " << wait << endl;// Когда вы вызываете его снова, вам не нужны новые аргументы
- // (они остаются неизменными с первого вызова)
- run_thread(); //cout << "Second thread " << wait << endl;
- }
- );
- lua.script(LUA);
- cout << endl;
- return 0;
- };
- const char* LUA1 = R"(
- while true do
- print()
- end
- )";
- const char* LUA = R"(
- while true do
- print('Hello world!')
- end
- )";
- int main(int argc, char* argv[]) {
- state lua;// Lua состояние.
- lua.open_libraries(lib::base, lib::package, lib::coroutine); // открыть доп.библиотеки.
- thread run = thread::create(lua.lua_state());
- state_view runstate = run.state();
- runstate.script(LUA1);
- lua.script(LUA);
- return 0;
- };
- struct coro_test {
- string identifier;
- reference obj;
- coro_test(this_state L, string id) : identifier(id), obj(L, lua_nil) {
- }
- void store(table ref) {
- // must be explicit
- obj = reference(obj.lua_state(), ref);
- }
- void copy_store(table ref) {
- // must be explicit
- obj = reference(obj.lua_state(), ref);
- }
- reference get() {
- return obj;
- }
- ~coro_test() {
- }
- };
- struct coro_test_implicit {
- string identifier;
- main_reference obj;
- coro_test_implicit(this_state L, string id) : identifier(id), obj(L, lua_nil) {
- }
- void store(table ref) {
- // main_reference does the state shift implicitly
- obj = move(ref);
- lua_State* Lmain = main_thread(ref.lua_state());
- //REQUIRE(obj.lua_state() == Lmain);
- }
- void copy_store(table ref) {
- // main_reference does the state shift implicitly
- obj = ref;
- lua_State* Lmain = main_thread(ref.lua_state());
- // REQUIRE(obj.lua_state() == Lmain);
- }
- reference get() {
- return obj;
- }
- ~coro_test_implicit() {
- }
- };
- const auto& script = R"(counter = 20
- function loop()
- while counter ~= 30
- do
- coroutine.yield(counter);
- counter = counter + 1;
- end
- return counter
- end
- )";
- int main(int argc, char* argv[]) {
- state lua;
- lua.open_libraries(lib::base, lib::table, lib::coroutine);
- {
- auto code = R"(
- unpack = unpack or table.unpack
- function loop()
- local i = 0
- while true do
- print("pre-yield in loop")
- coroutine.yield(i)
- print("post-yield in loop")
- i = i+1
- end
- end
- loop_th = coroutine.create(loop)
- loop_res = function(...)
- returns = { coroutine.resume(loop_th, ...) }
- return unpack(returns, 2)
- end
- )";
- auto result = lua.safe_script(code, script_pass_on_error);
- // REQUIRE(result.valid());
- }
- // Resume from lua via thread and coroutine
- thread runner_thread = lua["loop_th"];
- state_view runner_thread_state = runner_thread.state();
- auto test_resume = [&runner_thread_state]() {
- coroutine cr = runner_thread_state["loop"];
- stack::push(runner_thread_state, 50);
- stack::push(runner_thread_state, 25);
- int r = cr();
- return r;
- };
- lua.set_function("test_resume", ref(test_resume));
- // Resume via getting a function from the state
- function test_resume_lua = lua["loop_res"];
- // Resume via passing a function object
- auto test_resume_func = [](function f) {
- int r = f();
- return r;
- };
- lua.set_function("test_resume_func", ref(test_resume_func));
- int v0 = test_resume();
- int s0 = runner_thread_state.stack_top();
- int v1 = test_resume();
- int s1 = runner_thread_state.stack_top();
- int v2;
- {
- auto result = lua.safe_script("return test_resume()", script_pass_on_error);
- //REQUIRE(result.valid());
- v2 = result;
- }
- int s2 = runner_thread_state.stack_top();
- int v3;
- {
- auto result = lua.safe_script("return test_resume()", script_pass_on_error);
- //REQUIRE(result.valid());
- v3 = result;
- }
- int s3 = runner_thread_state.stack_top();
- int v4 = test_resume_lua();
- int s4 = runner_thread_state.stack_top();
- int v5;
- {
- auto result = lua.safe_script("return test_resume_func(loop_res)", script_pass_on_error);
- //REQUIRE(result.valid());
- v5 = result;
- }
- int s5 = runner_thread_state.stack_top();
- cout << endl;
- return 0;
- };
- struct coro_h {
- int x = 500;
- int func() {
- x += 1;
- return x;
- }
- };
- struct coro_test {
- string identifier;
- sol::reference obj;
- coro_test(sol::this_state L, string id) : identifier(id), obj(L, sol::lua_nil) {
- }
- void store(sol::table ref) {
- // must be explicit
- obj = sol::reference(obj.lua_state(), ref);
- }
- void copy_store(sol::table ref) {
- // must be explicit
- obj = sol::reference(obj.lua_state(), ref);
- }
- sol::reference get() {
- return obj;
- }
- ~coro_test() {
- }
- };
- struct coro_test_implicit {
- string identifier;
- sol::main_reference obj;
- coro_test_implicit(sol::this_state L, string id) : identifier(id), obj(L, sol::lua_nil) {
- }
- void store(sol::table ref) {
- // main_reference does the state shift implicitly
- obj = move(ref);
- lua_State* Lmain = sol::main_thread(ref.lua_state());
- // REQUIRE(obj.lua_state() == Lmain);
- }
- void copy_store(sol::table ref) {
- // main_reference does the state shift implicitly
- obj = ref;
- lua_State* Lmain = sol::main_thread(ref.lua_state());
- //REQUIRE(obj.lua_state() == Lmain);
- }
- sol::reference get() {
- return obj;
- }
- ~coro_test_implicit() {
- }
- };
- const auto& script = R"(counter = 20
- function loop()
- while counter ~= 30
- do
- coroutine.yield(counter);
- counter = counter + 1;
- end
- return counter
- end
- )";
- int main(int argc, char* argv[]) {
- sol::state lua;
- sol::stack_guard luasg(lua);
- lua.open_libraries(sol::lib::base, sol::lib::coroutine);
- auto result1 = lua.safe_script(script);
- // REQUIRE(result1.valid());
- sol::coroutine cr = lua["loop"];
- int counter;
- for (counter = 20; counter < 31 && cr; ++counter) {
- int value = cr();
- cout << value << endl;
- // REQUIRE(counter == value);
- }
- counter -= 1;
- cout << endl;
- return 0;
- };
- a separate state that can contain and run functions
- class thread : public reference { /* ... */ };
- sol::thread is a separate runnable part of the Lua VM that can be used to execute work separately from the main thread, such as with :doc:`coroutines<coroutine>`. To take a table or a coroutine and run it specifically on the sol::thread you either pulled out of lua or created, just get that function through the :ref:`state of the thread<thread_state>`
- Note
- A CPU thread is not always equivalent to a new thread in Lua: this_thread::get_id() can be the same for 2 callbacks that have 2 distinct Lua threads. In order to know which thread a callback was called in, hook into :doc:`sol::this_state<this_state>` from your Lua callback and then construct a sol::thread, passing in the sol::this_state for both the first and last arguments. Then examine the results of the status and is_... calls below.
- free function
- The function sol::main_thread( ... ) retrieves the main thread of the application on Lua 5.2 and above only. It is designed for code that needs to be multithreading-aware (e.g., uses multiple :doc:`threads<thread>` and :doc:`coroutines<coroutine>`).
- Warning
- This code function will be present in Lua 5.1/LuaJIT, but only have proper behavior when given a single argument on Lua 5.2 and beyond. Lua 5.1 does not support retrieving the main thread from its registry, and therefore it is entirely suggested if you are writing cross-platform Lua code that you must store the main thread of your application in some global storage accessible somewhere. Then, pass this item into the sol::main_thread( possibly_thread_state, my_actual_main_state )and it will select that my_actual_main_state every time. If you are not going to use Lua 5.1 / LuaJIT, you can ignore the last parameter.
- members
- Takes a thread from the Lua stack at the specified index and allows a person to use all of the abstractions therein. It can also take an actual thread state to make a thread from that as well.
- This retrieves the current state of the thread, producing a :doc:`state_view<state>` that can be manipulated like any other. :doc:`Coroutines<coroutine>` pulled from Lua using the thread's state will be run on that thread specifically.
- This function retrieves the lua_State* that represents the thread.
- Retrieves the :doc:`thread status<types>` that describes the current state of the thread.
- Checks to see if the thread is the main Lua thread.
- Creates a new thread from the given a lua_State*.
- int main(int argc, char* argv[]) {
- state lua;// Lua состояние.
- lua.open_libraries(lib::base, lib::package, lib::coroutine); // открыть доп.библиотеки.
- const auto& my_script = R"(
- local a,b,c = ...
- print(a,b,c)
- )";
- sol::load_result fx = lua.load(my_script);
- if (!fx.valid()) {
- sol::error err = fx;
- cerr << "failde to load string-based script in the program" << err.what() << endl;
- }
- // prints "your arguments here"
- fx("your", "arguments", "here");
- return 0;
- };
- void some_function() {
- cout << "some function!" << endl;
- }
- void some_other_function() {
- cout << "some other function!" << endl;
- }
- struct some_class {
- int variable = 30;
- double member_function() {
- return 24.5;
- }
- };
- int main(int argc, char* argv[]) {
- state lua;// Lua состояние.
- lua.open_libraries(lib::base, lib::package, lib::coroutine); // открыть доп.библиотеки.
- // put an instance of "some_class" into lua
- // (we'll go into more detail about this later
- // just know here that it works and is
- // put into lua as a userdata
- lua.set("sc", some_class());
- // binds a plain function
- lua["f1"] = some_function;
- lua.set_function("f2", &some_other_function);
- // binds just the member function
- lua["m1"] = &some_class::member_function;
- // binds the class to the type
- lua.set_function("m2", &some_class::member_function, some_class{});
- // binds just the member variable as a function
- lua["v1"] = &some_class::variable;
- // binds class with member variable as function
- lua.set_function("v2", &some_class::variable, some_class{});
- lua.script(R"(
- f1() -- some function!
- f2() -- some other function!
- -- need class instance if you don't bind it with the function
- print(m1(sc)) -- 24.5
- -- does not need class instance: was bound to lua with one
- print(m2()) -- 24.5
- -- need class instance if you
- -- don't bind it with the function
- print(v1(sc)) -- 30
- -- does not need class instance:
- -- it was bound with one
- print(v2()) -- 30
- -- can set, still
- -- requires instance
- v1(sc, 212)
- -- can set, does not need
- -- class instance: was bound with one
- v2(254)
- print(v1(sc)) -- 212
- print(v2()) -- 254
- )");
- cout << endl;
- return 0;
- };
- int main(int argc, char* argv[]) {
- state lua;// Lua состояние.
- lua.open_libraries(lib::base, lib::package, lib::coroutine); // открыть доп.библиотеки.
- lua.script("function f (a, b, c, d) return 1 end");
- lua.script("function g (a, b) return a + b end");
- // sol::function is often easier:
- // takes a variable number/types of arguments...
- sol::function fx = lua["f"];
- // fixed signature function<...>
- // can be used to tie a sol::function down
- function<int(int, double, int, string)> stdfx = fx;
- int is_one = stdfx(1, 34.5, 3, "bark");
- // c_assert(is_one == 1);
- int is_also_one = fx(1, "boop", 3, "bark");
- // c_assert(is_also_one == 1);
- // call through operator[]
- int is_three = lua["g"](1, 2);
- // c_assert(is_three == 3);
- double is_4_8 = lua["g"](2.4, 2.4);
- // c_assert(is_4_8 == 4.8);
- cout << endl;
- return 0;
- };
- int main(int argc, char* argv[]) {
- state lua;// Lua состояние.
- lua.open_libraries(lib::base, lib::package, lib::coroutine); // открыть доп.библиотеки.
- lua["abc_sol2"] = lua.create_table_with(
- 0, 24
- );
- sol::table inner_table = lua.create_table_with("bark", 50,
- // can reference other existing stuff too
- "woof", lua["abc_sol2"]
- );
- lua.create_named_table("def_sol2",
- "ghi", inner_table
- );
- string code = R"(
- abc = { [0] = 24 }
- def = {
- ghi = {
- bark = 50,
- woof = abc
- }
- }
- )";
- lua.script(code);
- lua.script(R"(
- assert(abc_sol2[0] == abc[0])
- assert(def_sol2.ghi.bark == def.ghi.bark)
- )");
- cout << endl;
- return 0;
- };
- struct Doge {
- int tailwag = 50;
- Doge() {
- }
- Doge(int wags)
- : tailwag(wags) {
- }
- ~Doge() {
- cout << "Dog at " << this << " is being destroyed..." << endl;
- }
- };
- int main(int argc, char* argv[]) {
- state lua;// Lua состояние.
- lua.open_libraries(lib::base, lib::package, lib::coroutine); // открыть доп.библиотеки.
- Doge dog{ 30 };
- lua["dog"] = Doge{};
- lua["dog_copy"] = dog;
- lua["dog_move"] = move(dog);
- lua["dog_unique_ptr"] = make_unique<Doge>(21);
- lua["dog_shared_ptr"] = make_shared<Doge>(51);
- // now we can access these types in Lua
- lua.new_usertype<Doge>("Doge",
- sol::constructors<Doge(), Doge(int)>(),
- "tailwag", &Doge::tailwag
- );
- lua.script(R"(
- function f (dog)
- if dog == nil then
- print('dog was nil!')
- return
- end
- print('dog wags its tail ' .. dog.tailwag .. ' times!')
- end
- )");
- lua.script(R"(
- dog_lua = Doge.new()
- f(dog_lua)
- f(dog)
- f(dog_copy)
- f(dog_move)
- f(dog)
- f(dog_unique_ptr)
- f(dog_shared_ptr)
- f(nil)
- )");
- cout << endl;
- return 0;
- };
- int use_sol2(lua_State * L) {
- sol::state_view lua(L);
- lua.script("print('bark bark bark!')");
- return 0;
- }
- int main(int argc, char* argv[]) {
- state lua;// Lua состояние.
- lua.open_libraries(lib::base, lib::package, lib::coroutine); // открыть доп.библиотеки.
- lua_pushcclosure(L, &use_sol2, 0);
- lua_setglobal(L, "use_sol2");
- if (luaL_dostring(L, "use_sol2()")) {
- lua_error(L);
- return -1;
- }
- cout << endl;
- return 0;
- };
- struct my_type {
- void stuff() {
- }
- };
- int main(int argc, char* argv[]) {
- state lua;// Lua состояние.
- lua.open_libraries(lib::base, lib::package, lib::coroutine); // открыть доп.библиотеки.
- /*
- // AAAHHH BAD
- // dangling pointer!
- lua["my_func"] = []() -> my_type* { return new my_type(); };
- // AAAHHH!
- lua.set("something", new my_type());
- // AAAAAAHHH!!!
- lua["something_else"] = new my_type();
- */
- // :ok:
- lua["my_func0"] = []() -> unique_ptr<my_type> { return make_unique<my_type>(); };
- // :ok:
- lua["my_func1"] = []() -> shared_ptr<my_type> { return make_shared<my_type>(); };
- // :ok:
- lua["my_func2"] = []() -> my_type { return my_type(); };
- // :ok:
- lua.set("something", unique_ptr<my_type>(new my_type()));
- shared_ptr<my_type> my_shared = make_shared<my_type>();
- // :ok:
- lua.set("something_else", my_shared);
- // :ok:
- auto my_unique = make_unique<my_type>();
- lua["other_thing"] = move(my_unique);
- // :ok:
- lua["my_func5"] = []() -> my_type * {
- static my_type mt;
- return &mt;
- };
- // THIS IS STILL BAD DON'T DO IT AAAHHH BAD
- // return a unique_ptr that's empty instead
- // or be explicit!
- lua["my_func6"] = []() -> my_type * { return nullptr; };
- // :ok:
- lua["my_func7"] = []() -> nullptr_t { return nullptr; };
- // :ok:
- lua["my_func8"] = []() -> unique_ptr<my_type> {
- // default-constructs as a nullptr,
- // gets pushed as nil to Lua
- return unique_ptr<my_type>();
- // same happens for shared_ptr
- };
- // Acceptable, it will set 'something' to nil
- // (and delete it on next GC if there's no more references)
- lua.set("something", nullptr);
- // Also fine
- lua["something_else"] = nullptr;
- cout << endl;
- return 0;
- };
- int main(int argc, char* argv[]) {
- // 2 states, transferring function from 1 to another
- sol::state lua;
- sol::state lua2;
- // we're not going to run the code on the first
- // state, so we only actually need
- // the base lib on the second state
- // (where we will run the serialized bytecode)
- lua2.open_libraries(sol::lib::base);
- // load this code (but do not run)
- sol::load_result lr = lua.load("a = function (v) print(v) return v end");
- // check if it's sucessfully loaded
- c_assert(lr.valid());
- // turn it into a function, then dump the bytecode
- sol::protected_function target = lr;
- sol::bytecode target_bc = target.dump();
- // reload the byte code
- // in the SECOND state
- auto result2 = lua2.safe_script(target_bc.as_string_view(), sol::script_pass_on_error);
- // check if it was done properly
- c_assert(result2.valid());
- // check in the second state if it was valid
- sol::protected_function pf = lua2["a"];
- int v = pf(25557);
- c_assert(v == 25557);
- return 0;
- };
- int main(int argc, char* argv[]) {
- state lua;// Lua состояние.
- lua.open_libraries(lib::base, lib::package, lib::coroutine); // открыть доп.библиотеки.
- environment env(lua, sol::create, lua.globals());
- env["func"] = []() { return 42; };
- environment env2(lua, sol::create, lua.globals());
- env2["func"] = []() { return 24; };
- lua.script("function foo() print(func()) end", env);
- lua.script("function foo() print(func()) end", env2);
- env["foo"](); // prints 42
- env2["foo"](); // prints 24
- cout << endl;
- return 0;
- };
- string func_2(string text) {
- return "received: " + text;
- }
- sol::variadic_results fallback(sol::this_state ts, sol::variadic_args args) {
- sol::variadic_results r;
- if (args.size() == 2) {
- r.push_back({ ts, sol::in_place, args.get<int>(0) + args.get<int>(1) });
- }
- else {
- r.push_back({ ts, sol::in_place, 52 });
- }
- return r;
- }
- int main(int argc, char* argv[]) {
- state lua;// Lua состояние.
- lua.open_libraries(lib::base, lib::package, lib::coroutine); // открыть доп.библиотеки.
- lua.set_function("f", sol::overload(
- func_1,
- func_2,
- fallback
- ));
- lua.script("print(f(1))"); // func_1
- lua.script("print(f('hi'))"); // func_2
- lua.script("print(f(22, 11))"); // fallback
- lua.script("print(f({}))"); // fallback
- return 0;
- };
- struct pup {
- int barks = 0;
- void bark() {
- ++barks; // bark!
- }
- bool is_cute() const {
- return true;
- }
- };
- void ultra_bark(pup& p, int barks) {
- for (; barks-- > 0;) p.bark();
- }
- void picky_bark(pup& p, string s) {
- if (s == "bark")
- p.bark();
- }
- int main(int argc, char* argv[]) {
- state lua;// Lua состояние.
- lua.open_libraries(lib::base, lib::package, lib::coroutine); // открыть доп.библиотеки.
- lua.set_function("bark", sol::overload(
- ultra_bark,
- []() { return "the bark from nowhere"; }
- ));
- lua.new_usertype<pup>("pup",
- // regular function
- "is_cute", &pup::is_cute,
- // overloaded function
- "bark", sol::overload(&pup::bark, &picky_bark)
- );
- const auto& code = R"(
- barker = pup.new()
- print(barker:is_cute())
- barker:bark() -- calls member function pup::bark
- barker:bark("meow") -- picky_bark, no bark
- barker:bark("bark") -- picky_bark, bark
- bark(barker, 20) -- calls ultra_bark
- print(bark()) -- calls lambda which returns that string
- )";
- lua.script(code);
- pup& barker = lua["barker"];
- cout << barker.barks << endl;
- // c_assert(barker.barks == 22);
- return 0;
- };
- struct pup {
- int barks = 0;
- void bark() {
- ++barks; // bark!
- }
- bool is_cute() const {
- return true;
- }
- };
- void ultra_bark(pup& p, int barks) {
- for (; barks-- > 0;) p.bark();
- }
- void picky_bark(pup& p, string s) {
- if (s == "bark")
- p.bark();
- }
- struct some_class {
- int bark = 2012;
- };
- sol::table open_mylib(sol::this_state s) {
- sol::state_view lua(s);
- sol::table module = lua.create_table();
- module["func"] = []() {
- /* super cool function here */
- return 2;
- };
- // register a class too
- module.new_usertype<some_class>("some_class",
- "bark", &some_class::bark
- );
- return module;
- }
- int main(int argc, char* argv[]) {
- state lua;// Lua состояние.
- lua.open_libraries(lib::base, lib::package, lib::coroutine); // открыть доп.библиотеки.
- // sol::c_call takes functions at the template level
- // and turns it into a lua_CFunction
- // alternatively, one can use sol::c_call<sol::wrap<callable_struct, callable_struct{}>> to make the call
- // if it's a constexpr struct
- lua.require("my_lib", sol::c_call<decltype(&open_mylib), &open_mylib>);
- // run some code against your require'd library
- lua.safe_script(R"(
- s = my_lib.some_class.new()
- assert(my_lib.func() == 2)
- s.bark = 20
- )");
- some_class& s = lua["s"];
- //c_assert(s.bark == 20);
- cout << "s.bark = " << s.bark << endl;
- cout << endl;
- return 0;
- };
- // Use raw function of form "int(lua_State*)"
- // -- this is called a "raw C function",
- // and matches the type for lua_CFunction
- int LoadFileRequire(lua_State* L) {
- // use sol3 stack API to pull
- // "first argument"
- string path = sol::stack::get<string>(L, 1);
- if (path == "a") {
- string script = R"(
- print("Hello from module land!")
- test = 123
- return "bananas"
- )";
- // load "module", but don't run it
- luaL_loadbuffer(L, script.data(), script.size(), path.c_str());
- // returning 1 object left on Lua stack:
- // a function that, when called, executes the script
- // (this is what lua_loadX/luaL_loadX functions return
- return 1;
- }
- sol::stack::push(L, "This is not the module you're looking for!");
- return 1;
- }
- int main(int argc, char* argv[]) {
- state lua;// Lua состояние.
- lua.open_libraries(lib::base, lib::package, lib::coroutine); // открыть доп.библиотеки.
- // need base for print,
- // need package for package/searchers/require
- lua.open_libraries(sol::lib::base, sol::lib::package);
- lua.clear_package_loaders();
- lua.add_package_loader(LoadFileRequire);
- // this will call our function for
- // the searcher and it will succeed
- auto a_result = lua.safe_script(R"(
- local a = require("a")
- print(a)
- print(test)
- )", sol::script_pass_on_error);
- // c_assert(a_result.valid());
- try {
- // this will always fail
- auto b_result = lua.safe_script(R"(
- local b = require("b")
- print(b)
- )", sol::script_throw_on_error);
- // this will not be executed because of the throw,
- // but it better be true regardless!
- // c_assert(!b_result.valid());
- }
- catch (const exception& ex) {
- // Whenever sol3 throws an exception from panic,
- // catch
- cout << "Something went wrong, as expected:\n" << ex.what() << endl;
- // and CRASH / exit the application
- return 0;
- }
- // If we get here something went wrong...!
- return -1;
- return 0;
- };
- #include <sol.hpp>
- #include <iostream>
- #include <string>
- #include <ctime>
- #include <list>
- #include <stack>
- #include <memory>
- #include <functional>
- // a base class for scheduler tasks
- class scheduler_task
- {
- public:
- virtual ~scheduler_task() {}
- virtual bool is_complete() = 0;
- };
- // callback_task is a convenience scheduler task
- // that allows user to bind functions, function objects,
- // lambdas, etc. as tasks to the scheduler
- class callback_task : public scheduler_task
- {
- public:
- callback_task(function<bool()> callback)
- : m_callback(callback)
- {
- }
- bool is_complete()
- {
- return m_callback();
- }
- private:
- function<bool()> m_callback;
- };
- namespace detail {
- struct scheduler_cothread;
- struct scheduler_thread_task;
- struct scheduler_task_list;
- struct thread_stack;
- }
- class scheduler
- {
- public:
- scheduler(sol::state& state);
- virtual ~scheduler();
- void start(sol::string_view code);
- void run();
- void yield(shared_ptr<scheduler_task> task);
- int pending_tasks();
- // yields a thread; copies the passed scheduler_task
- template <typename T>
- typename enable_if<is_base_of<scheduler_task, T>::value>::type
- yield(const T& task)
- {
- yield(make_shared<T>(task));
- }
- // we also want to support plain function types as tasks
- void yield(bool(*callback)())
- {
- yield(make_shared<callback_task>(callback));
- }
- // a convenience function for registering a yielding, optionally stateful, C++ function
- template <typename F>
- void register_function(string name, F&& f)
- {
- state[name] = sol::yielding(f);
- }
- private:
- sol::state& state;
- shared_ptr<detail::scheduler_cothread> m_current_thread;
- unique_ptr<detail::scheduler_task_list> m_tasks;
- unique_ptr<detail::thread_stack> m_thread_stack;
- };
- namespace detail {
- struct scheduler_cothread {
- sol::thread thread;
- sol::coroutine coroutine;
- scheduler_cothread(sol::state& state, sol::string_view code) {
- thread = sol::thread::create(state);
- coroutine = thread.state().load(code);
- }
- };
- struct scheduler_thread_task
- {
- shared_ptr<scheduler_cothread> thread;
- shared_ptr<scheduler_task> task;
- };
- struct scheduler_task_list
- {
- list<scheduler_thread_task> tasks;
- };
- struct thread_stack
- {
- stack<shared_ptr<scheduler_cothread>> threads;
- };
- }
- scheduler::scheduler(sol::state& state)
- : state(state)
- , m_current_thread(0)
- , m_tasks(new detail::scheduler_task_list)
- , m_thread_stack(new detail::thread_stack)
- {
- }
- scheduler::~scheduler()
- {
- }
- void scheduler::start(sol::string_view code)
- {
- // set the current thread
- m_thread_stack->threads.push(make_shared<detail::scheduler_cothread>(state, code));
- m_current_thread = m_thread_stack->threads.top();
- // start the thread
- m_current_thread->coroutine();
- // reset current thread
- m_thread_stack->threads.pop();
- if (m_thread_stack->threads.empty())
- m_current_thread = nullptr;
- else
- m_current_thread = m_thread_stack->threads.top();
- }
- void scheduler::run()
- {
- // iterate through all pending tasks
- for (auto i = m_tasks->tasks.begin(); i != m_tasks->tasks.end();) {
- // if the thread is dead, remove it
- if (i->thread->coroutine.status() != sol::call_status::yielded) {
- i = m_tasks->tasks.erase(i);
- continue;
- }
- // is this task complete?
- if (i->task->is_complete()) {
- // get the thread task
- detail::scheduler_thread_task thread_task = *i;
- // remove it from the pending list
- i = m_tasks->tasks.erase(i);
- // set the current thread
- m_thread_stack->threads.push(thread_task.thread);
- m_current_thread = thread_task.thread;
- // resume the thread
- thread_task.thread->coroutine();
- // reset current thread
- m_thread_stack->threads.pop();
- if (m_thread_stack->threads.empty())
- m_current_thread = nullptr;
- else
- m_current_thread = m_thread_stack->threads.top();
- }
- else {
- ++i;
- }
- }
- }
- void scheduler::yield(shared_ptr<scheduler_task> task)
- {
- detail::scheduler_thread_task thread_task;
- thread_task.thread = m_current_thread;
- thread_task.task = task;
- m_tasks->tasks.push_back(thread_task);
- }
- int scheduler::pending_tasks()
- {
- return m_tasks->tasks.size();
- }
- class wait_task : public scheduler_task
- {
- public:
- wait_task(int secs) : m_secs(secs), m_start(time(0)) {}
- bool is_complete() { return ((time(0) - m_start) >= m_secs); }
- private:
- int m_secs;
- time_t m_start;
- };
- struct waits
- {
- scheduler& m_scheduler;
- waits(scheduler& scheduler) : m_scheduler(scheduler) {}
- void operator()(int secs) const
- {
- cout << "C++ - Waiting for " << secs << " seconds" << endl;
- time_t start = time(0);
- return m_scheduler.yield<wait_task>(secs);
- }
- };
- int func() {
- cout << "C++ - Inside f" << endl;
- return 24;
- }
- void simple_test(sol::state& lua) {
- lua["f"] = sol::yielding(func);
- sol::string_view code = R"(
- print('Lua - Before coroutine')
- f()
- print('Lua - After coroutine')
- )";
- sol::coroutine co = lua.load(code);
- while (co) {
- cout << "C++ - Resuming coroutine" << endl;
- co();
- cout << "C++ - Coroutine status: " << sol::to_string(co.status()) << endl;
- }
- }
- int main() {
- try {
- sol::state lua;
- lua.open_libraries(sol::lib::base, sol::lib::coroutine);
- cout << endl << "Simple test" << endl;
- simple_test(lua);
- cout << endl;
- cout << "Scheduler test" << endl;
- scheduler s(lua);
- s.register_function("wait", waits(s));
- s.register_function("show_text", [&s](string text) {
- cout << "C++ - Showing text: " << text << endl;
- return s.yield([]() { return true; });
- });
- sol::string_view code = R"(
- print('Lua - Before first wait')
- wait(3)
- print('Lua - After wait, before show_text')
- show_text('hello')
- print('Lua - After show_text, before second wait')
- wait(5)
- print('Lua - After second wait')
- )";
- sol::string_view code2 = R"(
- print('Lua - Before first wait 2')
- wait(5)
- print('Lua - After wait, before show_text 2')
- show_text('hi')
- print('Lua - After show_text, before second wait 2')
- wait(3)
- print('Lua - After second wait 2')
- )";
- s.start(code);
- // s.start(code2) /* Uncomment to run concurrent threads */
- while (s.pending_tasks() > 0) {
- s.run();
- }
- }
- catch (exception& e) {
- cout << e.what() << endl;
- }
- }
- Потоки
- int embeds_sol(lua_State* L) { // start using sol with a pre-existing system
- sol::state_view lua(L); // non-owning// runner = thread(foo);
- effil::Thread::exportAPI(lua);
- lua.open_libraries(sol::lib::base, sol::lib::package); // открыть доп.библиотеки.
- lua.script(LUA);
- std::thread t([=]() {
- lua["f"](0);
- });
- t.detach();
- return 0; // все, что требуется для работы с необработанной функцией.
- };
- int main(int argc, char* argv[]) {
- lua_State* L = luaL_newstate();
- luaL_openlibs(L);
- embeds_sol(L);
- sol::state lua;// Lua состояние.
- lua.open_libraries(sol::lib::base, sol::lib::package); // открыть доп.библиотеки.
- lua.script(LUA);// Сначала сохранить результат в переменной, затем вызвать.
- sol::function f = lua["f1"];
- f();
- cin.get();
- lua_close(L);
- return 0;
- };
- lua_pushlightuserdata(L, L); /* отправить адрес памяти переменной в стек */
- lua_pushstring(L, luafile); /* отправить значение в стек */
- lua_settable(L, LUA_REGISTRYINDEX); /* уст ключа и значение таблице реестре. */
- //lua_pushlightuserdata(L, L); /*отправить адрес, который является ключом в стек. */
- //lua_gettable(L, LUA_REGISTRYINDEX); /* получить таблицу и значение ключа будет в -1 */
- //char const* str1 = lua_tostring(L, -1);
Add Comment
Please, Sign In to add comment