Skip to content

Commit

Permalink
Initial work to allow declaring typed parameters directly in the LUA_…
Browse files Browse the repository at this point in the history
…FUNCTION declaration

Overloading, tables, strings and variadic arguments are still not supported, experimental
  • Loading branch information
edo9300 committed Jun 1, 2024
1 parent 03f06e8 commit 52071e0
Show file tree
Hide file tree
Showing 3 changed files with 287 additions and 207 deletions.
241 changes: 218 additions & 23 deletions function_array_helper.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@
#define MAKE_LUA_NAME_IMPL(module, name) c_lua_##module##_##name
#define MAKE_LUA_NAME(module, name) MAKE_LUA_NAME_IMPL(module, name)

// if subsequent calls to __COUNTER__ don't produce consecutive integers, that macro is broken
// __COUNTER__ is a nonstandard extension, check if it's present and properly working,
// if that's not the case, fall back to an ISO C++ implementation using the standard
// __LINE__ preprocessor macro to do the lua function magic
#if !defined(__COUNTER__) || (__COUNTER__ + 0 != __COUNTER__ - 1)
#define HAS_COUNTER 0
#else
Expand All @@ -18,12 +20,38 @@

#if !defined(__INTELLISENSE__) || !HAS_COUNTER
#include <array>
#include <cmath> //std::round
#include <lauxlib.h>
#if HAS_COUNTER
#include <type_traits> //std::conditional_t
#endif
#include <optional>
#include <type_traits> //std::conditional_t, std::enable_if_t, std::false_type, std::is_same_v, std::true_type
#include <tuple>
#include <utility> //std::index_sequence, std::make_index_sequence
#include "common.h"
#include "scriptlib.h"

// ISO C++ prohibits calling variadic macros with no 0 arguments, but
// major compilers (GCC, MSVC and clang) have extensions always enabled
// to support this behaviour.
// If no such compiler is detected, fall back to a ISO C++ compliant
// implementation a bit more costly depending on the optimization levels
// of the compiler
#if defined(_MSC_VER) || defined(__GNUC__) || defined(__clang__)
#define NEEDS_DUMMY 0
// under pedantic, variadic macros with 0 arguments are treated as warning
// clang allows disabling that warning explicitly, gcc doesn't, so the only
// solution is to mark this header as system header, so that those warnings
// aren't diagnosed
#if defined(__clang__)
#pragma GCC diagnostic ignored "-Wgnu-zero-variadic-macro-arguments"
#elif defined(__GCC__)
#pragma GCC system_header
#endif
#else
#define NEEDS_DUMMY 1
#endif

// Works around MSVC preprocessor bugs
#define EXPAND( x ) x

