Q:什么是Lua的虚拟栈?

A:C与Lua之间通信关键内容在于一个虚拟的栈。差点儿全部的调用都是对栈上的值进行操作,全部C与Lua之间的数据交换也都通过这个栈来完毕。另外,你也能够使用栈来保存暂时变量。

每个与Lua通信的C函数都有其独有的虚拟栈。虚拟栈由Lua管理。

栈的使用攻克了C和Lua之间两个不协调的问题:第一。Lua会自己主动进行垃圾收集,而C要求显式的分配存储单元。两者引起的矛盾。第二。Lua中的动态类型和C中的静态类型不一致引起的混乱。

Q:虚拟栈索引?

A:正索引的顺序为元素入栈的先后顺序。栈中的第一个元素(也就是第一个被入栈的元素)索引为1。第二个元素索引为2,以此类推。我们也能够使用负索引,以栈顶作为索引的參照来存取元素。栈顶元素的索引为-1。其前面的一个元素索引为-2,以此类推。正负索引各有其适用的场合。灵活应用,简化编码。

Q:第一个样例?

A:一个简单的Lua解释器。

#include <stdio.h>
#include <string.h>
/* 定义对Lua的基本操作的函数,当中定义的全部函数都有"lua_"前缀。
* 比如调用Lua函数(如"lua_pcall()"),获取以及赋值Lua的全局变量,注冊函数供Lua调用,等等。 */
#include <lua.h>
/* 定义"auxiliary library",当中定义的全部函数都有"luaL_"前缀。
* "auxiliary library"使用lua.h中提供的基本API实现了更高一层抽象方法的实现。
* 全部的Lua标准库都使用了auxiliary library。
*/
#include <lauxlib.h>
/* 定义打开各种库的函数。
*/
#include <lualib.h> int main(void)
{
char buff[256] = {0};
int error = 0;
lua_State *L = luaL_newstate(); // 创建Lua状态机。 luaL_openlibs(L); // 打开Lua状态机"L"中的全部Lua标准库。 while (fgets(buff, sizeof(buff), stdin) != NULL) { // 获取用户输入。
/* "luaL_loadbuffer()",编译用户输入为Lua的"chunk"并将其入栈。
* "line"是"chunk"的名字。用于调试信息和错误消息。 * "lua_pcall()",以保护模式执行"chunk"并将其从栈顶弹出。
* 两个函数均是在成功时返回"LUA_OK"(实际的值是0),
* 失败时返回错误码,并将错误信息入栈。
*/
error = luaL_loadbuffer(L, buff, strlen(buff), "line") || lua_pcall(L, 0, 0, 0);
if (error) {
fprintf(stderr, "%s", lua_tostring(L, -1)); // 从栈顶取出错误打印信息。 lua_pop(L, 1); // 弹出栈顶的错误信息。
}
}
lua_close(L); // 关闭Lua状态机。 return 0;
}
prompt> gcc main.c -llua -ldl -lm
prompt> ./a.out
print("Hello World!")
Hello World!

Q:怎样将元素入栈与出栈?

A:Lua中的数据类型与C中的并不一一相应,对于不同的数据类型,须要不同的入栈函数。

