|
View:
New views
1 Messages
—
Rating Filter:
Alert me
|
|
|
Another policy class - converting tables to/from STLI've written another (in my opinion, very useful) policy class. This one
allows Lua tables to work interchangeably with many STL containers, including vector, deque, list, map, multimap and hash_map. (Currently only supports pair associative containers, and does not support simple associative containers such as set, multiset and hash_set.) So I could for example call the following C++ function: void printStrings(const std::vector<std::string> &values); ... from Lua with the following command: printStrings( { "Hello", "World" } ) Likewise I could have a C++ return a std::vector and have it automatically converted to an array (table) in Lua. The copy_table policy allows you to specify an input parameter or result that you want converted to/from an STL sequence (anything that supports push_back(), including vector, deque and list). The copy_table_assoc policy allows you to specify an input parameter or result that you want converted to/from an STL associative container (anything that has a value_type of pair<key_type, value_type>(), including map, multimap, hash_map and hash_multimap) *NOTE 1* ***THERE IS A COST ASSOCIATED WITH CONVERTING TO/FROM LUA.*** All entries in the container must be copied over from their source to their new destination format. This complexity is O(n * X), where X is the insertion time for the type of container. Some calculations: - Lua table to vector, deque or list: O(n) - Lua table to map: O(n log n) - Any STL sequence to Lua table: O(n) *NOTE 2* - Any STL associative container to Lua table: O(n^2) *NOTE 2* Obviously the sequences outperform the associative containers considerably. The "copy_*" in the naming of the two policies is to remind you that there is a cost (in some cases, substantial) to convert to and from tables. ***SO WHY USE IT THEN?*** Well, the most obvious reason is to provide your Lua coders with the convenience and familiarity of being able to submit and receive tables to/from functions. To my mind, this is a Big Deal... everything should be made as simple as possible for scripters, and to that end they should be able to use the built-in features of the language as much as possible. Another good reason is that you cannot expose a generic container class in Luabind; you must repeat the definition for every instantiation of the class, eg. vector<int>, vector<string>, vector<float>, etc. This is undesirable and impractical in most cases. You will want to weigh these considerations against the cost, and also consider other options (such as using the return_stl_iterator policy, or manually plumbing each function by creating a secondary function that takes/returns a luabind::object, or creating some other kind of beast-monster class for handling generic containers). But at least now you've got the option. *NOTE 1*: Although the copy_table_assoc policy syntactically supports Multiple Associative Containers such as multimap and hash_multimap, these have little functional meaning when going to/from Lua as Lua tables are Unique Associative Containers. So it may still be useful if you are attempting to plumb a function that takes/returns a multimap, but keep in mind that when going from Lua to CPP the CPP function will only ever get unique values, and (more dangerously) when going from CPP to Lua any duplicate entries in the multimap will be discarded. *NOTE 2*: Theoretical based on Lua 5.0 hybrid-table implementation; not actually tested. Associative containers where the key-type is int are assumed to have sparse values (eg. keys more likely to be 10, 500 and 10000 than 1, 2 and 3); it won't hurt but it wastes some memory if they aren't. Although the n^2 complexity of returning a map to Lua is scary, it's also worst-case... the average is likely much better. (Still a good idea to keep your maps on the smaller side or favour sequences instead.) Anyway, hopefully this will be of use to the community. Let me know if you have any questions or comments. Dan. ***SAMPLE CODE*** -------------------C++------------------- static void TestIn(const vector<string> &in) { for (int i = 0; i < in.size(); i++) { printf("%s\n", in[i].c_str()); } } static vector<QString> TestOut() { vector<QString> result; result.push_back("One"); result.push_back("Two"); result.push_back("Three"); result.push_back("Four"); return result; } static void TestAssocIn(const map<string, int> &in) { for (map<string, int>::const_iterator itr = in.begin(); itr != in.end(); ++itr) { printf("%s -> %d\n", itr->first.c_str(), itr->second); } } static map<string, int> TestAssocOut() { map<string, int> result; result["Hello"] = 123; result["World"] = 456; return result; } module(L) [ def("TestIn", &TestIn, copy_table(_1)), def("TestOut", &TestOut, copy_table(result)), def("TestAssocIn", &TestAssocIn, copy_table_assoc(_1)), def("TestAssocOut", &TestAssocOut, copy_table_assoc(result)) ]; -------------------Lua------------------- -- Test sequences arr = { "Testing", "An", "Array" } TestIn(arr) print("") result = TestOut() for k, v in ipairs(result) do print(k .. ": " .. v) end -- Test associative arrays print("") arr = { Hello = 50000, World = 2 } TestAssocIn(arr) print("") result = TestAssocOut() for k, v in pairs(result) do print(k .. ": " .. v) end -------------------Output------------------- Testing An Array 1: One 2: Two 3: Three 4: Four Hello -> 50000 World -> 2 Hello: 123 World: 456 ***POLICY SOURCE*** Here are the contents of table_policy.hpp: #ifndef LUABIND_TABLE_POLICY_HPP_INCLUDED #define LUABIND_TABLE_POLICY_HPP_INCLUDED #include <luabind/config.hpp> #include <luabind/detail/policy.hpp> namespace luabind { namespace detail { struct table_convert { template<class ContainerT> ContainerT apply(lua_State *L, by_value<ContainerT> data, int index) { luabind::object tbl(from_stack(L, index)); ContainerT result; for (luabind::iterator itr(tbl), end; itr != end; ++itr) { boost::optional<ContainerT::value_type> v = object_cast_nothrow<ContainerT::value_type>(*itr); if (v) { result.push_back(*v); } } return result; } template<class ContainerT> ContainerT apply(lua_State *L, by_const_reference<ContainerT> data, int index) { return apply(L, by_value<ContainerT>(), index); } template<class ContainerT> ContainerT apply(lua_State *L, by_reference<ContainerT> data, int index) { return apply(L, by_value<ContainerT>(), index); } template<class ContainerT> static int match(lua_State* L, by_value<ContainerT>, int index) { if (lua_type(L, index) == LUA_TTABLE) { return 0; } return -1; } template<class ContainerT> static int match(lua_State* L, by_const_reference<ContainerT>, int index) { return match(L, by_value<ContainerT>(), index); } template<class ContainerT> static int match(lua_State* L, by_reference<ContainerT>, int index) { return match(L, by_value<ContainerT>(), index); } template<class ContainerT> void apply(lua_State *L, const ContainerT &container) { lua_createtable(L, container.size(), 0); luabind::object tbl(from_stack(L, -1)); int n = 0; for (ContainerT::const_iterator itr = container.begin(); itr != container.end(); ++itr) { tbl[++n] = *itr; } } template<class T> void converter_postcall(lua_State*, T, int) {} }; struct table_convert_assoc { template<class ContainerT> ContainerT apply(lua_State *L, by_value<ContainerT> data, int index) { luabind::object tbl(from_stack(L, index)); ContainerT result; for (luabind::iterator itr(tbl), end; itr != end; ++itr) { typedef ContainerT::key_type KeyT; typedef ContainerT::mapped_type ValueT; boost::optional<KeyT> k = object_cast_nothrow<KeyT>(itr.key()); if (k) { boost::optional<ValueT> v = object_cast_nothrow<ValueT>(*itr); if (v) { result.insert(std::make_pair(*k, *v)); } } } return result; } template<class ContainerT> ContainerT apply(lua_State *L, by_const_reference<ContainerT> data, int index) { return apply(L, by_value<ContainerT>(), index); } template<class ContainerT> ContainerT apply(lua_State *L, by_reference<ContainerT> data, int index) { return apply(L, by_value<ContainerT>(), index); } template<class ContainerT> static int match(lua_State* L, by_value<ContainerT>, int index) { if (lua_type(L, index) == LUA_TTABLE) { return 0; } return -1; } template<class ContainerT> static int match(lua_State* L, by_const_reference<ContainerT>, int index) { return match(L, by_value<ContainerT>(), index); } template<class ContainerT> static int match(lua_State* L, by_reference<ContainerT>, int index) { return match(L, by_value<ContainerT>(), index); } template<class ContainerT> void apply(lua_State *L, const ContainerT &container) { lua_createtable(L, 0, container.size()); luabind::object tbl(from_stack(L, -1)); int n = 0; for (ContainerT::const_iterator itr = container.begin(); itr != container.end(); ++itr) { tbl[itr->first] = itr->second; } } template<class T> void converter_postcall(lua_State*, T, int) {} }; template<int N, bool Assoc> struct table_policy : conversion_policy<N> { struct only_accepts_values_or_references {}; static void precall(lua_State*, const index_map&) {} static void postcall(lua_State*, const index_map&) {} template<class T, class Direction> struct apply { static const bool IsPtr = luabind::detail::is_nonconst_pointer<T>::value || luabind::detail::is_const_pointer<T>::value; typedef typename boost::mpl::if_c< IsPtr, only_accepts_values_or_references, typename boost::mpl::if_c<Assoc, table_convert_assoc, table_convert>::type >::type type; }; }; }} namespace luabind { template<int N> detail::policy_cons<detail::table_policy<N, false>, detail::null_type> copy_table(LUABIND_PLACEHOLDER_ARG(N)) { return detail::policy_cons<detail::table_policy<N, false>, detail::null_type>(); } template<int N> detail::policy_cons<detail::table_policy<N, true>, detail::null_type> copy_table_assoc(LUABIND_PLACEHOLDER_ARG(N)) { return detail::policy_cons<detail::table_policy<N, true>, detail::null_type>(); } } #endif // LUABIND_TABLE_POLICY_HPP_INCLUDED ------------------------------------------------------------------------- This SF.net email is sponsored by: Microsoft Defy all challenges. Microsoft(R) Visual Studio 2008. http://clk.atdmt.com/MRT/go/vse0120000070mrt/direct/01/ _______________________________________________ luabind-user mailing list luabind-user@... https://lists.sourceforge.net/lists/listinfo/luabind-user |
| Free Forum Powered by Nabble | Forum Help |