사용자 도구

사이트 도구


kb:luasnippets


Lua Snippets

루아 코드 조각 모음

함수 오버라이드 검사

베이스 클래스에 있는 함수를 지정된 인덱스에 있는 테이블에서 오버라이드했는가?

/// \brief 베이스 클래스에 있는 함수를 지정된 인덱스에 있는 테이블에서 오버라이드했는가?
/// \param baseName 베이스 클래스 이름. ex) "SkillHandler"
/// \param funcName 함수 이름. ex) "OnStartCast"
/// \param tableName 전역 테이블 이름. ex) "g_SkillHandlers"
/// \param tableIndex 테이블 멤버 인덱스. ex) 스킬 인덱스
/// \return bool 오버라이드했다면 true
bool IsOverriden(lua_State* L, const char* baseName, const char* funcName, const char* tableName, int tableIndex)
{
    bool result = false;
 
    int top = lua_gettop(L);
 
    do
    {
        // 기본 함수를 가지는 테이블을 가져온다.
        lua_getglobal(L, baseName);
        if (lua_type(L, -1) != LUA_TTABLE)
            break;
 
        // 테이블에 있는 함수를 가져온다.
        lua_pushstring(L, funcName);
        lua_gettable(L, -2);
        if (lua_type(L, -1) != LUA_TFUNCTION)
            break;
 
        // 함수 인덱스를 저장해둔다.
        int baseFuncIndex = lua_gettop(L);
 
        // 비교할 전역 테이블을 가져온다.
        lua_getglobal(L, tableName);
        if (lua_type(L, -1) != LUA_TTABLE)
            break;
 
        // 전역 테이블 아이템 중에 해당하는 인덱스의 테이블을 다시 가져온다.
        lua_pushinteger(L, tableIndex);
        lua_gettable(L, -2)
        if (lua_type(L, -1) != LUA_TTABLE)
            break;
 
 
        // 해당 인덱스의 테이블의 같은 이름의 함수를 가져온다.
        lua_pushstring(L, funcName);
        lua_gettable(L, -2);
        if (lua_type(L, -1) != LUA_TFUNCTION)
            break;
 
        result = lua_equal(L, baseFuncIndex, -1) == 0;
 
    } while (0);
 
    if (top < lua_gettop(L))
        lua_pop(L, lua_gettop(L) - top);
 
    return result;
}

모듈 패스 등록하기

5.0 버전까지는 LUA_PATH 라는 이름의 전역 변수에다 설정하면 됐는데, 5.1 버전부터 package 테이블 안의 path 변수(package.path)를 이용해야한다.

/// \brief 루아 모듈 패스를 등록한다.
/// \param L 루아 상태 객체
/// \param path 패스 문자열. 각각의 디렉토리를 ';'로 구분지은 문자열로 들어가 있어야 한다.
/// \param bAppend 기존 경로에 추가하는가의 여부
void SetModulePath(lua_State* L, const char* path, bool bAppend)
{
    size_t before = 0;
    size_t current = 0;
 
    std::string original(path);
    std::string modified;
 
    while (current < original.size())
    {
        current = original.find_first_of(';', before);
        if (current == std::string::npos) current = original.size();
 
        std::string token = original.substr(before, current - before);
        if (!token.empty())
        {
            if (token[token.size()-1] != '\\')
                token += '\\';
 
#ifdef _DEBUG
            assert(::PathFileExists(token.c_str()) && "path not found");
#endif
 
            modified += token + "?.lua;";
            modified += token + "?.dll;";
        }
 
        before = current + 1;
    }
 
    if (!modified.empty())
    {
        if (bAppend)
        {
            lua_getfield(L, LUA_GLOBALSINDEX, "package");
            assert(lua_istable(L, -1));
 
            lua_getfield(L, -1, "path");
            std::string old_path = lua_tostring(L, -1);
            if (old_path[old_path.size()-1] != ';')
                old_path += ';';
            modified = old_path + modified;
 
            lua_pop(L, 2);
        }
 
        lua_getfield(L, LUA_GLOBALSINDEX, "package");
        assert(lua_istable(L, -1));
 
        lua_pushstring(L, modified.c_str());
        lua_setfield(L, -2, "path");
 
        lua_pop(L, 1);
    }
}

비트 연산

function extract(value, index)
    local v = value
    local m = 0
    for i=1,index do
        m = math.mod(v, 2)
        v = v / 2
    end
    if m >= 1 then return 1 end
    return 0
