This commit adds some QoI constructor keys / types, as well as implements make_object. The documentation has been updated as well.

Closes #78
Closes #77
Closes #76
Closes #75
This commit is contained in:
ThePhD 2016-05-03 14:19:30 -04:00
parent 06fecfb4ca
commit 9e31119f16
16 changed files with 307 additions and 109 deletions

View File

@ -26,6 +26,7 @@ Browse the various function and classes :doc:`Sol<../index>` utilizes to make yo
optional
state
table
metatable_key
this_state
thread
tie

View File

@ -0,0 +1,45 @@
metatable_key
=============
A key for setting and getting an object's metatable
---------------------------------------------------
.. code-block:: cpp
struct metatable_key_t {};
const metatable_key_t metatable_key;
You can use this in conjunction with :doc:`sol::table<table>` to set/get a metatable. Lua metatables are powerful ways to override default behavior of objects for various kinds of operators, among other things. Here is an entirely complete example, showing getting and working with a :doc:`usertype<usertype>`'s metatable defined by Sol:
.. code-block:: cpp
:caption: messing with metatables
:linenos:
#include <sol.hpp>
int main () {
struct bark {
int operator()(int x) {
return x;
}
};
sol::state lua;
lua.open_libraries(sol::lib::base);
lua.new_usertype<bark>("bark",
sol::meta_function::call_function, &bark::operator()
);
bark b;
lua.set("b", &b);
sol::table b_as_table = lua["b"];
sol::table b_metatable = b_as_table[sol::metatable_key];
sol::function b_call = b_metatable["__call"];
sol::function b_as_function = lua["b"];
int result1 = b_as_function(1);
int result2 = b_call(b, 1);
// result1 == result2 == 1
}

View File

@ -49,4 +49,23 @@ non-members
bool operator!=(const object& lhs, const nil_t&);
bool operator!=(const nil_t&, const object& rhs);
These allow a person to compare an ``sol::object`` against :ref:`nil<nil>`, which essentially checks if an object references a non-nil value, like so:
These allow a person to compare an ``sol::object`` against :ref:`nil<nil>`, which essentially checks if an object references a non-nil value, like so:
.. code-block:: cpp
if (myobj == sol::nil) {
// doesn't have anything...
}
Use this to check objects.
.. code-block:: cpp
:caption: function: make object
:name: make-object
template <typename T>
object make_object(lua_State* L, T&& value);
template <typename T, typename... Args>
object make_object(lua_State* L, Args&&... args);
Makes an object out of the value. It pushes it onto the stack, then pops it into the returned ``sol::object``.

View File

