Proof of concept Re: STL table using metatable to access
Thomas Nelson <[email protected]>
| Newsgroups | gmane.comp.lang.lua.bind.user |
|---|---|
| Message-ID | <[email protected]> |
This code is still WAY preliminary but I wanted to throw it out there as a proof of concept and see if anybody had any thoughts on it. I've not even tested it beyond checking to see if I can access the items in the table. It consists of two basic parts, a converter for translating a pointer to an STL collection into a lua table and an adapter that uses meta table methods to call the underlying STL collection access methods. There is also an attempt to use a weak table to preserve identity but I'm not convinced that works yet. Outstanding issues: - This has only been specialized for std::vector at this point. I've kept the metamethods split out from the adapter so they can be specialized individually. - Lua 5.1 does not respect the __length metamethod for tables (this appears to have been fixed for 5.2) Additionally I'm not sure what the right behavior should be since #table is defined to be the index of the last non nil entry and if I have a vector of pointers some of which are NULL, returning the length of the vector would violate the rule. - [i]pairs and [i]next. Lua does not directly support meta methods for these and inext calls rawgeti and so does not call the __index metamethod. I've found a patch/extension here (http://lua-users.org/wiki/GeneralizedPairsAndIpairs) that fixes this for inext, but I don't know if that's a good final solution. - I'm storing some book-keeping info in the table itself... I've not decided yet if that's a good idea or not. - The identity code isn't quite right yet. I should be able to do a = source; b = source; a == b and get back true. - It sure as heck isn't thread safe. Things I'd like to add someday... - a const pointer will create a read only table - triggers on add/set (rather than putting values directly into the tables.) TomN On 3/25/2010 1:25 PM, Thomas Nelson wrote: > Before I dive into this too far, has anyone thought of/implemented using > a metatable directly access a C++ STL container as a lua table? > > Correct me if I'm wrong but container_policy.hpp just copies the values > of the existing table into a lua table and returns that. Couldn't we > apply a meta table with "__index" "__newindex" and "__len" methods > specialized to the container type. > > TomN > ------------------------------------------------------------------------------ Download Intel® Parallel Studio Eval Try the new software tools for yourself. Speed compiling, find bugs proactively, and fine-tune applications for parallel performance. See why Intel Parallel Studio got high marks during beta. http://p.sf.net/sfu/intel-sw-dev _______________________________________________ luabind-user mailing list [email protected] https://lists.sourceforge.net/lists/listinfo/luabind-user
stl_container_converter.hpp
(text/plain, 1.1 KB)
#ifndef LUABIND_STL_CONTAINER_CONVERTER_100325_HPP
# define LUABIND_STL_CONTAINER_CONVERTER_100325_HPP
# include <luabind/get_main_thread.hpp>
# include <luabind/handle.hpp>
# include <luabind/detail/policy.hpp>
# include <luabind/detail/stl_container_adapter.hpp>
namespace luabind {
//#define LUABIND_STL_CONVERTER( type )
template <class T>
struct default_converter< std::vector< T > *>
{
typedef boost::mpl::false_ is_native;
typedef std::vector< typename T > * container;
template <class U>
int match(lua_State* L, U, int index)
{
return detail::stl_adapter<std::vector< T > >::container_match(L, index);
}
template <class U>
std::vector<T> *apply(lua_State* L, U, int index)
{
return detail::stl_adapter<std::vector< T > >::container_from(L, index);
}
void apply(lua_State* L, std::vector<T> * p)
{
detail::stl_adapter<std::vector< T > >::container_to(L, p);
}
template <class U>
void converter_postcall(lua_State*, U const&, int)
{}
};
} // namespace luabind
#endif // LUABIND_SHARED_PTR_CONVERTER_090211_HPP
stl_container_adapter.hpp
(text/plain, 6 KB)
#ifndef LUABIND_STL_CONTAINER_ADAPTER_HPP_INCLUDED
#define LUABIND_STL_CONTAINER_ADAPTER_HPP_INCLUDED
#include <luabind/config.hpp>
#include <luabind/detail/policy.hpp>
namespace luabind
{
namespace detail
{
static const char * container_lud_tag = "__luabind_stl_lud";
static const char * container_ref_tag = "__luabind_stl_ref";
//----------------------------------------
template <class Container_tt>
int stl_container__len(lua_State *L);
template <class Container_tt>
int stl_container__index(lua_State *L);
template <class Container_tt>
int stl_container__newindex(lua_State *L);
//----------------------------------------
void stl_register_collection(lua_State *L, void *p);
bool stl_find_table_for_collection(lua_State *L, void *p);
//----------------------------------------
template <class Container_tt>
struct stl_adapter
{
typedef typename Container_tt container_t;
static int container_create_metatable(lua_State *L)
{
lua_newtable(L);
lua_pushstring(L, "__len");
lua_pushcclosure(L, &stl_container__len<container_t>, 0);
lua_rawset(L, -3);
lua_pushstring(L, "__index");
lua_pushcclosure(L, &stl_container__index<container_t>, 0);
lua_rawset(L, -3);
lua_pushstring(L, "__newindex");
lua_pushcclosure(L, &stl_container__newindex<container_t>, 0);
lua_rawset(L, -3);
ref_stl_adapter = luaL_ref(L, LUA_REGISTRYINDEX);
lua_rawgeti(L, LUA_REGISTRYINDEX, ref_stl_adapter);
return ref_stl_adapter;
}
static void container_push_lua_table(lua_State *L, container_t *pContainer)
{
int nOldTable;
lua_newtable(L);
/* *TSN*: Should I clone the metatable rather than setting this state data here? */
lua_pushstring(L, container_lud_tag);
lua_pushlightuserdata(L, pContainer);
lua_rawset(L, -3);
nOldTable = lua_getmetatable(L, -1);
if (ref_stl_adapter == LUA_REFNIL)
container_create_metatable(L);
else
lua_rawgeti(L, LUA_REGISTRYINDEX, ref_stl_adapter);
if (nOldTable)
{
lua_insert(L, -2); // swap the tables
lua_setmetatable(L, -2);
}
lua_setmetatable(L, -2);
/* *TSN*: Should I clone the metatable rather than setting this state data here? */
lua_pushstring(L, container_ref_tag);
lua_pushinteger(L, ref_stl_adapter);
lua_rawset(L, -3);
}
static container_t * container_from_table(lua_State *L, int index)
{
container_t *pcontainer;
if (!lua_istable(L, index))
return 0;
lua_pushstring(L, container_lud_tag);
lua_rawget(L, index);
pcontainer = static_cast<container_t *>(lua_touserdata(L, -1));
lua_pop(L, 1);
return pcontainer;
}
static int container_match(lua_State *L, int index)
{
if (!lua_istable(L, index))
return -1; // not a table at all
// must be a table AND must be a compatible STL table
lua_pushstring(L, detail::container_ref_tag);
lua_rawget(L, index);
if (lua_isnil(L, -1))
{ // not one of our tables at all
lua_pop(L, 1);
return -1;
}
int nRefId = lua_tointeger(L, -1);
lua_pop(L, 1);
return (nRefId == ref_stl_adapter) ? 0 : -1;
}
static container_t *container_from(lua_State* L, int index)
{
lua_pushstring(L, detail::container_lud_tag);
lua_rawget(L, index);
container_t *raw_ptr = static_cast<container_t *>(lua_touserdata(L, -1));
lua_pop(L, 1);
return raw_ptr;
}
static void container_to(lua_State* L, container_t *p)
{
if (!stl_find_table_for_collection(L, p))
{
container_push_lua_table(L, p);
stl_register_collection(L, p);
}
// table now at top of stack
}
static int ref_stl_adapter;
};
template <class Container_tt>
int stl_adapter<Container_tt>::ref_stl_adapter = LUA_REFNIL;
/**
* Table length operation. '#table'
* metamethod
* ---
* @Note technically this is incorrect it returns the number of items in
* the STL collection, however Lua (2.2.5) defines the length of a table
* as follows:
* "The length of a table t is defined to be any integer index n such
* that t[n] is not nil and t[n+1] is nil."
*
* But do I want to scan looking for a nil value?
*/
template <class Container_tt>
int stl_container__len(lua_State *L)
{ /* stack:
* 1: table object
*/
Container_tt *container = stl_adapter<Container_tt>::container_from_table(L, 1);
int iSize(0);
if (container->empty())
iSize = container->size() + 1;
lua_pushnumber(L, iSize);
return 1;
}
/**
* metatable method, accessors. val = table[key];
* Note that lua indexes are 1 based.
*/
template <class Container_tt>
int stl_container__index(lua_State *L)
{ /*
* 1: table
* 2: key
*/
default_converter<Container_tt::value_type> converter;
Container_tt * container = stl_adapter<Container_tt>::container_from_table(L, 1);
int index = lua_tointeger(L, 2);
if ((index < 1) || (index >= (int)container->size()))
{ // the key is not convertible to an integer or is outside our range
// use the raw table
lua_pushvalue(L, 2);
lua_rawget(L, 1);
return 1;
}
converter.apply(L, container->at(index - 1));
return 1;
}
/**
* metatable method, setter. table[key] = value;
* Note that lua indexes are 1 based.
*/
template <class Container_tt>
int stl_container__newindex(lua_State *L)
{ /*
* 1: table object
* 2: key
* 3: value
*/
default_converter<typename Container_tt::value_type> converter;
Container_tt * container = stl_adapter<Container_tt>::container_from_table(L, 1);
int index = lua_tointeger(L, 2);
if (index < 1)
{ // the key is not convertible to an integer
// use the raw table
lua_pushvalue(L, 2);
lua_pushvalue(L, 3);
lua_rawset(L, 1);
return 0;
}
if (index >= (int)container->size())
{
container->resize(index);
}
container->at(index) = converter.apply(L,
LUABIND_DECORATE_TYPE(Container_tt::value_type), 3);
return 0;
}
}
}
#endif
stl_container_adapter.cpp
(text/plain, 1.2 KB)
#define LUABIND_BUILDING
#include <luabind/config.hpp>
#include <luabind/lua_include.hpp>
#include <luabind/detail/stl_container_adapter.hpp>
namespace luabind
{ namespace detail
{
static int s_stl_weak_registry = LUA_REFNIL;
static void stl_weak_registry_create(lua_State *L)
{
lua_newtable(L);
lua_newtable(L);
lua_pushstring(L, "__mode");
lua_pushstring(L, "v");
lua_settable(L, -3);
lua_setmetatable(L, -2);
s_stl_weak_registry = luaL_ref(L, LUA_REGISTRYINDEX);
}
// table should be at top of stack
void stl_register_collection(lua_State *L, void *p)
{
if (s_stl_weak_registry == LUA_REFNIL)
{
stl_weak_registry_create(L);
}
lua_pushinteger(L, s_stl_weak_registry);
lua_gettable(L, LUA_REGISTRYINDEX);
// registry now at top of stack
lua_pushlightuserdata(L, p);
lua_pushvalue(L, -3); // copy the table
lua_settable(L, -3);
lua_pop(L, 1);
}
bool stl_find_table_for_collection(lua_State *L, void *p)
{
lua_pushinteger(L, s_stl_weak_registry);
lua_gettable(L, LUA_REGISTRYINDEX);
if (lua_isnil(L, -1))
{ // not found
lua_pop(L, 1);
return false;
}
lua_pushlightuserdata(L, p);
lua_gettable(L, -2);
if (lua_isnil(L, -1))
{
lua_pop(L, 1);
return false;
}
return true;
}
}
}