Lua学习笔记五--真正的入门:编写Lua扩展库

Lua学习笔记五--真正的入门:编写Lua扩展库

Lua学习笔记五

通过前面4次的学习,我们已经具备了一些基本知识:
1、使用Lua脚本语法
2、编写Lua函数和C函数,并使他们交互
3、表和其他类型的参数的应用
4、处理函数的返回值的方法
很好,很强大。这些知识足以开启Lua世界的大门,现在让我们来真正的进入Lua的世界。

本节的目的:写一套操作动态浮点数组的函数,并把他们封装成库,侧重点为编写函数库的基本套路。


读到这里可以发现,该系列文章是从开发者的角度来进行Lua学习,而不是一般文章中介绍Lua语法和如何编写Lua脚本的角度。事实上我也正有此意--把以前的工作全部封装成Lua接口,然后只要懂得Lua脚本语法的人,就可以参与到我们的项目里来。


记得上节我们写的2个Lua脚本函数吗?他们是:

NewWindow = CreateWindow{ x = 0,y = 0, w = 1024, h = 768} ;
AddButton{hWindow = NewWindow , x =100, y = 100, w = 75, h = 25};

但是他们还不够好,以下的方式更符合我们习惯:

NewWindow = Object.CreateWindow();
Object.AddCompent{ Type = Button, HWND = NewWindow, x = 100, y = 100, w = 75, h = 25};
Object.Show(NewWindow);
Object.Destory(NewWindow);

这个Object 就是我们封装了一套处理窗体操作的函数库。
当然,我不能给出Object库的代码,因为它太大了。不过,让我们来写一套操作动态浮点数组的函数,并把他们封装成库吧。


RTFS,RTFS,RTRTRTFS~~

-------以下是Lua脚本--------
--test.lua
function f( x, y)
return x + y
end

LuaC_MessageBox( "Hello World!");

--这三句是普通的函数交互,因为不能在你们的代码中通过,所以注释起来。
--NewWindow = CreateWindow{ x = 0, y = 0, w = 1024, h = 768} ;
--AddButton{hWindow = NewWindow , x =100, y = 100, w = 75, h = 25};
--ShowWindow( NewWindow);

--这是我下个要实现的目标,暂时没找到方法。
--NewWindow.add_button{ x = 100, y = 100, w = 40, h = 18, dp2 = "button1"}
--NewWindow.add_bitmap{ x = 0, y = 0, w = 640, h 480}

--这两句是基于函数库的语法。
NewObject = object.new( 8);
LuaC_MessageBox( object.count(NewObject));

//------------以下是test.cpp文件----------------
// 由于总是在上一次的基础上进行的添加,因此已经实现过了的函数体我就省略了。
// 这也是与“给出完整可编译代码段”的原则的妥协---不然代码就太多了。
// 这次的代码可以编译通过,我移除了Allegro相关的东西。因为Userdata的尝试是在那个环境下
// 测试的,所以才用它举例。为了保持通用性和“给出完整可编译代码段”的原则,我会尽量用
// 原生态的例子来阐述问题。
//================================================================================================================
// Lua Test Object
// C++ Source lua_test.cpp
//================================================================================================================
//================================================================================================================
// Include Files
//================================================================================================================
extern "C"
{
#include "D://My Documents//Visual Studio 2005//Projects//lua//lua//lua.h"
#include "D://My Documents//Visual Studio 2005//Projects//lua//lua//lualib.h"
#include "D://My Documents//Visual Studio 2005//Projects//lua//lua//lauxlib.h"
}

#include <windows.h>
#include <stdio.h>
#include <string>
using namespace std;
//================================================================================================================
// Libraries
//================================================================================================================
#pragma comment( lib ,"D://My Documents//Visual Studio 2005//Projects//lua//release//lua.lib")
//================================================================================================================
// Type Define
//================================================================================================================
typedef struct MyStruct_TAG
{
int count;
double _array[1];
}MyStruct,*MyStruct_PTR;

