view C++/Luae.h @ 225:e01ee0c72c43

Luae: begin refactoring of Luae
author David Demelier <markand@malikania.fr>
date Fri, 09 May 2014 17:12:25 +0200
parents 8fc177bbc4a6
children 24501d428db3
line wrap: on
line source

/*
 * Luae.h -- Lua helpers and such
 *
 * Copyright (c) 2013, 2014 David Demelier <markand@malikania.fr>
 *
 * Permission to use, copy, modify, and/or distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

#ifndef _LUAE_H_
#define _LUAE_H_

/**
 * @file Luae.h
 * @brief Lua C++ extended API
 */

#include <cassert>
#include <cstddef>
#include <functional>
#include <memory>
#include <stdexcept>
#include <string>
#include <type_traits>
#include <unordered_map>
#include <vector>

#include <lua.hpp>

/**
 * @class Luae
 * @brief Add lot of convenience for Lua
 *
 * This class adds lot of functions for Lua and C++.
 */
class Luae {
private:
	static constexpr const char *FieldRefs	= "__luae_refs";
	static constexpr const char *FieldTop	= "__luae_topcheck";

	/*
	 * Wrapper for dofile and dostring.
	 */
	static void doexecute(lua_State *L, int status);

public:
	/**
	 * Map from string to function.
	 */
	using Reg = std::unordered_map<const char *, lua_CFunction>;

	/**
	 * ## Userdata object
	 *
	 * Test if the object can be pushed as a userdata. If the object can
	 * be pushed as a userdata, it must match the following requirements:
	 *
	 *	- Copy constructible
	 *	- TypeInfo overload must have const char *name and inherit TypeUserdata
	 *
	 * The following example code allows the class Object to be pushed
	 * and get as Lua userdata.
	 *
	 * @code
	 * struct Object { };
	 *
	 * template <>
	 * struct Luae::TypeInfo<Object> : Luae::TypeUserdata {
	 * 	static constexpr const char *name = "Object";
	 * };
	 *
	 * int l_push(lua_State *L)
	 * {
	 * 	Luae::push(L, Object());
	 * }
	 *
	 * int l_get(lua_State *L)
	 * {
	 * 	Object *o = Luae::get<Object>(L, 1);
	 * }
	 * @endcode
	 *
	 * @note You don't need to add the pointer type to the get template parameter
	 *
	 * ## Custom object
	 *
	 * This other example can be used to push custom objects but not as
	 * userdata. You can use this to push and read tables for instance.
	 *
	 * For this you must overload TypeInfo to the object and inherit to TypeCustom.
	 *
	 * @code
	 * struct Point {
	 * 	int x, y;
	 * };
	 *
	 * template <>
	 * struct Luae::TypeInfo<Point> : Luae::TypeCustom {
	 * 	static void push(lua_State *L, const Point &p)
	 * 	{
	 * 		lua_createtable(L, 0, 0);
	 * 		lua_pushinteger(L, p.x);
	 * 		lua_setfield(L, -2, "x");
	 * 		lua_pushinteger(L, p.y);
	 * 		lua_setfield(L, -2, "y");
	 * 	}
	 *
	 * 	static Point get(lua_State *L, int index)
	 * 	{
	 * 		Point p;
	 *
	 * 		if (lua_type(L, index) == LUA_TTABLE) {
	 * 			lua_getfield(L, index, "x");
	 * 			p.x = lua_tonumber(L, -1);
	 * 			lua_pop(L, 1);
	 * 			lua_getfield(L, index, "y");
	 * 			p.y = lua_tonumber(L, -1);
	 * 			lua_pop(L, 1);
	 * 		}
	 *
	 * 		return p;
	 * 	}
	 *
	 * 	static Point check(lua_State *L, int index)
	 * 	{
	 * 		// Do your check
	 *
	 * 		return get(L, index);
	 * 	}
	 * };
	 *
	 * int l_push(lua_State *L)
	 * {
	 * 	Luae::push<Point>(L, Point {1, 2});
	 * }
	 *
	 * int l_get(lua_State *L)
	 * {
	 * 	Point p = Luae::get<Point>(L, 1);
	 * }
	 * @endcode
	 *
	 * @note Here you get a T and not a T *
	 */
	template <typename T>
	struct TypeInfo {
		static const bool	isCustom = false;
		static const bool	isUserdata = false;
	};
	
