lua52 C API测试代码

//这是一篇lua与C++交互的情景测试
#include <lua.hpp>
#include <lauxlib.h>
#include <lualib.h>
#include <string.h>
#include <limits.h>

#pragma region not_importent_
static void stackDump (lua_State *L)
{
    int i;
    int top = lua_gettop(L);
    for(i=1;i<=top;i++){
        /*repeatforeachlevel*/
        int t = lua_type(L, i);
        switch (t) {
        case LUA_TSTRING:
            /* strings */
            printf("`%s'", lua_tostring(L, i));
            break;
        case LUA_TBOOLEAN:
            /* booleans */
            printf(lua_toboolean(L, i) ? "true" : "false");
            break;
        case LUA_TNUMBER:
            /* numbers */
            printf("%g", lua_tonumber(L, i));
            break;
        default:
            /* other values */
            printf("%s", lua_typename(L, t));
            break;
        }
        printf(" ");
        /* put a separator */
    }
    printf("\n");
    /* end the listing */
}

void error (lua_State *L, const char *fmt, const char *str) {
    printf(fmt, str);
}
//待Lua调用的C注册函数。
static int add2(lua_State* L)
{
    //检查栈中的参数是否合法,1表示Lua调用时的第一个参数(从左到右),依此类推。
    //如果Lua代码在调用时传递的参数不为number,该函数将报错并终止程序的执行。
    double op1 = luaL_checknumber(L,1);
    double op2 = luaL_checknumber(L,2);
    //将函数的结果压入栈中。如果有多个返回值,可以在这里多次压入栈中。
    lua_pushnumber(L,op1 + op2);
    //返回值用于提示该C函数的返回值数量,即压入栈中的返回值数量。
    return 1;
}
static int sub2(lua_State* L)
{
    double op1 = luaL_checknumber(L,1);
    double op2 = luaL_checknumber(L,2);
    lua_pushnumber(L,op1 - op2);
    return 1;
}
#pragma endregion

