[solved] Class metamethods?

It’s a bit difficult finding documentation on luabind class syntax. I have two small questions:

  1. Is there some way to make these support metamethods? (__index, __newindex)

I’m trying to make an alternative to the “viewbuilder” class, adding some behaviors and stuff that doesn’t exist natively. For simplicity, I want this class to revert to using the standard renoise.ViewBuilder() whenever I haven’t defined my own method that “overrides” it. Will I have to revert to oldschool Lua OO to access __index and make this possible?

Also tried this, but it doesn’t work:

Click to view contents
-- GUI builder class, "inheriting" all standard viewbuilder()
-- classes unless we override them.
class 'JouleBuilder'
function JouleBuilder:__init()
  self.vb = renoise.ViewBuilder()
  self.jb = { }
  -- revert to standard viewbuilder for all undefined methods
  self._mt = { __index = self.vb }
  setmetatable(self.jb, self._mt) 
end
local jb = JouleBuilder()
local real_jb = jb.jb
local content = real_jb:row { } -- returns error, probably due to not passing the correct "self" to method?
  1. A small question in the comment below:
class 'MyClass'

function MyClass:__init(value)
  self.value = value
  return self -- this line is pretty useless, right? when would it be useful?
end

This concept with traditional OO kind of works, but it won’t provide the friendliest syntax…

local jb = { }
jb.mt = { __call = function() return jb:new() end } -- jb() instead of jb:new()
setmetatable(jb, jb.mt)

function jb:new()
  local o = { }
  self.vb = renoise.ViewBuilder()
  setmetatable(o, self)
  self.__index = self
  return o
end

-- wrap all standard vb classes like this :(
function jb:row(args)
  return self.vb:row(args)
end

-- for the sake of sanity, these kind of methods could very well be externalized to their separate classes
-- bugger we'll then have to change all values with set() calls :(
function jb:my_element(args)
  local vb = self.vb
  local my_element = vb:row {
    vb:text {
      text = args.label },
    vb:minislider { min = args.min,
                    max = args.max,
                    value = args.value,
                  }
  }
  return my_element
end

local a = jb()
local test_content = a:row {
  a:my_element { min = 0, max = 100, value = 40, label = "slider label" }
}

local dialog = renoise.app():show_custom_dialog("test", test_content)

It seems only the __call method is available in classes.

Anyhow, I think I managed to make a transparent ViewBuilder wrapper like this. It’s pretty nifty having the possibility of adding your own “viewbuilder classes” (combinations of stuff, or additional behaviors to the native classes), while maintaining a transparent syntax to the standard viewbuilder.

class 'NewBuilder'

NewBuilder.subclasses = { "bitmap", "button", "checkbox", "chooser", "column",
  "horizontal_aligner", "minislider", "multiline_text", "multiline_textfield",
    "popup", "rotary", "row", "slider", "space", "switch", "text", "textfield",
    "value", "valuebox", "valuefield", "vertical_aligner", "xypad", -- "slider_labeled"
  }

function NewBuilder:__init()
  self.vb = renoise.ViewBuilder()
  for _, sub_class in ipairs(self.subclasses) do
    NewBuilder[sub_class] = function(self, properties)
      -- override or return your own "viewbuilder class" here
      -- if sub_class == "slider_labeled" then
      -- print("creating a slider with attached label")
      -- return your object
      -- end
      return self.vb[sub_class](self.vb, properties)
    end
  end
end

NewBuilder.views = property(
  function(obj) return obj.vb.views end,
  function(obj, val) obj.vb.views = val end
)

--[[ quick test
local nb = NewBuilder() 
local my_row = nb:row { id = "test" }
oprint(nb)
rprint(nb.views) 
]]

It seems only the __call method is available in classes.

I was hesitant to give you a reply, as we are in largely uncharted territory here.

But __index - yes, I think luabind is using that for it’s own getter/setter interface.

In my experience, comparison and toString methods are working though – especially the comparison could be interesting here?

In your code, the missing _index shouldn’t be much of a problem, should it? You’re the “gatekeeper” of your own code, so you know what methods to expect, etc.

Otherwise cLib.reflection.lua has a few (pretty hacky) ways of scanning object properties :slight_smile:

Well. What I’m really trying to do is create a class that is (close to) 100% compatible with how viewbuilder is used. An intermediate bridge, where custom classes can be added, so to speak. I want to use it as a general replacement for the ViewBuilder class, being “backwards” compatible syntax wise.

I concluded it’s probably not possible. Simply put, I can’t get it to support all of the below three syntaxes. But it’s good enough so I’ll leave it there.

-- Example 1: works just fine

local content = nb:row {
 nb:my_own_slider { label = "Volume", min = 0, max = 100 }
}
-- Example 2: doesn't work, since "element" must be a native vb object to support the syntax in example 1.

local element = vb:my_own_slider { label = "Volume", min = 0, max = 100 }
element.label = "Panning" -- not possible, of course.
-- Example 3: works thanks to building a custom "views" table. (could perhaps add a working add_child and remove_child even)

local element = nb:row {
 my_own_slider { id = "my_slider", label = "Volume" }
}
nb.views.my_slider.label = "Panning"