快速掌握Lua 5.3 —— 扩展你的程序 (2)

Q:如何在C中调用Lua的函数?

A:

1、将函数名入栈。

2、将参数按照形参的顺序依次入栈。

3、调用函数。此过程会将函数的参数出栈,调用完成后将函数的返回值入栈。

4、获取函数的返回值。

“config.lua”文件中:

function f(x, y)
    return (x ^ 2 * math.sin(y)) / (1 - x)
end

“main.c”文件中:

#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <lua.h>
#include <lauxlib.h>
#include <lualib.h>

void error(lua_State *L, const char *fmt, ...)
{
    va_list argp;
    va_start(argp, fmt);
    vfprintf(stderr, fmt, argp);
    va_end(argp);
    lua_close(L);
    exit(EXIT_FAILURE);
}

void load(lua_State *L, char *filename)
{
    if(luaL_loadfile(L, filename) || lua_pcall(L, 0, 0, 0))
        error(L, "cannot run configuration file: %s", lua_tostring(L, -1));
}

// 函数内部调用Lua的同名函数。
double f(lua_State *L, double x, double y)
{
    double z = 0.0;

    lua_getglobal(L, "f");    // 将函数名入栈。
    // 将参数按照Lua中函数的形参顺序依次入栈。
    lua_pushnumber(L, x);
    lua_pushnumber(L, y);

    if(lua_pcall(L, 2, 1, 0) != 0)    // 以保护模式调用函数,2个参数,1个返回值。
        error(L, "error running function `f‘: %s", lua_tostring(L, -1));

    if(!lua_isnumber(L, -1))    // 获取函数的返回值。
        error(L, "function `f‘ must return a number");
    z = lua_tonumber(L, -1);
    lua_pop(L, 1);    // 弹出返回值,保证栈的状态与调用该函数时一样。

    return z;
}

int main(void)
{
    lua_State *L = luaL_newstate();
    luaL_openlibs(L);

    load(L, "config.lua");
    printf("%f\n", f(L, 0.5, 0.5));

    lua_close(L);

    return 0;
}
prompt> gcc main.c -llua -ldl -lm -Wall
prompt> ./a.out
0.239713

Q:如何通过C中调用Lua函数的形式实现类似于C++中的函数重载?

A:我们继续扩展上面例子中的程序(本例也有些类似于JNI的调用方式),

“main.c”文件中:

#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include <lua.h>
#include <lauxlib.h>
#include <lualib.h>

void error(lua_State *L, const char *fmt, ...)
{
    va_list argp;
    va_start(argp, fmt);
    vfprintf(stderr, fmt, argp);
    va_end(argp);
    lua_close(L);    // 关闭Lua状态机。
    exit(EXIT_FAILURE);    // 以失败退出。
}

void load(lua_State *L, char *filename)
{
    if(luaL_loadfile(L, filename) || lua_pcall(L, 0, 0, 0))
        error(L, "cannot run configuration file: %s", lua_tostring(L, -1));
}

/* "func": 被调用的Lua函数的名字。
 * "sig": 参数以及返回值得类型。
   ‘d‘: double。
   ‘i‘: integer。
   ‘s‘: string。
   ‘>‘: 参数与返回值之间的分隔符。如果函数没有返回值,可以忽略。
 * "...": 参数以及返回值变量。
 */