	/**
	 * Helpers for custom types. It is already overloaded for all standard
	 * types :
	 *	- int
	 *	- bool
	 *	- double
	 *	- std::string
	 *	- std::32string
	 */
	struct TypeCustom {
		static const bool	canGet = true;
		static const bool	canPush = true;
		static const bool	canCheck = true;
		static const bool	isCustom = true;
		static const bool	isUserdata = false;
	};

	/**
	 * Helper for userdata. It only require to set the name field
	 * as the metatable name.
	 */
	struct TypeUserdata {
		static const bool	isUserdata = true;
		static const bool	isCustom = false;
	};

private:
	template <typename T>
	struct IsCustom {
		static const bool value = TypeInfo<T>::isCustom;
	};

	template <typename T>
	struct IsUserdata {
		static const bool value = TypeInfo<T>::isUserdata;
	};
	
	template <typename T>
	struct IsSharedUserdata : std::false_type {};

	template <typename T>
	struct IsSharedUserdata<std::shared_ptr<T>> {
		static const bool value = TypeInfo<T>::isUserdata;
	};

	template <bool Cond, typename Type = void>
	using EnableIf		= typename std::enable_if<Cond, Type>::type;

	template <typename T>
	using SharedType	= typename T::element_type;

public:
	/* -------------------------------------------------
	 * Standard Lua API wrappers
	 * ------------------------------------------------- */

	/**
	 * Calls a Lua function in non-protected mode.
	 *
	 * @param L the Lua state
	 * @param np the number of parameters
	 * @param nr the number of return values
	 */
	static inline void call(lua_State *L, int np = 0, int nr = 0)
	{
		lua_call(L, np, nr);
	}

	/**
	 * Ensure that there are at least extra free stack slots in the stack.
	 *
	 * @param L the Lua state
	 * @param extra the extra data
	 * @return true if possible
	 */
	static inline int checkstack(lua_State *L, int extra)
	{
		return lua_checkstack(L, extra);
	}

	/**
	 * Check the type at the given index. Calls luaL_error on bad
	 * type.
	 *
	 * @param L the Lua state
	 * @param index the the index
	 * @param type the type to check
	 */
	static inline void checktype(lua_State *L, int index, int type)
	{
		luaL_checktype(L, index, type);
	}

	/**
	 * Compares two Lua values.
	 *
	 * Operation is one of:
	 *
	 * * LUA_OPEQ,
	 * * LUA_OPLT,
	 * * LUA_OPLE
	 *
	 * @param L the Lua state
	 * @param index1 the first value
	 * @param index2 the second value
	 * @param op the operation
	 * @return true if index1 statisfies op compared to index2
	 */
	static inline bool compare(lua_State *L, int index1, int index2, int op)
	{
		return lua_compare(L, index1, index2, op) == 1;
	}

	/**
	 * Concatenate the n values at the top of the stack and pops them.
	 * Leaves the result at the top of the stack.
	 *
	 * @param L the Lua state
	 * @param n the number to concat
	 */
	static inline void concat(lua_State *L, int n)
	{
		lua_concat(L, n);
	}

	/**
	 * Moves the element at the index from into the valid index to.
	 *
	 * @param L the Lua state
	 * @param from the from index
	 * @param to the destination index
	 */
	static inline void copy(lua_State *L, int from, int to)
	{
		lua_copy(L, from, to);
	}

	/**
	 * Create a table.
	 *
	 * @param L the Lua state
	 * @param nr the number of array record
	 * @param ns the number of sequence
	 */
	static inline void createtable(lua_State *L, int nr = 0, int ns = 0)
	{
		lua_createtable(L, nr, ns);
	}

	/**
	 * Load and execute a file.
	 *
	 * @param L the Lua state
	 * @param path the the path
	 * @throw std::runtime_error on error
	 */
	static inline void dofile(lua_State *L, const std::string &path)
	{
		doexecute(L, luaL_dofile(L, path.c_str()));
	}

	/**
	 * Load and execute a string.
	 *
	 * @param L the Lua state
	 * @param data the data
	 * @throw std::runtime_error on error
	 */
	static inline void dostring(lua_State *L, const std::string &data)
	{
		doexecute(L, luaL_dostring(L, data.c_str()));
	}