// 将"b"作为一个布尔值入栈。
void lua_pushboolean(lua_State *L, int b) /* 将C函数"fn"以及其在虚拟栈上关联的"n"个值作为"Closure"入栈。 * "n"最大为255,第一个被关联的值首先入栈。栈顶是最后一个被关联的值,
* 这些值会在函数调用成功后被出栈。
*/
void lua_pushcclosure(lua_State *L, lua_CFunction fn, int n) // 将C函数"f"作为函数入栈。 内部实际调用"lua_pushcclosure(L, f, 0)"。 void lua_pushcfunction(lua_State *L, lua_CFunction f) /* 将一个被格式化后的字符串入栈。函数返回这个字符串的指针。
* 与C语言中的"sprintf()"相似。其差别在于:
* 1、不须要为结果分配空间。 * 其结果是一个Lua字符串,由Lua来关心其内存分配(同一时候通过垃圾收集来释放内存)。 * 2、"fmt"不支持符号、宽度、精度。 仅仅支持:
* "%%": 字符'%'。 * "%s": 带零终止符的字符串,没有长度限制。
* "%f": "lua_Number"(Lua中的浮点数类型)。 * "%L": "lua_Integer"(Lua中的整数类型)。 * "%p": 指针或是一个十六进制数。
* "%d": "int"。 * "%c": "char"。
* "%U": 用"long int"表示的UTF-8字。
*/
const char *lua_pushfstring(lua_State *L, const char *fmt, ...) /* 将长度为"len"。非字面形式的字符串"s"入栈。 * Lua对这个字符串做一个内部副本(或是复用一个副本),
* 因此"s"处的内存在函数返回后,能够释放掉或是立马重用于其他用途。 * 字符串内能够是随意二进制数据。包括'\0'。函数返回内部副本的指针。
*/
const char *lua_pushlstring(lua_State *L, const char *s, size_t len) // 将字面形式的字符串"s"入栈,函数自己主动给出字符串的长度。返回内部副本的指针。 const char *lua_pushliteral(lua_State *L, const char *s) /* 将以'\0'结尾的字符串"s"入栈。
* Lua对这个字符串做一个内部副本(或是复用一个副本)。
* 因此"s"处的内存在函数返回后,能够释放掉或是立马重用于其他用途。
* 函数返回内部副本的指针。 假设"s"为"NULL",将"nil"入栈并返回"NULL"。 */
const char *lua_pushstring(lua_State *L, const char *s) // 等价于"lua_pushfstring()"。只是是用"va_list"接收參数,而不是用可变数量的实际參数。
const char *lua_pushvfstring(lua_State *L, const char *fmt, va_list argp) // 将全局环境入栈。
void lua_pushglobaltable(lua_State *L) // 将值为"n"的整数入栈。
void lua_pushinteger(lua_State *L, lua_Integer n) /* 将一个轻量用户数据"p"入栈。
* 用户数据是保留在Lua中的C值。轻量用户数据表示一个指针"void*"。 * 它像一个数值,你不须要专门创建它,它也没有独立的元表,
* 并且也不会被收集(由于从来不须要创建)。仅仅要表示的C地址同样。两个轻量用户数据就相等。
*/
void lua_pushlightuserdata(lua_State *L, void *p) // 将空值入栈。
void lua_pushnil(lua_State *L) // 将一个值为"n"的浮点数入栈。
void lua_pushnumber(lua_State *L, lua_Number n) // "L"表示的线程入栈。假设这个线程是当前状态机的主线程的话。返回1。 int lua_pushthread(lua_State *L) // 将虚拟栈上索引"index"处的元素的副本入栈。
void lua_pushvalue(lua_State *L, int index)

相应的出栈函数就非常easy了,仅仅有一个lua_pop()

// 从虚拟栈中弹出"n"个元素。
void lua_pop(lua_State *L, int n)

Q:怎样检查虚拟栈中元素的类型?

A:

// 假设栈中索引"index"处的元素为"bool"类型,则返回1,否则返回0。

int lua_isboolean(lua_State *L, int index)

// 假设栈中索引"index"处的元素是一个C函数,则返回1。否则返回0。

int lua_iscfunction(lua_State *L, int index)

// 假设栈中索引"index"处的元素是一个C函数或是一个Lua函数,则返回1。否则返回0。

int lua_isfunction(lua_State *L, int index)