@ -43,7 +43,7 @@ The value of the reference in the registry.
bool valid () const noexcept;
explicit operator bool () const noexcept;
These functions check if the reference at ``T`` is valid: that is, if it is not :doc:`nil<types>` and if it is not non-existing (doesn't refer to anything, including nil) reference. The explicit operator bool allows you to use it in the context of an ``if ( my_obj )`` context.
These functions check if the reference at ``T`` is valid: that is, if it is not :ref:`nil<nil>` and if it is not non-existing (doesn't refer to anything, including nil) reference. The explicit operator bool allows you to use it in the context of an ``if ( my_obj )`` context.
.. code-block:: cpp
:caption: function: retrieves the type
@ -57,4 +57,19 @@ Gets the :doc:`sol::type<types>` of the reference; that is, the Lua reference.
lua_State* lua_state() const noexcept;
Gets the ``lua_State*`` this reference exists in.
Gets the ``lua_State*`` this reference exists in.
non-members
-----------
.. code-block:: cpp
:caption: functions: reference comparators
bool operator==(const reference&, const reference&);
bool operator!=(const reference&, const reference&);
Compares two references using the Lua API's `lua_compare`_ for equality.
.. _lua_compare: https://www.lua.org/manual/5.3/manual.html#lua_compare

View File

@ -1,5 +1,5 @@
userdata
===========
========
reference to a userdata
-----------------------

View File

@ -149,6 +149,10 @@ The constructor of usertype takes a variable number of arguments. It takes an ev
* ``"{name}", sol::initializers( func1, func2, ... )``
- Creates initializers that, given one or more functions, provides an overloaded lua function for creating a the specified type.
+ The function must have the argument signature ``func( T*, Arguments... )`` or ``func( T&, Arguments... )``, where the pointer or reference will point to a place of allocated memory that has an uninitialized ``T``. Note that lua controls the memory.
* ``"{name}", sol::no_constructor``
- Specifically tells Sol not to create a `.new()` if one is not specified and the type is default-constructible.
* ``sol::call_constructor, {any constructor type}``
- Specifies a function that makes the call turn into ``{usertype-name}( ... constructor arguments ... )``. This is compatible with luabind syntax.
.. _destructor:

View File

@ -60,7 +60,7 @@ inline int construct(Match&& matchfx, lua_State* L, int fxarity, int start) {
template <typename T, typename... TypeLists>
inline int construct(lua_State* L) {
static const auto& meta = usertype_traits<T>::metatable;
call_syntax syntax = stack::get_call_syntax(L, meta);
call_syntax syntax = stack::get_call_syntax(L, meta, 1);
int argcount = lua_gettop(L) - static_cast<int>(syntax);
T** pointerpointer = reinterpret_cast<T**>(lua_newuserdata(L, sizeof(T*) + sizeof(T)));

View File

@ -70,6 +70,22 @@ public:
}
};
template <typename T>
object make_object(lua_State* L, T&& value) {
int backpedal = stack::push(L, std::forward<T>(value));
object r = stack::get<object>(L, -backpedal);
lua_pop(L, backpedal);
return r;
}
template <typename T, typename... Args>
object make_object(lua_State* L, Args&&... args) {
int backpedal = stack::push<T>(L, std::forward<Args>(args)...);
object r = stack::get<sol::object>(L, -backpedal);
lua_pop(L, backpedal);
return r;
}
inline bool operator==(const object& lhs, const nil_t&) {
return !lhs.valid();
}

View File

@ -62,6 +62,12 @@ using constructors = constructor_list<Args...>;
const auto default_constructor = constructors<types<>>{};
struct no_construction {};
const auto no_constructor = no_construction{};
struct call_construction {};
const auto call_constructor = call_construction{};
template <typename... Functions>
struct constructor_wrapper {
std::tuple<Functions...> set;

View File

@ -144,6 +144,16 @@ public:
return L;
}
};
inline bool operator== (const reference& l, const reference& r) {
auto ppl = stack::push_pop(l);
auto ppr = stack::push_pop(r);
return lua_compare(l.lua_state(), -1, -2, LUA_OPEQ) == 0;
}
inline bool operator!= (const reference& l, const reference& r) {
return !operator==(l, r);
}
} // sol
#endif // SOL_REFERENCE_HPP

View File

