There is an array

local tab = ffi.new('float[?]', n); 

This code should fill it (example):

 for i=3,tonum,2 do tab[#tab+1] = i; end 

However, an error occurs:

 /usr/bin/lua: ./simplnums.lua:8: type float[21] does not implement the __len metamethod stack traceback: [C]: in function '__len' ./simplnums.lua:8: in main chunk [C]: in ? 

    1 answer 1

    To begin with, in your cases, tab not an array (by the way, in Lua there are no arrays as such, this is only a special case of tables). A call to ffi.new returns a certain cdata object - a C data object.

    Suppose we want this object to behave like an array. Lua has all the possibilities for this, we only need to set a metatable for it and define a number of metamethods. The simplest implementation will look like this:

     local ffi = require("ffi") local CFloatArray = {} CFloatArray.mt = { __len = function (o) -- print("#o = " .. o.size) return o.size end, __index = function (o, i) -- print("<= o.data[" .. i .. "]") return o.data[i] end, __newindex = function (o, i, v) -- print("o.data[" .. i .. "] <= " .. v) o.data[i] = v end, __tostring = function (o) local s = "{ " local comma = "" for i = 0, o.size - 1 do s = s .. comma .. o.data[i] comma = ", " end return s .. " }" end } function CFloatArray.new(size) local array = { size = size, data = ffi.new("float[?]", size) } setmetatable(array, CFloatArray.mt) return array end 

    What do we have here? We create a new object - a table that contains the size field (the number of elements in the array) and data (this is our sish array). Of course one could work like this:

     local a = CFloatArray.new(3) for i = 0, a.size - 1 do a.data[i] = i + 1 end 

    But we want convenience, and here metatables come to the rescue. You must define the following metamethods:

    • __len : to implement the #o operator
    • __index : for the implementation of the operator <= o[i]
    • __newindex : for the implementation of the operator o[i] <=
    • __tostring : to convert to string (heap)

    Their implementation is quite simple (in our cases). This case can be complicated, for example, in the index operators [] you can add a check for the index output beyond the allowable ranges, etc. etc.

    Now with our object we apply the work as with a regular array (access elements by index, get length, convert to string):

     local a = CFloatArray.new(3) for i = 0, #a - 1 do a[i] = i + 1 end print("#a = " .. #a .. ", " .. "a = " .. tostring(a)) 

    Result of work:

    #a = 3, a = { 1, 2, 3 }


    Remark 1

    All that is written above is good, but there is one thing. LuaJIT is compiled with Lua 5.1 support by default. And if we execute print("#a = " .. #a) , the result will be as follows:

    #a = 0

    Why it happens? The fact is that support for the metamethod __len for tables is implemented only starting Lua 5.2 .

    Here is the implementation of the # operator in Lua 5.1 , i.e. if the operand is a table, then its size is returned directly.

      function len_event (op) if type(op) == "string" then return strlen(op) -- primitive string length elseif type(op) == "table" then return #op -- primitive table length else local h = metatable(op).__len if h then -- call the handler with the operand return (h(op)) else -- no handler available: default behavior error(路路路) end end end 

    Here is the implementation of the # operator in Lua 5.2 , i.e. First, the presence of the metamethod __len . If it is defined, then a call occurs. Otherwise, the table size is returned directly.

      function len_event (op) if type(op) == "string" then return strlen(op) -- primitive string length else local h = metatable(op).__len if h then return (h(op)) -- call handler with the operand elseif type(op) == "table" then return #op -- primitive table length else -- no handler available: error error(路路路) end end end 

    The solution to this problem is very simple, namely, rebuilding LuaJIT from source code with support for Lua 5.2 (see section Extensions from Lua 5.2 ). For this we need:

    • Find the file (in the source tree) src\Makefile .

    • Find the line LUAJIT_ENABLE_LUA52COMPAT in it and uncomment it.

    • Rebuild. Reassembly method depends on target and host platform. In general, this is a call to make .


    Remark 2

    Now, by your code. Call:

    tab[#tab+1] = i;

    will result in an error, because C arrays are not dynamic. You are creating a fixed-length array and are trying to access its borders.

    If you need more flexibility when working with arrays, look towards lds - LuaJIT Data Structures :

    lds provides data structures that hold LuaJIT cdata.

    These containers cover the common use cases of Lua tables:

    • Array (fixed-size array)
    • Vector (dynamically-sized array)
    • HashMap (key-value store)

    Remark 3

    In general, everything can be done easier. After all, the size of the array is known to you initially. Why not do this:

     local n = 10 local tab = require("ffi").new('float[?]', n) for i = 0, n - 1 do tab[i] = i end 

    Supplement from comments

    So far we have considered the implementation of the FFI interface in the framework of LuaJIT , but there are libraries that implement this mechanism in the framework of "pure" Lua . For example luaffi and luaffifb . The first of them was abandoned for a long time (the last commit is dated November 2013), but the second library (by the way fork first) is quite lively.

    Lua 5.2.4 and luaffifb participated in further tests. luaffi did not participate because she was too lazy to collect. The test results showed that all of the above (the metatables mechanism) works fine without LuaJIT .

    But the lds - LuaJIT Data Structures library on "pure" Lua did not "fly up". Further study showed that in LuaJIT and luaffifb some moments are implemented in different ways, for example, the functions ffi.cdef(def) and ctype = ffi.typeof(ct) . Differences there are at the level of implementation C declaration parser (files lj_cparse.c from LuaJIT and parser.c from luaffifb).

    LuaJIT allows you to create template data types, and luaffifb has no such functionality. For example, in LuaJIT we can write the following;

     -- Declare a struct with a parameterized field type and name: local ctype = ffi.typeof("uint32_t") local name = "id" ffi.cdef([[ typedef struct { $ $; } foo_t; ]], ctype, name) -- Anonymous struct with dynamic names: local name1 = "a" local bar_t = ffi.typeof("struct { int $, $; }", name1, "b") -- Derived pointer type: local bar_ptr_t = ffi.typeof("$ *", bar_t) -- Parameterized dimensions work even where a VLA won't work: local height = 10 local matrix_t = ffi.typeof("uint8_t[$][$]", 20, height) 

    The lds - LuaJIT Data Structures library makes extensive use of this functionality and therefore cannot be used together with luaffifb on the go.

    • As you can see, in my example I write odd numbers starting with three. - val
    • And lds with the usual lua goes? There is also ffi for lua. - val
    • Unfortunately not. But if you are confused by the use of LuaJIT, then you can look at these projects; github.com/jmckaskill/luaffi and github.com/facebook/luaffifb . These are implementations of the FFI interface compatible with LuaJIT. True, I鈥檒l make a reservation right away, I didn鈥檛 try them myself and can鈥檛 say anything. The first of them seems to be abandoned, the last commit is dated November 2013, but the second is April of this year. There seems to be support for Lua 5.3. And one more thing - this is the Facebook hub. - Embedder
    • one
      Odd numbers starting from 3: for i = 3, n - 1, 2 do tab[i] = i end or for i = 3, #a - 1, 2 do a[i] = i end if implemented using metatables. - Embedder
    • one
      There will be no recursion, because the __index __index is associated with the array = { size = size, data = ffi.new("float[?]", size) } array.data , and not with the array.data object. This is easy to verify if you uncomment print("<= o.data[" .. i .. "]") . How many calls of the operator [] , so many messages will be displayed in the console. - Embedder