//////////////////////////////////////////////////////////////////////////
void test_c_call_lua_fun_noParam()
{
    const char* buff = "print(\"hello\")";
    int err;

    //init
    lua_State* L = luaL_newstate();
    luaL_openlibs(L);

    //load buff
    err = luaL_loadbuffer(L,buff,strlen(buff),"line") ;
    err += lua_pcall(L,0,0,0);
    int s = lua_gettop(L);
    if(err){
        fprintf(stderr,"%s",lua_tostring(L,-1));
        lua_pop(L,1);
    }

    //close
    lua_close(L);

}
//////////////////////////////////////////////////////////////////////////
void test_c_call_lua_fun_withParam()
{
    lua_State *L = luaL_newstate();
    luaL_openlibs(L);
    if (luaL_loadfile(L, "luafile2.lua") || lua_pcall(L, 0, 0, 0))
    {
            error(L, "cannot run configuration file: %s",lua_tostring(L, -1));
            return;
    }
    lua_getglobal(L, "do_lua_kk");
    lua_getglobal(L, "a");
    lua_getglobal(L, "do_lua_error");
    lua_getglobal(L, "b");
    lua_getglobal(L, "do_c_one");
    lua_getglobal(L, "do_c");
    lua_getglobal(L, "width");
    lua_getglobal(L, "height");
    // lua_getglobal(L, "do_c_two");
    stackDump(L); //打印结果:function `lua_str_a' function `lua_str_b' function function 999 300
    int k = 100;

    k = lua_pcall(L, 2, 1, 1);
    stackDump(L); //打印结果:function `lua_str_a' function `lua_str_b' function 8888 
    /*lua 执行栈上最上面的函数,使用栈上2个参数,向栈压入3个返回值
    使用栈上第1位置的函数do_lua_kk作为错误处理回调函数,输出:
    -------->do_c
    999--add--300-->--2222
    */
    k = lua_pcall(L, 1, 0, 0);
    stackDump(L); 

    lua_close(L);
}
//////////////////////////////////////////////////////////////////////////
void test_c_call_lua_fun_withParam2() 
{
    lua_State* L = luaL_newstate();

    //luaL_dostring 等同于luaL_loadstring() || lua_pcall()
    //注意:在能够调用Lua函数之前必须执行Lua脚本,否则在后面实际调用Lua函数时会报错,
    //错误信息为:"attempt to call a nil value."
    const char* lua_function_code = "function add(x,y) return x + y end";
    if (luaL_dostring(L,lua_function_code)) {
        printf("Failed to run lua code.\n");
        return;
    }
    double x = 1.0, y = 2.3;
    lua_getglobal(L,"add");
    lua_pushnumber(L,x);
    lua_pushnumber(L,y);
    //下面的第二个参数表示带调用的lua函数存在两个参数。
    //第三个参数表示即使带调用的函数存在多个返回值,那么也只有一个在执行后会被压入栈中。
    //lua_pcall调用后,虚拟栈中的函数参数和函数名均被弹出。
    if (lua_pcall(L,2,1,0)) {
        printf("error is %s.\n",lua_tostring(L,-1));
        return;
    }
    //此时结果已经被压入栈中。
    if (!lua_isnumber(L,-1)) {
        printf("function 'add' must return a number.\n");
        return;
    }
    double ret = lua_tonumber(L,-1);
    lua_pop(L,-1); //弹出返回值。
    printf("The result of call function is %f.\n",ret);
    lua_close(L);

}
//////////////////////////////////////////////////////////////////////////
void test_c_use_lua_variant()
{
    const char* luascript = "width=111 ; height=222";
    lua_State* L = luaL_newstate();
    int w,h;
    //luaL_loadfile的作用是编译lua文件为一个chunk
    //把这个chunk当作一个匿名函数压在栈上
    //lua_pcall的作用是执行这个匿名函数,然后才能取得栈上的值
    if(luaL_loadstring(L,luascript) || lua_pcall(L,0,0,0)){
        printf("Error msg is %s.\n",lua_tostring(L,-1));
        return;
    }
    lua_getglobal(L,"width"); //调用次函数,则把全局变量压栈
    lua_getglobal(L,"height");//这里压栈次序决定了后面取值的索引
    if(!lua_isnumber(L,-2)){
        printf("width must be number\n");
        return;
    }
    if(!lua_isnumber(L,-1)){
        printf("height must be number\n");
        return ;
    }
    w = lua_tointeger(L,-2);
    h = lua_tointeger(L,-1);

    printf("width = %d height = %d\n",w,h);
    lua_close(L);
}
//////////////////////////////////////////////////////////////////////////
void test_c_use_lua_table()
{
    lua_State* L = luaL_newstate();
    if (luaL_loadstring(L,"background = { r = 0.30, g = 0.10, b = 0 }") 
        || lua_pcall(L,0,0,0)) {
            printf("Error Msg is %s.\n",lua_tostring(L,-1));
            return;
    }
    lua_getglobal(L,"background");
    if (!lua_istable(L,-1)) {
        printf("'background' is not a table.\n" );
        return;
    }
    //下面取栈上的table内容
    lua_getfield(L,-1,"r");
    if (!lua_isnumber(L,-1)) {
        printf("Invalid component in background color.\n");
        return;
    }
    int r = (int)(lua_tonumber(L,-1) * 255);
    lua_pop(L,1);//出栈

    lua_getfield(L,-1,"g");
    if (!lua_isnumber(L,-1)) {
        printf("Invalid component in background color.\n");
        return;
    }
    int g = (int)(lua_tonumber(L,-1) * 255);
    lua_pop(L,1);

    lua_pushnumber(L,0.4);//入栈,栈顶位置-1的值是0.4
    lua_setfield(L,-2,"b");//设置background["b"]=0.4,注意-2这个位置表示table的位置!!
    lua_getfield(L,-1,"b");//取table值
    stackDump(L);
    if (!lua_isnumber(L,-1)) {
        printf("Invalid component in background color.\n");
        return;
    }
    int b = (int)(lua_tonumber(L,-1) * 255);
    printf("r = %d, g = %d, b = %d\n",r,g,b);
    lua_pop(L,1);
    stackDump(L);
    lua_pop(L,1); //栈被清空了

    lua_close(L);
}
//////////////////////////////////////////////////////////////////////////
void test_c_new_lua_table()
{
    lua_State* L = luaL_newstate();
    lua_newtable(L);//Lua会生成一个新的table对象并将其压入栈中。
    lua_pushnumber(L,0.3);
    stackDump(L);//table 0.3
    lua_setfield(L,-2,"r");//设置table["r"]=0.3完了把0.3弹出
    stackDump(L);//table

    lua_pushnumber(L,0.1);
    lua_setfield(L,-2,"g");

    lua_pushnumber(L,0.4);
    lua_setfield(L,-2,"b");

    stackDump(L);//table
    lua_setglobal(L,"background");//栈顶元素出栈
    stackDump(L);//nil
    //调用该宏后,Lua会将当前栈顶的值赋值给第二个参数指定的全局变量名。
    //该宏在执行成功后,会将刚刚赋值的值从栈顶弹出。

    lua_getglobal(L,"background");//把background压栈
    stackDump(L);//table
    if (!lua_istable(L,-1)) {
        printf("'background' is not a table.\n" );
        return;
    }
    lua_getfield(L,-1,"r");//table["r"]压栈
    stackDump(L);//table 0.3
    if (!lua_isnumber(L,-1)) {
        printf("Invalid component in background color.\n");
        return;
    }
    int r = (int)(lua_tonumber(L,-1) * 255);
    lua_pop(L,1);//table

    lua_getfield(L,-1,"g");//table 0.1
    if (!lua_isnumber(L,-1)) {
        printf("Invalid component in background color.\n");
        return;
    }
    int g = (int)(lua_tonumber(L,-1) * 255);
    lua_pop(L,1);//table

    lua_getfield(L,-1,"b");//table 0.4
    if (!lua_isnumber(L,-1)) {
        printf("Invalid component in background color.\n");
        return;
    }
    int b = (int)(lua_tonumber(L,-1) * 255);
    printf("r = %d, g = %d, b = %d\n",r,g,b);
    lua_pop(L,1);//table
    lua_pop(L,1);//nil

    lua_close(L);
}
//////////////////////////////////////////////////////////////////////////
void test_lua_call_c_fun()
{
    const char* testfunc = "print(add2(1.0,2.0)) print(sub2(20.1,19))";
    lua_State* L = luaL_newstate();
    luaL_openlibs(L);
    //将指定的函数注册为Lua的全局函数变量,其中第一个字符串参数为Lua代码在调用C函数时使用的全局函数名
    //,第二个参数为实际C函数的指针。
    lua_register(L, "add2", add2);
    lua_register(L, "sub2", sub2);
    //在注册完所有的C函数之后,即可在Lua的代码块中使用这些已经注册的C函数了。
    if (luaL_dostring(L,testfunc))
        printf("Failed to invoke.\n");
    lua_close(L);
}
//////////////////////////////////////////////////////////////////////////
//lua文件里的print被C调用时,是可以打印到终端的!!!
void test_lua_call_c_dll_fun()
{
    lua_State* L = luaL_newstate();
    luaL_openlibs(L);
    if(luaL_dofile(L,"lua_call_c.lua"))
        printf("load lua_call_c.lua failed\n");;
    lua_close(L);
}