@ -93,7 +93,7 @@ inline void call(types<void>, types<Args...> ta, std::index_sequence<I...> tai,
inline void remove( lua_State* L, int index, int count ) {
if ( count < 1 )
return;
int top = lua_gettop( L );
int top = lua_gettop(L);
if ( index == -1 || top == index ) {
// Slice them right off the top
lua_pop( L, static_cast<int>(count) );
@ -104,11 +104,11 @@ inline void remove( lua_State* L, int index, int count ) {
// Probably slower, maybe, haven't benchmarked,
// but necessary
if ( index < 0 ) {
index = lua_gettop( L ) + (index + 1);
index = lua_gettop(L) + (index + 1);
}
int last = index + count;
for ( int i = index; i < last; ++i ) {
lua_remove( L, i );
lua_remove(L, i);
}
}
@ -160,9 +160,9 @@ inline int call_into_lua(types<Ret0, Ret...>, types<Args...> ta, lua_State* L, i
return push_reference(L, std::forward<decltype(r)>(r));
}
inline call_syntax get_call_syntax(lua_State* L, const std::string& key) {
inline call_syntax get_call_syntax(lua_State* L, const std::string& key, int index = -2) {
luaL_getmetatable(L, key.c_str());
if (lua_compare(L, -1, -2, LUA_OPEQ) == 1) {
if (lua_compare(L, -1, index, LUA_OPEQ) == 1) {
lua_pop(L, 1);
return call_syntax::colon;
}

View File

@ -41,7 +41,8 @@ struct field_getter {
template <bool b, typename C>
struct field_getter<metatable_key_t, b, C> {
void get(lua_State* L, metatable_key_t, int tableindex = -1) {
lua_getmetatable( L, tableindex );
if (lua_getmetatable(L, tableindex) == 0)
push(L, nil);
}
};
@ -166,7 +167,7 @@ template <typename... Args, bool b, typename C>
struct field_setter<std::tuple<Args...>, b, C> {
template <bool g, std::size_t I, typename Key, typename Value>
void apply(std::index_sequence<I>, lua_State* L, Key&& keys, Value&& value, int tableindex) {
I > 0 ?
I < 1 ?
set_field<g>(L, detail::forward_get<I>(keys), std::forward<Value>(value)) :
set_field<g>(L, detail::forward_get<I>(keys), std::forward<Value>(value), tableindex);
}

View File

@ -1,73 +1,81 @@
// The MIT License (MIT)
// Copyright (c) 2013-2016 Rapptz, ThePhD and contributors
// 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_STACK_REFERENCE_HPP
#define SOL_STACK_REFERENCE_HPP
namespace sol {
class stack_reference {
private:
lua_State* L = nullptr;
int index = 0;
protected:
int registry_index () const noexcept {
return LUA_NOREF;
}
public:
stack_reference() noexcept = default;
stack_reference(lua_State* L, int i) noexcept : L(L), index(lua_absindex(L, i)) {}
stack_reference(stack_reference&& o) noexcept = default;
stack_reference& operator=(stack_reference&&) noexcept = default;
stack_reference(const stack_reference&) noexcept = default;
stack_reference& operator=(const stack_reference&) noexcept = default;
int push() const noexcept {
lua_pushvalue(L, index);
return 1;
}
void pop(int n = 1) const noexcept {
lua_pop(lua_state( ), n);
}
int stack_index () const noexcept {
return index;
}
type get_type() const noexcept {
int result = lua_type(L, index);
return static_cast<type>(result);
}
lua_State* lua_state() const noexcept {
return L;
}
bool valid () const noexcept {
type t = get_type();
return t != type::nil && t != type::none;
}
};
} // sol
#endif // SOL_STACK_REFERENCE_HPP
// The MIT License (MIT)
// Copyright (c) 2013-2016 Rapptz, ThePhD and contributors
// 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_STACK_REFERENCE_HPP
#define SOL_STACK_REFERENCE_HPP
namespace sol {
class stack_reference {
private:
lua_State* L = nullptr;
int index = 0;
protected:
int registry_index () const noexcept {
return LUA_NOREF;
}
public:
stack_reference() noexcept = default;
stack_reference(lua_State* L, int i) noexcept : L(L), index(lua_absindex(L, i)) {}
stack_reference(stack_reference&& o) noexcept = default;
stack_reference& operator=(stack_reference&&) noexcept = default;
stack_reference(const stack_reference&) noexcept = default;
stack_reference& operator=(const stack_reference&) noexcept = default;
int push() const noexcept {
lua_pushvalue(L, index);
return 1;
}
void pop(int n = 1) const noexcept {
lua_pop(lua_state( ), n);
}
int stack_index () const noexcept {
return index;
}
type get_type() const noexcept {
int result = lua_type(L, index);
return static_cast<type>(result);
}
lua_State* lua_state() const noexcept {
return L;
}
bool valid () const noexcept {
type t = get_type();
return t != type::nil && t != type::none;
}
};
inline bool operator== (const stack_reference& l, const stack_reference& r) {
return lua_compare(l.lua_state(), l.stack_index(), l.stack_index(), LUA_OPEQ) == 0;
}
inline bool operator!= (const stack_reference& l, const stack_reference& r) {
return !operator==(l, r);
}
} // sol
#endif // SOL_STACK_REFERENCE_HPP

View File

@ -48,6 +48,9 @@ struct is_constructor<constructors<Args...>> : std::true_type {};
template <typename... Args>
struct is_constructor<constructor_wrapper<Args...>> : std::true_type {};
template <>
struct is_constructor<no_construction> : std::true_type {};
template <typename... Args>
using has_constructor = meta::Or<is_constructor<meta::Unqualified<Args>>...>;
@ -179,6 +182,7 @@ private:
function_detail::base_function* indexfunc;
function_detail::base_function* newindexfunc;
function_map_t indexwrapper, newindexwrapper;
const char* constructfuncname;
lua_CFunction constructfunc;
const char* destructfuncname;
lua_CFunction destructfunc;
@ -263,12 +267,16 @@ private:
return make_functor_function(std::is_base_of<Arg0, T>(), name, std::forward<Fx>(func));
}
template<std::size_t N>
void build_function(std::string funcname, no_construction) {}
template<std::size_t N, typename... Args>
void build_function(std::string funcname, constructors<Args...>) {
functionnames.push_back(std::move(funcname));
std::string& name = functionnames.back();
// Insert bubble to keep with compile-time argument count (simpler and cheaper to do)
functions.push_back(nullptr);
constructfuncname = name.c_str();
constructfunc = function_detail::construct<T, Args...>;
metafunctiontable.push_back({ name.c_str(), constructfunc });
}
@ -283,27 +291,28 @@ private:
auto metamethodfind = std::find(meta_function_names.begin(), meta_function_names.end(), name);
if (metamethodfind != meta_function_names.end()) {
metafunctiontable.push_back({ name.c_str(), function_detail::usertype_call<N> });
meta_function metafunction = static_cast<meta_function>(metamethodfind - meta_function_names.begin());
switch (metafunction) {
case meta_function::garbage_collect:
destructfuncname = name.c_str();
destructfunc = function_detail::usertype_call<N>;
return;
case meta_function::index:
indexfunc = functions.back().get();
needsindexfunction = true;
break;
case meta_function::new_index:
newindexfunc = functions.back().get();
break;
case meta_function::construct:
constructfunc = function_detail::usertype_call<N>;
break;
metafunctiontable.push_back({ name.c_str(), function_detail::usertype_call<N> });
meta_function metafunction = static_cast<meta_function>(metamethodfind - meta_function_names.begin());
switch (metafunction) {
case meta_function::garbage_collect:
destructfuncname = name.c_str();
destructfunc = function_detail::usertype_call<N>;
return;
case meta_function::index:
indexfunc = functions.back().get();
needsindexfunction = true;
break;
case meta_function::new_index:
newindexfunc = functions.back().get();
break;
case meta_function::construct:
constructfuncname = name.c_str();
constructfunc = function_detail::usertype_call<N>;
break;
default:
break;
}
return;
}
return;
}
functiontable.push_back({ name.c_str(), direct });
}
@ -396,6 +405,11 @@ private:
build_function_tables<N>(funcname, std::forward<Fx>(func), std::forward<Args>(args)...);
}
template<std::size_t N, typename Fx, typename... Args>
void build_function_tables(call_construction, Fx&& func, Args&&... args) {
build_function_tables<N>("__call", std::forward<Fx>(func), std::forward<Args>(args)...);
}
template<std::size_t N, typename... Bases, typename... Args>
void build_function_tables(base_classes_tag, bases<Bases...>, Args&&... args) {
build_function_tables<N>(std::forward<Args>(args)...);
@ -447,8 +461,8 @@ private:
}
template<typename... Args>
usertype(usertype_detail::verified_tag, Args&&... args) : indexfunc(nullptr), newindexfunc(nullptr), constructfunc(nullptr),
destructfunc(nullptr), functiongcfunc(nullptr), needsindexfunction(false), baseclasscheck(nullptr), baseclasscast(nullptr) {
usertype(usertype_detail::verified_tag, Args&&... args) : indexfunc(nullptr), newindexfunc(nullptr), constructfuncname(""), constructfunc(nullptr),
destructfuncname(""), destructfunc(nullptr), functiongcfunc(nullptr), needsindexfunction(false), baseclasscheck(nullptr), baseclasscast(nullptr) {
static_assert((sizeof...(Args) % 2) == 0, "Incorrect argument count to usertype creation: not in pairs. Might be missing name, function/property/variable, comma");
functionnames.reserve(sizeof...(args)+3);
functiontable.reserve(sizeof...(args)+3);
@ -480,8 +494,15 @@ public:
usertype_detail::push_metatable<detail::unique_usertype<T>, usertype_detail::stage::uniquemeta>(L, needsindexfunction, functions, functiontable, metafunctiontable, baseclasscheck, baseclasscast);
lua_pop(L, 1);
// but leave the regular T table on last
// so it can be linked to a type for usage with `.new(...)` or `:new(...)`
// so it can be linked to a key for usage with `.new(...)` or `:new(...)`
usertype_detail::push_metatable<T, usertype_detail::stage::normalmeta>(L, needsindexfunction, functions, functiontable, metafunctiontable, baseclasscheck, baseclasscast);
// be sure to link the construction function to allow for people to do the whole lua_bind thing
if (constructfunc != nullptr && constructfuncname != nullptr && std::find(meta_function_names.cbegin(), meta_function_names.cend(), constructfuncname) != meta_function_names.cend()) {
lua_createtable(L, 0, 0);
sol::stack_table mt(L, -1);
mt[constructfuncname] = constructfunc;
lua_setmetatable(L, -2);
}
// Make sure to drop a table in the global namespace to properly destroy the pushed functions
// at some later point in life
usertype_detail::set_global_deleter<T>(L, functiongcfunc, functions);

View File

@ -272,6 +272,8 @@ TEST_CASE("tables/create-local-named", "Check if creating a table is kosher") {
sol::table testtable = lua.create_table("testtable", 0, 0, "Woof", "Bark", 1, 2, 3, 4);
sol::object testobj = lua["testtable"];
REQUIRE(testobj.is<sol::table>());
REQUIRE((testobj == testtable));
REQUIRE_FALSE((testobj != testtable));
REQUIRE((testtable["Woof"] == std::string("Bark")));
REQUIRE((testtable[1] == 2));
REQUIRE((testtable[3] == 4));

View File

@ -186,6 +186,13 @@ bool something() {
return true;
}
struct thing {
int v = 100;
thing() {}
thing(int x) : v(x) {}
};
TEST_CASE("table/traversal", "ensure that we can chain requests and tunnel down into a value if we desire") {
sol::state lua;
@ -935,8 +942,8 @@ TEST_CASE("usertype/readonly-and-static-functions", "Check if static functions c
sol::meta_function::call_function, &bark::operator()
);
lua.script("assert(bark.oh_boy('woo') == 3)");
lua.script("bark.oh_boy()");
REQUIRE_NOTHROW(lua.script("assert(bark.oh_boy('woo') == 3)"));
REQUIRE_NOTHROW(lua.script("bark.oh_boy()"));
bark b;
lua.set("b", &b);
@ -1088,3 +1095,46 @@ TEST_CASE("utilities/this_state", "Ensure this_state argument can be gotten anyw
REQUIRE(a == 625);
REQUIRE(la == 625);
}
TEST_CASE("usertype/call_constructor", "make sure lua types can be constructed with function call constructors") {
sol::state lua;
lua.open_libraries(sol::lib::base);
lua.new_usertype<thing>("thing",
"v", &thing::v
, sol::call_constructor, sol::constructors<sol::types<>, sol::types<int>>()
);
lua.script(R"(
t = thing(256)
)");
thing& y = lua["t"];
std::cout << y.v << std::endl;
REQUIRE(y.v == 256);
}
TEST_CASE("usertype/blank_constructor", "make sure lua types cannot be constructed if a blank / empty constructor is provided") {
sol::state lua;
lua.open_libraries(sol::lib::base);
lua.new_usertype<thing>("thing",
"v", &thing::v
, sol::call_constructor, sol::constructors<>()
);
REQUIRE_THROWS(lua.script("t = thing(256)"));
}
TEST_CASE("usertype/no_constructor", "make sure lua types cannot be constructed if a blank / empty constructor is provided") {
sol::state lua;
lua.open_libraries(sol::lib::base);
lua.new_usertype<thing>("thing",
"v", &thing::v
, sol::call_constructor, sol::no_constructor
);
REQUIRE_THROWS(lua.script("t = thing.new()"));
}