2012-10-07 7 views
0

루아의 C API를 사용하여 루아를 확장했습니다. 내 모듈에서 luaL_ref을 사용하여 테이블을 채우고 luaL_unref을 사용하여 필드를 제거하고 싶습니다. 또한 lua_next을 사용하여이 테이블을 반복 할 수 있기를 바랍니다.luaL_ref 및 luaL_unref로 수정 된 테이블을 반복하는 방법은 무엇입니까?

luaL_unref 때문에 테이블을 반복하는 것이 문제입니다. 루아에서는 초기화되지 않은 테이블 필드가 nil으로 계산되기 때문에 nil을 할당하여 테이블 필드를 "삭제"하는 것이 일반적입니다. next 함수는 똑똑하여 nil을 건너 뛸 수 있습니다. luaL_unref이 참조되지 않은 테이블 필드에 nil을 할당 할 것으로 예상되었지만 정수를 할당하는 것처럼 보입니다. 이 정수 값은 문서화되지 않은 것 같습니다.

다음 코드를 고려하십시오

/* tableDump prints a table: */ 
/* key: value, key: value, ... */ 

lua_newtable(L); 

lua_pushboolean(L, 0); 
int ref1 = luaL_ref(L, -2); 

lua_pushinteger(L, 7); 
int ref2 = luaL_ref(L, -2); 

lua_pushstring(L, "test"); 
int ref3 = luaL_ref(L, -2); 

tableDump(L, -1); 

luaL_unref(L, -1, ref1); 
tableDump(L, -1); 

luaL_unref(L, -1, ref3); 
tableDump(L, -1); 

luaL_unref(L, -1, ref2); 
    tableDump(L, -1); 

printf("done.\n"); 

출력 : 여기

1: false, 2: 7, 3: `test', 
3: `test', 2: 7, 0: 1, 
3: 1, 2: 7, 0: 3, 
3: 1, 2: 3, 0: 2, 
done. 

을 무슨 일이야? 이 문제를 어떻게 해결할 수 있습니까? 참조를 반복하고 참조되지 않은 것을 무시하는 트릭이 있습니까? luaL_refluaL_unref의 사용을 중지해야합니까?


편집

첫째로, 당신의 응답을 주셔서 감사합니다!

아마도 잘못된 질문을했습니다.

좀 더 구체적으로 설명해주세요. 많은 구독 userdatas를 관리해야하는 클라이언트 userdata가 있습니다. 구독은 클라이언트의 구독 방법에 의해 생성됩니다. 구독은 클라이언트의 구독 취소 방법으로 제거됩니다. 구독 userdatas는 기본적으로 구현 세부 사항이므로 클라이언트 API에 노출되지 않습니다. 대신 클라이언트 API는 구독 참조를 사용하므로 luaL_ref을 사용하여 구독 테이블을 채 웁니다.

ref = client:sub(channel, func) 
cleint:unsub(ref) 

다음은 catch입니다. 클라이언트가 __gc에 남아있는 모든 구독을 자동으로 구독 취소하길 원합니다 (그렇지 않으면 사용자는 segfault를 얻습니다). 그래서 구독을 반복 할 필요가있는 것 같습니다. 나는 여기 API를 실제로 학대하고 있는가? 이 작업을 수행하는 더 좋은 방법이 있습니까?

+0

저는 Lua 5.1을 사용하고 있습니다. – tprk77

+0

API 악용을 중지하십시오. 'luaL_ref'와'luaL_unref'의 목적은 "테이블을 채우는"것이 아닙니다. 참조로 값을 저장하고 나중에 다시 가져올 수있는 간단한 참조 시스템을 만드는 것입니다. 당신은 테이블 위에서 반복해서는 안됩니다. 반환 된 참조를 사용하여 테이블 항목에 액세스해야합니다. –

답변

2

luaL_refluaL_unref 기능은 lauxlib.c에 정의되어 있습니다. 그들은 작동중인 테이블에 저장하는 무료 참조 목록을 추적하여 작동합니다. 이 함수는 비교적 짧기 때문에 여기서 그 함수를 포함 할 것입니다. 그들이 (그들은에서 작동하는 테이블 이외의) 추가 스토리지를 필요로하지 않기 때문에

LUALIB_API int luaL_ref (lua_State *L, int t) { 
    int ref; 
    t = abs_index(L, t); 
    if (lua_isnil(L, -1)) { 
    lua_pop(L, 1); /* remove from stack */ 
    return LUA_REFNIL; /* 'nil' has a unique fixed reference */ 
    } 
    lua_rawgeti(L, t, FREELIST_REF); /* get first free element */ 
    ref = (int)lua_tointeger(L, -1); /* ref = t[FREELIST_REF] */ 
    lua_pop(L, 1); /* remove it from stack */ 
    if (ref != 0) { /* any free element? */ 
    lua_rawgeti(L, t, ref); /* remove it from list */ 
    lua_rawseti(L, t, FREELIST_REF); /* (t[FREELIST_REF] = t[ref]) */ 
    } 
    else { /* no free elements */ 
    ref = (int)lua_objlen(L, t); 
    ref++; /* create new reference */ 
    } 
    lua_rawseti(L, t, ref); 
    return ref; 
} 

