lua_tinker源码笔记1
本篇僅簡單介紹使用lua_tinker讓lua調用C++函數的過程, C++調用lua函數可以參見博客Lua腳本和C++交互(一).
未完待續.
(一) lua調用C++全局函數
這里以lua_tinker自帶的sample1作為分析例子.C++源碼如下:
// sample1.cpp #include "stdafx.h" #include "../lua_tinker/lua_tinker.h"int cpp_func(int arg1, int arg2) {return arg1 + arg2; }int _tmain(int argc, _TCHAR* argv[]) { lua_State* L = lua_open();luaopen_base(L);lua_tinker::def(L, "cpp_func", cpp_func);lua_tinker::dofile(L, "sample1.lua");int result = lua_tinker::call<int>(L, "lua_func", 3, 4);printf("lua_func(3,4) = %d\n", result);lua_close(L);return 0; }lua文件源碼如下:
-- sample1.lua result = cpp_func(1, 2)print("cpp_func(1,2) = "..result)function lua_func(arg1, arg2)return arg1 + arg2 end從main函數開始分析. 首先通過lua_open獲取lua棧對象,再打開lua庫(luaopen_base). 接下來,lua_tinker::def注冊一個C++全局函數,lua_tinker::dofile打開lua文件,通過lua_tinker::call調用到sample1.lua里面的lua函數.最后關閉釋放lua棧對象(lua_close).本文重點在于探究ua_tinker::def如何完成C++全局函數的注冊.
(1) lua_tinker只有一個.h和.cpp文件,在lua_tinker.h里面,我們找到了上面的函數調用,如下:
// global function // lua_tinker::def(L, "cpp_func", cpp_func); template<typename F> void def(lua_State* L, const char* name, F func) { lua_pushstring(L, name);lua_pushlightuserdata(L, func);push_functor(L, func);lua_settable(L, LUA_GLOBALSINDEX); }在代碼中,我們可以看到,首先將全局函數名稱"cpp_func"壓入lua棧(), 此時的lua棧只有這一個元素且為棧頂, 然后我們再將全局函數對應的函數指針壓入棧(查lua_reference可以知道,如果是light userdata(而不是full userdata)的話,lua_pushlightuserdata只是簡單地壓入函數指針), 此時的lua棧有兩個元素,棧頂為func函數指針,第2個元素是我們剛才壓入的全局函數名稱;第3步的push_functor是lua_tinker對壓入cclosure(c閉包)函數lua_pushcclosure的一個封裝,查看此函數:
template<typename RVal, typename T1, typename T2> void push_functor(lua_State *L, RVal (*func)(T1,T2)) { lua_pushcclosure(L, functor<T1,T2>::invoke<RVal>, 1); }這里涉及到模板函數的重載,模板相關知識參見系列博客C++ template —— 模板基礎(一).這里不做過多闡述.
上面我們的全局函數cpp_func是一個接受兩個參數且有一個返回值的函數,所以我們找到了如上的模板函數, 從代碼中可以看到,push_functor()只是簡單地調用lua函數lua_pushcclosure,但是對參數做了一層封裝.lua_pushcclosure接受三個參數,第2個參數表示要壓入的c閉包函數體(此處為functor結構體的invoke函數),第3個參數表示此c閉包關聯的upvalue個數(最大255).本例中只有一個(會獲取到上面通過lua_pushlightuserdata壓入的函數指針), lua_pushcclosure壓入c閉包,同時會把所有的upvalue(這里只有函數指針)全都pop出棧.此時的lua棧只有兩個元素,棧頂也就是剛剛壓入的cclosure,下面一個元素是函數名稱. 接下來,我們再看看functor如何封裝此函數指針:
template<typename T1, typename T2> struct functor<T1,T2> {template<typename RVal>static int invoke(lua_State *L) { push(L,upvalue_<RVal(*)(T1,T2)>(L)(read<T1>(L,1),read<T2>(L,2))); return 1; }template<>static int invoke<void>(lua_State *L) { upvalue_<void(*)(T1,T2)>(L)(read<T1>(L,1),read<T2>(L,2)); return 0; } };這里涉及到的是類(結構體)模板和類模板偏特化的內容,這里也不做過多闡述,感興趣同樣可以參見上面模板系列博客.
根據類模板偏特化,我們找到了上面接收兩個模板參數的對應functor代碼,可以看到,invoke執行的操作就是將全局函數壓入棧. upvalue_獲取閉包的upvalue,這里是全局函數cpp_func的指針,再通過read指定(cpp_func全局函數所需要的)兩個參數,最后通過push壓入lua棧.upvalue_代碼如下:
// get value from cclosure template<typename T> T upvalue_(lua_State *L) {return user2type<T>::invoke(L, lua_upvalueindex(1)); }lua_upvalueindex(1)獲取cclosure中的第1個upvalue,也即全局函數地址.
最后,通過lua_settable(L, LUA_GLOBALSINDEX);找到全局表索引位置的全局表t,并以棧頂為value(cclosure),下一個元素為key(全局函數名稱),設置t[key] = value;將封裝過的元素函數指針設置進lua全局表里面,這樣,在lua文件sample1.lua中,就可以直接通過全局函數名稱"cpp_func"調用到C++的函數了.
?
本篇博文為個人分析,如發現錯誤,請留言指出,共同進步,謝謝!
?
?(二) lua調用C++ 的非全局函數
[參考]:lua 函數調用1 -- 閉包詳解和C調用
轉載于:https://www.cnblogs.com/yyxt/p/5503449.html
總結
以上是生活随笔為你收集整理的lua_tinker源码笔记1的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: linux主机名的修改
- 下一篇: 第三周复习总结