在线时间:8:00-16:00
迪恩网络APP
随时随地掌握行业动态
扫描二维码
关注迪恩网络微信公众号
开源软件名称(OpenSource Name):SteveKChiu/lua-intf开源软件地址(OpenSource Url):https://github.com/SteveKChiu/lua-intf开源编程语言(OpenSource Language):C++ 100.0%开源软件介绍(OpenSource Introduction):lua-intf
Lua and C++ error handlingBy default LuaIntf expect the Lua library to build under C++, this will allow Lua library to throw exception upon error, and make sure C++ objects on stack to be destructed correctly. For more info about error handling issues, please see: http://lua-users.org/wiki/ErrorHandlingBetweenLuaAndCplusplus If you really want to use Lua library compiled under C and want to live with #define LUAINTF_LINK_LUA_COMPILED_IN_CXX 0
#include "LuaIntf/LuaIntf.h" Compile Lua library in C++Most distributions of precompiled Lua library are compiled under C, if you need Lua library compiled under C++, you probably need to compile it by yourself. It is actually very easy to build Lua library under C++, first get a copy of source code of the Lua library:
To compile on Linux:
To compile on Mac OSX:
To compile on Windows with MINGW and MSYS:
And then install to your chosen directory in
Export C++ class or function to Lua scriptYou can easily export C++ class or function for Lua script, consider the following C++ class: class Web
{
public:
// base_url is optional
Web(const std::string& base_url);
~Web();
static void go_home();
static std::string home_url();
static void set_home_url(const std::string& url);
std::string url() const;
void set_url(const std::string& url);
std::string resolve_url(const std::string& uri);
// doing reload if uri is empty
std::string load(const std::string& uri);
}; You can export the LuaBinding(L).beginClass<Web>("Web")
.addConstructor(LUA_ARGS(_opt<std::string>))
.addStaticProperty("home_url", &Web::home_url, &Web::set_home_url)
.addStaticFunction("go_home", &Web::go_home)
.addProperty("url", &Web::url, &Web::set_url)
.addFunction("resolve_url", &Web::resolve_url)
.addFunction("load", &Web::load, LUA_ARGS(_opt<std::string>))
.addStaticFunction("lambda", [] {
// you can use C++11 lambda expression here too
return "yes";
})
.endClass(); To access the exported local w = Web() -- auto w = Web("");
w.url = "http://www.yahoo.com" -- w.set_url("http://www.yahoo.com");
local page = w:load() -- auto page = w.load("");
page = w:load("http://www.google.com") -- page = w.load("http://www.google.com");
local url = w.url -- auto url = w.url(); Module and classC++ class or functions exported are organized by module and class. The general layout of binding looks like: LuaBinding(L)
.beginModule(string module_name)
.addFactory(function* func)
.addConstant(string constant_name, VALUE_TYPE value)
.addVariable(string property_name, VARIABLE_TYPE* var, bool writable = true)
.addVariableRef(string property_name, VARIABLE_TYPE* var, bool writable = true)
.addProperty(string property_name, FUNCTION_TYPE getter, FUNCTION_TYPE setter)
.addProperty(string property_name, FUNCTION_TYPE getter)
.addFunction(string function_name, FUNCTION_TYPE func)
.beginModule(string sub_module_name)
...
.endModule()
.beginClass<CXX_TYPE>(string class_name)
...
.endClass()
.beginExtendClass<CXX_TYPE, SUPER_CXX_TYPE>(string sub_class_name)
...
.endClass()
.endModule()
.beginClass<CXX_TYPE>(string class_name)
.addFactory(FUNCTION_TYPE func) // you can only have one addFactory or addConstructor
.addConstructor(LUA_ARGS(...))
.addConstant(string constant_name, VALUE_TYPE value)
.addStaticVariable(string property_name, VARIABLE_TYPE* var, bool writable = true)
.addStaticVariableRef(string property_name, VARIABLE_TYPE* var, bool writable = true)
.addStaticProperty(string property_name, FUNCTION_TYPE getter, FUNCTION_TYPE setter)
.addStaticProperty(string property_name, FUNCTION_TYPE getter)
.addStaticFunction(string function_name, FUNCTION_TYPE func)
.addVariable(string property_name, CXX_TYPE::FIELD_TYPE* var, bool writable = true)
.addVariableRef(string property_name, CXX_TYPE::FIELD_TYPE* var, bool writable = true)
.addProperty(string property_name, CXX_TYPE::FUNCTION_TYPE getter, CXX_TYPE::FUNCTION_TYPE setter)
.addProperty(string property_name, CXX_TYPE::FUNCTION_TYPE getter, CXX_TYPE::FUNCTION_TYPE getter_const, CXX_TYPE::FUNCTION_TYPE setter)
.addProperty(string property_name, CXX_TYPE::FUNCTION_TYPE getter)
.addPropertyReadOnly(string property_name, CXX_TYPE::FUNCTION_TYPE getter, CXX_TYPE::FUNCTION_TYPE getter_const)
.addPropertyReadOnly(string property_name, CXX_TYPE::FUNCTION_TYPE getter)
.addFunction(string function_name, CXX_TYPE::FUNCTION_TYPE func)
.endClass()
.beginExtendClass<CXX_TYPE, SUPER_CXX_TYPE>(string sub_class_name)
...
.endClass() A module binding is like a package or namespace, it can also contain other sub-module or class. A module can have the following bindings:
A class binding is modeled after C++ class, it models the const-ness correctly, so const object can not access non-const functions. It can have the following bindings:
For module and class, you can have only one constructor or factory function. To access the factory or constructor in Lua script, you call the module or class name like a function, for example the above local w = Web("http://www.google.com") The static or module variable, property and constant is accessible by module/class name, it is just like table field: local url = Web.home_url
Web.home_url = "http://www.google.com" The static function can be called by the following, note the '.' syntax and class name: Web.go_home() The member variable and property is associated with object, so it is accessible by variable: local session = Web("http://www.google.com")
local url = session.url
session.url = "http://www.google.com" The member function can be called by the following, note the ':' syntax and object name: session:load("http://www.yahoo.com") Integrate with Lua module systemThe lua module system don't register modules in global variables. So you'll need to pass a local reference to extern "C" int luaopen_modname(lua_State* L)
{
LuaRef mod = LuaRef::createTable(L);
LuaBinding(mod)
...;
mod.pushToStack();
return 1;
} C++ object life-cycle
Using shared pointerIf both C++ and Lua code need to access the same object, it is usually better to use shared pointer in both side, thus avoiding memory leak. You can use any kind of shared pointer class, as long as it provides:
Before you can use it, you need to register it with namespace LuaIntf
{
LUA_USING_SHARED_PTR_TYPE(std::shared_ptr)
} For constructing shared pointer inside Lua LuaBinding(L).beginClass<Web>("web")
.addConstructor(LUA_SP(std::shared_ptr<Web>), LUA_ARGS(_opt<std::string>))
...
.endClass(); Using custom deleterIf custom deleter is needed instead of the destructor, you can register the constructor with deleter by adding LUA_DEL macro, for example: class MyClass
{
public:
MyClass(int, int);
void release();
};
struct MyClassDeleter
{
void operator () (MyClass* p)
{
p->release();
}
};
LuaBinding(L).beginClass<MyClass>("MyClass")
.addConstructor(LUA_DEL(MyClassDeleter), LUA_ARGS(int, int))
...
.endClass(); Using STL-style containerBy default namespace LuaIntf
{
LUA_USING_LIST_TYPE(std::vector)
LUA_USING_MAP_TYPE(std::map)
} For non-template or non-default template container type, you can use: class non_template_int_list
{ ... };
template <typename T, typename A, typename S>
class custom_template_list<T, A, S>
{ ... };
namespace LuaIntf
{
LUA_USING_LIST_TYPE_X(non_template_int_list)
// you need to use LUA_COMMA for , to workaround macro limitation
LUA_USING_LIST_TYPE_X(custom_template_list<T LUA_COMMA A LUA_COMMA S>,
typename T, typename A, typename S)
} Function calling conventionC++ function exported to Lua can follow one of the two calling conventions:
// regular lua_CFunction convention
int func_1(lua_state* L);
// lua_CFunction convention, but allow arg1, arg2 to map to arguments
int func_2(lua_state* L, const std::string& arg1, int arg2);
// this is *NOT* lua_CFunction
// the L can be placed anywhere, and it is stub to capture lua_State*,
// and do not contribute to actual Lua arguments
int func_3(const std::string& arg1, lua_state* L);
class Object
{
public:
// class method can follow lua_CFunction convention too
int func_1(lua_state* L);
// class lua_CFunction convention, but allow arg1, arg2 to map to arguments
int func_2(lua_state* L, const std::string& arg1, int arg2);
};
// the following can also be lua_CFunction convention if it is added as class functions
// note the first argument must be the pointer type of the registered class
int obj_func_1(Object* obj, lua_state* L);
int obj_func_2(Object* obj, lua_state* L, const std::string& arg1, int arg2); For every function registration, LuaBinding(L).beginClass<Web>("Web")
.addStaticFunction("lambda", [] {
// you can use C++11 lambda expression here too
return "yes";
})
.addStaticFunction<std::function<std::string()>>("bind",
std::bind(&Web::url, other_web_object))
.endClass(); If your C++ function is overloaded, pass static int test(string, int);
static string test(string);
LuaBinding(L).beginModule("utils")
// this will bind int test(string, int)
.addFunction("test_1", static_cast<int(*)(string, int)>(&test))
// this will bind string test(string), by using our LUA_FN macro
// LUA_FN(RETURN_TYPE, FUNC_NAME, ARG_TYPES...)
.addFunction("test_2", LUA_FN(string, test, string))
.endModule(); Function argument modifiersBy default the exported functions expect every argument to be mandatory, if the argument is missing or not compatible with the expected type, the Lua error will be raised. You can change the function passing requirement by adding argument passing modifiers in
All output modifiers require the argument to be reference type, using pointer type for output is not supported. The reason struct MyString
{
std::string indexOf(const std::string& str, int pos);
std::string desc(float number);
...
};
#define _def_float(f) _def<float, long((f) * 1000000), 1000000>
LuaBinding(L).beginClass<MyString>("mystring")
// this will make pos = 1 if it is not specified in Lua side
.addFunction("indexOf", &MyString::indexOf, LUA_ARGS(std::string, _def<int, 1>))
// this will make number = 1.333 = (4 / 3) if it is not specified in Lua side
// because C++ does not allow float as non-type template parameter
// you have to use ratio to specify floating numbers 1.333 = (4 / 3)
// LUA_ARGS(_def<float, 1.33333f>) will result in error
.addFunction("indexOf", &MyString::desc, LUA_ARGS(_def<float, 4, 3>))
// you can define your own macro to make it easier to specify float
// please see _def_float for example
.addFunction("indexOf2", &MyString::desc, LUA_ARGS(_def_float(1.3333f)))
.endClass(); Return multiple results for LuaIt is possible to return multiple results by telling which argument is for output, for example: static std::string match(const std::string& src, const std::string& pat, int pos, int& found_pos);
LuaBinding(L).beginModule("utils")
// this will return (string) (found_pos)
.addFunction("match", &match, LUA_ARGS(std::string, std::string, _def<int, 1>, _out<int&>))
.endModule(); Yet another way to return multiple results is to use static std::tuple<std::string, int> match(const std::string& src, const std::string& pat, int pos);
LuaBinding(L).beginModule("utils")
// this will return (string) (found_pos)
.addFunction("match", &match)
.endModule(); And you can always use Lua for-loop iteration function
class MyIterator : public CppFunctor
{
MyIterator(...)
{
...
}
virtual ~MyIterator()
{
...
}
// the for-loop will call this function for each step until it return 0
virtual int run(lua_State* L) override
{
...
// return the number of variables for each step of for-loop
// or return 0 to end the for-loop
return 2;
}
}
int xpairs(lua_State* L)
{
return CppFunctor::make<MyIterator>(L, ...); // ... is constructor auguments
} To register the for-loop iteration function: LuaBinding(L).beginModule("utils")
.addFunction("xpairs", &xpairs)
.endModule(); To use the iteration function in Lua code: for x, y in utils.xpairs(...) do
...
end Custom type mappingIt is possible to add primitive type mapping to the
For example, to add namespace LuaIntf
{
template <>
struct LuaTypeMapping <std::wstring>
{
static void push(lua_State* L, const std::wstring& str)
{
if (str.empty()) {
lua_pushliteral(L, "");
} else {
std::wstring_convert<std::codecvt_utf8<wchar_t>> conv;
std::string buf = conv.to_bytes(str);
lua_pushlstring(L, buf.data(), buf.length());
}
}
static std::wstring get(lua_State* L, int index)
{
size_t len;
const char* p = luaL_checklstring(L, index, &len);
std::wstring_convert<std::codecvt_utf8<wchar_t>> conv;
return conv.from_bytes(p, p + len);
}
static std::wstring opt(lua_State* L, int index, const std::wstring& def)
{
return lua_isnoneornil(L, index) ? def : get(L, index);
}
};
} // namespace LuaIntf After that, you are able to push std::wstring s = ...;
Lua::push(L, s);
...
s = Lua::pop<std::wstring>(L); High level API to access Lua object
lua_State* L = ...;
lua_getglobal(L, "module");
lua_getfield(L, -1, "method");
lua_pushintteger(L, 1);
lua_pushstring(L, "yes");
lua_pushboolean(L, true);
lua_call(L,
|
2023-10-27
2022-08-15
2022-08-17
2022-09-23
2022-08-13
请发表评论