	/**
	 * Generate an error with the string at the top of the stack.
	 *
	 * @param L the Lua state
	 * @return nothing
	 */
	static inline int error(lua_State *L)
	{
		return lua_error(L);
	}

	/**
	 * Raises a Lua error, thus calling longjmp.
	 *
	 * @param L the Lua state
	 * @param fmt the format
	 * @param args the arguments
	 * @return nothing
	 */
	template <typename... Args>
	static inline int error(lua_State *L, const char *fmt, Args&&... args)
	{
		return luaL_error(L, fmt, std::forward<Args>(args)...);
	}

	/**
	 * Controls the garbage collector.
	 *
	 * @param L the Lua state
	 * @param what the action
	 * @param data optional GC data
	 */
	static inline int gc(lua_State *L, int what, int data = 0)
	{
		return lua_gc(L, what, data);
	}

	/**
	 * Get a field at the given index.
	 *
	 * @param L the Lua state
	 * @param idx the table index
	 * @param name the field name
	 */
	static inline void getfield(lua_State *L, int idx, const std::string &name)
	{
		lua_getfield(L, idx, name.c_str());
	}

	/**
	 * Get a global value from Lua.
	 *
	 * @param L the Lua state
	 * @param name the value name
	 */
	static inline void getglobal(lua_State *L, const std::string &name)
	{
		lua_getglobal(L, name.c_str());
	}

	/**
	 * Get the metatable of the value at the given index. Returns false
	 * if does not have a metatable and pushes nothing.
	 *
	 * @param L the Lua state
	 * @param index the value index
	 */
	static inline bool getmetatable(lua_State *L, int index)
	{
		return lua_getmetatable(L, index) == 1;
	}

	/**
	 * Set the value at the given index. Top value is the value to assign
	 * key is just below the value.
	 *
	 * @param L the Lua state
	 * @param index the value index
	 */
	static inline void gettable(lua_State *L, int index)
	{
		lua_gettable(L, index);
	}

	/**
	 * Get the current stack size.
	 *
	 * @param L the Lua state
	 * @return the stack size
	 */
	static inline int gettop(lua_State *L)
	{
		return lua_gettop(L);
	}

	/**
	 * Pushes the Lua value associated with the userdata.
	 *
	 * @param L the Lua state
	 * @param index the value index
	 */
	static inline void getuservalue(lua_State *L, int index)
	{
		lua_getuservalue(L, index);
	}

	/**
	 * Move the top element at the given index.
	 *
	 * @param L the Lua state
	 * @param index the new index
	 */
	static inline void insert(lua_State *L, int index)
	{
		lua_insert(L, index);
	}

	/**
	 * Push the result of the operator '#' from the value at the given
	 * index.
	 *
	 * @param L the Lua state
	 * @param index the value index
	 */
	static inline void len(lua_State *L, int index)
	{
		lua_len(L, index);
	}

	/**
	 * Create or get a metatable in the registry.
	 *
	 * @param L the Lua state
	 * @param name the metatable name
	 */
	static inline void newmetatable(lua_State *L, const std::string &name)
	{
		luaL_newmetatable(L, name.c_str());
	}

	/**
	 * Create a new table and fill it with functions.
	 *
	 * @param L the Lua state
	 * @param functions the functions
	 */
	static inline void newlib(lua_State *L, const luaL_Reg *functions)
	{
		lua_createtable(L, 0, 0);
		luaL_setfuncs(L, functions, 0);
	}

	/**
	 * Create a new table and fill it with functions.
	 *
	 * @param L the Lua state
	 * @param functions the functions
	 */
	static inline void newlib(lua_State *L, const Reg &functions)
	{
		lua_createtable(L, 0, 0);
		for (auto &p : functions) {
			lua_pushcfunction(L, p.second);
			lua_setfield(L, -2, p.first);
		}
	}

	/**
	 * Pops a key from the stack and pushes a key-value pair from the table
	 * at the given index.
	 *
	 * @param L the Lua state
	 * @param index the table index
	 * @return true if there are still elements
	 */
	static inline bool next(lua_State *L, int index)
	{
		return lua_next(L, index) == 1;
	}

