/*
* Copyright (C) 2006-2025 by the Widelands Development Team
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see .
*
*/
/*
* This code was inspired from the lua_users wiki:
* http://lua-users.org/wiki/SimplerCppBinding
* and by some code written by TheBunny:
* http://plaidworld.com/
*
* A lot of new features (e.g. inheritance) were added for Widelands.
*/
#ifndef WL_SCRIPTING_LUNA_H
#define WL_SCRIPTING_LUNA_H
#define LUNA_CLASS_HEAD(klass) \
static const char className[]; \
static const MethodType Methods[]; \
static const PropertyType Properties[]
/*
* Macros for filling the description tables
*/
#define PROP_RO(klass, name) \
{ #name, &klass::get_##name, 0 }
#define PROP_RW(klass, name) \
{ #name, &klass::get_##name, &klass::set_##name }
#define METHOD(klass, name) \
{ #name, &klass::name }
/*
* Macros for helping with persistence and unpersistence
*/
#define PERS_TYPE(name, value, type) \
lua_push##type(L, value); \
lua_setfield(L, -2, name)
#define PERS_INT32(name, value) PERS_TYPE(name, value, int32)
#define PERS_UINT32(name, value) PERS_TYPE(name, value, uint32)
#define PERS_STRING(name, value) PERS_TYPE(name, value.c_str(), string)
#define UNPERS_TYPE(name, value, type) \
lua_getfield(L, lua_upvalueindex(1), name); \
value = luaL_check##type(L, -1); \
lua_pop(L, 1);
#define UNPERS_INT32(name, value) UNPERS_TYPE(name, value, int32)
#define UNPERS_UINT32(name, value) UNPERS_TYPE(name, value, uint32)
#define UNPERS_STRING(name, value) UNPERS_TYPE(name, value, string)
#include
#include "base/macros.h"
#include "scripting/lua.h"
#include "scripting/luna_impl.h"
/**
* Base Class. All Luna class must derive from this
*/
class LunaClass {
public:
virtual ~LunaClass() = default;
CLANG_DIAG_RESERVED_IDENTIFIER_OFF
// NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp)
virtual void __persist(lua_State*) = 0;
// NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp)
virtual void __unpersist(lua_State*) = 0;
CLANG_DIAG_RESERVED_IDENTIFIER_ON
virtual const char* get_modulename() = 0;
};
/**
* Register the class as a Lua class (that is a metatable and a function to
* create an object out of this table).
*
* The class will always be created in the namespace wl, sub_namespace can
* be used to create the class in one depth deeper (e.g. wl.map)
*/
template
void register_class(lua_State* const L,
char const* const sub_namespace = "",
bool return_metatable = false) {
int to_pop = 0;
// Get wl table which MUST already exist
lua_getglobal(L, "wl");
to_pop++;
// push the wl sub namespace table onto the stack, if desired
if (strlen(sub_namespace) != 0) {
lua_getfield(L, -1, sub_namespace);
to_pop++;
}
add_constructor_to_lua(L);
add_instantiator_to_lua(L);
lua_pop(L, to_pop); // Pop everything we used so far.
create_metatable_for_class(L);
register_properties_in_metatable(L);
register_methods_in_metatable(L);
if (!return_metatable) {
lua_pop(L, 1); // remove the Metatable
}
}
/**
* Makes the first class a children of the second. Make sure that T is really a
* child class of PT before calling this. This must also be called directly
* after register_class, so that the Metatable index is still valid
*/
template void add_parent(lua_State* L) {
register_properties_in_metatable(L);
register_methods_in_metatable(L);
}
/*
* Get the instance of this C object to lua. This is usually used
* as last call in a function that should create a new Lua object
*/
template int to_lua(lua_State* const L, T* const obj) {
// Create a new table with some slots preallocated
lua_createtable(L, 0, 30); // table
// get the index of the new table on the stack
int const newtable = lua_gettop(L);
// push index of position of user data in our array
lua_pushnumber(L, 0); // table 0
// Make a new userdata. A lightuserdata won't do since we want to assign
// a metatable to it
T** const a = static_cast(lua_newuserdata(L, sizeof(T*)));
*a = obj;
int const userdata = lua_gettop(L); // table 0 ud
// Assign this metatable to this userdata. We only do this to add
// garbage collection to this userdata, so that the destructor gets
// called. As a (unwanted, but not critical) side effect we also add all
// other methods to this object
luaL_getmetatable(L, T::className); // table 0 ud mt
lua_setmetatable(L, userdata); // table 0 ud
// table[ 0 ] = USERDATA;
lua_settable(L, newtable); // table
// Assign this metatable to the newly created table
luaL_getmetatable(L, T::className);
lua_setmetatable(L, newtable);
// table
return 1;
}
/*
* Our userdata is saved in table[0]. This function fetches it and makes sure
* that it is the correct userdata.
*/
template T** get_user_class(lua_State* const L, int narg) {
extract_userdata_from_user_class(L, narg);
T** rv = static_cast(luaL_checkudata(L, -1, T::className));
lua_pop(L, 1);
return rv;
}
/*
* This forces the pointer to be a base class. ONLY use this if you are sure
* that indeed the object is a base class, like you can be in __eq
*/
template T** get_base_user_class(lua_State* const L, int narg) {
extract_userdata_from_user_class(L, narg);
T** rv = static_cast(lua_touserdata(L, -1));
lua_pop(L, 1);
return rv;
}
#endif // end of include guard: WL_SCRIPTING_LUNA_H