// use forceinline only in release builds
#if defined(_DEBUG) || (!defined(_MSC_VER) && !defined(__OPTIMIZE__))
Expand Down Expand Up @@ -114,43 +142,210 @@ constexpr auto make_lua_functions_array() {

#endif

using dummy = struct {}*;

template <typename T, typename Enable = void>
struct is_optional : std::false_type {};

template <typename T>
struct is_optional<std::optional<T>> : std::true_type {};

template<typename T>
inline constexpr bool is_optional_v = is_optional<T>::value;

template <typename Arg, typename... Args>
constexpr auto count_trailing_optionals(std::tuple<Arg, Args...>) {
if constexpr(sizeof...(Args) == 0) {
return static_cast<int>(is_optional_v<Arg>);
} else {
constexpr auto result = count_trailing_optionals(std::tuple<Args...>{});
if constexpr(result != sizeof...(Args))
return result;
else
return result + static_cast<int>(is_optional_v<Arg>);
}
}

template<typename T, std::enable_if_t<!is_optional_v<T>, int> = 0>
inline constexpr T get_lua([[maybe_unused]] lua_State* L, [[maybe_unused]] int idx) {
using namespace scriptlib;
using actual_type = T;
if constexpr(std::is_same_v<T, dummy>) {
return nullptr;
} else if constexpr(std::is_same_v<actual_type, lua_obj*>) {
if(lua_isnoneornil(L, idx)) {
lua_error(L, "Error.");
}
auto obj = lua_touserdata(L, idx);
if(!obj)
lua_error(L, "Error.");
auto* ret = *static_cast<lua_obj**>(obj);
if(ret->lua_type == LuaParam::DELETED)
lua_error(L, "Attempting to access deleted object.");
return ret;
} else {
constexpr auto lua_type = get_lua_param_type<actual_type>();
if constexpr(lua_type == LuaParam::CARD || lua_type == LuaParam::GROUP || lua_type == LuaParam::EFFECT) {
lua_obj* ret = nullptr;
check_param<lua_type>(L, idx, &ret);
return reinterpret_cast<actual_type>(ret);
} else {
check_param<lua_type>(L, idx);
if constexpr(lua_type == LuaParam::BOOLEAN) {
return static_cast<bool>(lua_toboolean(L, idx));
} else if constexpr(lua_type == LuaParam::INT) {
if(lua_isinteger(L, idx))
return static_cast<actual_type>(lua_tointeger(L, idx));
return static_cast<actual_type>(static_cast<lua_Integer>(std::round(lua_tonumber(L, idx))));
} else if constexpr(lua_type == LuaParam::FUNCTION) {
return idx;
}
}
}
}

template<typename T, std::enable_if_t<is_optional_v<T>, int> = 0>
inline constexpr T get_lua([[maybe_unused]] lua_State* L, [[maybe_unused]] int idx) {
using namespace scriptlib;
using actual_type = typename T::value_type;
if(lua_isnoneornil(L, idx))
return std::nullopt;
if constexpr(std::is_same_v<actual_type, lua_obj*>) {
auto obj = lua_touserdata(L, idx);
if(!obj)
lua_error(L, "Error.");
auto* ret = *static_cast<lua_obj**>(obj);
if(ret->lua_type == LuaParam::DELETED)
lua_error(L, "Attempting to access deleted object.");
return std::make_optional<actual_type>(ret);
} else {
constexpr auto lua_type = get_lua_param_type<actual_type>();
if constexpr(lua_type == LuaParam::CARD || lua_type == LuaParam::GROUP || lua_type == LuaParam::EFFECT) {
lua_obj* ret = nullptr;
check_param<lua_type>(L, idx, &ret);
return std::make_optional<actual_type>(reinterpret_cast<actual_type>(ret));
} else {
check_param<lua_type>(L, idx);
if constexpr(lua_type == LuaParam::BOOLEAN) {
return std::make_optional<actual_type>(static_cast<bool>(lua_toboolean(L, idx)));
} else if constexpr(lua_type == LuaParam::INT) {
return std::make_optional<actual_type>(lua_isinteger(L, idx) ? static_cast<actual_type>(lua_tointeger(L, idx)) :
static_cast<actual_type>(static_cast<lua_Integer>(std::round(lua_tonumber(L, idx)))));
} else if constexpr(lua_type == LuaParam::FUNCTION) {
return std::make_optional<actual_type>(idx);
}
}
}
}

template<typename Sig>
struct get_function_arguments;

template<typename Ret, typename... Args>
struct get_function_arguments<Ret(*)(Args...)> {
using type = std::tuple<Args...>;
};
template<typename Sig>
using get_function_arguments_t = typename get_function_arguments<Sig>::type;

template<typename tuple, size_t offset, size_t... indices>
constexpr decltype(auto) parse_helper([[maybe_unused]] lua_State* L, std::index_sequence<indices...>) {
return std::make_tuple(get_lua<std::tuple_element_t<indices + offset, tuple>>(L, indices + 1)...);
}

template<typename tuple, size_t offset>
decltype(auto) parse_arguments_tuple(lua_State* L) {
return parse_helper<tuple, offset>(L, std::make_index_sequence<std::tuple_size_v<tuple> - offset>{});
}

} // namespace Detail
} // namespace

