luajit FFI简单使用(1)

本文参考了luajit的官方文档编写。文档的结构也基本上是按照官网的来。

luajit FFI是一个简化的调用c库函数的途径。目的是简化lua和c语言之间调用的繁琐的push和pop。如果你已经有一个c语言编写的一个库了,可以不改代码的前提下,使用lua直接调用。

开胃菜

调用函数

先使用c编写一个工程,对外部提供一个函数

int c_add(int a, int b);

编写makefile将其编译成一个libc_utils.so库文件出来。
在lua中定义这个c函数并且加载这个so库。

local ffi = require("ffi")
ffi.cdef[[
int c_add(int a,int b);
]]
local cutils = ffi.load('libc_utils')
print(cutils.c_add(200,50))

如果是在windows版本上,可以直接来执行这个命令:

local ffi = require("ffi")
ffi.cdef[[
int MessageBoxA(void *w, const char *txt, const char *cap, int type);
]]
ffi.C.MessageBoxA(nil, "Hello world!", "Test", 0)

将会直接调出一个dialog出来。

使用数据结构

在c库里面定义计算向量相关的函数。我们在代码里面实现一个向量的点×

概念

点乘(Dot Product)
几何意义:
A·B = |A| |B| cos(θ).
θ是向量A和向量B见的夹角。
计算公式:
V1( x1, y1) V2(x2, y2) = x1*x2 + y1*y2

代码实现

typedef struct { uint8_t x, y; } position;
int dot_produce(position *a, position *b);
int dot_produce(position *a, position *b) {
    int ret = (a->x) * (b->x) + (a->y * b->y);
    return ret;
}

lua中调用


local ffi = require("ffi")
ffi.cdef[[
typedef struct { uint8_t x, y; } position;
int dot_produce(position *a, position *b);
]]
local cutils = ffi.load('libc_utils')
local pos = ffi.new("position[?]", 2)
pos[0].x=10
pos[0].y=10
pos[1].x=500
pos[1].y=300
print(cutils.dot_produce(pos[0],pos[1]))

进阶

调用zlib库

local ffi = require("ffi")
1 ffi.cdef[[
unsigned long compressBound(unsigned long sourceLen);
int compress2(uint8_t *dest, unsigned long *destLen,
          const uint8_t *source, unsigned long sourceLen, int level);
int uncompress(uint8_t *dest, unsigned long *destLen,
           const uint8_t *source, unsigned long sourceLen);
]]
2 local zlib = ffi.load(ffi.os == "Windows" and "zlib1" or "z")

