2012-06-24 5 views
1

루아 클래스 개체를 스택에 푸시하려고합니다. 객체에 대한 포인터는 여러 함수에 의해 반환 될 수 있습니다.Lua 사용자 데이터 개체 관리

다른 말로하면 '==', '~ ='등을 사용할 수 있도록 userdata 값을 푸시해야합니다. 따라서 동일한 C++ 객체 인 경우 userdata 포인터가 동일해야합니다.

-- this should push the object onto the stack 
local firstObject = GetClassObject(); 
firstObject:doSomething(); 

firstObject는 루아 스크립트에 의해 저장되고 나중에 코드에서 내가 다시이 작업을 수행해야합니다 같은 C++ 클래스 포인터가 어딘가에 이미 존재하는 경우

-- the c++ class pointer has not changed here 
-- so I would like to push the same userdata pointer as in the first call... 
local object = GetClassObject(); 

-- if I would not do this the following here would fail... :C 
if object == firstObject then 
... 

내 누름 기능은 기본적으로 확인해야합니다 (아무리 내가 밀어도 객체가 1 : 1로 작동해야 함)

그렇지 않은 경우 새 userdata를 만들고 그것을 클래스 객체에 전달합니다.

template <typename T> 
void Push(const T &tObject) 
{ 
    lua_State *L = GetLuaState(); 

    // Here i need to check if such a C++ object (the same tObject) 
    // already exists! 
    // 
    // If so i want to push the associated userdata. 


    // Object didn't exist yet -> we need a new userdata 
    void *pUserData = lua_newuserdata(L, sizeof(tObject)); 
    *reinterpret_cast<T*>(pUserData) = tObject; 
} 

template <typename T> 
void Push(const T &tObject, const char *pszTable) 
{ 
    Push(tObject); 
    lua_State *L = GetLuaState(); 
    luaL_getmetatable(L, pszTable); 
    lua_setmetatable(L, -2); 
} 

template <typename T> 
T& Get(int nIndex) 
{ 
    T *pUserData = reinterpret_cast<T*>(lua_touserdata(GetLuaState(), nIndex)); 
    if(pUserData == nullptr) 
     throw std::exception("Invalid userdata!"); 

    return *pUserData; 
} 

template <typename T> 
T& Get(int nIndex, const char *pszTable) 
{ 
    T *pUserData = reinterpret_cast<T*>(LuaToUData(nIndex, pszTable)); 
    if(pUserData == nullptr) 
     throw std::exception("Invalid userdata!"); 

    return *pUserData; 
} 

LuaToUData 내가 루아 오류가 발생하지 않도록 쓴 자신의 기능입니다 : 여기

내 코드의

void* LuaToUData(int nIndex, const char *pszTable) 
{ 
    void *pUserData = lua_touserdata(g_luaState, nIndex); 
    if(pUserData != nullptr) 
    { 
     if(lua_getmetatable(g_luaState, nIndex) != 0) 
     { 
      lua_getfield(g_luaState, LUA_REGISTRYINDEX, pszTable); 
      bool bEqual = (lua_rawequal(g_luaState, -1, -2) == 1); 
      lua_pop(g_luaState, 2); 

      if(bEqual) 
       return pUserData; 
     } 
    } 

    return nullptr; 
} 
+0

나는 당신이 여기서하려고하는 것을 정확히 해결하는 것이 어렵다는 것을 알고 있습니다. 당신이 만든 Lua userdata 인스턴스를 그냥 캐시하려고합니까? – Rook

+0

나는 '==', '~ ='등을 사용할 수있는 능력을 유지하면서 userdata 값을 푸시하려고합니다. 따라서 userdata 포인터는 동일한 C++ 객체 인 경우 동일해야합니다. – user1478081

+0

Lua userdata 객체는 참조에 의해 내부적으로 비교됩니다. 동일한 기본 포인터에서 생성 된 두 개의 사용자 데이터 인스턴스가 동일한 것으로 비교되어야합니다. 이 경우'=='이 작동하지 않는다고 말하고 있습니까? 그리고'__eq' 메타 테이블 엔트리를 오버라이드 시켰습니까? – Rook

답변

0

저 약한 테이블이 작동하는 방식입니까?

void Push(const T &tObject) 
{ 
    std::ostringstream o; 
    o << tObject; 
    std::string sIdentifier = o.str(); 
    const char *pszIdentifier = sIdentifier.c_str(); 

    lua_State *L = GetLuaState(); 
    luaL_getmetatable(L, "lua_userdata"); 
    if(!lua_istable(L, -1)) 
    { 
     // create new weak table 
     luaL_newmetatable(L, "lua_userdata"); 
     lua_pushstring(L, "v"); 
     lua_setfield(L, -2, "__mode"); 
    } 

    lua_getfield(L, -1, pszIdentifier); 
    if(lua_isuserdata(L, -1) == TRUE) 
     return lua_remove(L, -2); 

    lua_pop(L, 1); // didnt exist yet - getfield is nil -> need to pop that 
    void *pUserData = lua_newuserdata(L, sizeof(UINT64)); 
    *reinterpret_cast<UINT64*>(pUserData) = UINT64(tObject); 

    lua_pushvalue(L, -1); 
    lua_setfield(L, -3, pszIdentifier); 
    lua_remove(L, -2); 
} 
+0

