1
0
Fork 0
mirror of https://gitlab.com/niansa/SomeBot.git synced 2025-03-06 20:48:26 +01:00
SomeBot/LuaBridge3/Source/LuaBridge/detail/LuaHelpers.h
2023-03-02 14:10:47 +01:00

581 lines
13 KiB
C++

// https://github.com/kunitoki/LuaBridge3
// Copyright 2020, Lucio Asnaghi
// Copyright 2012, Vinnie Falco <vinnie.falco@gmail.com>
// Copyright 2007, Nathan Reed
// SPDX-License-Identifier: MIT
#pragma once
#include "Config.h"
#include <cassert>
#include <cstdio>
#include <cstdlib>
#include <limits>
#include <type_traits>
#include <utility>
namespace luabridge {
/**
* @brief Helper for unused vars.
*/
template <class... Args>
constexpr void unused(Args&&...)
{
}
// These functions and defines are for Luau.
#if LUABRIDGE_ON_LUAU
inline int luaL_ref(lua_State* L, int idx)
{
assert(idx == LUA_REGISTRYINDEX);
const int ref = lua_ref(L, -1);
lua_pop(L, 1);
return ref;
}
inline void luaL_unref(lua_State* L, int idx, int ref)
{
unused(idx);
lua_unref(L, ref);
}
template <class T>
inline void* lua_newuserdata_x(lua_State* L, size_t sz)
{
return lua_newuserdatadtor(L, sz, [](void* x)
{
T* object = static_cast<T*>(x);
object->~T();
});
}
inline void lua_pushcfunction_x(lua_State *L, lua_CFunction fn)
{
lua_pushcfunction(L, fn, "");
}
inline void lua_pushcclosure_x(lua_State* L, lua_CFunction fn, int n)
{
lua_pushcclosure(L, fn, "", n);
}
inline int lua_error_x(lua_State* L)
{
lua_error(L);
return 0;
}
inline int lua_getstack_info_x(lua_State* L, int level, const char* what, lua_Debug* ar)
{
return lua_getinfo(L, level, what, ar);
}
#else
using ::luaL_ref;
using ::luaL_unref;
template <class T>
inline void* lua_newuserdata_x(lua_State* L, size_t sz)
{
return lua_newuserdata(L, sz);
}
inline void lua_pushcfunction_x(lua_State *L, lua_CFunction fn)
{
lua_pushcfunction(L, fn);
}
inline void lua_pushcclosure_x(lua_State* L, lua_CFunction fn, int n)
{
lua_pushcclosure(L, fn, n);
}
inline int lua_error_x(lua_State* L)
{
return lua_error(L);
}
inline int lua_getstack_info_x(lua_State* L, int level, const char* what, lua_Debug* ar)
{
lua_getstack(L, level, ar);
return lua_getinfo(L, what, ar);
}
#endif // LUABRIDGE_ON_LUAU
// These are for Lua versions prior to 5.3.0.
#if LUA_VERSION_NUM < 503
inline lua_Number to_numberx(lua_State* L, int idx, int* isnum)
{
lua_Number n = lua_tonumber(L, idx);
if (isnum)
*isnum = (n != 0 || lua_isnumber(L, idx));
return n;
}
inline lua_Integer to_integerx(lua_State* L, int idx, int* isnum)
{
int ok = 0;
lua_Number n = to_numberx(L, idx, &ok);
if (ok)
{
const auto int_n = static_cast<lua_Integer>(n);
if (n == static_cast<lua_Number>(int_n))
{
if (isnum)
*isnum = 1;
return int_n;
}
}
if (isnum)
*isnum = 0;
return 0;
}
#endif // LUA_VERSION_NUM < 503
// These are for Lua versions prior to 5.2.0.
#if LUA_VERSION_NUM < 502
using lua_Unsigned = std::make_unsigned_t<lua_Integer>;
#if ! LUABRIDGE_ON_LUAU
inline int lua_absindex(lua_State* L, int idx)
{
if (idx > LUA_REGISTRYINDEX && idx < 0)
return lua_gettop(L) + idx + 1;
else
return idx;
}
#endif
inline void lua_rawgetp(lua_State* L, int idx, const void* p)
{
idx = lua_absindex(L, idx);
luaL_checkstack(L, 1, "not enough stack slots");
lua_pushlightuserdata(L, const_cast<void*>(p));
lua_rawget(L, idx);
}
inline void lua_rawsetp(lua_State* L, int idx, const void* p)
{
idx = lua_absindex(L, idx);
luaL_checkstack(L, 1, "not enough stack slots");
lua_pushlightuserdata(L, const_cast<void*>(p));
lua_insert(L, -2);
lua_rawset(L, idx);
}
#define LUA_OPEQ 1
#define LUA_OPLT 2
#define LUA_OPLE 3
inline int lua_compare(lua_State* L, int idx1, int idx2, int op)
{
switch (op)
{
case LUA_OPEQ:
return lua_equal(L, idx1, idx2);
case LUA_OPLT:
return lua_lessthan(L, idx1, idx2);
case LUA_OPLE:
return lua_equal(L, idx1, idx2) || lua_lessthan(L, idx1, idx2);
default:
return 0;
}
}
inline int get_length(lua_State* L, int idx)
{
return static_cast<int>(lua_objlen(L, idx));
}
#else // LUA_VERSION_NUM >= 502
inline int get_length(lua_State* L, int idx)
{
lua_len(L, idx);
const int len = static_cast<int>(luaL_checknumber(L, -1));
lua_pop(L, 1);
return len;
}
#endif // LUA_VERSION_NUM < 502
#ifndef LUA_OK
#define LUABRIDGE_LUA_OK 0
#else
#define LUABRIDGE_LUA_OK LUA_OK
#endif
/**
* @brief Helper to throw or return an error code.
*/
template <class T, class ErrorType>
std::error_code throw_or_error_code(ErrorType error)
{
#if LUABRIDGE_HAS_EXCEPTIONS
throw T(makeErrorCode(error).message().c_str());
#else
return makeErrorCode(error);
#endif
}
template <class T, class ErrorType>
std::error_code throw_or_error_code(lua_State* L, ErrorType error)
{
#if LUABRIDGE_HAS_EXCEPTIONS
throw T(L, makeErrorCode(error));
#else
return unused(L), makeErrorCode(error);
#endif
}
/**
* @brief Helper to throw or assert.
*/
template <class T, class... Args>
void throw_or_assert(Args&&... args)
{
#if LUABRIDGE_HAS_EXCEPTIONS
throw T(std::forward<Args>(args)...);
#else
unused(std::forward<Args>(args)...);
assert(false);
#endif
}
/**
* @brief Helper to set unsigned.
*/
template <class T>
void pushunsigned(lua_State* L, T value)
{
static_assert(std::is_unsigned_v<T>);
lua_pushinteger(L, static_cast<lua_Integer>(value));
}
/**
* @brief Helper to convert to integer.
*/
inline lua_Number tonumber(lua_State* L, int idx, int* isnum)
{
#if ! LUABRIDGE_ON_LUAU && LUA_VERSION_NUM > 502
return lua_tonumberx(L, idx, isnum);
#else
return to_numberx(L, idx, isnum);
#endif
}
/**
* @brief Helper to convert to integer.
*/
inline lua_Integer tointeger(lua_State* L, int idx, int* isnum)
{
#if ! LUABRIDGE_ON_LUAU && LUA_VERSION_NUM > 502
return lua_tointegerx(L, idx, isnum);
#else
return to_integerx(L, idx, isnum);
#endif
}
/**
* @brief Register main thread, only supported on 5.1.
*/
inline constexpr char main_thread_name[] = "__luabridge_main_thread";
inline void register_main_thread(lua_State* threadL)
{
#if LUA_VERSION_NUM < 502
if (threadL == nullptr)
lua_pushnil(threadL);
else
lua_pushthread(threadL);
lua_setglobal(threadL, main_thread_name);
#else
unused(threadL);
#endif
}
/**
* @brief Get main thread, not supported on 5.1.
*/
inline lua_State* main_thread(lua_State* threadL)
{
#if LUA_VERSION_NUM < 502
lua_getglobal(threadL, main_thread_name);
if (lua_isthread(threadL, -1))
{
auto L = lua_tothread(threadL, -1);
lua_pop(threadL, 1);
return L;
}
assert(false); // Have you forgot to call luabridge::registerMainThread ?
lua_pop(threadL, 1);
return threadL;
#else
lua_rawgeti(threadL, LUA_REGISTRYINDEX, LUA_RIDX_MAINTHREAD);
lua_State* L = lua_tothread(threadL, -1);
lua_pop(threadL, 1);
return L;
#endif
}
/**
* @brief Get a table value, bypassing metamethods.
*/
inline void rawgetfield(lua_State* L, int index, char const* key)
{
assert(lua_istable(L, index));
index = lua_absindex(L, index);
lua_pushstring(L, key);
lua_rawget(L, index);
}
/**
* @brief Set a table value, bypassing metamethods.
*/
inline void rawsetfield(lua_State* L, int index, char const* key)
{
assert(lua_istable(L, index));
index = lua_absindex(L, index);
lua_pushstring(L, key);
lua_insert(L, -2);
lua_rawset(L, index);
}
/**
* @brief Returns true if the value is a full userdata (not light).
*/
[[nodiscard]] inline bool isfulluserdata(lua_State* L, int index)
{
return lua_isuserdata(L, index) && !lua_islightuserdata(L, index);
}
/**
* @brief Test lua_State objects for global equality.
*
* This can determine if two different lua_State objects really point
* to the same global state, such as when using coroutines.
*
* @note This is used for assertions.
*/
[[nodiscard]] inline bool equalstates(lua_State* L1, lua_State* L2)
{
return lua_topointer(L1, LUA_REGISTRYINDEX) == lua_topointer(L2, LUA_REGISTRYINDEX);
}
/**
* @brief Return the size of lua table, even if not a sequence { 1=x, 2=y, 3=... }.
*/
[[nodiscard]] inline int table_length(lua_State* L, int index)
{
assert(lua_istable(L, index));
int items_count = 0;
lua_pushnil(L);
while (lua_next(L, index) != 0)
{
++items_count;
lua_pop(L, 1);
}
return items_count;
}
/**
* @brief Return an aligned pointer of type T.
*/
template <class T>
[[nodiscard]] T* align(void* ptr) noexcept
{
const auto address = reinterpret_cast<size_t>(ptr);
const auto offset = address % alignof(T);
const auto aligned_address = (offset == 0) ? address : (address + alignof(T) - offset);
return reinterpret_cast<T*>(aligned_address);
}
/**
* @brief Return the space needed to align the type T on an unaligned address.
*/
template <class T>
[[nodiscard]] constexpr size_t maximum_space_needed_to_align() noexcept
{
return sizeof(T) + alignof(T) - 1;
}
/**
* @brief Deallocate lua userdata taking into account alignment.
*/
template <class T>
int lua_deleteuserdata_aligned(lua_State* L)
{
assert(isfulluserdata(L, 1));
T* aligned = align<T>(lua_touserdata(L, 1));
aligned->~T();
return 0;
}
/**
* @brief Allocate lua userdata taking into account alignment.
*
* Using this instead of lua_newuserdata directly prevents alignment warnings on 64bits platforms.
*/
template <class T, class... Args>
void* lua_newuserdata_aligned(lua_State* L, Args&&... args)
{
#if LUABRIDGE_ON_LUAU
void* pointer = lua_newuserdatadtor(L, maximum_space_needed_to_align<T>(), [](void* x)
{
T* aligned = align<T>(x);
aligned->~T();
});
#else
void* pointer = lua_newuserdata_x<T>(L, maximum_space_needed_to_align<T>());
lua_newtable(L);
lua_pushcfunction_x(L, &lua_deleteuserdata_aligned<T>);
rawsetfield(L, -2, "__gc");
lua_setmetatable(L, -2);
#endif
T* aligned = align<T>(pointer);
new (aligned) T(std::forward<Args>(args)...);
return pointer;
}
/**
* @brief Safe error able to walk backwards for error reporting correctly.
*/
inline int raise_lua_error(lua_State *L, const char *fmt, ...)
{
va_list argp;
va_start(argp, fmt);
bool pushed_error = false;
for (int level = 2; level > 0; --level)
{
lua_Debug ar;
#if LUABRIDGE_ON_LUAU
if (lua_getinfo(L, level, "sl", &ar) == 0)
continue;
#else
if (lua_getstack(L, level, &ar) == 0 || lua_getinfo(L, "Sl", &ar) == 0)
continue;
#endif
if (ar.currentline <= 0)
continue;
lua_pushfstring(L, "%s:%d: ", ar.short_src, ar.currentline);
pushed_error = true;
break;
}
if (! pushed_error)
lua_pushliteral(L, "");
lua_pushvfstring(L, fmt, argp);
va_end(argp);
lua_concat(L, 2);
return lua_error_x(L);
}
/**
* @brief Checks if the value on the stack is a number type and can fit into the corresponding c++ integral type..
*/
template <class U = lua_Integer, class T>
constexpr bool is_integral_representable_by(T value)
{
constexpr bool same_signedness = (std::is_unsigned_v<T> && std::is_unsigned_v<U>)
|| (!std::is_unsigned_v<T> && !std::is_unsigned_v<U>);
if constexpr (sizeof(T) == sizeof(U))
{
if constexpr (same_signedness)
return true;
if constexpr (std::is_unsigned_v<T>)
return value <= static_cast<T>(std::numeric_limits<U>::max());
return value >= static_cast<T>(std::numeric_limits<U>::min())
&& static_cast<U>(value) <= std::numeric_limits<U>::max();
}
if constexpr (sizeof(T) < sizeof(U))
{
return static_cast<U>(value) >= std::numeric_limits<U>::min()
&& static_cast<U>(value) <= std::numeric_limits<U>::max();
}
if constexpr (std::is_unsigned_v<T>)
return value <= static_cast<T>(std::numeric_limits<U>::max());
return value >= static_cast<T>(std::numeric_limits<U>::min())
&& value <= static_cast<T>(std::numeric_limits<U>::max());
}
template <class U = lua_Integer>
bool is_integral_representable_by(lua_State* L, int index)
{
int isValid = 0;
const auto value = tointeger(L, index, &isValid);
return isValid ? is_integral_representable_by<U>(value) : false;
}
/**
* @brief Checks if the value on the stack is a number type and can fit into the corresponding c++ numerical type..
*/
template <class U = lua_Number, class T>
constexpr bool is_floating_point_representable_by(T value)
{
if constexpr (sizeof(T) == sizeof(U))
return true;
if constexpr (sizeof(T) < sizeof(U))
return static_cast<U>(value) >= -std::numeric_limits<U>::max()
&& static_cast<U>(value) <= std::numeric_limits<U>::max();
return value >= static_cast<T>(-std::numeric_limits<U>::max())
&& value <= static_cast<T>(std::numeric_limits<U>::max());
}
template <class U = lua_Number>
bool is_floating_point_representable_by(lua_State* L, int index)
{
int isValid = 0;
const auto value = tonumber(L, index, &isValid);
return isValid ? is_floating_point_representable_by<U>(value) : false;
}
} // namespace luabridge