#define BITS_PER_WORD (CHAR_BIT * sizeof(int))
#define I_WORD(i)     ((unsigned int)(i))/BITS_PER_WORD
#define I_BIT(i)      (1 << ((unsigned int)(i)%BITS_PER_WORD))

typedef struct NumArray {
    int size;
    unsigned int values[1];
} NumArray;

extern "C" int newArray(lua_State* L)
{
    //1. 检查第一个参数是否为整型。以及该参数的值是否大于等于1.
    int n = luaL_checkint(L,1);
    luaL_argcheck(L, n >= 1, 1, "invalid size.");
    size_t nbytes = sizeof(NumArray) + I_WORD(n - 1) * sizeof(int);
    //2. 参数表示Lua为userdata分配的字节数。同时将分配后的userdata对象压入栈中。
    NumArray* a = (NumArray*)lua_newuserdata(L,nbytes);
    a->size = n;
    for (int i = 0; i < I_WORD(n - 1); ++i)
        a->values[i] = 0;
    //获取注册表变量myarray,该key的值为metatable。
    luaL_getmetatable(L,"myarray");
    //将userdata的元表设置为和myarray关联的table。同时将栈顶元素弹出。
    lua_setmetatable(L,-2);
    return 1;
}

extern "C" int setArray(lua_State* L)
{
    //1. Lua传给该函数的第一个参数必须是userdata,该对象的元表也必须是注册表中和myarray关联的table。
    //否则该函数报错并终止程序。
    NumArray* a = (NumArray*)luaL_checkudata(L,1,"myarray");
    int index = luaL_checkint(L,2) - 1;
    //2. 由于任何类型的数据都可以成为布尔值,因此这里使用any只是为了确保有3个参数。
    luaL_checkany(L,3);
    luaL_argcheck(L,a != NULL,1,"'array' expected.");
    luaL_argcheck(L,0 <= index && index < a->size,2,"index out of range.");
    if (lua_toboolean(L,3))
        a->values[I_WORD(index)] |= I_BIT(index);
    else
        a->values[I_WORD(index)] &= ~I_BIT(index);
    return 0;
}

