断言 / 先决条件
你需要在代码中包含 #include <sol/sol.hpp> ,这只是一个头文件,不需要编译,但是你的lua 必须是编译可用的。
断言如下:
#ifndef EXAMPLES_ASSERT_HPP
#define EXAMPLES_ASSERT_HPP
# define m_assert(condition, message) \
do { \
if (! (condition)) { \
std::cerr << "Assertion `" #condition "` failed in " << __FILE__ \
<< " line " << __LINE__ << ": " << message << std::endl; \
std::terminate(); \
} \
} while (false)
# define c_assert(condition) \
do { \
if (! (condition)) { \
std::cerr << "Assertion `" #condition "` failed in " << __FILE__ \
<< " line " << __LINE__ << std::endl; \
std::terminate(); \
} \
} while (false)
#else
# define m_assert(condition, message) do { if (false) { (void)(condition); (void)sizeof(message); } } while (false)
# define c_assert(condition) do { if (false) { (void)(condition); } } while (false)
#endif
#endif // EXAMPLES_ASSERT_HPP
下面是在代码中快速使用断言
先打开一个lua state
#define SOL_ALL_SAFETIES_ON 1
#include <sol/sol.hpp>
#include <iostream>
#include <assert.hpp>
int main(int, char*[]) {
std::cout << "=== 打开 a state ===" << std::endl;
sol::state lua;
// open some common libraries 加载必要的库
lua.open_libraries(sol::lib::base, sol::lib::package);
lua.script("print('bark bark bark!')"); //直接执行字符串脚本
std::cout << std::endl;
return 0;
}
在lua_state 中 使用 sol2 3.2
#define SOL_ALL_SAFETIES_ON 1
#include <sol/sol.hpp>
#include <iostream>
int use_sol2(lua_State* L) {
sol::state_view lua(L);
lua.script("print('bark bark bark!')");
return 0;
}
int main(int, char*[]) {
std::cout << "=== 打开 sol::state_view on raw Lua ===" << std::endl;
lua_State* L = luaL_newstate();
luaL_openlibs(L);
lua_pushcclosure(L, &use_sol2, 0);
lua_setglobal(L, "use_sol2");
if (luaL_dostring(L, "use_sol2()")) {
lua_error(L);
return -1;
}
std::cout << std::endl;
return 0;
}
运行lua代码
#define SOL_ALL_SAFETIES_ON 1
#include <sol/sol.hpp>
#include <fstream>
#include <iostream>
#include <assert.hpp>
int main(int, char*[]) {
std::cout << "=== running lua code ===" << std::endl;
sol::state lua;
lua.open_libraries(sol::lib::base);
// load and execute from string 执行lua中的 a = test
lua.script("a = 'test'");
// load and execute from file
lua.script_file("a_lua_script.lua");
// run a script, get the result
int value = lua.script("return 54");
c_assert(value == 54);
要运行Lua代码但有一个错误处理程序以防出错,请执行以下操作:
auto bad_code_result = lua.script("123 herp.derp", [](lua_State*, sol::protected_function_result pfr) {
// pfr will contain things that went wrong, for either loading or executing the script
// Can throw your own custom error
// You can also just return it, and let the call-site handle the error if necessary.
return pfr;
});
// it did not work
c_assert(!bad_code_result.valid());
// the default handler panics or throws, depending on your settings
// uncomment for explosions:
//auto bad_code_result_2 = lua.script("bad.code", &sol::script_default_on_error);
return 0;
}
通过使用 .safe_script 脚本,您可以看到更多安全性的使用,该脚本返回一个受保护的结果,您可以使用该结果来正确检查错误和类似情况。
运行lua代码(低级)
您可以使用单个 load 和 function 执行操作符来加载、检查代码,然后运行和检查代码。
#define SOL_ALL_SAFETIES_ON 1
#include <sol/sol.hpp>
#include <fstream>
#include <iostream>
#include <cstdio>
#include <assert.hpp>
int main(int, char*[]) {
std::cout << "=== 运行 lua code (low level) ===" << std::endl;
sol::state lua; //本地 state
lua.open_libraries(sol::lib::base); //加载基础库
// load file without execute 加载lua文件,并执行
sol::load_result script1 = lua.load_file("a_lua_script.lua");
//execute
script1();
// load string without execute 加载字符串并执行
sol::load_result script2 = lua.load("a = 'test'");
//execute
sol::protected_function_result script2result = script2();
// optionally, check if it worked 检查返回的结果
if (script2result.valid()) {
// yay!
}
else {
// aww
}
sol::load_result script3 = lua.load("return 24");
// execute, get return value
int value2 = script3();
c_assert(value2 == 24);
return 0;
}
向 lua 传递参数
#define SOL_ALL_SAFETIES_ON 1
#include <sol/sol.hpp>
#include <iostream>
#include <assert.hpp>
int main(int, char* []) {
std::cout << "=== 向 lua 传递 参数 ===" << std::endl;
sol::state lua;
lua.open_libraries(sol::lib::base);
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;
std::cerr << "failed to load string-based script into the program" << err.what() << std::endl;
}
// prints "your arguments here" 执行,并传入参数
fx("your", "arguments", "here");
return 0;
}
传递函数(转储字节码)
您可以转储函数的字节码,这允许您将其传输到另一个状态(或保存或加载)。请注意,字节码通常特定于Lua版本!
#define SOL_ALL_SAFETIES_ON 1
#include <sol/sol.hpp>
#include <iostream>
#include "assert.hpp"
int main() {
std::cout << "=== dump (serialize between states) ===" << std::endl;
// 2 states, transferring function from 1 to another
sol::state lua; //lua state 1
sol::state lua2; //lua state 2
// 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) state 1 加载函数字符串
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 得到 state 1 的函数信息,字节码
sol::protected_function target = lr.get<sol::protected_function>();
sol::bytecode target_bc = target.dump();
// reload the byte code
// in the SECOND state state 2将函数字节码view化后重新加载
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 state 2 执行函数a,传入参数,并检查结果
sol::protected_function pf = lua2["a"];
int v = pf(25557);
c_assert(v == 25557);
return 0;
}
设置和获取变量
您可以使用 set/get 语法来 设置/获取 所有内容。
#define SOL_ALL_SAFETIES_ON 1
#include <sol/sol.hpp>
#include <assert.hpp>
int main(int, char*[]) {
sol::state lua;
lua.open_libraries(sol::lib::base);
// integer types 设置一个变量
lua.set("number", 24);
// floating point numbers 设置变量2
lua["number2"] = 24.5;
// string types 设置字符串
lua["important_string"] = "woof woof";
// is callable, therefore gets stored as a function that can be called 设置一个函数
lua["a_function"] = []() { return 100; };
// make a table 设置一个表
lua["some_table"] = lua.create_table_with("value", 24);
上面的设置和下面效果一样
// equivalent to this code
std::string equivalent_code = R"(
t = {
number = 24,
number2 = 24.5,
important_string = "woof woof",
a_function = function () return 100 end,
some_table = { value = 24 }
}
)";
// check in Lua
lua.script(equivalent_code);
你可以证明它们是等价的:
lua.script(R"(
assert(t.number == number)
assert(t.number2 == number2)
assert(t.important_string == important_string)
assert(t.a_function() == a_function())
assert(t.some_table.value == some_table.value)
)");
使用以下语法检索这些变量:
// implicit conversion 直接获取变量
int number = lua["number"];
c_assert(number == 24);
// explicit get 获取double类型变量
auto number2 = lua.get<double>("number2");
c_assert(number2 == 24.5);
// strings too 获取字符串
std::string important_string = lua["important_string"];
c_assert(important_string == "woof woof");
// dig into a table 获取表中的数据
int value = lua["some_table"]["value"];
c_assert(value == 24);
// get a function 获取函数
sol::function a_function = lua["a_function"];
int value_is_100 = a_function();
// convertible to std::function
std::function<int()> a_std_function = a_function;
int value_is_still_100 = a_std_function();
c_assert(value_is_100 == 100);
c_assert(value_is_still_100 == 100);
使用 object 和 sol:: 类型检索Lua类型。
sol::object number_obj = lua.get<sol::object>("number");
// sol::type::number 获取number变量的类型
sol::type t1 = number_obj.get_type();
c_assert(t1 == sol::type::number);
sol::object function_obj = lua["a_function"];
// sol::type::function 获取变量的类型
sol::type t2 = function_obj.get_type();
c_assert(t2 == sol::type::function);
bool is_it_really = function_obj.is<std::function<int()>>(); //检车函数的类型
c_assert(is_it_really);
// will not contain data
sol::optional<int> check_for_me = lua["a_function"];
c_assert(check_for_me == sol::nullopt);
return 0;
}
您可以通过将其设置为 nullptr 或sol::lua nil来擦除内容。
#define SOL_ALL_SAFETIES_ON 1
#include <sol/sol.hpp>
#include <assert.hpp>
int main(int, char*[]) {
sol::state lua;
lua.open_libraries(sol::lib::base);
lua.script("exists = 250");
int first_try = lua.get_or("exists", 322);
c_assert(first_try == 250);
lua.set("exists", sol::lua_nil);
int second_try = lua.get_or("exists", 322);
c_assert(second_try == 322);
return 0;
}
表
#define SOL_ALL_SAFETIES_ON 1
#include <sol/sol.hpp>
#include <assert.hpp>
int main(int, char*[]) {
sol::state lua;
lua.open_libraries(sol::lib::base);
lua.script(R"(
abc = { [0] = 24 }
def = {
ghi = {
bark = 50,
woof = abc
}
}
)");
sol::table abc = lua["abc"];
sol::table def = lua["def"];
sol::table ghi = lua["def"]["ghi"];
int bark1 = def["ghi"]["bark"];
int bark2 = lua["def"]["ghi"]["bark"];
c_assert(bark1 == 50);
c_assert(bark2 == 50);
int abcval1 = abc[0];
int abcval2 = ghi["woof"][0];
c_assert(abcval1 == 24);
c_assert(abcval2 == 24);
构建 表
#define SOL_ALL_SAFETIES_ON 1
#include <sol/sol.hpp>
#include <assert.hpp>
int main(int, char* []) {
sol::state lua;
lua.open_libraries(sol::lib::base);
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
);//在已经存在的表中插入变量
检查一下
std::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)
)");
return 0;
}
函数
#define SOL_ALL_SAFETIES_ON 1
#include <sol/sol.hpp>
#include <assert.hpp>
int main(int, char*[]) {
sol::state lua;
lua.open_libraries(sol::lib::base);
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 std::function<...>
// can be used to tie a sol::function down
std::function<int(int, double, int, std::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);
return 0;
}
函数 绑定
#define SOL_ALL_SAFETIES_ON 1
#include <sol/sol.hpp>
#include <assert.hpp>
#include <iostream>
void some_function() {
std::cout << "some function!" << std::endl;
}
void some_other_function() {
std::cout << "some other function!" << std::endl;
}
struct some_class {
int variable = 30;
double member_function() {
return 24.5;
}
};
int main(int, char*[]) {
std::cout << "=== functions (all) ===" << std::endl;
sol::state lua;
lua.open_libraries(sol::lib::base);
// 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
)");
std::cout << std::endl;
return 0;
}
您可以使用sol::readonly(&some_class::variable)将变量设为只读,如果有人试图对其进行写入,则会出错。
self call
您可以通过C++传递自变量来模拟Lua中的“成员函数”调用。
#define SOL_ALL_SAFETIES_ON 1
#include <sol/sol.hpp>
#include <iostream>
int main() {
std::cout << "=== self_call ===" << std::endl;
sol::state lua;
lua.open_libraries(sol::lib::base, sol::lib::package, sol::lib::table);
// a small script using 'self' syntax
lua.script(R"(
some_table = { some_val = 100 }
function some_table:add_to_some_val(value)
self.some_val = self.some_val + value
end
function print_some_val()
print("some_table.some_val = " .. some_table.some_val)
end
)");
// do some printing
lua["print_some_val"]();
// 100
sol::table self = lua["some_table"];
self["add_to_some_val"](self, 10);
lua["print_some_val"]();
std::cout << std::endl;
return 0;
}
lua 返回多个值
#define SOL_ALL_SAFETIES_ON 1
#include <sol/sol.hpp>
#include <assert.hpp>
int main(int, char* []) {
sol::state lua;
lua.script("function f (a, b, c) return a, b, c end");
std::tuple<int, int, int> result;
result = lua["f"](100, 200, 300);
// result == { 100, 200, 300 }
int a;
int b;
std::string c;
sol::tie(a, b, c) = lua["f"](100, 200, "bark");
c_assert(a == 100);
c_assert(b == 200);
c_assert(c == "bark");
return 0;
}
返回多个值到lua
#define SOL_ALL_SAFETIES_ON 1
#include <sol/sol.hpp>
#include <assert.hpp>
int main(int, char* []) {
sol::state lua;
lua.open_libraries(sol::lib::base);
lua["f"] = [](int a, int b, sol::object c) {
// sol::object can be anything here: just pass it through
return std::make_tuple(a, b, c);
};
std::tuple<int, int, int> result = lua["f"](100, 200, 300);
const std::tuple<int, int, int> expected(100, 200, 300);
c_assert(result == expected);
std::tuple<int, int, std::string> result2;
result2 = lua["f"](100, 200, "BARK BARK BARK!");
const std::tuple<int, int, std::string> expected2(100, 200, "BARK BARK BARK!");
c_assert(result2 == expected2);
int a, b;
std::string c;
sol::tie(a, b, c) = lua["f"](100, 200, "bark");
c_assert(a == 100);
c_assert(b == 200);
c_assert(c == "bark");
lua.script(R"(
a, b, c = f(150, 250, "woofbark")
assert(a == 150)
assert(b == 250)
assert(c == "woofbark")
)");
return 0;
}
C++ classes from C++
#define SOL_ALL_SAFETIES_ON 1
#include <sol/sol.hpp>
#include <assert.hpp>
#include <iostream>
struct Doge {
int tailwag = 50;
Doge() {
}
Doge(int wags)
: tailwag(wags) {
}
~Doge() {
std::cout << "Dog at " << this << " is being destroyed..." << std::endl;
}
};
int main(int, char* []) {
std::cout << "=== userdata ===" << std::endl;
sol::state lua;
Doge dog{ 30 };
// fresh one put into Lua
lua["dog"] = Doge{};
// Copy into lua: destroyed by Lua VM during garbage collection
lua["dog_copy"] = dog;
// OR: move semantics - will call move constructor if present instead
// Again, owned by Lua
lua["dog_move"] = std::move(dog);
lua["dog_unique_ptr"] = std::make_unique<Doge>(25); //引用计数
lua["dog_shared_ptr"] = std::make_shared<Doge>(31); //引用计数
// Identical to above
Doge dog2{ 30 };
lua.set("dog2", Doge{});
lua.set("dog2_copy", dog2);
lua.set("dog2_move", std::move(dog2));
lua.set("dog2_unique_ptr", std::unique_ptr<Doge>(new Doge(25)));
lua.set("dog2_shared_ptr", std::shared_ptr<Doge>(new Doge(31)));
// Note all of them can be retrieved the same way:
Doge& lua_dog = lua["dog"];
Doge& lua_dog_copy = lua["dog_copy"];
Doge& lua_dog_move = lua["dog_move"];
Doge& lua_dog_unique_ptr = lua["dog_unique_ptr"];
Doge& lua_dog_shared_ptr = lua["dog_shared_ptr"];
c_assert(lua_dog.tailwag == 50);
c_assert(lua_dog_copy.tailwag == 30);
c_assert(lua_dog_move.tailwag == 30);
c_assert(lua_dog_unique_ptr.tailwag == 25);
c_assert(lua_dog_shared_ptr.tailwag == 31);
std::cout << std::endl;
return 0;
}
#define SOL_ALL_SAFETIES_ON 1
#include <sol/sol.hpp>
#include <assert.hpp>
#include <iostream>
struct Doge {
int tailwag = 50;
Doge() {
}
Doge(int wags)
: tailwag(wags) {
}
~Doge() {
std::cout << "Dog at " << this << " is being destroyed..." << std::endl;
}
};
int main(int, char* []) {
std::cout << "=== userdata memory reference ===" << std::endl;
sol::state lua;
lua.open_libraries(sol::lib::base);
Doge dog{}; // Kept alive somehow
// Later...
// The following stores a reference, and does not copy/move
// lifetime is same as dog in C++
// (access after it is destroyed is bad)
lua["dog"] = &dog;
// Same as above: respects std::reference_wrapper
lua["dog"] = std::ref(dog);
// These two are identical to above
lua.set( "dog", &dog );
lua.set( "dog", std::ref( dog ) );
Doge& dog_ref = lua["dog"]; // References Lua memory 引用lua内存
Doge* dog_pointer = lua["dog"]; // References Lua memory 引用lua内存
Doge dog_copy = lua["dog"]; // Copies, will not affect lua 不会影响lua
您可以用与其他方法相同的方式检索用户数据。重要的是,请注意,您可以更改usertype变量的数据,如果您获得指针或引用,它将影响lua中的内容:
lua.new_usertype<Doge>("Doge",
"tailwag", &Doge::tailwag
);
dog_copy.tailwag = 525;
// Still 50
lua.script("assert(dog.tailwag == 50)");
dog_ref.tailwag = 100;
// Now 100
lua.script("assert(dog.tailwag == 100)");
dog_pointer->tailwag = 345;
// Now 345
lua.script("assert(dog.tailwag == 345)");
std::cout << std::endl;
return 0;
}
C++类进入Lua
您可以通过在注册枚举或用户类型之前创建一个表并为其指定所需的命名空间名称来模拟命名空间:
#define SOL_ALL_SAFETIES_ON 1
#include <sol/sol.hpp>
#include <iostream>
#include <assert.hpp>
int main() {
std::cout << "=== namespacing ===" << std::endl;
struct my_class {
int b = 24;
int f() const {
return 24;
}
void g() {
++b;
}
};
sol::state lua;
lua.open_libraries();
// "bark" namespacing in Lua
// namespacing is just putting things in a table
// forces creation if it does not exist
auto bark = lua["bark"].get_or_create<sol::table>();
// equivalent-ish:
//sol::table bark = lua["bark"].force(); // forces table creation
// equivalent, and more flexible:
//sol::table bark = lua["bark"].get_or_create<sol::table>(sol::new_table());
// equivalent, but less efficient/ugly:
//sol::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) { std::cout << "my_class { b: " << self.b << " }" << std::endl; });
// 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"];
c_assert(obj.b == 25);
std::cout << std::endl;
return 0;
}