#define GET_LUA_FUNCTIONS_ARRAY() \
Detail::make_lua_functions_array<COUNTER_MACRO>()

#define LUA_STATIC_FUNCTION(name) LUA_STATIC_FUNCTION_INT(name, COUNTER_MACRO)
#if NEEDS_DUMMY

#define LUA_STATIC_FUNCTION(...) EXPAND(LUA_STATIC_FUNCTION_INT1(__VA_ARGS__, [[maybe_unused]] Detail::dummy))

#define LUA_STATIC_FUNCTION_INT1(name, ...) LUA_STATIC_FUNCTION_INT(name, COUNTER_MACRO, \
[[maybe_unused]] lua_State* const L, \
[[maybe_unused]] duel* const pduel, \
__VA_ARGS__)
#else

#define LUA_STATIC_FUNCTION(...) EXPAND(LUA_STATIC_FUNCTION_INT1(__VA_ARGS__))

#define LUA_STATIC_FUNCTION_INT(name, COUNTER) \
static LUA_INLINE int32_t MAKE_LUA_NAME(LUA_MODULE,name) \
(lua_State* const, duel* const); \
#define LUA_STATIC_FUNCTION_INT1(name, ...) LUA_STATIC_FUNCTION_INT(name, COUNTER_MACRO, \
[[maybe_unused]] lua_State* const L, \
[[maybe_unused]] duel* const pduel, \
## __VA_ARGS__)
#endif

#define LUA_STATIC_FUNCTION_INT(name, COUNTER, ...) \
static LUA_INLINE int32_t MAKE_LUA_NAME(LUA_MODULE,name)(__VA_ARGS__); \
template<> \
struct Detail::LuaFunction<COUNTER - Detail::COUNTER_OFFSET> { \
TAG_STRUCT(COUNTER) \
static int32_t call(lua_State* L) { \
return MAKE_LUA_NAME(LUA_MODULE,name)(L, lua_get<duel*>(L)); \
using function_arguments = get_function_arguments_t<decltype(MAKE_LUA_NAME(LUA_MODULE,name))*>; \
static constexpr auto extra_args = 2 + NEEDS_DUMMY; \
static constexpr int required_args = (static_cast<int>(std::tuple_size_v<function_arguments>) - count_trailing_optionals(function_arguments{})) - extra_args; \
if constexpr(required_args > 0) \
check_param_count(L, required_args); \
return std::apply(MAKE_LUA_NAME(LUA_MODULE,name), \
std::tuple_cat( \
std::make_tuple(L, lua_get<duel*>(L)), \
Detail::parse_arguments_tuple<function_arguments, 2>(L) \
) \
); \
} \
static constexpr luaL_Reg elem{#name, call}; \
}; \
static LUA_INLINE int32_t MAKE_LUA_NAME(LUA_MODULE,name) \
([[maybe_unused]] lua_State* const L, [[maybe_unused]] duel* const pduel)
static LUA_INLINE int32_t MAKE_LUA_NAME(LUA_MODULE,name)(__VA_ARGS__)

#if NEEDS_DUMMY

#define LUA_FUNCTION(...) EXPAND(LUA_FUNCTION_INT1(__VA_ARGS__, [[maybe_unused]] Detail::dummy))

#define LUA_FUNCTION_INT1(name, ...) LUA_FUNCTION_INT(name, COUNTER_MACRO, \
[[maybe_unused]] lua_State* const L, \
[[maybe_unused]] LUA_CLASS* const self, \
[[maybe_unused]] duel* const pduel, \
__VA_ARGS__)
#else

#define LUA_FUNCTION(...) EXPAND(LUA_FUNCTION_INT1(__VA_ARGS__))

#define LUA_FUNCTION_INT1(name, ...) LUA_FUNCTION_INT(name, COUNTER_MACRO, \
[[maybe_unused]] lua_State* const L, \
[[maybe_unused]] LUA_CLASS* const self, \
[[maybe_unused]] duel* const pduel, \
## __VA_ARGS__)

#endif