extern "C" int getArray(lua_State* L)
{
    NumArray* a = (NumArray*)luaL_checkudata(L,1,"myarray");
    int index = luaL_checkint(L,2) - 1;
    luaL_argcheck(L, a != NULL, 1, "'array' expected.");
    luaL_argcheck(L, 0 <= index && index < a->size,2,"index out of range");
    lua_pushboolean(L,a->values[I_WORD(index)] & I_BIT(index));
    return 1;
}

extern "C" int getSize(lua_State* L)
{
    NumArray* a = (NumArray*)luaL_checkudata(L,1,"myarray");
    luaL_argcheck(L,a != NULL,1,"'array' expected.");
    lua_pushinteger(L,a->size);
    return 1;
}

extern "C" int array2string(lua_State* L)
{
    NumArray* a = (NumArray*)luaL_checkudata(L,1,"myarray");
    lua_pushfstring(L,"array(%d)",a->size);
    return 1;
}

static luaL_Reg arraylib_f [] = { 
    {"new", newArray},
    {NULL, NULL} 
}; 

static luaL_Reg arraylib_m [] = {
    {"set", setArray},
    {"get", getArray},
    {"size", getSize},
    {"__tostring", array2string}, //print(a)时Lua会调用该元方法。
    {NULL, NULL} 
};

    int luaopen_testuserdata(lua_State* L)
{
    //1. 创建元表,并将该元表指定给newArray函数新创建的userdata。在Lua中userdata也是以table的身份表现的。
    //这样在调用对象函数时,可以通过验证其metatable的名称来确定参数userdata是否合法。
    luaL_newmetatable(L,"myarray");
    lua_pushvalue(L,-1);
    //2. 为了实现面对对象的调用方式,需要将元表的__index字段指向自身,同时再将arraylib_m数组中的函数注册到
    //元表中,之后基于这些注册函数的调用就可以以面向对象的形式调用了。
    //lua_setfield在执行后会将栈顶的table弹出。
    lua_setfield(L,-2,"__index");
    //将这些成员函数注册给元表,以保证Lua在寻找方法时可以定位。NULL参数表示将用栈顶的table代替第二个参数。
    luaL_newlib(L,arraylib_m);
    //这里只注册的工厂方法。
    luaL_newlib(L,arraylib_f);
    return 1;
}
int main()
{
    test_c_call_lua_fun_noParam();
    test_c_call_lua_fun_withParam();
    test_c_call_lua_fun_withParam2();
    test_c_use_lua_variant();
    test_c_use_lua_table();
    test_c_new_lua_table();
    test_lua_call_c_fun();
    test_lua_call_c_dll_fun();
    getchar();
    return 0;
}