luaのクラスその4

多重継承。
「Programming Lua」の16章を参考。

--------------------------------------------------------------------------------
-- class utility
--------------------------------------------------------------------------------
-- root class
local g_root_class={
  classname="#root#",

  class=function(self)
    return getmetatable(self).__index
  end,
}

local function g_factory(class, ...)
  local instance={}
  setmetatable(instance, class)
  if instance.initialize then
    instance:initialize(...)
  end

  return instance
end

-- define new class
function define_class(name, ...)
  local base_table={...}
  if #base_table==0 then
    base_table={g_root_class}
  end

  -- new class
  local class_table={
    classname=name,
    super=#base_table==1 and base_table[1] or base_table,
    __tostring=function(self)
      return string.format("<instance of: %s>", name)
    end,
  }
  class_table.__index=class_table

  -- extend
  setmetatable(class_table, {
    __index=#base_table==1 and base_table[1] or function(t, k)
      for i=1, #base_table do
        local v=base_table[i][k]
        if v then
          -- cache
          t[k]=v
          return v
        end
      end
    end,
    __call=g_factory,
    __tostring=function(self)
      return string.format("<class: %s>", name)
    end,
  })

  --set global
  --global(name)
  _G[name]=class_table
end

-- 使う
define_class("ClassA")
define_class("ClassB")

-- ClassAとClassBを多重継承
-- indexが被ったら先に継承した方(ClassA)が使われる
define_class("ClassAB", ClassA, ClassB)

local ab=ClassAB()

luaunit導入のためあるクラスの機能を分離したかったのだが、
元の変更を最小限にするために多重継承を入れてしまった。
今まで多重継承はできるだけ避けていたのだがどうなんだろう。

utf8バイト列を1文字ずつ処理する

function each_utf8(text)
  local i=1
  return function()
    if i>#text then
      -- 終了
      return
    end
    local b=string.byte(text, i)
    if b==0 then
      -- eof
      return
    elseif b<128 then
      -- ascii
      local m=string.sub(text, i, i)
      i=i+1
      return m
    elseif b<192 then
      assert(false, 'invalid byte')
      return
    elseif b<224 then
      -- 2bytes
      local m=string.sub(text, i, i+1)
      i=i+2
      return m
    elseif b<240 then
      -- 3bytes(japanese multibyte)
      local m=string.sub(text, i, i+2)
      i=i+3
      return m
    elseif b<248 then
      -- 4bytes
      local m=string.sub(text, i, i+3)
      i=i+4
      return m
    elseif b<252 then
      -- 5bytes
      local m=string.sub(text, i, i+4)
      i=i+5
      return m
    elseif b<254 then
      -- 6bytes
      local m=string.sub(text, i, i+5)
      i=i+6
      return m
    else
      assert(false, 'unknown')
      return
    end
  end
end

-- 使う
for v in each_utf8('Abあソ漢ア') do
  print(v)
end

UTF8文字列を1文字ずつ処理するイテレータ関数。
無さそうだったので作ってみた。
string.gmatchでもっと短く書けそうな気がしたのだができなかったのでとりあえず動くもの。