	/**
	 * Load all Lua libraries.
	 *
	 * @param L the Lua state
	 */
	static inline void openlibs(lua_State *L)
	{
		luaL_openlibs(L);
	}

	/**
	 * Wrapper around pcall, throw instead of returning an error code.
	 *
	 * @param L the Lua state
	 * @param np the number of parameters
	 * @param nr the number of return value
	 * @param error the message handler
	 */
	static inline void pcall(lua_State *L, int np, int nr, int error = 0)
	{
		if (lua_pcall(L, np, nr, error) != LUA_OK) {
			auto error = lua_tostring(L, -1);
			lua_pop(L, 1);

			throw std::runtime_error(error);
		}
	}

	/**
	 * Pop arguments from the stack.
	 *
	 * @param L the Lua state
	 * @param count the number of values to pop
	 */
	static inline void pop(lua_State *L, int count = 1)
	{
		lua_pop(L, count);
	}

	/**
	 * Pushes a copy of the value at the given index.
	 *
	 * @param L the Lua state
	 * @param index the value to copy
	 */
	static inline void pushvalue(lua_State *L, int index)
	{
		lua_pushvalue(L, index);
	}

	/**
	 * Returns true if the values are primitively equal.
	 *
	 * @param L the Lua state
	 * @param index1 the first value
	 * @param index2 the second value
	 * @return true if they equals
	 */
	static inline bool rawequal(lua_State *L, int index1, int index2)
	{
		return lua_rawequal(L, index1, index2) == 1;
	}

	/**
	 * Like gettable but with raw access.
	 *
	 * @param L the Lua state
	 * @param index the value index
	 */
	static inline void rawget(lua_State *L, int index)
	{
		lua_rawget(L, index);
	}

	/**
	 * Like gettable but with raw access.
	 *
	 * @param L the Lua state
	 * @param index the value index
	 * @param n the nt
	 */
	static inline void rawget(lua_State *L, int index, int n)
	{
		lua_rawgeti(L, index, n);
	}

	/**
	 * Like rawgeti but with pointer.
	 *
	 * @param L the Lua state
	 * @param index the value index
	 * @param p the pointer key
	 */
	static inline void rawget(lua_State *L, int index, const void *p)
	{
		lua_rawgetp(L, index, p);
	}

	/**
	 * Get the value length.
	 *
	 * @param L the Lua state
	 * @param index the value index
	 * @return the raw length
	 */
	static inline int rawlen(lua_State *L, int index)
	{
		return lua_rawlen(L, index);
	}

	/**
	 * Similar to settable but without raw assignment.
	 *
	 * @param L the Lua state
	 * @param index the value index
	 */
	static inline void rawset(lua_State *L, int index)
	{
		lua_rawset(L, index);
	}

	/**
	 * Set the value at the top of stack to the ntn value at the value
	 * at the given index.
	 *
	 * @param L the Lua state
	 * @param index the value index
	 * @param n the nth index
	 */
	static inline void rawset(lua_State *L, int index, int n)
	{
		lua_rawseti(L, index, n);
	}

	/**
	 * Like rawseti with a void pointer as the key.
	 *
	 * @param L the Lua state
	 * @param index the value index
	 * @param ptr the pointer key
	 */
	static inline void rawset(lua_State *L, int index, const void *ptr)
	{
		lua_rawsetp(L, index, ptr);
	}

	/**
	 * Push a formatted string like lua_pushfstring. Warning, it is not type
	 * safe and you should for instance not pass std::String to %s.
	 *
	 * @param L the Lua state
	 * @param fmt the format
	 * @param args the arguments
	 * @return the formatted string
	 */
	template <typename... Args>
	static inline const char *pushfstring(lua_State *L, const char *fmt, Args&&... args)
	{
		return lua_pushfstring(L, fmt, std::forward<Args>(args)...);
	}

	/**
	 * Push a function with an optional number of upvalues.
	 *
	 * @param L the Lua state
	 * @param func the function
	 * @param nup the number of up values
	 */
	static inline void pushfunction(lua_State *L, lua_CFunction func, int nup = 0)
	{
		lua_pushcclosure(L, func, nup);
	}

	/**
	 * Create a unique reference to the table at the given index.
	 *
	 * @param L the Lua state
	 * @param index the table index
	 * @return the reference
	 */
	static inline int ref(lua_State *L, int index)
	{
		return luaL_ref(L, index);
	}

