lua-users home
lua-l archive

[Date Prev][Date Next][Thread Prev][Thread Next] [Date Index] [Thread Index]


В Сб., 13/08/2011 в 00:52 +0200, Lars Doelle пишет:
> Hi All,
> 
> 
> reviewing the tables, i found them limited w.r.t. composed keys.
> To ensure, all keys stay the same during their existience, tables
> as possible (and only) structuring technic are only identified
> by their pointer (i.e. object name).
> 
> --
> 
> Practical example:
> 
>   Key1 = { first="John", last="Doe" }
>   Key2 = { first="John", last="Doe" }
> 
> When using a key like the above for an table, currently Key1
> and Key2 would identify different entries, i.e. are of no use.

A function like Table.todata in not too hard to implement since you can
use lua_topointer for tables, userdata, functions and threads. Here is a
sketch (may even not compile) of a function called table_to_string which
converts a table to its unique string representation:

static void encode_lua_value(lua_State* L, int index, std::ostream& o)
{
    switch (lua_type(L, index))
    {
    case LUA_TNIL:      o << 'l'; break;
    case LUA_TNUMBER:   o << 'n' << lua_tonumber(L, index); break;
    case LUA_TBOOLEAN:  o << 'b' << lua_toboolean(L, index); break;
    case LUA_TSTRING: { o << 's';
        size_t len;
        const char* = lua_tolstring(L, index, &len);
        o.write((char*)(void*)&len, sizeof(len));
        o.write(s, len);
        } break;
    case LUA_TTABLE:
    case LUA_TFUNCTION:
    case LUA_TUSERDATA:
    case LUA_TLIGHTUSERDATA:
    case LUA_TTHREAD: { o << 'p';
        void* p = lua_topointer(L, index);
        o.write((char*)(void*)&p, sizeof(p));
        } break;
    }
}

int table_to_string(lua_State* L)
{
    std::ostringstream o;
    lua_pushnil(L);
    while (lua_next(L, 1) != 0) {
        encode_lua_value(L, -2, o);
        encode_lua_value(L, -1, o);
        lua_pop(L, 1);
    }
    std::string s = o.str();
    lua_pushlstring(L, s.data(), s.length());
    return 1;
}


Now, you could use the same idea as in interned strings to provide table
uniqueness. Set up a registry of tables:

local registry = {}
setmetatable(registry, {__mode = "v"})

local function intern_table(t)
    local s = table_to_string(t)
    if registry[s] then return registry[s] end
    registry[s] = t
    return t
end

Now use intern_table to make keys:

Key1 = intern_table { first="John", last="Doe" }
Key2 = intern_table { first="John", last="Doe" }

assert(Key1 == Key2)

Unfortunately, you cannot catch all modifications of tables in registry
because the semantics of __newindex are so that an assignment t[v] = k
ignores the metatable if there is already a key v in the table. Still,
you can implement a consistency check function which traverses the
registry and checks if tables really match their string
representations. 

Alternatively, you could give up the idea of interned tables and just
use table_to_string function to produce keys, e.g.

Key1 = table_to_string { first="John", last="Doe" }

-- Gregory