void call_va(lua_State *L, const char *func, const char *sig, ...)
{
    va_list vl;
    int narg = 0, nres = 0;    // 参数的个数以及返回值的个数。

    va_start(vl, sig);
    lua_getglobal(L, func);    // 将Lua函数入栈。

    narg = 0;
    while(*sig)    // 逐一判断参数的类型,并以合适的方式入栈。
    {
        switch(*sig++)
        {
            case ‘d‘:  /* double argument */
                lua_pushnumber(L, va_arg(vl, double));
                break;

            case ‘i‘:  /* int argument */
                lua_pushinteger(L, va_arg(vl, int));
                break;

            case ‘s‘:  /* string argument */
                lua_pushstring(L, va_arg(vl, char *));
                break;

            case ‘>‘:    // 参数已经获取完毕,跳出"while"。
                goto endwhile;

            default:
                error(L, "invalid option(%c)", *(sig - 1));
        }
        narg++;
        /* 将栈的大小加1。如果执行失败(没有足够的内存空间了),则报错。
         * 最后一个参数是错误信息中显示的额外信息("NULL"代表无额外信息)。
         * 由于本函数接收可变参数,即可以接收任意多的参数,所以必须检查栈空间的大小。
         */
        luaL_checkstack(L, 1, "too many arguments");
    }
endwhile:

    nres = strlen(sig);    // 获取返回值的数量。
    if(lua_pcall(L, narg, nres, 0) != 0)
        error(L, "error running function `%s‘: %s", func, lua_tostring(L, -1));

    nres = -nres;    // 返回值数量取反,可以通过负索引从第一个返回值开始获取。
    while(*sig)    // 获取返回值。
    {
        switch(*sig++)
        {
            case ‘d‘:  /* double result */
                if(!lua_isnumber(L, nres))
                    error(L, "wrong result type");
                *va_arg(vl, double *) = lua_tonumber(L, nres);
                break;

            case ‘i‘:  /* int result */
                if(!lua_isinteger(L, nres))
                    error(L, "wrong result type");
                *va_arg(vl, int *) =(int)lua_tointeger(L, nres);
                break;

            case ‘s‘:  /* string result */
                if(!lua_isstring(L, nres))
                    error(L, "wrong result type");
                *va_arg(vl, const char **) = lua_tostring(L, nres);
                break;

            default:
                error(L, "invalid option(%c)", *(sig - 1));
        }
        nres++;
    }
    va_end(vl);
}

int main(void)
{
    double z = 0.0f;
    lua_State *L = luaL_newstate();

    luaL_openlibs(L);

    load(L, "config.lua");
    // 调用Lua的"f"函数,第一个参数是浮点数,第二个参数是字符串,函数返回一个浮点数由"z"接收。
    call_va(L, "f", "ds>d", 0.5, "0.5", &z);
    printf("%f\n", z);

    lua_close(L);

    return 0;
}
prompt> gcc main.c -llua -ldl -lm -Wall
prompt> ./a.out
0.239713

附加:

1、call_va()中无需判断指定的”func”是否是Lua中的一个有效的函数,lua_pcall()可以捕捉这个错误。

2、由于Lua函数的返回值可能是字符串,所以call_va()中不能将返回值出栈。这个工作需要由调用者将返回值转存后,再手动将结果出栈。

时间: 2024-10-10 16:30:30

快速掌握Lua 5.3 —— 扩展你的程序 (2)的相关文章

快速掌握Lua 5.3 —— 扩展你的程序 (1)

Q:如何在C中将Lua作为配置文件语言使用? A: "config.lua"文件中: -- window size width = 200 height = 300 "main.c"文件中: #include <stdarg.h> #include <stdio.h> #include <stdlib.h> #include <lua.h> #include <lauxlib.h> #include <

高速掌握Lua 5.3 —— 扩展你的程序 (1)

Q:怎样在C中将Lua作为配置文件语言使用? A: "config.lua"文件里: -- window size width = 200 height = 300 "main.c"文件里: #include <stdarg.h> #include <stdio.h> #include <stdlib.h> #include <lua.h> #include <lauxlib.h> #include <

对lua的简单扩展,使它能快速关联C++的数据。

很早的时候,我纠结过一件事,假如,我在C++公开给脚本的接口中,要使用C++里的某一个对象,并且,我的程序中有很多个不同的lua虚拟机,每一个虚拟机要关联一个C++对象,并且这是多线程的,那么这时候应该如何快速的利用lua_State指针来定位到对象指针呢? 以前我没有能力读懂lua的源码,也可以说不知道关键部分如何操作,我当时的做法,是利用临界区和std::map来解决问题的,很明显这个方式的效率很低很低. 现在有能力读lua源码了,当然有更有效的解决办法了,因为在我们利用lua的过程中,lu