	/**
	 * Remove the element at the given index.
	 *
	 * @param L the Lua state
	 * @param index the table index
	 */
	static inline void remove(lua_State *L, int index)
	{
		lua_remove(L, index);
	}

	/**
	 * Replace the element at the given index by the one at the top.
	 *
	 * @param L the Lua state
	 * @param index the new index
	 */
	static inline void replace(lua_State *L, int index)
	{
		lua_replace(L, index);
	}

	/**
	 * Set a field to the table at the given index.
	 *
	 * @param L the Lua state
	 * @param idx the table index
	 * @param name the field name
	 * @see set
	 */
	static inline void setfield(lua_State *L, int idx, const std::string &name)
	{
		lua_setfield(L, idx, name.c_str());
	}

	/**
	 * Set the functions to the table at the top of stack.
	 *
	 * @param L the Lua state
	 * @param functions the functions
	 * @param nup the number of upvalues
	 */
	static inline void setfuncs(lua_State *L, const luaL_Reg *functions, int nup = 0)
	{
		luaL_setfuncs(L, functions, nup);
	}

	/**
	 * Set the functions to the table at the top of stack.
	 *
	 * @param L the Lua state
	 * @param functions the functions
	 * @param nup the number of upvalues
	 */
	static inline void setfuncs(lua_State *L, const Reg &functions, int nup = 0)
	{
		luaL_checkversion(L);
		luaL_checkstack(L, nup, "too many upvalues");

		for (auto &l : functions) {
			for (int i = 0; i < nup; i++)
				lua_pushvalue(L, -nup);
			lua_pushcclosure(L, l.second, nup);
			lua_setfield(L, -(nup + 2), l.first);
		}

		lua_pop(L, nup);
	}

	/**
	 * Set a global.
	 *
	 * @param L the Lua state
	 * @param name the name
	 */
	static inline void setglobal(lua_State *L, const std::string &name)
	{
		lua_setglobal(L, name.c_str());
	}

	/**
	 * Pops the table at the top of the stack and sets it as the metatable
	 * of the value at the given index.
	 *
	 * @param L the Lua state
	 * @param index the value index
	 */
	static inline void setmetatable(lua_State *L, int index)
	{
		lua_setmetatable(L, index);
	}

	/**
	 * Does t[n] where n is the value at the top of the stack and the key
	 * just below the value.
	 *
	 * @param L the Lua state
	 * @param index the value index
	 */
	static inline void settable(lua_State *L, int index)
	{
		lua_settable(L, index);
	}

	/**
	 * Set the stack size.
	 *
	 * @param L the Lua state
	 * @param index the index
	 */
	static inline void settop(lua_State *L, int index = 0)
	{
		lua_settop(L, index);
	}

	/**
	 * Pops a table or nil from the top stack and set it as the new
	 * associated value to the userdata.
	 *
	 * @param L the Lua state
	 * @param index the userdata index
	 */
	static inline void setuservalue(lua_State *L, int index)
	{
		lua_setuservalue(L, index);
	}

	/**
	 * Get the type at the given index.
	 *
	 * @param L the Lua state
	 * @param index the value index
	 * @return the type
	 */
	static inline int type(lua_State *L, int index)
	{
		return lua_type(L, index);
	}

	/**
	 * Get the type name.
	 *
	 * @param L the Lua state
	 * @param type the type
	 * @return the name
	 * @see type
	 */
	static inline const char *typeName(lua_State *L, int type)
	{
		return lua_typename(L, type);
	}

	/**
	 * Get the type name from a index.
	 *
	 * @param L the Lua state
	 * @param index the value index
	 * @return the name
	 */
	static inline const char *typeNamei(lua_State *L, int index)
	{
		return luaL_typename(L, index);
	}

	/**
	 * Unref the value from the table.
	 *
	 * @param L the Lua state
	 * @param index the table index
	 * @param ref the reference
	 */
	static inline void unref(lua_State *L, int index, int ref)
	{
		luaL_unref(L, index, ref);
	}

	/**
	 * Get the up value index.
	 *
	 * @param index the index
	 * @return the real index
	 */
	static inline int upvalueindex(int index)
	{
		return lua_upvalueindex(index);
	}

