问题描述
我是一个需要区别对待 cdata 和 ctypes 的库的作者。目前,我使用 ffi.typeof(value) == value
,但这会导致某些带有定义 __eq
的元表的 cdata 出错。提交此错误报告的用户建议改为检查 tostring(value):match"^ctype"
,但这只是将问题转移到带有定义 __tostring
的元表的 cdata。我已经尝试过 rawequal
,但我的原始条件首先依赖于元魔法,因此根本不起作用。
我正在考虑使用 local success,eq = pcall(function() return ffi.typeof(value) == value end)
并使用条件 success and eq
,但我想知道:有没有更可靠、更少黑客的方法来实现这一目标?
解决方法
下面的解决方案也是 hack-y,但您可能会感兴趣。
它滥用了 tonumber()
适用于 ctypes 的未记录功能。
local function is_ctype(x)
return type(x) == "cdata"
and tonumber(x) ~= nil
and tostring(x):sub(1,5) == "ctype"
end
请注意,tostring()
仅适用于可转换为数字的 cdata。ctype<complex>
是唯一一种其值可转换为数字的元类型 ctype。
令人高兴的是,ctype<complex>
不尊重 __tostring
元方法。 :-)
这也困扰了我一段时间,这是我目前得到的。
代码适用于我的用例,但它不处理这种边缘情况:
assert(ffi.new("int",9) == ffi.typeof("int")) --> this assertion is true.
我找不到解决这个问题的方法,我认为这就是 LuaJIT 的工作方式。 所有其他基本类型也发生同样的情况(带有各自的 ID)
完整代码:
local ffi = require "ffi"
function isctype(o)
-- ctypes can be converted to a number
local check = tonumber(o)
if check == nil then
return false
end
-- typeinfo expects a number,we have a number
local maybeInfo = ffi.typeinfo(check)
local sureInfo = ffi.typeinfo(ffi.typeof(o))
-- type infos differ
if maybeInfo.info ~= sureInfo.info then
return false
end
local type = ffi.typeof(o)
-- No way around this except calling tostring(o)
-- which would have it's own drawbacks.
-- When we got here we have the special case
-- that the type-id is equal to the result
-- of tonumber(o).
-- this happens e.g. for typeof"int" and 9.
-- In this case the result is also true,-- which is actually not quite what I'd expect.
return o == type
end
local ct = ffi.typeof("struct { int x; }")
print("type",isctype(ct)) --> true
print("data",isctype(ffi.new(ct))) --> false
print("type",isctype(ffi.typeof("int"))) --> true
print("type",isctype(ffi.typeof("const char*"))) --> true
print("data",isctype(ffi.new("float",3))) --> false
print("data",isctype(ffi.new("int",isctype(ffi.new("intptr_t",isctype(ffi.new("int[1]",isctype(ffi.new("const char*","yo"))) --> false
print("data",8))) --> false
-- this is sad :(
print("data",9))) --> true
assert(ffi.new("int",9) == ffi.typeof("int"))
print("assertion did not fail :(")