LUA-C++ 绑定库"lua_tinker" 扩展为支持c++14和lua 5.3
0xE=14, ^_^
E=Expand,我们为luatinker增加了大量的新特性
我们使用了 c++11/14 中大量的新特新: 可变模板参数,整数序列,基于enable_if的SFINAE,tuple,function对象, forward_ref, decltype(auto)
master版本最低支持c++11 c++17分支使用if constexpr来优化代码的可读性
complied with vc2015,g++4.8,g++5.0,clang3.6
QQ群:518398988
- 导入lua的函数,现在支持任意多的参数
- 支持的read/push的元素类型增加std::string,std::function,lua_function_ref,stl容器,std::shared_ptr对象
- 支持最多16个参数的非精确匹配的函数重载(c++类型downcast到lua类型后生成签名), 支持普通函数、类成员函数、构造函数
- 支持函数默认参数及带默认参数的重载函数的匹配
- 移除int64相关函数,使用lua5.3的luaInterager来替代
- 支持调用lua函数时返回多个返回值用tuple包裹
- 支持通过宏定义打开类型一致性检查和常量类成员函数检查
- 支持通过宏定义允许已注册的shared_ptr对象调用类成员函数
- 支持类静态函数注册
- 支持类静态变量注册
- 支持property注册
- 支持继承多个父类,查找时根据顺序依次查找,深度优先
- 支持注册namespace及内的函数/变量/枚举/类
- 支持注册嵌套的类
- 支持在lua中扩展已经注册的类
- 支持在lua中注册一个新类继承已经注册的类
- 将内部实现移动到detail名字空间
- 使用变参模板重写 functor/memberfunctor/constructor/call 函数来支持多参数
- 使用enable_if机制实现 函数条件重载
- 通过stack_help类实现read/push函数
- 移除int64相关函数,使用lua5.3的luaInterager来替代
- 加入stl容器类向lua导入/导出一个table的功能
- 可以从lua中返回多个返回值用tuple包裹
- 使用weak_ptr来存储导出到lua的shared_ptr对象来避免lua控制c++对象生命周期
- 当push到lua的shared_ptr只有1个引用时(一个右值引用),使用lua来储存shared_ptr对象,由lua控制该对象生命周期
- 不允许shared_ptr对象到raw_ptr的自动转换,已注册shared_ptr可以通过默认注册的成员函数_get_raw_ptr()来获取一个raw_ptr
- class_add函数增加参数bInitShared来注册导出类对应的shared_ptr对象
- 未注册shared_ptr对象统一使用默认metatable来GC
- 可以向lua注册一个std::function对象(通过functor/memberfunctor warp类)
- 可以read/push一个lua_function_ref来对应lua内部的function(使用LUA_REGISTRYINDEX), lua_function_ref是一个引用计数对象, 全部释放后将会从lua中unref,如果lua关闭后再调用该对象将产生一个std::runtime_error
- 可以read/push一个std::function对象来包裹 lua_function_ref
- 通过定义宏 _ALLOW_SHAREDPTR_INVOKE 可以允许已注册的shared_ptr对象调用类成员函数
- 通过调用register_lua_close_callback注册回调函数,当lua关闭时回调
- 允许向lua导出常量对象,但是会丢失常量限定符,请注意
- 定义宏 LUATINKER_USERDATA_CHECK_CONST 将会储存const信息(bool)到userdata中,当调用成员函数时会检查是否允许调用,同时read是会检查是否发生丢失常量限定符的问题
- args_type_overload_functor/member_functor/constructor是简单的将c++参数列表转换为luatype后存储到int64作为函数签名,运行时进行非精确匹配
- 新增class_def_static函数可以注册类静态函数,使用class.foo()来调用,不要使用class:foo()
- 新增class_property函数可以为一个名字注册一对get/set函数
- 新增class_mem_static函数可以注册类静态变量
- 新增class_mem_readonly注册只读变量(const变量)
- 新增class_mem_static_readonly注册只读静态变量(const变量)
- 允许调用def/class_def/class_def_static/class_con时同时加入参数默认值,当lua中invoke时,如果参数不足,会使用参数默认值
- overload相关函数允许加入参数默认值,但不推荐人工生成,请使用自动化生成工具export2lua
- 定义宏LUATINKER_MULTI_INHERITANCE,将会允许继承多个父类,查找时根据顺序依次查找,深度优先,比单次继承是多了一个继承表遍历的过程
- 通过namespace_add注册一个namespace
- 通过namespace_def注册一个namespace中的函数
- 通过namespace_set/get 注册一个namespace中的变量或枚举
- 通过scope_inner关联meta表,getmetatable(scope_global_name)[name] = getmetatable(global_name),来实现namespace, inner class的关联
- 通过在lua中调用lua_create_class(class_name,base_name)来注册一个新的类继承base
lua_tinker::def("func_name", &func);
普通全局函数被注册在lua全局表中,所以你可以直接调用
func_name(xxx,xxx,xxx)
ret_val_t ret = lua_tinker::call<ret_val_t>("func_name", param1, param2);
//设置一个变量到lua全局表
lua_tinker::set("var_name", &var);
//lua全局表读取一个变量
var_t var = lua_tinker::get<var_t>("var_name");
lua_tinker::class_add<class_t>("class_name");
lua_tinker::class_con<class_t>(&class_t::class_t);
//这样你就能在lua中通过同名函数的方式直接构造一个class对象
local class_instance = class_t(param1);
lua_tinker::class_def<class_t>("func_name", &class_t::func);
lua
class_instance:func();
lua_tinker::class_mem<class_t>("member_name", &class_t::member);
lua
if class_instance.member == 99 then
class_instance.member = 88;
endif
lua_tinker::class_mem_readonly<class_t>("member_name", &class_t::member);
lua
function test_lua_member_readonly_1()
local pFF1 = ff(1);
pFF1.m_member_readonly = 88; --error readonly
end
lua_tinker::class_mem_static<class_t>("member_name", &class_t::member);
lua
function test_lua_member_static()
local pFF1 = ff(1);
pFF1.s_val = 901;
local pFF2 = ff(2);
return pFF2.s_val == 901;
end
lua_tinker::class_mem_static_readonly<class_t>("member_name", &class_t::member);
lua_tinker::class_var_static<ff>(L, "ENUM_1", ff::ENUM_1);
lua
function test_lua_var_static()
return ff.ENUM_1;
end
lua_tinker::class_property<ff>(L, "m_prop", &ff::getVal, &ff::setVal);
lua_tinker::class_property<ff>(L, "m_prop_readonly", &ff::getVal, nullptr);
lua
function test_lua_member_readonly_2()
local pFF1 = ff(1);
local val = pFF1.m_prop_readonly;
pFF1.m_prop_readonly = 88; --error readonly
end
lua_tinker::class_add<ff_base>(L, "ff_base");
lua_tinker::class_add<ff_other>(L, "ff_other");
lua_tinker::class_add<ff>(L, "ff");
lua_tinker::class_inh<ff, ff_base>(L);
lua_tinker::class_inh<ff, ff_other>(L);
这样就ff对象就能在lua中访问基类注册的函数 多继承需要打开宏开关
lua_create_class("ff_inlua","ff")
function ff_inlua:test(x)
return self:getVal() + x;
end
function test_lua_inhert_lua()
local local_ff = ff_inlua(1);
return local_ff:test(3) == (1+3);
end
std::tuple<int, int> push_tuple()
{
return std::make_tuple(7,10);
}
bool test_tuple(std::tuple<int, int> tuple)
{
return std::get<0>(tuple) == 8 && std::get<1>(tuple) == 9;
}
lua_tinker::def("push_tuple",&push_tuple);
lua_tinker::def("test_tuple",&test_tuple);
lua
local tuple_table = push_tuple();
return tuple_table[1] == 7 and tuple_table[2] == 10;
local tuple_table = {8, 9};
return test_tuple(tuple_table);
function test_lua_multireturn()
return 1,2.0,3,4.0,"5"
end
c++
int c = 0;
double d = 0.0;
char e = 0;
float f = 0.0;
std::string g;
std::tie(c, d, e, f, g) = lua_tinker::call< std::tuple<int, double, char, float, std::string> >(L, "test_lua_multireturn");
return c == 1 && d == 2.0 && e == 3 && f == 4.0 && g == "5";
std::vector<int> g_testvec = { 2,4,6,8,10 };
std::vector<int> push_vector()
{
return g_testvec;
}
std::vector<int>& push_vector_ref()
{
return g_testvec;
}
const std::vector<int>& push_vector_const()
{
return g_testvec;
}
lua_tinker::def("push_vector", &push_vector);
lua_tinker::def("push_vector_ref", &push_vector_ref);
lua_tinker::def("push_vector_const", &push_vector_const);
lua
local vec_copy = push_vector_const();
--遍历vector
local total_k = 0;
local total_v = 0;
for k,v in pairs(vec_copy) do
total_k = total_k + k;
total_v = total_v + v;
end
--转换为table后再遍历table
local vec_table = vec_copy:to_table();
local total_k1 = 0;
local total_v1 = 0;
for k,v in pairs(vec_table) do
total_k1 = total_k1 + k;
total_v1 = total_v1 + v;
end
local container_ref = push_vector_ref();
container_ref:push(15);
--container_ref={2,4,6,8,10,15}
container_ref:erase(6);
--container_ref={2,4,8,10,15}
int test_lua_function(std::function<int(int)> func)
{
return func(1);
}
int test_lua_function_ref(const std::function<int(int)>& func)
{
return func(1);
}
std::function<int(int)> get_cpp_function()
{
auto func = [](int v)->int
{
return v + 1;
};
return std::function<int(int)>(func);
}
lua_tinker::def("test_lua_function", &test_lua_function);
lua_tinker::def("test_lua_function_ref", &test_lua_function_ref);
lua_tinker::def("get_c_function", &get_c_function);
std::function<int(int, int)> func = [](int k, int j)->int { return k + j; };
lua_tinker::def(L, "std_function_int_int", func); //can hold function
std::function<int(int)> func_c = std::bind(func, std::placeholders::_1, 88);
lua_tinker::def(L, "std_function_int_bind88", func_c);
lua
local c_func = get_cpp_function();
return c_func(1) == 2;
local upval = 0;
local localtest = function(intval)
upval = upval+ intval;
return upval;
end
return 1 == test_lua_function(localtest);
lua
function test_lua_luafunction_ref_1()
local upval = 0;
local localtest = function (intval)
upval = upval + intval;
return upval;
end
return localtest;
end
c++
lua_tinker::lua_function_ref<int> lua_func =
lua_tinker::call<decltype(lua_func)>(L, "test_lua_luafunction_ref_1");
//执行第一次
lua_func(7);
//执行第二次
return 14 == lua_func(7);
lua
function test_lua_table_iter1()
return {
[1]={key=1,value="a"},
[2]={key=2,value="b"},
};
end
c++
lua_tinker::table_onstack table = lua_tinker::call<decltype(table)>(L, "test_lua_table_iter1");
lua_tinker::table_onstack t1 = table.get<lua_tinker::table_onstack>(1);
if(t1.get<int>("key") != 1 || t1.get<std::string>("value") != "a")
return false;
lua_tinker::table_onstack t2 = table.get<lua_tinker::table_onstack>(2);
if(t2.get<int>("key") != 2 || t2.get<std::string>("value") != "b")
return false;
lua
g_test_table_ref={[1]=10, test1=5,};
c++
lua_tinker::table_ref g_table_ref;
lua_tinker::table_onstack table(L, "g_test_table_ref");
g_table_ref = lua_tinker::table_ref::make_table_ref(table);
//每次使用table_ref必须将他push到栈上才能使用 table_ref本身不是一个table
lua_tinker::table_onstack refTable = g_table_ref.push_table_to_stack();
refTable.get<int>(1) == 10;
int test_overload(int n)
{
return n;
}
int test_overload(double d)
{
return int(d);
}
int test_overload(int n, double d)
{
return n + (int)d;
}
int test_overload(int n1, int n2, double d)
{
return n1 + n2 + (int)d;
}
lua_tinker::def(L, "test_overload", lua_tinker::args_type_overload_functor(
lua_tinker::make_functor_ptr((int(*)(int))(&test_overload)),
lua_tinker::make_functor_ptr((int(*)(double))(&test_overload)),
lua_tinker::make_functor_ptr((int(*)(int, double))(&test_overload)),
lua_tinker::make_functor_ptr((int(*)(int, int, double))(&test_overload))));
lua
test_overload(1) == 1;
test_overload(2.0) == 2;
test_overload(1,2.0) == 3;
test_overload(1,2,3.0) == 6;
int test_overload_default(int n, bool b)
{
return n;
}
int test_overload_default(int n1, int n2, bool b = true)
{
return n1 + n2;
}
int test_overload_default(int n1, int n2, int n3, double d1 = 1.0, double d2 = 2.0, double d3 = 3.0, double d4 = 4.0, const std::string &refString = std::string("test"))
{
return n1 + n2;
}
lua_tinker::def(L, "test_overload_default", lua_tinker::args_type_overload_functor(
lua_tinker::make_functor_ptr((int(*)(int, bool))(&test_overload_default)),
lua_tinker::make_functor_ptr((int(*)(int, int, bool))(&test_overload_default), 1 /*default_args_count*/, 1 /*default_args_start*/),
lua_tinker::make_functor_ptr((int(*)(int, int, int, double, double, double, double, const std::string &))(&test_overload_default), 5 /*default_args_count*/, 2 /*default_args_start*/)),
true, 1.0, 2.0, 3.0, 4.0, std::string("test"));
test_overload_default(1,2) == 3;
test_overload_default(1,2,false) == 3;
test_overload_default(1,2,3) == 3;
lua_tinker::namespace_add(L, "NS_TEST");
lua_tinker::namespace_def(L, "NS_TEST", "test_function_in_namespace", &NS_TEST::test_function_in_namespace);
lua_tinker::namespace_set(L, "NS_TEST", "ENUM_1", NS_TEST::ENUM_1);
通过scope_inner关联meta表,getmetatable(scope_global_name)[name] = getmetatable(global_name),来实现namespace, inner class的关联
namespace NS_TEST
{
namespace NS_INNER
{
struct Test
{
struct Iterator
{
enum class ENUM_T
{
ENUM_1 = 8,
ENUM_2 = 6,
ENUM_3 = 2,
};
};
};
}
}
lua_tinker::scope_inner(L, "NS_TEST::NS_INNER::Test::Iterator", "ENUM_T", "NS_TEST::NS_INNER::Test::Iterator::ENUM_T");
lua_tinker::scope_inner(L, "NS_TEST::NS_INNER::Test", "Iterator", "NS_TEST::NS_INNER::Test::Iterator");
lua_tinker::scope_inner(L, "NS_TEST::NS_INNER", "Test", "NS_TEST::NS_INNER::Test");
lua_tinker::scope_inner(L, "NS_TEST", "NS_INNER", "NS_TEST::NS_INNER");