//================================================================================================================
// Global Variables
//================================================================================================================
lua_State *L;
//================================================================================================================
// Lua Functions
//================================================================================================================
double f( double x, double y )
{
// 已经实现
}
//================================================================================================================
// C/C++ Functions
//================================================================================================================
int LuaC_MessageBox( lua_State *L)
{
// 已经实现
}

int LuaC_GetObject( lua_State* L)
{
// LuaC_GetObject( object, index)
// 获取第1个参数
MyStruct_PTR object = (MyStruct_PTR)lua_touserdata(L, 1);
// 获取第2个参数
int index = luaL_checkint( L, 2);
// 参数有效性判断
luaL_argcheck(L, object != NULL, 1, "`object expected");
luaL_argcheck(L, 1 <= index && index <= object->count, 2, "index out of range");
// 返回值压栈
lua_pushnumber(L, object->_array[index - 1]);
// 设置返回参数个数
return 1;
}

int LuaC_CreateObject( lua_State *)
{
int count;
size_t size;
void* lp;

// LuaC_CreateObject(1000)
// 获取参数
count = luaL_checkint(L, 1);
// 计算大小
size = sizeof(MyStruct) + (count - 1) * sizeof( double);
// 申请空间
lp = lua_newuserdata( L, size);
// 初始化
memset( lp, '/0',size);
// 设置个数
((MyStruct_PTR)lp)->count = count;

// 设置返回值个数
return 1;
}

int LuaC_SetObject(lua_State *L)
{
// LuaC_SetObject( object, value, index)
// 获取第1个参数
MyStruct_PTR object = (MyStruct_PTR)lua_touserdata(L, 1);
// 获取第2个参数
double value = luaL_checknumber( L, 2);
// 获取第3个参数
int index = luaL_checkint( L, 3);
// 参数有效性判断
luaL_argcheck(L, object != NULL, 1, "`object expected");
luaL_argcheck(L, 1 <= index && index <= object->count, 2, "index out of range");
// 执行逻辑
object->_array[index - 1] = value;

// 设置返回参数个数
return 0;
}

int LuaC_GetCount( lua_State* L)
{
// LuaC_GetCount( object)
// 获取第1个参数
MyStruct_PTR object = (MyStruct_PTR)lua_touserdata(L, 1);
// 参数有效性判断
luaL_argcheck(L, object != NULL, 1, "`object expected");
// 返回值压栈
lua_pushnumber(L, object->count);
// 设置返回参数个数
return 1;
}

// 初始化库
static const struct luaL_reg object_lib[] =
{
{"new", LuaC_CreateObject},
{"set", LuaC_SetObject},
{"get", LuaC_GetObject},
{"count", LuaC_GetCount},
{NULL, NULL}
};

int _luaopen_object (lua_State *L)
{
luaL_openlib(L, "object", object_lib, 0);
return 1;
}
//================================================================================================================
// Main Functions
//================================================================================================================
int main( void)
{

int error;

L = lua_open();
luaopen_base(L);
luaL_openlibs(L);
// 自己的扩展库
_luaopen_object(L);

// 注册C/C++函数
lua_register( L, "LuaC_MessageBox", LuaC_MessageBox);

//luaopen_table(L);
//luaopen_io(L);
//luaopen_string(L);
//luaopen_math(L);

/*
while( fgets( buff, sizeof( buff), stdin ) != NULL)
{
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);
}
}
*/
// load the script
if ( (error = luaL_dofile(L, "test.lua")) != 0)
{
allegro_message( "error!!");
return 0;
}
/*
double ret = f( 10, 3.4);
printf( "ret = %f", ret);

getchar();
*/
lua_close( L);
return 1;
}
END_OF_MAIN();


代码里面的注释越来越多了,不是吗?很多时候就是这个样子样,看代码比看文字要来的直接。

新出现了很多Lua库函数调用,不过都是涉及参数有效性检测的函数。所以如果你不了解用法的话,最好查找《Lua参考手册》。另外,记得下载的Lua源码吗?在名为“doc”的文件夹里有一个contents.html,打开看看吧。