LUALIB_API void luaL_unref (lua_State *L, int t, int ref) { 
    if (ref >= 0) { 
    t = abs_index(L, t); 
    lua_rawgeti(L, t, FREELIST_REF); 
    lua_rawseti(L, t, ref); /* t[ref] = t[FREELIST_REF] */ 
    lua_pushinteger(L, ref); 
    lua_rawseti(L, t, FREELIST_REF); /* t[FREELIST_REF] = ref */ 
    } 
} 

이 기능은 정말 영리하다. 그러나 참조 된 값을 반복 할 필요가있을 때처럼 참조 된 값과 동일한 테이블에 자유 참조 목록을 저장하는 것이 바람직하지 않은 경우가 있습니다.

이 문제를 해결하기 위해 luaX_refluaX_unref으로 작성했습니다. luaL_refluaL_unref과 거의 동일하게 작동하지만, 무료 참조 목록을 별도의 테이블에 저장합니다 (l목록). (내 프로젝트를 위해, 나는 별도의 소스 파일에 다음을 넣어했고 필요에 포함 한 내가 lauxlib.c을 수정하지 않는 것이 좋습니다..)

static int abs_index(lua_State * L, int i){ 
    return i > 0 || i <= LUA_REGISTRYINDEX ? i : lua_gettop(L) + i + 1; 
} 

LUALIB_API int luaX_ref(lua_State *L, int t, int l){ 
    int ref; 
    t = abs_index(L, t); 
    l = abs_index(L, l); 
    if(lua_isnil(L, -1)){ 
    lua_pop(L, 1); /* remove from stack */ 
    return LUA_REFNIL; /* 'nil' has a unique fixed reference */ 
    } 
    lua_rawgeti(L, l, FREELIST_REF); /* get first free element */ 
    ref = (int) lua_tointeger(L, -1); /* ref = l[FREELIST_REF] */ 
    lua_pop(L, 1); /* remove it from stack */ 
    if(ref != 0){ /* any free element? */ 
    lua_rawgeti(L, l, ref); /* remove it from list */ 
    lua_rawseti(L, l, FREELIST_REF); /* (l[FREELIST_REF] = l[ref]) */ 
    }else{ /* no free elements */ 
    ref = (int)lua_objlen(L, l); 
    ref++; /* create new reference */ 
    } 
    lua_pushboolean(L, 1); 
    lua_rawseti(L, l, ref); /* l[ref] = true */ 
    lua_rawseti(L, t, ref); /* t[ref] = value */ 
    return ref; 
} 

LUALIB_API void luaX_unref(lua_State *L, int t, int l, int ref){ 
    if(ref >= 0){ 
    t = abs_index(L, t); 
    l = abs_index(L, l); 
    lua_rawgeti(L, l, FREELIST_REF); 
    lua_rawseti(L, l, ref); /* l[ref] = l[FREELIST_REF] */ 
    lua_pushinteger(L, ref); 
    lua_rawseti(L, l, FREELIST_REF); /* l[FREELIST_REF] = ref */ 
    lua_pushnil(L); 
    lua_rawseti(L, t, ref); /* t[ref] = nil */ 
    } 
} 

지금 사용을 참조하십시오

lua_newtable(L); /* 1 */ 
lua_newtable(L); /* 2 */ 

lua_pushboolean(L, 0); 
int ref1 = luaX_ref(L, 1, 2); 

lua_pushinteger(L, 7); 
int ref2 = luaX_ref(L, 1, 2); 

lua_pushstring(L, "test"); 
int ref3 = luaX_ref(L, 1, 2); 

tableDump(L, 1); 
tableDump(L, 2); 

luaX_unref(L, 1, 2, ref1); 
tableDump(L, 1); 
tableDump(L, 2); 

luaX_unref(L, 1, 2, ref3); 
tableDump(L, 1); 
tableDump(L, 2); 

luaX_unref(L, 1, 2, ref2); 
tableDump(L, 1); 
tableDump(L, 2); 

printf("done.\n"); 

출력 :

1: false, 2: 7, 3: `test', 
1: true, 2: true, 3: true, 
2: 7, 3: `test', 
3: true, 2: true, 0: 1, 
2: 7, 
3: 1, 2: true, 0: 3, 

3: 1, 2: 3, 0: 2, 
done. 
2

"반환하는 키의 고유성을 보장하려면"luaL_ref은 사용 된 목록을 유지하고 그 후에는 luaL_unref 키를 제거해야합니다. 이 목록은 t[0]에서 시작하여 인덱스 체인이 nil이 될 때까지 계속됩니다. 이 목록은 활성 참조와 동일한 테이블에 보관됩니다.

니콜 (Nicol)이 관찰 한대로 "API를 악용하다"를 계속하고 구현 정의 동작에 의존하는 경우이 링크 된 목록을 따라 테이블을 반복 할 때 키가 제거 된 참조인지 확인할 수 있습니다. 또는 구현 정의 동작에 대한 의존도를 피하고 성능이 더 뛰어나려면 표 머리를 무시해야하지만 t[0]에서 제거 된 참조 테이블을 별도로 유지하고 테이블을 반복 할 때 건너 뛸 수 있습니다.

참조를 반복해야한다면 다른 메커니즘을 사용하는 것이 더 나을 것입니다.모든 참조/값 쌍을 별도의 테이블에 넣고 참조를 제거하면 별도의 테이블에있는 값을 nil으로 설정할 수 있습니다. 그런 다음 별도의 테이블을 반복 할 수 있습니다.