local function compress(txt)
3  local n = zlib.compressBound(#txt)
  local buf = ffi.new("uint8_t[?]", n)
4  local buflen = ffi.new("unsigned long[1]", n)
  local res = zlib.compress2(buf, buflen, txt, #txt, 9)
  assert(res == 0)
5  return ffi.string(buf, buflen[0])
end

local function uncompress(comp, n)
  local buf = ffi.new("uint8_t[?]", n)
  local buflen = ffi.new("unsigned long[1]", n)
  local res = zlib.uncompress(buf, buflen, comp, #comp)
  assert(res == 0)
  return ffi.string(buf, buflen[0])
end

-- Simple test code.
local txt = string.rep("abcd", 1000)
print("Uncompressed size: ", #txt)
local c = compress(txt)
print("Compressed size: ", #c)
local txt2 = uncompress(c, #txt)
assert(txt2 == txt)

1.将zlib中的函数方法都定义到ffi接口中;
2.开始加载zlib库的C库文件;
3.通过compressBound函数估算压缩包结果的size;并且new一块n大小的内存。
4.新建一个unsigned long的变量用于提供给compress2函数中作为返回,压缩之后内存块的长度。
调用compress2函数,将内存块加压。
5.将返回的内存块通过ffi.string转换成lua中的内存块。
后面的是他的逆过程。

在这段代码里面还是演示了一些功能:
关于ffi.new的用法。这个里面都演示了如何创建一块buf,如何创建一块内存,并且得到他的地址。
关于ffi.string,这个模块是用于将内存块和lua的string做转换时使用。

为c类型绑定上元表

local ffi = require("ffi")
ffi.cdef[[
typedef struct { double x, y; } point_t;
]]

local point
local mt = {
  __add = function(a, b) return point(a.x+b.x, a.y+b.y) end,
  __len = function(a) return math.sqrt(a.x*a.x + a.y*a.y) end,
  __index = {
    area = function(a) return a.x*a.x + a.y*a.y end,
  },
}
point = ffi.metatype("point_t", mt)

local a = point(3, 4)
print(a.x, a.y)  --> 3  4
print(#a)        --> 5
print(a:area())  --> 25
local b = a + point(0.5, 8)
print(#b)        --> 12.5

这个就是在lua里面加载一个c语言的struct,并且在这个对象里面绑定上lua的实现方法。这个其实很奇妙的一个功能,今后如果一个c库,想做成面向对象就能通过这个调配一下就好了。
在官网中的解释

The C type metamethod mechanism is most useful when used in conjunction with C libraries that are written in an object-oriented style.
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 可以使用luajit ffi库的相关函数来读取文件路径下的文件名,以下是一个简单的示例代码: ``` local ffi = require("ffi") -- 定义readdir系统调用函数 ffi.cdef[[ typedef struct dirent { long d_ino; off_t d_off; unsigned short d_reclen; unsigned char d_type; char d_name[256]; } dirent; typedef struct DIR { int fd; dirent ent; } DIR; DIR *opendir(const char *name); dirent *readdir(DIR *dirp); int closedir(DIR *dirp); ]] -- 读取文件路径下的文件名 function list_files(path) local dir = ffi.C.opendir(path) local files = {} if dir ~= nil then repeat local entry = ffi.C.readdir(dir) if entry ~= nil then local name = ffi.string(entry.d_name) if name ~= "." and name ~= ".." then table.insert(files, name) end end until entry == nil ffi.C.closedir(dir) end return files end -- 测试 local files = list_files(".") for i, file in ipairs(files) do print(i, file) end ``` 在这个示例中,我们使用ffi.cdef()函数定义了readdir、opendir和closedir系统调用函数。接下来我们定义了一个list_files()函数,该函数使用opendir()函数打开指定路径下的目录,使用readdir()函数读取目录下的文件列表,并使用closedir()函数关闭目录。最后,我们遍历文件列表,将每个文件的文件名添加到一个数组中,并返回该数组。 要使用这个函数,只需将要读取的文件路径传递给list_files()函数,它将返回一个包含该目录下所有文件名的数组。在本例中,我们传递了"."作为路径,它将返回当前目录下的所有文件名。 ### 回答2: 在LuaJIT使用ffi库读取文件路径下的文件名相对简单,只需要用到ffi库的C语言接口和Lua的文件操作函数就可以完成。以下是一个可以实现该功能的示例代码: ```lua local ffi = require("ffi") -- 定义C函数readdir实现读取文件路径下的文件名 ffi.cdef[[ typedef struct dirent { char d_name[256]; } dirent; typedef struct DIR DIR; DIR* opendir(const char* path); dirent* readdir(DIR* dirp); int closedir(DIR* dirp); ]] -- 定义读取文件路径下文件名的函数 function getFilenames(path) local dir = ffi.C.opendir(path) -- 打开目录 if dir == nil then return nil end local filenames = {} -- 存储文件名的数组 local entry = ffi.C.readdir(dir) -- 读取第一个文件 while entry ~= nil do local filename = ffi.string(entry.d_name) -- 转换为Lua字符串 if filename ~= "." and filename ~= ".." then table.insert(filenames, filename) -- 加入数组 end entry = ffi.C.readdir(dir) -- 读取下一个文件 end ffi.C.closedir(dir) -- 关闭目录 return filenames end -- 示例使用: local path = "./folder" -- 文件路径 local filenames = getFilenames(path) -- 获取文件名数组 if filenames == nil then print("无法打开目录") else for i, filename in ipairs(filenames) do print(filename) end end ``` 以上示例代码使用LuaJITffi库调用了C语言的函数opendir、readdir和closedir来实现读取文件路径下的文件名。首先,通过调用opendir打开指定路径的目录;然后使用readdir循环读取目录中的所有文件,并将文件名加入到一个Lua数组中;最后,调用closedir关闭目录并返回获取到的文件名数组。示例中也处理了当前目录(".")和上层目录(".. ")两个特殊文件名的情况。 你可以将示例代码保存在一个Lua文件中,并根据自己的实际需求修改文件路径,然后运行该Lua文件,即可看到打印出的文件名列表。 ### 回答3: 使用LuaJITffi库可以很方便地调用C函数,进而实现读取文件路径下的文件名的功能。下面是一个示例代码: ```lua -- 引入ffi库 local ffi = require("ffi") -- 定义C函数 ffi.cdef[[ typedef struct { void *d; } DIR; DIR *opendir(const char *filename); void closedir(DIR *dirp); const char *readdir(DIR *dirp); ]] -- 遍历文件路径下的文件名 function getFilenames(filepath) local dir = ffi.C.opendir(filepath) -- 打开目录 if dir ~= nil then local filenames = {} local dirent = ffi.C.readdir(dir) while dirent ~= nil do local name = ffi.string(dirent) -- 排除当前目录和上级目录 if name ~= '.' and name ~= '..' then table.insert(filenames, name) end dirent = ffi.C.readdir(dir) end ffi.C.closedir(dir) -- 关闭目录 return filenames end return nil end -- 测试 local path = "/path/to/directory" local filenames = getFilenames(path) if filenames ~= nil then for i, filename in ipairs(filenames) do print(filename) end else print("Failed to open directory") end ``` 上述代码首先使用ffi.cdef定义了C的DIR结构和相关函数,其中opendir用于打开目录,readdir用于读取目录中的文件名,closedir用于关闭目录。 然后,在getFilenames函数中,我们首先使用ffi.C.opendir打开指定的文件路径,然后循环使用ffi.C.readdir读取目录中的文件名,将有效的文件名加入到filenames表中,最后使用ffi.C.closedir关闭目录。 最后,在测试的部分,我们指定一个文件路径并调用getFilenames函数获取文件名列表,然后遍历打印每个文件名。如果无法打开目录,会打印"Failed to open directory"。 以上便是使用LuaJITffi库实现读取文件路径下的文件名的代码。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值