Files
Goober5000andClaude Fable 5.1 37b0b6ea82 Standardize isValid across the Lua API by making the types auto-generate it (#7761)
* Automatically generate the isValid() Lua API function for handle types

If an ADE object's stored type has an isValid() member, ade_obj now registers an isValid function for it automatically.  An explicit ADE_FUNC(isValid, ...) declared after the ADE_OBJ replaces the generated one, so types with special semantics (e.g. sound) keep their own.  The 67 hand-written functions that merely delegated to the member are removed, and 26 types that previously exposed no isValid() gain one.

Co-Authored-By: Claude Fable 5.1 <[email protected]>

* Add ADE_OBJ_VALIDATOR for index-based Lua handles

Int-index handles (shipclass, weaponclass, team, etc.) can now declare their validity predicate with a single ADE_OBJ_VALIDATOR or ADE_OBJ_VALIDATOR_RANGE line after the ADE_OBJ.  The registered validator drives the generated isValid() function and is also honored by ade_set_args when the mod table asks for nil instead of invalid objects, which previously never applied to these handles.  The 19 hand-written range-check functions are replaced, and loadout_ship, default_primary, default_secondary, and support_rearm_pool_team gain isValid().  Add a validator to HudGauge while we're at it.

Also fully qualify the scripting namespace in LuaConvert.h so its templates can be instantiated from the unit tests.

Co-Authored-By: Claude Fable 5.1 <[email protected]>

---------

Co-authored-by: Claude Fable 5.1 <[email protected]>
2026-09-08 10:50:53 -04:00

150 lines
4.2 KiB
C++

#ifndef LUAUTIL_H
#define LUAUTIL_H
#pragma once
#include "LuaHeaders.h"
#include "LuaException.h"
#include "LuaConvert.h"
#include "LuaReference.h"
#include "scripting/ade.h"
#include <cstdlib>
namespace luacpp {
/**
* @brief Contains functions to convert C++ values to and from lua values.
*
* Currently the following types are supported:
* - `double`
* - `float`
* - `int`
* - `std::string`
* - `const char*` (only for pushing as using the pointer after it was removed from the stack is dangerous)
* - `bool`
* - `lua_CFunction`
* - `LuaTable` (only pop, use LuaValue version for pushing)
* - `LuaFunction` (only pop, use LuaValue version for pushing)
* - `LuaValue` (this will reference any value at the specified poition)
*/
namespace convert {
namespace internal {
bool isValidIndex(lua_State* state, int index);
bool ade_odata_helper(lua_State* L, int stackposition, size_t idx);
}
bool ade_odata_is_userdata_type(lua_State* L, int stackposition, size_t typeIdx, bool cleanup = true);
template<typename T>
bool ade_odata_is_userdata_type(lua_State* L, int stackposition, const T& obj_type) {
return ade_odata_is_userdata_type(L, stackposition, obj_type.GetIdx());
}
void pushValue(lua_State* luaState, const double& value);
void pushValue(lua_State* luaState, const float& value);
void pushValue(lua_State* luaState, const int& value);
void pushValue(lua_State* luaState, const size_t& value);
void pushValue(lua_State* luaState, const std::string& value);
void pushValue(lua_State* luaState, const char* value);
void pushValue(lua_State* luaState, const bool& value);
void pushValue(lua_State* luaState, const lua_CFunction& value);
template<typename T>
void pushValue(lua_State* L, ::scripting::ade_odata_setter<T>&& value) {
using namespace ::scripting;
//WMC - char must be 1 byte, foo.
static_assert(sizeof(char) == 1, "char must be 1 byte!");
//WMC - step by step
//Create new LUA object and get handle
auto newod = (char*)lua_newuserdata(L, sizeof(T));
//Create or get object metatable
luaL_getmetatable(L, ::scripting::internal::getTableEntry(value.idx).Name);
//Set the metatable for the object
lua_setmetatable(L, -2);
//Copy the actual object data to the Lua object
new(newod) T(std::move(value.value));
}
/**
* @brief Convenience function for string literals
*
* @param luaState The lua_State to push the values to
* @param value The value which should be pushed.
* @return void
*/
template<size_t N>
inline void pushValue(lua_State* luaState, const char(& value)[N]) {
pushValue(luaState, (const char*)value);
}
bool popValue(lua_State* luaState, float& target, int stackposition = -1, bool remove = true);
bool popValue(lua_State* luaState, double& target, int stackposition = -1, bool remove = true);
bool popValue(lua_State* luaState, int& target, int stackposition = -1, bool remove = true);
bool popValue(lua_State* luaState, size_t& target, int stackposition = -1, bool remove = true);
bool popValue(lua_State* luaState, std::string& target, int stackposition = -1, bool remove = true);
bool popValue(lua_State* luaState, bool& target, int stackposition = -1, bool remove = true);
bool popValue(lua_State* luaState, lua_CFunction& target, int stackposition = -1, bool remove = true);
template <typename T>
bool popValue(lua_State* L, ::scripting::ade_odata_getter<T>&& od, int stackposition = -1, bool remove = true)
{
// Use the helper to reduce the amount of code here
if (!internal::ade_odata_helper(L, stackposition, od.idx)) {
return false;
}
auto lua_ptr = lua_touserdata(L, stackposition);
// Copy the value over by using the standard copy constructor
*od.value_ptr = *reinterpret_cast<T*>(lua_ptr);
if (remove) {
lua_remove(L, stackposition);
}
return true;
}
template <typename T>
bool popValue(lua_State* L, ::scripting::ade_odata_ptr_getter<T>&& od, int stackposition = -1, bool remove = true)
{
// Use the helper to reduce the amount of code here
if (!internal::ade_odata_helper(L, stackposition, od.idx)) {
return false;
}
auto lua_ptr = lua_touserdata(L, stackposition);
// Only write the pointer value to the output pointer
*od.value_ptr = reinterpret_cast<T*>(lua_ptr);
if (remove) {
lua_remove(L, stackposition);
}
return true;
}
}
}
#endif