userdata<T> now works and compiles on MSVC.

It's going to take serious work to make it happen in GCC, plus the fact that 4.9 is still giving me
so many errors I can't even read it...
 I'll let Rapptz figure it out, but later.
Sexy class bindings, yes!
This commit is contained in:
ThePhD 2014-04-25 19:41:03 -04:00 committed by Rapptz
parent 359848f371
commit 2243bec052
12 changed files with 371 additions and 226 deletions

118
main.cpp
View File

@ -1,118 +0,0 @@
#include <sol.hpp>
#include <sol/demangle.hpp>
namespace sol {
template <typename... Tn>
struct constructors;
template <typename T>
class lua_class {
public:
static const std::string classname;
static const std::string meta;
private:
std::string luaname;
std::vector<std::string> functionnames;
std::vector<class_lua_func> functions;
std::vector<luaL_Reg> functiontable;
std::vector<luaL_Reg> metatable;
struct maker {
static int construct( lua_State* L ) {
// First argument is now a table that represent the class to instantiate
luaL_checktype( L, 1, LUA_TTABLE );
lua_newtable( L ); // Create table to represent instance
// Set first argument of new to metatable of instance
lua_pushvalue( L, 1 );
lua_setmetatable( L, -2 );
// Do function lookups in metatable
lua_pushvalue( L, 1 );
lua_setfield( L, 1, "__index" );
void* userdata = lua_newuserdata( L, sizeof( T ) );
T* obj = static_cast<T*>( userdata );
std::allocator<T> alloc{ };
alloc.construct( obj );
}
template <std::size_t n>
static int destruct( lua_State* L ) {
void* userdata = lua_touserdata( L, 0 );
T* obj = static_cast<T*>( userdata );
std::allocator<T> alloc{ };
alloc.destroy( obj );
}
};
void build_function_tables( ) {
}
template <typename... Args, typename Ret, typename... MArgs>
void build_function_tables( Ret( T::* func )( MArgs... ), std::string name, Args&&... args ) {
functionnames.push_back( std::move( name ) );
functions.emplace_back( std::move( func ) );
functiontable.push_back( { functionnames.back().c_str(), &lua_func::call } );
build_function_tables( std::forward<Args>( args )... );
}
public:
template <typename... Args>
lua_class( Args&&... args ) : lua_class( classname, std::forward<Args>( args )... ) {
}
template <typename... Args>
lua_class( std::string name, Args&&... args ) : lua_class( name, constructors<>(), std::forward<Args>( args )... ) {
}
template <typename... Args, typename... CArgs>
lua_class( std::string name, constructors<CArgs...> c, Args&&... args ) : luaname( std::move( name ) ) {
functionnames.reserve( sizeof...( args ) );
functiontable.reserve( sizeof...( args ) );
metatable.reserve( sizeof...( args ) );
build_function_tables( std::forward<Args>( args )... );
functiontable.
}
};
template <typename T>
const std::string lua_class<T>::classname = detail::demangle( typeid( T ) );
template <typename T>
const std::string lua_class<T>::meta = std::string( "sol.stateful." ).append( classname );
}
struct f {
int x;
f( ) : x( 1 ) {
}
f( int x ) : x( x ) {
}
int add( int y ) {
return x + y;
}
};
#include <iostream>
int main( ) {
sol::state s;
f x( 20 );
sol::lua_class<f> lc{ };
std::cout << lc.classname << std::endl;
std::cout << lc.meta << std::endl;
s.set_function( "add", &f::add, f(10) );
s.script( "t = add(20)" );
std::cout << s.get<int>( "t" );
}

View File