약한 테이블에 캐시 된 userdata가 여전히 거기에있을 수 있음을 잊지 마십시오. 첫 번째 사용자 데이터 가비지 수집 단계 이후에 추가 검사가 필요합니다.이 양식은 클래스 인스턴스에 대한 실제 포인터가 udata 청크에 저장되는 방식에 따라 다릅니다. 단순히 nil이 아닌 값을 확인하면이 경우를 놓칠 수 있습니다. udata에 단일 포인터가 포함되어 있으면 __gc metamethod가 수행해야하는 작업이므로 NULL에 대해 확인해야합니다. 그렇지 않으면 부활했지만 죽은 물건을 다시 통역관으로 돌려줍니다. – user3125367

1

오른쪽, 루아에서의 같은 유저 데이터의 두 예은 동일한 것으로 보장됩니다. 그러나 C++ 클래스 인스턴스를 boxing 할 때 각 boxed 인스턴스는 새로운 userdatum에 놓입니다. 즉, 직접 비교할 수 없습니다.

당신이해야 할 일은 객체에 대해 __eq 메타 메서드를 정의하는 것입니다.

int l_compare_things(lua_State* l) 
{ 
    MyClass* a = reinterpret_cast<MyClass*>(lua_touserdata(L, 1)); 
    MyClass* b = reinterpret_cast<MyClass*>(lua_touserdata(L, 2)); 

    lua_pushboolean(L, (*a) == (*b)); 

    return 1; 
} 

MyClassoperator== 재정의 일종이 있다고 가정합니다 : 그것은이 같은 작은 선물을 보일 수 있습니다. 이 함수를 MyClass 사용자 데이터 항목과 연결된 메타 데이터의 __eq 메타 메소드로 설정할 수 있습니다. 이미 metatable 처리가 적용된 것 같습니다. 그래서 여기서는 그다지 신경 쓰지 않을 것입니다.

이제 다음 문제는 전체 클래스 인스턴스를 루아의 완전한 사용자 데이터 항목으로 복싱하는 것입니다. 당신은 아마 똑같은 것을 반복해서 반복해서 사용하지 않고 당신에게 사용 가능한 모든 메모리를 사용하고 싶지 않을 것입니다. 포인터를 밀고 있으면서도 문제가되지는 않지만 그렇게하지는 않습니다. 따라서 ... C++ 클래스의 각 인스턴스를 식별 할 수있는 고유 한 방법이 필요합니다. 다음은 문자열 예는 다음과 같습니다

class MyClass 
{ 
private: 
    std::string _id; 
public: 
    MyClass(const std::string& id) : _id(id) {} 

    const std::string& get_id() { return _id; } 

    // setters and operator= overrides not included. 
}; 

void l_push_thing(lua_State* L, const MyClass& thing) 
{ 
    // try to get our instance by ID from the registry table: 
    lua_getfield(L, LUA_REGISTRYINDEX, thing.id()); 

    // if so, return, leaving it at the top of the stack. 
    if (lua_isuserdata(L, -1)) 
     return; 

    void *ud = lua_newuserdata(L, sizeof(MyClass));      
    *reinterpret_cast<MyClass*>(ud) = thing; 
    // set up the metatable, etc 

    // duplicate the userdata reference: 
    lua_pushvalue(L, -1); 

    // push our new userdata into the registry. pops the duplicate from the stack 
    lua_setfield(L, LUA_REGISTRYINDEX, thing.get_id()); 
} 

(주의 :.! 내가 컴파일 또는이 예제를 테스트하지했습니다 E & OE)를

이 상단에 어떤 특별한 MyClass 인스턴스와 연관된 userdatum를 떠나 스택의 클래스 인스턴스의 등록을 취소하려면 자체 단계를 수행해야합니다. 이 경우 레지스트리에 각 인스턴스에 대한 하드 참조가 있으므로 사용자 데이터 은 해당 참조를 삭제할 때까지 가비지 수집되지 않습니다. 약점/천력 현상 표는 여기에서 사용하는 것이 좋습니다.

+0

감사합니다. 슬프게도 약한/천력 현상 테이블이 어떻게 작동하는지 잘 모릅니다. – user1478081

+0

모든 userdata를 std :: map에 값과 함께 매핑하려고 시도했지만 자신의'__gc' 메소드를 추가했지만 작동하지 않았습니다. 전체 userdata 포인터를 가벼운 userdata로 푸시했습니다. 작동하지 않습니다. c – user1478081

+0

@ user1478081 한 번에 한 단계 씩. 기본 푸시 시스템이 올바르게 작동 할 때까지 약한 테이블에 대해 걱정하지 마십시오! 나는 당신이 당신의'std :: map'으로 무엇을 달성하려고하는지 확신하지 못한다. 위에서 작성한 코드를 사용할 수 있었습니까? – Rook