打算寫一個系列文章,講述C++和lua如何協(xié)同工作曼尊。
從中可以看到C++1z的一系列的新的語言功能是怎么使得這個協(xié)同工作更加“有味道”的均驶。
這是一個系列文章,一步一步引入C++1z有趣的東西鸳碧。
文章有一個對應(yīng)的GitHub項目
第一節(jié):原始的協(xié)同方式
首先,我們看看如何手工編寫代碼導(dǎo)出C++函數(shù)到LUA犬性?
- 假設(shè)我們有這樣的一個C++的函數(shù):
std::string join_vector(const std::vector<int>& vec, const char* sep = ",")
{
std::string result;
for (size_t i=0; i<vec.size(); ++i)
{
char buf[64];
if (i>0)
{
snprintf(buf, sizeof(buf), "%s%d", sep, vec[i]);
}
else
{
snprintf(buf, sizeof(buf), "%d", vec[i]);
}
result += buf;
}
return result;
}
這個函數(shù)將一個整數(shù)數(shù)組連接成一個字符串瞻离,sep參數(shù)是分隔符,默認(rèn)參數(shù)是逗號(,)乒裆。
- 首先需要用一個標(biāo)準(zhǔn)的函數(shù)封裝它:
int join_vector_wrapper(lua_State* L)
{
// 從lua的棧獲得參數(shù)
// 調(diào)用join_vector計算結(jié)果
// 把結(jié)果入棧
return 1;
}
- 然后套利,給它關(guān)聯(lián)一個名字,設(shè)置到lua全局的名字空間
void test_manual(lua_State* L)
{
// 默認(rèn)參數(shù)鹤耍,加入到閉包的upvalue
lua_pushstring(L, ",");
lua_pushcclosure(L, join_vector_wrapper, 1);
lua_setglobal(L, "test");
}
- 最后寫個測試main函數(shù)肉迫,執(zhí)行一段lua腳本,跑起來看看稿黄。
int main(void)
{
lua_State *L = luaL_newstate();
luaL_openlibs(L);
test_manual(L);
std::string lua_code = "a = {10,9,8}; s = test(a); print(s);";
cpp2lua::dostring(L, lua_code);
lua_close(L);
return 0;
}
現(xiàn)在回頭看看join_vector_wrapper
的具體實現(xiàn):
int join_vector_wrapper(lua_State* L)
{
int args = lua_gettop(L);
if (args == 1)
{
lua_pushvalue(L, lua_upvalueindex(1));
}
std::vector<int> vec;
lua_pushnil(L);
int tb_index = 1;
while (lua_next(L, tb_index) != 0)
{
vec.emplace_back((int)lua_tointeger(L, lua_gettop(L)));
lua_pop(L, 1); // remain key for next loop
}
const char* sep = lua_tostring(L, 2);
std::string s = join_vector(vec, sep);
lua_pushstring(L, s.c_str());
return 1;
}
包裝函數(shù)首先判斷一下lua傳進(jìn)來的參數(shù)個數(shù)喊衫,如果只有1個參數(shù),則意味著杆怕,沒有傳遞sep參數(shù)格侯。
這時候,我們就可以從閉包的upvalue中獲取默認(rèn)參數(shù)财著,并把默認(rèn)參數(shù)入棧联四。
經(jīng)過這樣的處理之后,不管lua代碼以后沒有傳sep參數(shù)撑教,后續(xù)的處理都可以當(dāng)時參數(shù)完整的情況了朝墩。
接下來是聲明一個std::vector,遍歷lua的table,復(fù)制到數(shù)組收苏;并且亿卤,獲得sep參數(shù)。
最后調(diào)用join_vector鹿霸,然后把結(jié)果入棧排吴。
再補(bǔ)充cpp2lua::dostring
的實現(xiàn)如下:
void cpp2lua::dobuffer(lua_State* L, const char* buf, size_t len)
{
lua_pushcclosure(L, on_lua_error, 0);
int error_index = lua_gettop(L);
int ret = luaL_loadbuffer(L, buf, len, "cpp2lua::dobuffer");
if (ret == LUA_OK) {
lua_pcall(L, 0, 1, error_index);
}
else {
printf("load buffer error:%d", ret);
}
lua_remove(L, error_index);
lua_pop(L, 1);
}
考慮到table在lua中是如此的重要,我們建立一個輔助的類來遍歷lua的table懦鼠。
struct stack_object
{
lua_State* m_L;
int m_stack_pos; // absolution position
int to_integer();
};
class table_iterator
{
public:
table_iterator(const stack_object& table);
bool has_next();
void next();
stack_object key();
stack_object value();
};
這個類的具體實現(xiàn)可以閱讀cpp2lua_stack_helper.h
使用這個輔助的類之后join_vector_wrapper
變成這樣了:
int join_vector_wrapper(lua_State* L)
{
int args = lua_gettop(L);
if (args == 1)
{
lua_pushvalue(L, lua_upvalueindex(1));
}
using namespace cpp2lua::helper;
table_iterator table(stack_object(L, 1));
std::vector<int> vec;
while (table.has_next())
{
vec.emplace_back(table.value().to_integer());
table.next();
}
const char* sep = lua_tostring(L, 2);
std::string s = join_vector(vec, sep);
lua_pushstring(L, s.c_str());
return 1;
}
以上钻哩,就是手寫導(dǎo)出一個C++函數(shù)到lua的全過程,包括執(zhí)行一段測試代碼驗證我們的導(dǎo)出結(jié)果肛冶。
完整代碼在test_manual_01.cpp