// 假设栈中索引"index"处的元素是一个整数。则返回1,否则返回0。
int lua_isinteger(lua_State *L, int index) // 假设栈中索引"index"处的元素是一个轻量级的"userdata",则返回1,否则返回0。
int lua_islightuserdata(lua_State *L, int index) // 假设栈中索引"index"处的元素是一个"nil",则返回1。否则返回0。
int lua_isnil(lua_State *L, int index // 假设"index"是一个无效索引时,返回1,否则返回0。 int lua_isnone(lua_State *L, int index) // 假设"index"是一个无效索引或者"index"处的元素是一个"nil",则返回1,否则返回0。 int lua_isnoneornil(lua_State *L, int index) /* 假设栈中索引"index"处的元素是一个数值或者是一个能够转换为数值的字符串,
* 则返回1。否则返回0。
*/
int lua_isnumber(lua_State *L, int index) /* 假设栈中索引"index"处的元素是一个字符串或者是一个能够转换为字符串的数值。
* 则返回1。否则返回0。 */
int lua_isstring(lua_State *L, int index) // 假设栈中索引"index"处的元素是一个"table"。则返回1,否则返回0。 int lua_istable(lua_State *L, int index) // 假设栈中索引"index"处的元素是一个线程。则返回1,否则返回0。
int lua_isthread(lua_State *L, int index) // 假设栈中索引"index"处的元素是一个"userdata",则返回1,否则返回0。 int lua_isuserdata (lua_State *L, int index) // 假设栈中的"coroutine"能够被挂起,则返回1,否则返回0。
int lua_isyieldable(lua_State *L) /* 返回栈中索引"index"处元素的类型。这些类型在"lua.h"中定义,例如以下:
* #define LUA_TNONE (-1) // 无效
* #define LUA_TNIL 0 // "nil"
* #define LUA_TBOOLEAN 1 // "bool"
* #define LUA_TLIGHTUSERDATA 2 // 轻量级"userdata"
* #define LUA_TNUMBER 3 // 数值
* #define LUA_TSTRING 4 // 字符串
* #define LUA_TTABLE 5 // "table"
* #define LUA_TFUNCTION 6 // 函数
* #define LUA_TUSERDATA 7 // "userdata"
* #define LUA_TTHREAD 8 // 线程
*/
int lua_type(lua_State *L, int index) // 返回"tp"表示的类型的名字。"tp"是"lua_type()"的返回值之中的一个。 const char *lua_typename(lua_State *L, int tp)

Q:怎样转换虚拟栈中元素的类型?

A:

// 将栈中"index"处的元素转换为C中的"bool"值返回。

int lua_toboolean(lua_State *L, int index)

// 将栈中"index"处的元素转换为一个C函数返回。

指定的元素必须是一个C函数,否则返回"NULL"。
lua_CFunction lua_tocfunction(lua_State *L, int index) /* 将栈中"index"处的元素转换为一个整数返回。
* 指定的元素必须是一个整数或是一个能够被转换为整数的数字或字符串,否则返回0。
* 假设"isnum"非"NULL"。"*isnum"会被赋值为操作是否成功的"bool"值。 */
lua_Integer lua_tointegerx(lua_State *L, int index, int *isnum) // 内部调用"lua_tointegerx(L, index, NULL)"。 lua_Integer lua_tointeger(lua_State *L, int index) /* 将栈中"index"处的元素转换为一个C字符串并将其指针返回。 * 假设"len"非"NULL","*len"将获得字符串的长度。 * 指定元素必须是一个字符串或是一个数字。否则返回返回"NULL"。 * 假设指定元素是一个数字,函数会将元素的类型转换为字符串。
* 返回的字符串结尾包括'\0',而在字符串中同意包括多个'\0'。
* 函数返回的字符串应马上转存,否则有可能被Lua垃圾回收器回收。
*/
const char *lua_tolstring(lua_State *L, int index, size_t *len) /* 将栈中"index"处的元素转换为一个浮点数返回。
* 指定的元素必须是一个数字或是一个可被转换为数字的字符串。否则返回0。 * 假设"isnum"非"NULL","*isnum"会被赋值为操作是否成功的"bool"值。
*/
lua_Number lua_tonumberx(lua_State *L, int index, int *isnum) // 内部调用"lua_tonumberx(L, index, NULL)"。
lua_Number lua_tonumber(lua_State *L, int index) /* 将栈中"index"处的元素转换为一个C指针返回。 * 指定的元素必须是一个"userdata"。"table",线程或是一个函数,否则返回"NULL"。
*/
const void *lua_topointer(lua_State *L, int index) // 内部调用"lua_tolstring(L, index, NULL)"。
const char *lua_tostring(lua_State *L, int index) /* 将栈中"index"处的元素转换为一个Lua线程返回。 * 指定的元素必须是一个线程,否则返回"NULL"。 */
lua_State *lua_tothread(lua_State *L, int index) /* 栈中"index"处的元素假设是一个全然"userdata",则返回其内存地址的指针。
* 假设是一个轻量级"userdata",则返回其存储的指针。
*/
void *lua_touserdata(lua_State *L, int index)

Q:一些维护虚拟栈的方法?

A:

/* int lua_gettop(lua_State *L)
* 返回栈顶元素的索引。
* 由于栈中元素的索引是从1開始编号的。所以函数的返回值相当于栈中元素的个数。
* 返回值为0表示栈为空。 */
lua_gettop(L); // 返回栈中元素的个数。 /* void lua_settop(lua_State *L, int index)
* 设置栈顶为索引"index"指向处。
* 假设"index"比"lua_gettop()"的值大。那么多出的新元素将被赋值为"nil"。
*/
lua_settop(L, 0); // 清空栈。 /* void lua_remove(lua_State *L, int index)
* 移除栈中索引"index"处的元素,该元素之上的全部元素下移。 */ /* void lua_insert(lua_State *L, int index)
* 将栈顶元素移动到索引"index"处。索引"index"(含)之上的全部元素上移。 */ /* void lua_replace(lua_State *L, int index)
* 将栈顶元素移动到索引"index"处。(相当于覆盖了索引"index"处的元素)
*/

一个”dump”整个堆栈内容的样例。

#include <stdio.h>
#include <lua.h>
#include <lualib.h>
#include <lauxlib.h> static void stackDump(lua_State *L)
{
int i = 0;
int top = lua_gettop(L); // 获取栈中元素个数。
for (i = 1; i <= top; ++i) // 遍历栈中每个元素。
{
int t = lua_type(L, i); // 获取元素的类型。
switch(t)
{
case LUA_TSTRING: // strings
printf("'%s'", lua_tostring(L, i));
break; case LUA_TBOOLEAN: // bool
printf(lua_toboolean(L, i) ? "true" : "false");
break; case LUA_TNUMBER: // number
printf("%g", lua_tonumber(L, i)); // %g,自己主动选择%e或%f表示数值。
break; default: // other values
printf("%s", lua_typename(L, t)); // 将宏定义的类型码转换为类型名称。
break;
}
printf(" "); // put a separator
}
printf("\n");
} int main(void)
{
lua_State *L = luaL_newstate(); // 创建Lua状态机。
// 向虚拟栈中压入值。 lua_pushboolean(L, 1); // true
lua_pushnumber(L, 10); // 10
lua_pushnil(L); // nil
lua_pushstring(L, "hello"); // "hello"
stackDump(L); // true 10 nil 'hello' lua_pushvalue(L, -4); // 将索引-4处的值的副本入栈。 stackDump(L); // true 10 nil 'hello' true lua_replace(L, 3); // 将栈顶元素移动到索引3处。并覆盖原先的元素。 stackDump(L); // true 10 true 'hello' lua_settop(L, 6); // 将栈顶设置为索引6处,多出来的新元素被赋值为"nil"。 stackDump(L); // true 10 true 'hello' nil nil lua_remove(L, -3); // 移除索引-3处的元素,其上全部元素下移。
stackDump(L); // true 10 true nil nil lua_settop(L, -5); // 将栈顶设置为索引-5处。
stackDump(L); // true lua_close(L); // 关闭Lua状态机。 return 0;
}

附加:

1、Lua库未定义不论什么全局变量,它全部的状态都保存在动态结构lua_State中,并且指向这个结构的指针作为全部Lua函数的一个參数。这种实现方式使得Lua能够重入(reentrant)。并且为在多线程中的使用中作好准备。

2、假设你是在C++的代码中使用Lua,别忘了,

extern "C" {
#include <lua.h>
}

3、Lua中的字符串能够不以'\0'作为结束符。这样,字符串中能够包括随意的二进制(甚至是'\0'),字符串的长度由明白的长度指定。

4、在lua_pushlstring()lua_pushliteral()以及lua_pushstring()中。Lua不保存字符串(变量)指针。因此当这些函数返回时。你就能够改动你的字符串了。

5、对于入栈是否有栈空间的情况,你须要自己推断,别忘了如今你是一个C程序猿。当Lua启动或者不论什么Lua调用C的时候,虚拟栈中至少有20个空间(在”lua.h”中LUA_MINSTACK定义),这对于普通情况下够用了,所以一般不用考虑。但有时候确实须要很多其他的栈空间(比方调用一个不定參数的函数),此时你须要使用lua_checkstack检查栈空间的情况。

/* 确保堆栈上至少有"n"个额外空位。假设不能把堆栈扩展到相应的尺寸,函数返回"false"。
* 失败的原因包括将把栈扩展到比固定最大尺寸还大(至少是几千个元素)或分配内存失败。
* 这个函数永远不会缩小堆栈,假设堆栈已经比须要的大了,那么就保持原样。 */
int lua_checkstack(lua_State *L, int sz)

6、遍历一个”table”时。不要将lua_tolstring()作用在”key”上,这样会导致lua_next()无法正常执行。

7、在寻常的编码中,对于执行失败时会返回0lua_to*()类别的函数。我们最好先使用lua_is*()类别的函数推断參数的类型,之后再使用lua_to*()类别的函数对參数进行转换;而对于执行失败时会返回NULLlua_to*()类别的函数,我们能够直接使用lua_to*()类别的函数直接对參数进行转换。推断函数的返回值非NULL与否,就能推断转换是否成功。

8、lua_pop()就是通过lua_settop()实现的(在”lua.h”中定义),

#define lua_pop(L,n) lua_settop(L, -(n)-1)

9、下面操作对于虚拟栈没有不论什么影响,

lua_settop(L, -1);    /* set top to its current value */
lua_insert(L, -1); /* move top element to the top */
lua_replace(L, -1); /* replace top element by the top element */

高速掌握Lua 5.3 —— Lua与C之间的交互概览的更多相关文章

  1. Lua 架构 The Lua Architecture

    转载自:http://magicpanda.net/2010/10/lua%E6%9E%B6%E6%9E%84%E6%96%87%E6%A1%A3/ Lua架构文档(翻译) 十 102010 前段时间 ...

  2. Lua和C之间的交互

    转自:http://blog.csdn.net/sumoyu/article/details/2592693 (一) Lua 调C函数 什么样类型的函数可以被Lua调用   typedef int ( ...

  3. vJine 第三波 之 Lua 来袭 vJine.Lua

    vJine.Lua vJine.Lua是Lua语言的C#封装库,可实现通过C#直接运行Lua脚本并与Lua脚本交互的功能. 1. 授权: MPL2.0 相关资源: nuget:(https://www ...

  4. VC和VS调用Lua设置以及Lua C API使用。

    通过c++调用lua 脚本,    环境VC++6.0    lua  sdk 5.1.4 在调用前先认识几个函数.1.调用lua_open()将创建一个指向Lua解释器的指针.2. luaL_ope ...

  5. lua调用不同lua文件中的函数

    a.lua和b.lua在同一个目录下 a.lua调用b.lua中的test方法,注意b中test的写法 _M 和 a中调用方法: b.lua local _M = {}function _M.test ...

  6. 通过lua栈了解lua与c的交互

    lua是如何执行的 其中分析.执行部分都是c语言实现的. lua与c的关系 lua的虚拟机是用c语言实现的,换句话说一段lua指令最终在执行时都是当作c语言来执行的,lua的global表,函数调用栈 ...

  7. lua编程之lua与C相互调用

    lua是扩展性非常良好的语言,虽然核心非常精简,但是用户可以依靠lua库来实现大部分工作.除此之外,lua还可以通过与C函数相互调用来扩展程序功能.在C中嵌入lua脚本既可以让用户在不重新编译代码的情 ...

  8. lua脚本之lua语言简介以及lua的安装

    本博主不擅于进行文字创作,所以,相当一部分文字皆复制于其他博文.还希望能够得到谅解.  一.Lua语言简介 Lua是一个免费的开源软件,可以免费用于科研及商业.Lua具有一个专家团队在维护和升级,其设 ...

  9. Lua基本语法-lua与C#的交互(相当简单详细的例子)

    lua脚本 与 C#的交互 本文提供全流程,中文翻译.Chinar坚持将简单的生活方式,带给世人!(拥有更好的阅读体验 -- 高分辨率用户请根据需求调整网页缩放比例) 1 Lua And C# -- ...

随机推荐

  1. 自己搭建了一个blog

    https://svtt.sinaapp.com 利用JustWriting开源项目搭建的,不过还是有些许问题.但是考虑到自己的blog好处多多,暂且用着--有时间或者乐趣来了,自己再用wordpre ...

  2. springboot使用restTemplate post提交值 restTemplate post值

      post提交有 FormData和Payload  两种形式: 第一种是formdata形式,在header参数里可以直接看到  payload则封装成json格式post过去,获取以后需要再解析 ...

  3. 【12】react 之 redux(1)

    TodoList小demo 效果展示 项目地址 (单向)数据流 数据流是我们的行为与响应的抽象:使用数据流能帮我们明确了行为对应的响应,这和react的状态可预测的思想是不谋而合的. 常见的数据流框架 ...

  4. Eclipse SVN冲突详细解决方案

         大家一起开发,难免有时会同时修改同一个文件,这样就要学会解决冲突.当大家更新代码,发现以下情况的时候,就说明你的修改的文件和服务器的文件产生了冲突(一般是别人也改了同一个文件). 1)和服务 ...

  5. DataSet导出Excel,比以往的方法导出的Excel外观更加好看

    原文发布时间为:2010-06-21 -- 来源于本人的百度文章 [由搬家工具导入] ======目前方法=========== #region 生成Excel/// <summary>/ ...

  6. error C2275 将此类型用作表达式非法

    http://blog.csdn.net/lqk1985/article/details/7389159 C2275: “size_t”: 将此类型用作表达式非法,同时还导致一堆变量未定义的bug. ...

  7. enum枚举类型

    枚举类型的实例是常量,且它们都用大写字母表示. 简单枚举例子: public enum Spiciness { NOT, MILD, MEDIUM, HOT, FLAMING } public cla ...

  8. Oracle的隔离级别

    隔离级别(isolation level)       隔离级别定义了事务与事务之间的隔离程度.     隔离级别与并发性是互为矛盾的:隔离程度越高,数据库的并发性越差:隔离程度越低,数据库的并发性越 ...

  9. poj 1185 炮兵阵地 [经典状态压缩DP]

    题意:略. 思路:由于每个大炮射程为2,所以如果对每一行状态压缩的话,能对它造成影响的就是上面的两行. 这里用dp[row][state1][state2]表示第row行状态为state2,第row- ...

  10. 基于Java实现的选择排序算法

    选择排序和冒泡排序同样是基础排序算法,现在也做个学习积累. 简述 选择排序算法较为稳定,基本上都是O(n2)的时间复杂度,规模越小排序越快,不需要占用额外空间.其实选择排序原理很简单,就是在未排序序列 ...