好了,现在让我们把注意力转回今天的重点。

写一个自己的库的步骤是:

1、写一套功能函数,如:
int LuaC_CreateObject( lua_State *)
int LuaC_SetObject(lua_State *L)
int LuaC_GetObject(lua_State *L)
int LuaC_GetObjectCount(lua_State *L)

在这个步骤里,与编写普通的C/Lua交互函数没有什么区别。

2、声明一个Lua表,作为函数库,如:
// 初始化库
static const struct luaL_reg object_lib[] =
{
{"new", LuaC_CreateObject},
{"set", LuaC_SetObject},
{"get", LuaC_GetObject},
{"count", LuaC_GetCount},
{NULL, NULL}
};

注意上面的粗体,声明一个作为库的表,需要使用关键字 Static const
另外这个结构体 struct luaL_reg:
typedef struct luaL_Reg {
const char *name;
lua_CFunction func;
} luaL_Reg;
作为库的表实际上就是一个luaL_Reg类型的数组。Lua中其他一些标准库也都是按照这个规矩来定义的。
最后这个数组需要使用一个哑员(空值)来作为结束符。

3、调用luaL_openlib();函数,如:

int _luaopen_object (lua_State *L)
{
luaL_openlib(L, "object", object_lib, 0);
return 1;
}

在这里,我把这个函数封装了一下,只是为了风格统一。另外,在所有我自己写的类似Lua原有函数的函数前面我都加了下划线,为了几个月后让我明白:这是我自己写的,而不是Lua自己带的。另外,这是跟Andre LaMothe学的。

4、在main函数里加载库。如:

// 自己的扩展库
_luaopen_object(L);

OK.以上就是编写自己的库的基本套路。
如果一切正常的话,会弹出一个对话框,告诉你刚刚申请的数组对象有8个元素。

好了,下一个笔记可能会是 “面向对象”、“Weaktable(弱表)”、“lightuserdata的应用”、“Metatable(元表)”、4个内容中的一个或几个。如果你学习的够快的话,代替我来写这些内容吧``

推荐阅读

    excel怎么用乘法函数

    excel怎么用乘法函数,乘法,函数,哪个,excel乘法函数怎么用?1、首先用鼠标选中要计算的单元格。2、然后选中单元格后点击左上方工具栏的fx公

    opporeno8参数配置及价格

    opporeno8参数配置及价格,面部,亿元,Oppo的荣誉2020年1月4日,接近屏幕关闭传感器是否支持双卡:支持oppor11splus什么时候上市的Oppo R11S P

    excel中乘法函数是什么?

    excel中乘法函数是什么?,乘法,函数,什么,打开表格,在C1单元格中输入“=A1*B1”乘法公式。以此类推到多个单元。1、A1*B1=C1的Excel乘法公式

    魅蓝note6性能参数有哪些

    魅蓝note6性能参数有哪些,摄像头,蓝牙,魅蓝note6性能参数有哪些魅力蓝色Note6最好拍照。电池寿命更长。蓝色Note6使用高通 snapdragon 625

    标准差excel用什么函数?

    标准差excel用什么函数?,函数,标准,什么,在数据单元格的下方输入l标准差公式函数公式“=STDEVPA(C2:C6)”。按下回车,求出标准公差值。详细

    设置总账参数|用友u8设置总账参数

    设置总账参数|用友u8设置总账参数,,1. 用友u8设置总账参数1、首先要点开数据权限控制设置;2、选择想要设置控制的单据;3、打开后看到左上角

    csgo参数设置|csgo怎么保存

    csgo参数设置|csgo怎么保存,,csgo怎么保存第一步下载csgo的官方版本。然后再下载一个5e对战平台,PS:5e的账号和csgo的账号不是一个账号。第

    移动apn设置|移动apn设置参数

    移动apn设置|移动apn设置参数,,移动apn设置参数1、打开手机系统设置界面应用,点击页面中的“移动网络”设置选项。2、进入移动网络设置页面