	/* -------------------------------------------------
	 * Extended API
	 * ------------------------------------------------- */

	/**
	 * Call this prior to assertEnd.
	 *
	 * @param L the Lua state
	 */
	static void assertBegin(lua_State *L);

	/**
	 * Call this to check the stack size after asserBegin. The stack size
	 * is automatically adjusted to the last size so the expected argument
	 * can just be set to the real expected size just like if the stack size
	 * were 0.
	 *
	 * @param L the Lua state
	 * @param expected the stack size expected
	 */
	static void assertEnd(lua_State *L, int expected);

	/**
	 * Preload a library, it will be added to package.preload so the
	 * user can successfully call require "name". In order to work, you need
	 * to open luaopen_package and luaopen_base first.
	 *
	 * @param L the Lua state
	 * @param name the module name
	 * @param func the opening library
	 * @see require
	 */
	static void preload(lua_State *L, const std::string &name, lua_CFunction func);

	/**
	 * Load a library just like it was loaded with require.
	 *
	 * @param L the Lua state
	 * @param name the module name
	 * @param func the function
	 */
	static void require(lua_State *L, const std::string &name, lua_CFunction func);

	template <typename T>
	static int push(lua_State *L,
			 const T &value,
			 EnableIf<IsCustom<T>::value, T> * = 0)
	{
		static_assert(TypeInfo<T>::canPush, "type not supported");

		TypeInfo<T>::push(L, value);

		return 1;
	}

	template <typename T>
	static int push(lua_State *L,
			 const T &value,
			 EnableIf<IsUserdata<T>::value, T> * = 0)
	{
		new (L, TypeInfo<T>::name) T(value);

		return 1;
	}

	template <typename T>
	static int push(lua_State *L,
			const std::shared_ptr<T> &value,
			EnableIf<IsSharedUserdata<std::shared_ptr<T>>::value, T> * = 0)
	{
		assertBegin(L);

		/*
		 * If already in the registry use the same shared_ptr to get the same
		 * pointer address within Lua. This let scripts using the object
		 * as a table key for instance.
		 */
		getfield(L, LUA_REGISTRYINDEX, FieldRefs);
		if (type(L, -1) == LUA_TNIL) {
			pop(L);
			createtable(L, 0, 0);
			createtable(L, 0, 0);
			pushfstring(L, "v");
			setfield(L, -2, "__mode");
			setmetatable(L, -2);
			pushvalue(L, -1);
			setfield(L, LUA_REGISTRYINDEX, FieldRefs);
		}

		rawget(L, -1, value.get());
		if (type(L, -1) == LUA_TNIL) {
			pop(L);
			new (L, TypeInfo<T>::name) std::shared_ptr<T>(value);
			pushvalue(L, -1);
			rawset(L, -3, value.get());
		}

		// Remove refs table
		remove(L, -2);

		return 1;
	}

	/**
	 * Overload for string literals.
	 *
	 * @param L the Lua state
	 * @param s the string
	 */
	template <size_t N>
	static void push(lua_State *L, const char (&s)[N])
	{
		push<const char *>(L, s);
	}

	template <typename T>
	static EnableIf<IsCustom<T>::value, T>
	get(lua_State *L, int index)
	{
		static_assert(TypeInfo<T>::canGet, "type not supported");

		return TypeInfo<T>::get(L, index);
	}

	template <typename T>
	static EnableIf<IsUserdata<T>::value, T *>
	get(lua_State *L, int index)
	{
		return Luae::toType<T *>(L, index);
	}
	
	template <typename T>
	static EnableIf<IsSharedUserdata<std::shared_ptr<T>>::value, std::shared_ptr<T>>
	get(lua_State *L, int index)
	{
		return *Luae::toType<std::shared_ptr<T> *>(L, index);
	}
	
	template <typename T>
	static EnableIf<IsCustom<T>::value, T>
	check(lua_State *L, int index)
	{
		static_assert(TypeInfo<T>::canCheck, "type not supported");

		return TypeInfo<T>::check(L, index);
	}

	template <typename T>
	static EnableIf<IsUserdata<T>::value, T *>
	check(lua_State *L, int index)
	{
		return Luae::toType<T *>(L, index, TypeInfo<T>::name);
	}

