Querying the type name of ffi-declared cdata

Questions about the LÖVE API, installing LÖVE and other support related questions go here.
Forum rules
Before you make a thread asking for help, read this.
Post Reply
grump
Party member
Posts: 947
Joined: Sat Jul 22, 2017 7:43 pm

Querying the type name of ffi-declared cdata

Post by grump »

I want to serialize cdata objects, ideally like this (pseudo code):

Code: Select all

local function serializeCData(cdata)
	storeType(cdata) -- needs a stable type identifier
	storeSize(cdata)
	storeData(cdata)
end

local function deserializeCData(source)
	local type = restoreType(source)
	local size = restoreSize(source)
	return ffi.cast(type .. '*', source:read(size))
end
Example cdata declaration:

Code: Select all

ffi.cdef [[
	typedef struct {
		int i;
		float f;
	} MyData;
]]

local MyData = ffi.typeof('MyData')
Using MyData:

Code: Select all

local data = MyData(23, 42)

print(MyData)           -- 'ctype<struct xyz>'
print(type(MyData))     -- 'cdata'
print(type(data))       -- 'cdata'
print(ffi.typeof(data)) -- 'ctype<struct xyz>'
print(data)             -- 'cdata<struct xyz>: 0xbaadc0de'
Is there a way to retrieve the original 'MyData' type identifier from the cdata object alone, so I can restore the object?

'ctype<struct xyz>' can not be used, since it's not a stable identifier. It will change when the order of cdefs changes, or when a new cdef is inserted.
grump
Party member
Posts: 947
Joined: Sat Jul 22, 2017 7:43 pm

Re: Querying the type name of ffi-declared cdata

Post by grump »

Looks like it's not possible. Bummer.

Can someone explain this behavior:

Code: Select all

local Integer = ffi.typeof('int')
local val = Integer(23)
local mapVal = { [ffi.typeof(val)] = 'int' }
local mapInt = { [Integer] = 'int' }

print(ffi.typeof(val) == Integer) -- true

print(mapVal[ffi.typeof(val)])    -- nil
print(mapVal[Integer])            -- nil

print(mapInt[ffi.typeof(val)])    -- nil
print(mapInt[Integer])            -- int
Since the first print outputs 'true', I expected the bottom four lines to print 'int', but only the last one does.

This works though:

Code: Select all

local mapVal = { [tostring(ffi.typeof(val))] = 'int' }
local mapInt = { [tostring(Integer)] = 'int' }

print(mapInt[tostring(ffi.typeof(val))])    -- int
print(mapInt[tostring(Integer)])            -- int
User avatar
pgimeno
Party member
Posts: 3541
Joined: Sun Oct 18, 2015 2:58 pm

Re: Querying the type name of ffi-declared cdata

Post by pgimeno »

grump wrote: Fri Jan 25, 2019 8:22 pm Can someone explain this behavior:

<snip>

Since the first print outputs 'true', I expected the bottom four lines to print 'int', but only the last one does.
ffi.typeof "[c]reates a ctype object for the given ct" (according to the manual). If it's a new object, it's not surprising that using it as a table key doesn't allow you to address it with an object resulting from a different call. Therefore, the only surprising behaviour is that of the == operator, which is probably treated specially. I've tried this but got no answers from it:

Code: Select all

print(getmetatable(Integer).__eq)  -- prints nil
Edit: Nope, it seems like cdata objects just can't be table keys:

Code: Select all

local Integer = ffi.typeof('int')
local val = Integer(23)
local mapVal = { [ffi.typeof(val)] = 'int' }
print(#mapVal)  -- prints 0

(edited my edit, per grump's reply below)
Last edited by pgimeno on Sat Jan 26, 2019 12:03 am, edited 1 time in total.
grump
Party member
Posts: 947
Joined: Sat Jul 22, 2017 7:43 pm

Re: Querying the type name of ffi-declared cdata

Post by grump »

What confuses me is that these ctype objects, even though they are distinct objects, have no distinct identity when converted to a string.
tostring(cdata) is `cdata<whatever>: 0x12345678`, so tostring(ctype) should be `ctype<whatever>: 0x87654321`, but it's just `ctype<whatever>`. A(nother) pretty annoying inconsistency.
pgimeno wrote: Fri Jan 25, 2019 9:24 pm

Code: Select all

print(getmetatable(Integer).__eq)  -- prints nil
I tried that too. There are no visible metamethods - not surprising, since the metatable of any cdata is the string "ffi".
Edit: Nope, it seems like cdata objects just can't be table keys:
Yes, they can. You missed the fact that associative tables have no length.

Code: Select all

for k, v in pairs(mapVal) do print(k, v) end -- ctype<int> int
User avatar
pgimeno
Party member
Posts: 3541
Joined: Sun Oct 18, 2015 2:58 pm

Re: Querying the type name of ffi-declared cdata

Post by pgimeno »

Gah, rookie mistake! You're right of course, sorry.
grump wrote: Fri Jan 25, 2019 9:54 pm What confuses me is that these ctype objects, even though they are distinct objects, have no distinct identity when converted to a string.
tostring(cdata) is `cdata<whatever>: 0x12345678`, so tostring(ctype) should be `ctype<whatever>: 0x87654321`, but it's just `ctype<whatever>`. A(nother) pretty annoying inconsistency.
Yeah, but they are still different, confirmed by this code:

Code: Select all

local ffi = require'ffi'
local Integer = ffi.typeof('int')
local val = Integer(23)
local mapVal = { [ffi.typeof(val)] = 'int' }
mapVal[ffi.typeof(val)] = 'int'
mapVal[ffi.typeof(val)] = 'int'
for k, v in next, mapVal do print(k, v) end -- prints 3 lines of ctype<int>	int
Post Reply

Who is online

Users browsing this forum: Ahrefs [Bot] and 22 guests