@ -26,37 +26,6 @@
#include <memory>
namespace sol {
namespace detail {
template<typename T, std::size_t N>
void get_upvalue_ptr(lua_State* L, T*& data, std::size_t datasize, std::array<void*, N> voiddata, int& upvalue) {
for (std::size_t i = 0, d = 0; d < datasize; ++i, d += sizeof(void*)) {
voiddata[i] = lua_touserdata(L, lua_upvalueindex(upvalue++));
}
data = reinterpret_cast<T*>(static_cast<void*>(voiddata.data()));
}
template<typename T, std::size_t N>
void get_upvalue_ptr(lua_State* L, T*& data, std::array<void*, N> voiddata, int& upvalue) {
get_upvalue_ptr(L, data, sizeof(T), voiddata, upvalue);
}
template<typename T>
void get_upvalue_ptr(lua_State* L, T*& data, int& upvalue) {
const static std::size_t data_t_count = (sizeof(T)+(sizeof(void*)-1)) / sizeof(void*);
typedef std::array<void*, data_t_count> data_t;
data_t voiddata{{}};
return get_upvalue_ptr(L, data, voiddata, upvalue);
}
template<typename T>
void get_upvalue(lua_State* L, T& data, int& upvalue) {
const static std::size_t data_t_count = (sizeof(T)+(sizeof(void*)-1)) / sizeof(void*);
typedef std::array<void*, data_t_count> data_t;
data_t voiddata{{}};
for (std::size_t i = 0, d = 0; d < sizeof(T); ++i, d += sizeof(void*)) {
voiddata[i] = lua_touserdata(L, lua_upvalueindex(upvalue++));
}
data = *reinterpret_cast<T*>(static_cast<void*>(voiddata.data()));
}
} // detail
template<typename TFx>
struct static_lua_func {
@ -83,9 +52,8 @@ struct static_lua_func {
}
static int call(lua_State* L) {
int upvalue = 1;
fx_t* fx;
detail::get_upvalue(L, fx, upvalue);
auto udata = stack::get_user<fx_t*>(L);
fx_t* fx = udata.first;
int r = typed_call(tuple_types<typename fx_traits::return_type>(), typename fx_traits::args_type(), fx, L);
return r;
}
@ -122,19 +90,11 @@ struct static_object_lua_func {
}
static int call(lua_State* L) {
const static std::size_t data_t_count = (sizeof(fx_t)+(sizeof(void*)-1)) / sizeof(void*);
typedef std::array<void*, data_t_count> data_t;
int upvalue = 1;
data_t data = {{}};
fx_t* fxptr;
for (std::size_t i = 0, d = 0; d < sizeof(fx_t*); ++i, d += sizeof(void*)) {
data[i] = lua_touserdata(L, lua_upvalueindex(upvalue++));
}
fxptr = reinterpret_cast<fx_t*>(static_cast<void*>(data.data()));
fx_t& mem_ptr = *fxptr;
void* objectdata = lua_touserdata(L, lua_upvalueindex(upvalue++));
T& obj = *static_cast<T*>(objectdata);
int r = typed_call(tuple_types<typename fx_traits::return_type>(), typename fx_traits::args_type(), obj, mem_ptr, L);
auto memberdata = stack::get_user<fx_t>(L, 1);
auto objdata = stack::get_user<T*>(L, memberdata.second);
fx_t& memfx = memberdata.first;
T& obj = *objdata.first;
int r = typed_call(tuple_types<typename fx_traits::return_type>(), typename fx_traits::args_type(), obj, memfx, L);
return r;
}
@ -145,7 +105,7 @@ struct static_object_lua_func {
struct lua_func {
static int call(lua_State* L) {
void** pinheritancedata = static_cast<void**>(lua_touserdata(L, lua_upvalueindex(1)));
void** pinheritancedata = static_cast<void**>(stack::get<lightuserdata_t>(L, 1).value);
void* inheritancedata = *pinheritancedata;
if (inheritancedata == nullptr)
throw sol_error("call from Lua to C++ function has null data");
@ -156,7 +116,7 @@ struct lua_func {
}
static int gc(lua_State* L) {
void** puserdata = static_cast<void**>(lua_touserdata(L, 1));
void** puserdata = static_cast<void**>(stack::get<userdata_t>(L, 1).value);
void* userdata = *puserdata;
lua_func* ptr = static_cast<lua_func*>(userdata);
std::default_delete<lua_func> dx{};
@ -175,15 +135,11 @@ template<typename TFx>
struct functor_lua_func : public lua_func {
typedef decltype(&TFx::operator()) fx_t;
typedef function_traits<fx_t> fx_traits;
fx_t fx;
TFx fx;
template<typename... FxArgs>
functor_lua_func(FxArgs&&... fxargs): fx(std::forward<FxArgs>(fxargs)...) {}
virtual int operator()(lua_State* L) override {
return (*this)(tuple_types<typename fx_traits::return_type>(), typename fx_traits::args_type(), L);
}
template<typename... Args>
int operator()(types<void>, types<Args...> t, lua_State* L) {
stack::pop_call(L, fx, t);
@ -202,6 +158,10 @@ struct functor_lua_func : public lua_func {
stack::push(L, r);
return sizeof...(Ret);
}
virtual int operator()(lua_State* L) override {
return (*this)(tuple_types<typename fx_traits::return_type>(), typename fx_traits::args_type(), L);
}
};
template<typename TFx, typename T = TFx, bool is_member_pointer = std::is_member_function_pointer<TFx>::value>
@ -286,20 +246,20 @@ struct class_lua_func : public lua_func {
typedef typename std::remove_pointer<typename std::decay<TFx>::type>::type fx_t;
typedef function_traits<fx_t> fx_traits;
struct functor {
T& member;
T* item;
fx_t invocation;
template<typename... FxArgs>
functor(FxArgs&&... fxargs): member(*static_cast<T*>(nullptr)), invocation(std::forward<FxArgs>(fxargs)...) {}
functor(FxArgs&&... fxargs): item(nullptr), invocation(std::forward<FxArgs>(fxargs)...) {}
void pre_call(lua_State* L) {
void* userdata = lua_touserdata(L, 0);
T* item = static_cast<T*>( userdata );
member = *item;
item = static_cast<T*>(userdata);
}
template<typename... Args>
typename fx_traits::return_type operator()(Args&&... args) {
T& member = *item;
return (member.*invocation)(std::forward<Args>(args)...);
}
} fx;
@ -309,7 +269,6 @@ struct class_lua_func : public lua_func {
template<typename... Args>
int operator()(types<void>, types<Args...> t, lua_State* L) {
fx.pre_call(L);
stack::pop_call(L, fx, t);
return 0;
}
@ -322,13 +281,13 @@ struct class_lua_func : public lua_func {
template<typename... Ret, typename... Args>
int operator()(types<Ret...>, types<Args...> t, lua_State* L) {
typedef typename multi_return<Ret...>::type return_type;
fx.pre_call(L);
return_type r = stack::pop_call(L, fx, t);
stack::push(L, std::move(r));
return sizeof...(Ret);
}
virtual int operator()(lua_State* L) override {
fx.pre_call(L);
return (*this)(tuple_types<typename fx_traits::return_type>(), typename fx_traits::args_type(), L);
}
};

View File

@ -46,11 +46,14 @@ public:
luaL_unref(L, LUA_REGISTRYINDEX, ref);
}
void push() const noexcept {
lua_rawgeti(L, LUA_REGISTRYINDEX, ref);
}
void pop() const noexcept {
lua_pop(L, 1);
}
reference(reference&& o) noexcept {
L = o.L;
ref = o.ref;

View File

@ -112,6 +112,16 @@ inline bool get<bool>(lua_State* L, int index) {
return lua_toboolean(L, index) != 0;
}
template<>
inline lightuserdata_t get<lightuserdata_t>(lua_State* L, int index) {
return {lua_touserdata(L, lua_upvalueindex(index))};
}
template<>
inline userdata_t get<userdata_t>(lua_State* L, int index) {
return {lua_touserdata(L, index)};
}
template<>
inline std::string get<std::string>(lua_State* L, int index) {
std::string::size_type len;
@ -124,6 +134,17 @@ inline const char* get<const char*>(lua_State* L, int index) {
return lua_tostring(L, index);
}
template <typename T>
inline std::pair<T, int> get_user(lua_State* L, int index = 1) {
const static std::size_t data_t_count = (sizeof(T)+(sizeof(void*)-1)) / sizeof(void*);
typedef std::array<void*, data_t_count> data_t;
data_t voiddata{ {} };
for (std::size_t i = 0, d = 0; d < sizeof(T); ++i, d += sizeof(void*)) {
voiddata[ i ] = stack::get<lightuserdata_t>(L, index++);
}
return std::pair<T, int>(*reinterpret_cast<T*>(static_cast<void*>(voiddata.data())), index);
}
template<typename T>
inline T pop(lua_State* L) {
auto r = get<T>(L);

View File

@ -144,9 +144,15 @@ public:
return *this;
}
template<typename T, typename TFx, typename TM>
state& set_function(T&& key, TFx&& fx, TM& mem) {
global.set_function(std::forward<T>(key), std::forward<TFx>(fx), mem);
template<typename T, typename TFx, typename TObj>
state& set_function(T&& key, TFx&& fx, TObj&& obj) {
global.set_function(std::forward<T>(key), std::forward<TFx>(fx), std::forward<TObj>(obj));
return *this;
}
template<typename T>
state& set_class(userdata<T>& user) {
global.set_class(user);
return *this;
}

View File

@ -25,8 +25,7 @@
#include "proxy.hpp"
#include "stack.hpp"
#include "lua_function.hpp"
#include <array>
#include <cstring>
#include "userdata.hpp"
namespace sol {
namespace detail {
@ -114,6 +113,46 @@ public:
std::forward<T>(key), std::forward<TFx>(fx), std::forward<TObj>(obj));
}
template <typename T>
table& set_class(userdata<T>& user) {
push();
lua_createtable(state(), 0, 0);
int classid = lua_gettop(state());
// Register metatable for user data in registry
// using the metaname key generated from the demangled name
luaL_newmetatable(state(), user.meta.c_str());
int metaid = lua_gettop(state());
// Meta functions: have no light up values
luaL_setfuncs(state(), user.metatable.data(), 0);
// Regular functions: each one references an upvalue at its own index,
// resulting in [function count] upvalues
//luaL_newlib(L, functiontable.data());
// the newlib macro doesn't have a form for when you need upvalues:
// we duplicate the work below
lua_createtable(state(), 0, user.functiontable.size() - 1);
for (std::size_t upvalues = 0; upvalues < user.functions.size(); ++upvalues) {
stack::push(state(), static_cast<void*>(user.functions[ upvalues ].get()));
}
luaL_setfuncs(state(), user.functiontable.data(), static_cast<uint32_t>(user.functions.size()));
lua_setfield(state(), metaid, "__index");
// Meta functions: no upvalues
lua_createtable(state(), 0, user.metatable.size() - 1);
luaL_setfuncs(state(), user.metatable.data(), 0); // 0, for no upvalues
lua_setfield(state(), metaid, "__metatable");
lua_setmetatable(state(), classid);
lua_setglobal(state(), user.luaname.c_str());
pop();
return *this;
}
size_t size() const {
push();
return lua_rawlen(state(), -1);
@ -194,7 +233,7 @@ private:
stack::push(state(), userobjdata);
luaL_setfuncs(state(), funcreg, upvalues + 1);
lua_pop(state(), 1);
pop();
return *this;
}
@ -210,11 +249,10 @@ private:
};
push();
int upvalues = stack::push_user(state(), target);
luaL_setfuncs(state(), funcreg, upvalues);
pop();
lua_pop(state(), 1);
return *this;
}
@ -243,7 +281,7 @@ private:
push();
stack::push_user(state(), userdata, metatablename);
luaL_setfuncs(state(), funcreg, 1);
lua_pop(state(), 1);
pop();
return *this;
}
};

View File

@ -73,6 +73,10 @@ struct tuple_types : types<Args...>, std::false_type {};
template<typename... Args>
struct tuple_types<std::tuple<Args...>> : types<Args...>, std::true_type {};
template <typename... Tn>
struct constructors {};
} // sol
#endif // SOL_TUPLE_HPP

View File

@ -31,6 +31,17 @@ struct nil_t {};
const nil_t nil {};
struct void_type {};
const void_type Void {};
struct lightuserdata_t {
void* value;
lightuserdata_t(void* data) : value(data) {}
operator void* () const { return value; }
};
struct userdata_t {
void* value;
userdata_t(void* data) : value(data) {}
operator void* () const { return value; }
};
enum class type : int {
none = LUA_TNONE,
@ -58,6 +69,8 @@ inline void type_assert(lua_State* L, int index, type expected) {
}
}
template <typename T>
class userdata;
class table;
class function;
class object;
@ -114,6 +127,16 @@ inline type type_of<bool>() {
return type::boolean;
}
template<>
inline type type_of<lightuserdata_t>() {
return type::lightuserdata;
}
template<>
inline type type_of<userdata_t>() {
return type::userdata;
}
inline bool operator==(nil_t, nil_t) { return true; }
inline bool operator!=(nil_t, nil_t) { return false; }
} // sol

161
sol/userdata.hpp Normal file
View File

@ -0,0 +1,161 @@
// The MIT License (MIT)
// Copyright (c) 2013 Danny Y., Rapptz
// Permission is hereby granted, free of charge, to any person obtaining a copy of
// this software and associated documentation files (the "Software"), to deal in
// the Software without restriction, including without limitation the rights to
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
// the Software, and to permit persons to whom the Software is furnished to do so,
// subject to the following conditions:
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#ifndef SOL_USERDATA_HPP
#define SOL_USERDATA_HPP
#include <sol/state.hpp>
#include <sol/lua_function.hpp>
#include <sol/demangle.hpp>
#include <vector>
namespace sol {
template <typename T>
class userdata {
private:
friend table;
static const std::string classname;
static const std::string meta;
std::string luaname;
std::vector<std::string> functionnames;
std::vector<std::unique_ptr<lua_func>> functions;
std::vector<luaL_Reg> functiontable;
std::vector<luaL_Reg> metatable;
struct constructor {
static int construct(lua_State* L) {
// First argument is now a table that represent the class to instantiate
luaL_checktype(L, 1, LUA_TTABLE);
lua_createtable(L, 0, 0); // Create table to represent instance
// Set first argument of new to metatable of instance
lua_pushvalue(L, 1);
lua_setmetatable(L, -2);
// Do function lookups in metatable
lua_pushvalue(L, 1);
lua_setfield(L, 1, "__index");
void* userdata = lua_newuserdata(L, sizeof(T));
T* obj = static_cast<T*>(userdata);
std::allocator<T> alloc{};
alloc.construct(obj);
luaL_getmetatable(L, meta.c_str());
lua_setmetatable(L, -2);
lua_setfield(L, -2, "__self");
return 1;
}
};
template <std::size_t n>
struct destructor {
static int destruct(lua_State* L) {
for (std::size_t i = 0; i < n; ++i) {
lightuserdata_t luserdata = stack::get<lightuserdata_t>(L, i);
}
userdata_t userdata = stack::get<userdata_t>(L, 0);
T* obj = static_cast<T*>(userdata.value);
std::allocator<T> alloc{};
alloc.destroy(obj);
return 0;
}
};
template <std::size_t i>
struct class_func {
static int call(lua_State* L) {
// Zero-based template parameter, but upvalues start at 1
void* inheritancedata = stack::get<lightuserdata_t>(L, i + 1);
if (inheritancedata == nullptr)
throw sol_error("call from Lua to C++ function has null data");
lua_func* pfx = static_cast<lua_func*>(inheritancedata);
lua_func& fx = *pfx;
int r = fx(L);
return r;
}
};
template <std::size_t n>
void build_function_tables() {
}
template <std::size_t n, typename... Args, typename Ret, typename... MArgs>
void build_function_tables(Ret(T::* func)(MArgs...), std::string name, Args&&... args) {
typedef typename std::decay<decltype(func)>::type fx_t;
functionnames.push_back(std::move(name));
functions.emplace_back(std::make_unique<class_lua_func<fx_t, T>>(std::move(func)));
functiontable.push_back({ functionnames.back().c_str(), &class_func<n>::call });
build_function_tables<n + 1>(std::forward<Args>(args)...);
}
public:
template <typename... Args>
userdata(Args&&... args) : userdata(classname, std::forward<Args>(args)...) {
}
template <typename... Args>
userdata(std::string name, Args&&... args) : userdata(name, constructors<>(), std::forward<Args>(args)...) {
}
template <typename... Args, typename... CArgs>
userdata(constructors<CArgs...> c, Args&&... args) : userdata(classname, std::move(c), std::forward<Args>(args)...) {
}
template <typename... Args, typename... CArgs>
userdata(std::string name, constructors<CArgs...> c, Args&&... args) : luaname(std::move(name)) {
functionnames.reserve(sizeof...(args));
functiontable.reserve(sizeof...(args));
functions.reserve(sizeof...(args));
metatable.reserve(sizeof...(args));
build_function_tables<0>(std::forward<Args>(args)...);
functionnames.push_back("new");
functiontable.push_back({ functionnames.back().c_str(), &constructor::construct });
functiontable.push_back({ nullptr, nullptr });
metatable.push_back({ "__gc", &destructor<sizeof...(Args) / 2>::destruct });
metatable.push_back({ nullptr, nullptr });
}
void register_into(const table& s) {
}
};
template <typename T>
const std::string userdata<T>::classname = detail::demangle(typeid(T));
template <typename T>
const std::string userdata<T>::meta = std::string("sol.stateful.").append(classname);
}
#endif SOL_USERDATA_HPP

View File

@ -16,6 +16,26 @@ struct object {
};
struct fuser {
int x;
fuser( ) : x( 0 ) {
}
fuser( int x ) : x( x ) {
}
int add( int y ) {
return x + y;
}
int add2( int y ) {
return x + y + 2;
}
};
int plop_xyz(int x, int y, std::string z) {
std::cout << x << " " << y << " " << z << std::endl;
return 11;
@ -324,3 +344,31 @@ TEST_CASE("tables/operator[]", "Check if operator[] retrieval and setting works
REQUIRE_NOTHROW(assert1(lua.global_table()));
}
TEST_CASE( "tables/userdata", "Show that we can create classes from userdata and use them" ) {
sol::state lua;
sol::userdata<fuser> lc{ &fuser::add, "add", &fuser::add2, "add2" };
lua.set_class( lc );
lua.script( "a = fuser:new()\n"
"b = a:add(1)\n"
"c = a:add2(1)\n"
"\n" );
sol::object a = lua.get<sol::object>( "a" );
sol::object b = lua.get<sol::object>( "b" );
sol::object c = lua.get<sol::object>( "c" );
REQUIRE( ( a.is<sol::table>( ) ) );
auto atype = a.get_type( );
auto btype = b.get_type( );
auto ctype = c.get_type( );
REQUIRE( ( atype == sol::type::table ) );
REQUIRE( ( btype == sol::type::number ) );
REQUIRE( ( ctype == sol::type::number ) );
int bresult = b.as<int>( );
int cresult = c.as<int>( );
REQUIRE( bresult == 1 );
REQUIRE( cresult == 3 );
}