Snippet: Renoise objects as table keys

I cannot be the first to have written about this, but I couldn’t find anything, so here goes:

Sometimes it’s far more sensible to use Renoise objects as table keys directly, rather than use their index. Indexes can change, and some use cases like storing many samples across multiple instruments can get pretty painful this way.

The trouble is that every reference to a Renoise object in Lua code returns a unique userdata. But, the same underlying object will always be rawequal() to other userdatas that represent that object. So here is something I’ll call a rawtable, which works like a table in all ways except that when looking up objects, it tests by rawequal():

local rawtable = {}

function rawtable.new(t)
  setmetatable(t, rawtable)
  return t
end

function rawtable:__index(key)
  for k, v in pairs(self) do
    if (rawequal(k, key)) then
      return v
    end
  end
end

function rawtable:__newindex(key, value)
  for k, _ in pairs(self) do
    if (rawequal(k, key)) then
      rawset(self, k, value)
      return
    end
  end
  rawset(self, key, value)
end

-- Usage:
local instrs = rawtable.new {}
instrs[renoise.song().selected_instrument] = "foo"
print(instrs[renoise.song().selected_instrument]) -- Prints "foo"

Note that doing this is less efficient than using integer keys, as we’re doing a linear-time lookup by comparing against all objects in the table. But it should be fine unless you are storing a truly ridiculous number of objects.

4 Likes

Thank you very much, this is exactly what I was looking for! By the way, I am a beginner in Lua scripting, how can I get the index of a Renoise object?

Something like this should do the trick:

function object_index(list, object)
  for index, obj in ipairs(list) do
    if rawequal(obj, object) then
      return index
    end
  end
end

list would be the table that includes the object you’re looking for.

1 Like

Unfortunately, rawequal returns false for the same NoteColumns:

local note1 = renoise.song():pattern(1):track(1):line(1):note_column(1)
local note2 = renoise.song():pattern(1):track(1):line(1):note_column(1)

print(rawequal(note1, note2))
false

It seems those need to be compared using ==.

(Edit: I had written up a workaround for broken == behavior, but it’s no longer required in Renoise 3.5.4. :slight_smile:)