C++与LUA如何协同工作(一)

打算写一个系列文章,讲述C++和lua如何协同工作。
从中可以看到C++1z的一系列的新的语言功能是怎么使得这个协同工作更加“有味道”的。

这是一个系列文章,一步一步引入C++1z有趣的东西。
文章有一个对应的GitHub项目

第一节:原始的协同方式

首先,我们看看如何手工编写代码导出C++函数到LUA?

  1. 假设我们有这样的一个C++的函数:
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;
}

这个函数将一个整数数组连接成一个字符串,sep参数是分隔符,默认参数是逗号(,)。

  1. 首先需要用一个标准的函数封装它:
int join_vector_wrapper(lua_State* L)
{
    // 从lua的栈获得参数
    // 调用join_vector计算结果
    // 把结果入栈
    return 1;
}
  1. 然后,给它关联一个名字,设置到lua全局的名字空间
void test_manual(lua_State* L)
{
    // 默认参数,加入到闭包的upvalue
    lua_pushstring(L, ",");
    lua_pushcclosure(L, join_vector_wrapper, 1);
    lua_setglobal(L, "test");
}
  1. 最后写个测试main函数,执行一段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;
}

现在回头看看join_vector_wrapper的具体实现:

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;
}

包装函数首先判断一下lua传进来的参数个数,如果只有1个参数,则意味着,没有传递sep参数。
这时候,我们就可以从闭包的upvalue中获取默认参数,并把默认参数入栈。
经过这样的处理之后,不管lua代码以后没有传sep参数,后续的处理都可以当时参数完整的情况了。

接下来是声明一个std::vector,遍历lua的table,复制到数组;并且,获得sep参数。
最后调用join_vector,然后把结果入栈。

再补充cpp2lua::dostring的实现如下:

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();
        };

这个类的具体实现可以阅读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;
}

以上,就是手写导出一个C++函数到lua的全过程,包括执行一段测试代码验证我们的导出结果。

完整代码在test_manual_01.cpp

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,772评论 6 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,458评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,610评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,640评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,657评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,590评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,962评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,631评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,870评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,611评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,704评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,386评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,969评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,944评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,179评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,742评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,440评论 2 342

推荐阅读更多精彩内容

  • Lua 5.1 参考手册 by Roberto Ierusalimschy, Luiz Henrique de F...
    苏黎九歌阅读 13,727评论 0 38
  • 第一篇 语言 第0章 序言 Lua仅让你用少量的代码解决关键问题。 Lua所提供的机制是C不擅长的:高级语言,动态...
    testfor阅读 2,649评论 1 7
  • Nginx API for Lua Introduction ngx.arg ngx.var.VARIABLE C...
    吃瓜的东阅读 5,700评论 0 5
  • 文/箫剑 人生之路漫漫,只要还活着,我们总要向前:趟无数的河流,越无数的高山,见形形色色的人,经历千奇百怪...
    箫剑木风阅读 591评论 1 8
  • 想你了 我会望望窗外 回忆你的身影 想你了 我会掏出手机 看看聊天记录 想你了 我会去河边走走 寻找我们的足迹 想...
    蝶之语阅读 363评论 0 0