快速掌握Lua 5.3 —— 字符串库 (3)

Q:什么情况下"pattern"会匹配空串? A:要小心的使用*和-,因为它们可以匹配零次. -- 如果你打算用"%a*"匹配单词,你会发现到处都是单词. print(string.find(";$% **#$hello13", "%a*")) --> 1 0 print(string.find(";$% **#$hello13", "%a*", 6)) --> 6 5 --

快速掌握Lua 5.3 —— 编写提供给Lua使用的C库函数的技巧 (2)

Q:什么是"registry"? A:有时候,我们需要在程序中使用一些非局部的变量.在C中我们可以使用全局变量或是静态变量来实现,而在为Lua编写C库的过程中,使用以上类型的变量并不是一个好的方式.首先,这些变量中无法存储Lua的值.其次,这些变量如果在多个Lua状态机中被使用,则很可能造成非预期的结果. 一个替代方案是,将这些值存储在Lua的全局变量中.这种方式解决了上面提到的两个问题,Lua全局变量可以存储任何Lua的值,同时每一个Lua状态机都有自己独立的一套全局变量.但这依旧不

快速掌握Lua 5.3 —— 资源管理

Q:Lua的"finalizer"? A:在我们之前看到的使用"userdata"的例子中,我们只关心如何创建并使用"userdata",从未关心何时以及如何释放我们创建的"userdata",因为这些事都由Lua的垃圾回收器帮我们处理.然而很多时候,程序并不会这么简单,有可能在其中还会涉及到文件句柄,窗口句柄等,此时这些资源就需要创建者进行管理. 一些面向对象语言提供了析够器用来帮助用户管理这些资源,Lua同样提供了类似的机

Lua_第24章 扩展你的程序

 第24章扩展你的程序 作为配置语言是 LUA的一个重要应用.在这个章节里,我们举例说明如何用 LUA 设 置一个程序.让我们用一个简单的例子开始然后展开到更复杂的应用中. 首先,让我们想象一下一个简单的配置情节:你的 C程序(程序名为 PP)有一个 窗口界面并且可以让用户指定窗口的初始大小.显然,类似这样简单的应用,有多种解决方法比使用LUA更简单,比如环境变量或者存有变量值的文件.但,即使是用一个 简单的文本文件,你也不知道如何去解析.所以,最后决定采用一个 LUA 配置文件(这就是 LUA

[转] 扩展微信小程序框架功能

通过第三方 JavaScript 库,扩展微信小程序框架功能. 扩展微信小程序框架功能(1)——Promise ES6 对 Promise 有了原生的支持,但微信开发者工具更新版本(0.11.112200)后, 移除了开发者工具对 ES6 中Promise 特性原生的支持, 需要引入第三方的 Promise 库. 扩展微信小程序框架功能(2)——Generator Generator函数是ES6提供的一种异步编程解决方案,语法行为与传统函数完全不同. 扩展微信小程序框架功能(3)——函数功能增强

扩展Web应用程序(上)

1.8 扩展Web应用程序 1.8.1 性能 对于开发者而言,他们主要关注程序的响应时间和延展性. 响应时间也是衡量一个应用程序的效率的指标之一.如果一个请求到响应时间超出了良好的感知范围,我们就可以认为这个程序的性能很糟糕.一般而言,最好能够在一两秒内得到想要的页面. 延展性,说了是添加了更多的硬件设备,应用程序能够线性地承受更多的请求.添加硬件的方式有两种: l  向上扩展(垂直扩展)--增加CUPs数量或者在一个单一的盒子(指的是一台机器)里添加更快的CUP l  横向扩展(水平扩展)--