#define LUA_FUNCTION(name) LUA_FUNCTION_INT(name, COUNTER_MACRO)
#define LUA_FUNCTION_INT(name, COUNTER) \
static LUA_INLINE int32_t MAKE_LUA_NAME(LUA_MODULE,name) \
(lua_State* const, LUA_CLASS* const, duel* const); \
#define LUA_FUNCTION_INT(name, COUNTER, ...) \
static LUA_INLINE int32_t MAKE_LUA_NAME(LUA_MODULE,name)(__VA_ARGS__); \
template<> \
struct Detail::LuaFunction<COUNTER - Detail::COUNTER_OFFSET> { \
TAG_STRUCT(COUNTER) \
static int32_t call(lua_State* L) { \
return MAKE_LUA_NAME(LUA_MODULE,name)(L, lua_get<LUA_CLASS*, true>(L, 1), lua_get<duel*>(L)); \
using function_arguments = get_function_arguments_t<decltype(MAKE_LUA_NAME(LUA_MODULE,name))*>; \
static constexpr int extra_args = 3 + NEEDS_DUMMY; \
static constexpr int required_args = (static_cast<int>(std::tuple_size_v<function_arguments>) - count_trailing_optionals(function_arguments{})) - extra_args; \
if constexpr(required_args > 0) \
check_param_count(L, required_args); \
return std::apply(MAKE_LUA_NAME(LUA_MODULE,name), \
std::tuple_cat( \
std::make_tuple(L, lua_get<LUA_CLASS*, true>(L, 1), lua_get<duel*>(L)), \
Detail::parse_arguments_tuple<function_arguments, 3>(L) \
) \
); \
} \
static constexpr luaL_Reg elem{#name, call}; \
}; \
static LUA_INLINE int32_t MAKE_LUA_NAME(LUA_MODULE,name) \
([[maybe_unused]] lua_State* const L, \
[[maybe_unused]] LUA_CLASS* const self, [[maybe_unused]] duel* const pduel)
static LUA_INLINE int32_t MAKE_LUA_NAME(LUA_MODULE,name)(__VA_ARGS__)


#define LUA_FUNCTION_EXISTING(name,...) LUA_FUNCTION_EXISTING_INT(name, COUNTER_MACRO, __VA_ARGS__)
Expand All @@ -169,10 +364,10 @@ struct Detail::LuaFunction<COUNTER - Detail::COUNTER_OFFSET> { \
static constexpr luaL_Reg elem{#name,prev_element::elem.func}; \
}
#else
#define LUA_FUNCTION(name) static int32_t MAKE_LUA_NAME(LUA_MODULE,name) \
([[maybe_unused]] lua_State* const L, [[maybe_unused]] LUA_CLASS* const self, [[maybe_unused]] duel* const pduel)
#define LUA_STATIC_FUNCTION(name) static int32_t MAKE_LUA_NAME(LUA_MODULE,name) \
([[maybe_unused]] lua_State* const L, [[maybe_unused]] duel* const pduel)
#define LUA_FUNCTION(name, ...) static int32_t MAKE_LUA_NAME(LUA_MODULE,name) \
([[maybe_unused]] lua_State* const L, [[maybe_unused]] LUA_CLASS* const self, [[maybe_unused]] duel* const pduel, __VA_ARGS__)
#define LUA_STATIC_FUNCTION(name, ...) static int32_t MAKE_LUA_NAME(LUA_MODULE,name) \
([[maybe_unused]] lua_State* const L, [[maybe_unused]] duel* const pduel, __VA_ARGS__)
#define LUA_FUNCTION_EXISTING(name,...) struct MAKE_LUA_NAME(LUA_MODULE,name) {}
#define LUA_FUNCTION_ALIAS(name) struct MAKE_LUA_NAME(LUA_MODULE,name) {}
#define GET_LUA_FUNCTIONS_ARRAY() std::array{luaL_Reg{nullptr,nullptr}}
Expand Down
Loading

0 comments on commit 52071e0

Please sign in to comment.