	template <typename T>
	static EnableIf<IsSharedUserdata<T>::value, T>
	check(lua_State *L, int index)
	{
		return *Luae::toType<T *>(L, index, TypeInfo<SharedType<T>>::name);
	}

	/**
	 * Convert a new placement made object, without testing if its a real
	 * object.
	 *
	 * @param L the Lua state
	 * @param idx the object index
	 * @return the converted object
	 */
	template<class T>
	static inline T toType(lua_State *L, int idx)
	{
		return reinterpret_cast<T>(lua_touserdata(L, idx));
	}

	/**
	 * Check for a userdata from the stack but without checking if it's a real
	 * LuaeClass one.
	 *
	 * @param L the Lua state
	 * @param idx the index
	 * @param meta the metatable name
	 * @return the object
	 */
	template <class T>
	static inline T toType(lua_State *L, int idx, const char *meta)
	{
		return reinterpret_cast<T>(luaL_checkudata(L, idx, meta));
	}
};

/**
 * @brief Overload for nil.
 */
template <>
struct Luae::TypeInfo<std::nullptr_t> : Luae::TypeCustom {
	static const bool canGet	= false;
	static const bool canCheck	= false;

	/**
	 * Push nil.
	 *
	 * @param L the Lua state
	 */
	static void push(lua_State *L, const std::nullptr_t &)
	{
		lua_pushnil(L);
	}
};

/**
 * @brief Overload for booleans.
 */
template <>
struct Luae::TypeInfo<bool> : Luae::TypeCustom {
	/**
	 * Push the boolean value.
	 *
	 * @param L the Lua state
	 * @param value the value
	 */
	static void push(lua_State *L, const bool &value)
	{
		lua_pushboolean(L, value);
	}

	/**
	 * Get a boolean.
	 *
	 * @param L the Lua state
	 * @param index the index
	 * @return a boolean
	 */
	static bool get(lua_State *L, int index)
	{
		return lua_toboolean(L, index);
	}

	/**
	 * Check for a bool.
	 *
	 * @param L the Lua state
	 * @param index the index
	 */
	static bool check(lua_State *L, int index)
	{
		return lua_toboolean(L, index);
	}
};

/**
 * @brief Overload for integers.
 */
template <>
struct Luae::TypeInfo<int> : Luae::TypeCustom {
	/**
	 * Push the integer value.
	 *
	 * @param L the Lua state
	 * @param value the value
	 */
	static void push(lua_State *L, const int &value)
	{
		lua_pushinteger(L, value);
	}

	/**
	 * Get a integer.
	 *
	 * @param L the Lua state
	 * @param index the index
	 * @return a boolean
	 */
	static int get(lua_State *L, int index)
	{
		return lua_tointeger(L, index);
	}

	/**
	 * Check for an integer.
	 *
	 * @param L the Lua state
	 * @param index the index
	 */
	static int check(lua_State *L, int index)
	{
		return luaL_checkinteger(L, index);
	}
};

/**
 * @brief Overload for longs.
 */
template <>
struct Luae::TypeInfo<long> : Luae::TypeCustom {
	/**
	 * Push the integer value.
	 *
	 * @param L the Lua state
	 * @param value the value
	 */
	static void push(lua_State *L, const long &value)
	{
		lua_pushinteger(L, value);
	}

	/**
	 * Get a integer.
	 *
	 * @param L the Lua state
	 * @param index the index
	 * @return a boolean
	 */
	static long get(lua_State *L, int index)
	{
		return lua_tointeger(L, index);
	}

	/**
	 * Check for an integer.
	 *
	 * @param L the Lua state
	 * @param index the index
	 */
	static long check(lua_State *L, int index)
	{
		return luaL_checkinteger(L, index);
	}
};

/**
 * @brief Overload for doubles.
 */
template <>
struct Luae::TypeInfo<double> : Luae::TypeCustom {
	/**
	 * Push the double value.
	 *
	 * @param L the Lua state
	 * @param value the value
	 */
	static void push(lua_State *L, const double &value)
	{
		lua_pushnumber(L, value);
	}

	/**
	 * Get a double.
	 *
	 * @param L the Lua state
	 * @param index the index
	 * @return a boolean
	 */
	static double get(lua_State *L, int index)
	{
		return lua_tonumber(L, index);
	}