end
 
function bitwise_and(n1, n2)
    local rvalue = 0
    for i=1,32 do
        if extract(n1, i) == 1 and extract(n2, i) == 1 then
            rvalue = rvalue + math.pow(2, i-1)
        end
    end
    return rvalue
end
 
function bitwise_or(n1, n2)
    local rvalue = 0
    for i=1,32 do
        if extract(n1, i) == 1 or extract(n2, i) == 1 then
            rvalue = rvalue + math.pow(2, i-1)
        end
    end
    return rvalue
end
 
assert(bitwise_or(4, 1) == 5)
assert(bitwise_or(4, 2) == 6)
assert(bitwise_or(4, 4) == 4)
assert(bitwise_and(4, 1) == 0)
assert(bitwise_and(4, 2) == 0)
assert(bitwise_and(4, 4) == 4)

너무 길다. 루아를 잘 알지 못해서 그런 것일수도 있겠으나, 개인적으로는 mathlib.c 파일을 수정해서 C 함수 하나 추가하는 편이 나을 거라고 생각한다.

...
static int math_bwand(lua_State* L) 
{
  int lhs = (int)luaL_checknumber(L, 1);
  int rhs = (int)luaL_checknumber(L, 2);
  lua_pushnumber(L, lhs & rhs);
  return 1;
}
 
static int math_bwor(lua_State* L) 
{
  int lhs = (int)luaL_checknumber(L, 1);
  int rhs = (int)luaL_checknumber(L, 2);
  lua_pushnumber(L, lhs | rhs);
  return 1;
}
...
 
static const luaL_reg mathlib[] = 
{
  {"abs",        math_abs},
  ...생략...
  {"randomseed", math_randomseed},
+  {"bwand",      math_bwand},
+  {"bwor",       math_bwor},
  {NULL,         NULL}
};

CSV 파서

“Programming in Lua”란 책에 나오는 CSV 파서를 약간 수정한 파서

CSVRow = 
{
    Columns = {}, -- 컬럼 문자열들
 
    new = function (self) 
        o = {} 
        setmetatable(o, self)
        self.__index = self
        return o
    end,
 
    parse = function(self, line)
        self.Columns = {}
 
        s = line .. ','        -- ending comma
        local fieldstart = 1
        repeat
            -- next field is quoted? (start with `"'?)
            if string.find(s, '^"', fieldstart) then
                local a, c
                local i  = fieldstart
 
                repeat
                    -- find closing quote
                    a, i, c = string.find(s, '"("?)', i+1)
                until c ~= '"'    -- quote not followed by quote?
 
                if not i then 
                    error('unmatched "') 
                end
 
                local f = string.sub(s, fieldstart+1, i-1)
                table.insert(self.Columns, (string.gsub(f, '""', '"')))
                fieldstart = string.find(s, ',', i) + 1
            else                -- unquoted; find next comma
                local nexti = string.find(s, ',', fieldstart)
                table.insert(self.Columns, string.sub(s, fieldstart, nexti-1))
                fieldstart = nexti + 1
            end
        until fieldstart > string.len(s)
    end,
 
    to_string = function(self)
        local line = ""
        for _, token in ipairs(self.Columns) do
            line = line .. ","
            if string.find(token, '[,"]') then
                line = line .. '"' .. string.gsub(token, '"', '""') .. '"'
            else
                line = line .. token
            end
        end
        return string.sub(line, 2)
    end
}
 
CSVFile = 
{
    Rows = {},
 
    new = function (self)
        o = {} 
        setmetatable(o, self)
        self.__index = self
        return o
    end,
 
    load = function(self, filename)
        local file = assert(io.open(filename, "r"))
 
        while true do
            local line = file:read("*line")
            if line == nil then break end
            local row = CSVRow:new()
            row:parse(line)
            table.insert(self.Rows, row)
        end
 
        file:close()
    end,
 
    save = function(self, filename)
        local file = assert(io.open(filename, "w"))
        for _, row in pairs(self.Rows) do
            file:write(row:to_string() .. "\n")
        end
    end
}
 
-- 여기서부터는 샘플 코드
f = CSVFile:new()
f:load("test.csv")
print(f.Rows[1].Columns[1])
f:save("output.csv")

—-

kb/luasnippets.txt · 마지막으로 수정됨: 2014/11/10 16:23 (바깥 편집)