关于 luajit 的 ffi 命名空间的郁闷

619 views
Skip to first unread message

Lance

unread,
Dec 3, 2012, 11:19:39 PM12/3/12
to open...@googlegroups.com
首先说明一下,这个应该是与 luajit 的 namespaces 设计有关,似乎不是 openresty 的问题,但鉴于我只在使用 openresty 的时候才会用到 luajit,所以发到这里看看哪位有什么好办法。

nginx.conf

    server
    {
        listen 80;
        server_name test;
        error_log  /data/logs/test.error.log;

        location /a
        {
            default_type 'text/plain';
            content_by_lua '
                local a = require("a")
                ngx.say(a.ip2long("8.8.8.8"))
            ';
        }

        location /b
        {
            default_type 'text/plain';
            content_by_lua '
                local b = require("b")
                ngx.say(b.ip2long("8.8.8.8"))
            ';
        }
    }

a.lua 与 b.lua 内容完全一样,是之前agentzh写的的作为ffi例子的php.utils

local ffi = require "ffi"

module(...)

ffi.cdef[[
    struct in_addr {
        uint32_t s_addr;
    };

    int inet_aton(const char *cp, struct in_addr *inp);
    uint32_t ntohl(uint32_t netlong);

    char *inet_ntoa(struct in_addr in);
    uint32_t htonl(uint32_t hostlong);
]]

function ip2long(ip)
    local inp = ffi.new("struct in_addr[1]")
    if ffi.C.inet_aton(ip, inp) ~= 0 then
        return ffi.C.ntohl(inp[0].s_addr)
    end
    return nil
end

function long2ip(long)
    if type(long) ~= "number" then
        return nil
    end
    local addr = ffi.new("struct in_addr")
    addr.s_addr = ffi.C.htonl(long)
    return ffi.string(ffi.C.inet_ntoa(addr))
end

实际请求时,结果却不同

[root@xx conf]# curl "http://test/a"                          
134744072
[root@xx conf]# curl "http://test/b"
<html>
<head><title>500 Internal Server Error</title></head>
<body bgcolor="white">
<center><h1>500 Internal Server Error</h1></center>
<hr><center>nginx</center>
</body>
</html>

同时 error.log 里出现如下错误
2012/12/04 11:54:49 [error] 10709#0: *17 lua entry thread aborted: runtime error: /usr/local/openresty/nginx/conf/luapkg/b.lua:15: attempt to redefine 'in_addr'
stack traceback:
coroutine 0:
        [C]: in function 'require'
        [string "content_by_lua"]:2: in function <[string "content_by_lua"]:1>, client: 127.0.0.1, server: test, request: "GET /b HTTP/1.1", host: "test"

看错误,是 b.lua 里用 cdef 定义 in_addr 时出现重新定义,也就是说在 a.lua 里定义过的 in_addr,即使写法上是在一个 lua module 里,但实际是全局定义的,似乎与 luajit 文档中
http://luajit.org/ext_ffi_semantics.html 的 C Library Namespaces 一段描述有关。

假如在大量使用 ffi 的场合,这种重复定义似乎很难避免,哪位有什么好办法?


Lance

agentzh

unread,
Dec 4, 2012, 2:24:40 PM12/4/12
to open...@googlegroups.com
Hello!

2012/12/3 Lance:


> 首先说明一下,这个应该是与 luajit 的 namespaces 设计有关,似乎不是 openresty 的问题,但鉴于我只在使用 openresty
> 的时候才会用到 luajit,所以发到这里看看哪位有什么好办法。

[...]


> 同时 error.log 里出现如下错误
> 2012/12/04 11:54:49 [error] 10709#0: *17 lua entry thread aborted: runtime
> error: /usr/local/openresty/nginx/conf/luapkg/b.lua:15: attempt to redefine
> 'in_addr'

[...]


>
> 假如在大量使用 ffi 的场合,这种重复定义似乎很难避免,哪位有什么好办法?
>

这是使用 LuaJIT FFI 时常见的一个错误,即通过 ffi.cdef 重复定义同名的 C 函数或者 C 结构体。

一个解决的办法是总是使用 ffi.typeof 函数在定义前进行存在性测试,例如

local ffi = require "ffi"

if pcall(ffi.typeof, "struct in_addr") then
-- already defined! do nothing here...

else
-- undefined! let's define it!


ffi.cdef[[
struct in_addr {
uint32_t s_addr;
};

]]
end

Best regards,
-agentzh

Lance

unread,
Dec 5, 2012, 2:49:43 AM12/5/12
to open...@googlegroups.com
那类似
ffi.cdef[[

int inet_aton(const char *cp, struct in_addr *inp);
]]

这样怎么判存在呢?似乎 ffi.typeof 不行

Lance


2012/12/5 agentzh <age...@gmail.com>

agentzh

unread,
Dec 5, 2012, 2:14:38 PM12/5/12
to open...@googlegroups.com
Hello!

2012/12/4 Lance:


> 那类似
> ffi.cdef[[
>
> int inet_aton(const char *cp, struct in_addr *inp);
> ]]
>
> 这样怎么判存在呢?似乎 ffi.typeof 不行
>

对于 C 函数也很简单,可以如是判断:

local C = ffi.C
if pcall(function () return C.inet_aton end) then
-- inet_aton defined
else
-- inet_aton not defined
end

Best regards,
-agentzh

Reply all
Reply to author
Forward
0 new messages