	/**
	 * Check for a double.
	 *
	 * @param L the Lua state
	 * @param index the index
	 */
	static double check(lua_State *L, int index)
	{
		return luaL_checknumber(L, index);
	}
};

/**
 * @brief Overload for std::string.
 */
template <>
struct Luae::TypeInfo<std::string> : Luae::TypeCustom {
	/**
	 * Push the string value.
	 *
	 * @param L the Lua state
	 * @param value the value
	 */
	static void push(lua_State *L, const std::string &value)
	{
		lua_pushlstring(L, value.c_str(), value.length());
	}

	/**
	 * Get a string.
	 *
	 * @param L the Lua state
	 * @param index the index
	 * @return a boolean
	 */
	static std::string get(lua_State *L, int index)
	{
		return lua_tostring(L, index);
	}

	/**
	 * Check for a string.
	 *
	 * @param L the Lua state
	 * @param index the index
	 */
	static std::string check(lua_State *L, int index)
	{
		return luaL_checkstring(L, index);
	}
};

/**
 * @brief Overload for std::u32string.
 */
template <>
struct Luae::TypeInfo<std::u32string> : Luae::TypeCustom {
	/**
	 * Push the string value.
	 *
	 * @param L the Lua state
	 * @param str the value
	 */
	static void push(lua_State *L, const std::u32string &str)
	{
		lua_createtable(L, str.size(), 0);
		for (size_t i = 0; i < str.size(); ++i) {
			lua_pushinteger(L, str[i]);
			lua_rawseti(L, -2, i + 1);
		}
	}

	/**
	 * Get a string.
	 *
	 * @param L the Lua state
	 * @param index the index
	 * @return a boolean
	 */
	static std::u32string get(lua_State *L, int index)
	{
		std::u32string result;

		if (lua_type(L, index) == LUA_TNUMBER) {
			result.push_back(lua_tonumber(L, index));
		} else if (lua_type(L, index) == LUA_TTABLE) {
			if (index < 0)
				-- index;

			lua_pushnil(L);
			while (lua_next(L, index)) { 
				if (lua_type(L, -1) == LUA_TNUMBER)
					result.push_back(lua_tonumber(L, -1));

				lua_pop(L, 1);
			}
		}

		return result;
	}

	/**
	 * Check for a string.
	 *
	 * @param L the Lua state
	 * @param index the index
	 */
	static std::u32string check(lua_State *L, int index)
	{
		if (lua_type(L, index) != LUA_TNUMBER &&
		    lua_type(L, index) != LUA_TTABLE)
			luaL_error(L, "expected table or number");
			// NOTREACHED

		return get(L, index);
	}
};

/**
 * @brief Overload for const char *
 */
template <>
struct Luae::TypeInfo<const char *> : Luae::TypeCustom {
	/**
	 * Push the string value.
	 *
	 * @param L the Lua state
	 * @param value the value
	 */
	static void push(lua_State *L, const char *value)
	{
		lua_pushstring(L, value);
	}

	/**
	 * Get a string.
	 *
	 * @param L the Lua state
	 * @param index the index
	 * @return a boolean
	 */
	static const char *get(lua_State *L, int index)
	{
		return lua_tostring(L, index);
	}

	/**
	 * Check for a string.
	 *
	 * @param L the Lua state
	 * @param index the index
	 */
	static const char *check(lua_State *L, int index)
	{
		return luaL_checkstring(L, index);
	}
};

/**
 * Push a Lua userdata.
 *
 * @param size the object size
 * @param L the Lua state
 * @return the allocated data
 */
void *operator new(size_t size, lua_State *L);

/**
 * Push a Lua userdata with a metatable.
 *
 * @param size the object size
 * @param L the Lua state
 * @param metaname the metatable name
 * @return the allocated data
 */
void *operator new(size_t size, lua_State *L, const char *metaname);

/**
 * Delete the Lua userdata.
 *
 * @param ptr the data
 * @param L the Lua state
 */
void operator delete(void *ptr, lua_State *L);

/**
 * Delete the Lua userdata.
 *
 * @param ptr the data
 * @param L the Lua state
 * @param metaname the metatable name
 */
void operator delete(void *ptr, lua_State *L, const char *metaname);

#endif // !_LUAE_H_