The Battle for Wesnoth 1.21.0-dev
 
Loading...
Searching...
No Matches
lua_cpp_function.cpp
Go to the documentation of this file.
1/*
2 Copyright (C) 2014 - 2025
3 by Chris Beck <render787@gmail.com>
4 Part of the Battle for Wesnoth Project https://www.wesnoth.org/
5
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY.
12
13 See the COPYING file for more details.
14*/
15
17
18#include "log.hpp"
19
20#include <sstream>
21#include <string>
22
23#include "lua/wrapper_lauxlib.h"
24#include "scripting/lua_common.hpp" // for new(L)
25
26static lg::log_domain log_scripting_lua("scripting/lua");
27#define DBG_LUA LOG_STREAM(debug, log_scripting_lua)
28#define LOG_LUA LOG_STREAM(info, log_scripting_lua)
29#define WRN_LUA LOG_STREAM(warn, log_scripting_lua)
30#define ERR_LUA LOG_STREAM(err, log_scripting_lua)
31
32namespace lua_cpp {
33
34char const * cpp_function = "CPP_Function";
35
36static int intf_dispatcher ( lua_State* L )
37{
38 //make a temporary copy, in case lua_remove(L,1) might cause lua to garbage collect and destroy it
39 lua_function f = * static_cast<lua_function *> (luaL_checkudata(L, 1, cpp_function));
40 // remove from the stack before executing, so that like all other callbacks, f finds only its intended arguments on the stack.
41 lua_remove(L,1);
42 int result = (f)(L);
43 return result;
44}
45
46static int intf_cleanup ( lua_State* L )
47{
48 lua_function * d = static_cast< lua_function *> (luaL_testudata(L, 1, cpp_function));
49 if (d == nullptr) {
50 ERR_LUA << "lua_cpp::intf_cleanup called on data of type: " << lua_typename( L, lua_type( L, 1 ) );
51 ERR_LUA << "This may indicate a memory leak, please report at bugs.wesnoth.org";
52 lua_pushstring(L, "C++ function object garbage collection failure");
53 lua_error(L);
54 } else {
55 d->~lua_function();
56 }
57 return 0;
58}
59
60static int intf_tostring( lua_State* L )
61{
62 luaL_checkudata(L, 1, cpp_function);
63 lua_pushstring(L, " function: C++");
64 return 1;
65}
66
67static int intf_tostring2(lua_State* L)
68{
69 lua_Debug info;
70 lua_getinfo(L, ">S", &info);
71 std::stringstream result;
72 result << "function: " << info.what;
73 lua_pushstring(L, result.str().c_str());
74 return 1;
75}
76
77static int intf_thread_tostring(lua_State* L)
78{
79 luaL_checktype(L, 1, LUA_TTHREAD);
80 luaW_getglobal(L, "coroutine", "status");
81 lua_pushvalue(L, 1);
82 lua_call(L, 1, 1);
83 lua_pushfstring(L, "thread: %s", luaL_checkstring(L, -1));
84 return 1;
85}
86
87static int intf_light_userdata_tostring(lua_State* L)
88{
89 luaL_checktype(L, 1, LUA_TLIGHTUSERDATA);
90 lua_pushstring(L, "userdata: light");
91 return 1;
92}
93
94void register_metatable ( lua_State* L )
95{
96 luaL_newmetatable(L, cpp_function);
97 lua_pushcfunction(L, intf_dispatcher);
98 lua_setfield(L, -2, "__call");
99 lua_pushcfunction(L, intf_cleanup);
100 lua_setfield(L, -2, "__gc");
101 lua_pushcfunction(L, intf_tostring);
102 lua_setfield(L, -2, "__tostring");
103 lua_pushstring(L, "function");
104 lua_setfield(L, -2, "__metatable");
105 lua_pushvalue(L, -1); //make a copy of this table, set it to be its own __index table
106 lua_setfield(L, -2, "__index");
107
108 lua_pop(L, 1);
109
110 // Also set the metatable for regular functions
111 lua_pushcfunction(L, intf_tostring2);
112 lua_createtable(L, 0, 1);
113 lua_pushvalue(L, -2);
114 lua_setfield(L, -2, "__tostring");
115 lua_pushstring(L, "function");
116 lua_setfield(L, -2, "__metatable");
117 lua_setmetatable(L, -2);
118
119 lua_newthread(L);
120 lua_createtable(L, 0, 1);
121 lua_pushcfunction(L, intf_thread_tostring);
122 lua_setfield(L, -2, "__tostring");
123 lua_pushstring(L, "thread");
124 lua_setfield(L, -2, "__metatable");
125 lua_setmetatable(L, -2);
126
127 lua_pop(L, 1);
128
129 lua_pushlightuserdata(L, nullptr);
130 lua_createtable(L, 0, 1);
131 lua_pushcfunction(L, intf_light_userdata_tostring);
132 lua_setfield(L, -2, "__tostring");
133 lua_pushstring(L, "userdata: light");
134 lua_setfield(L, -2, "__metatable");
135 lua_setmetatable(L, -2);
136
137 lua_pop(L, 1);
138}
139
140void push_function( lua_State* L, const lua_function & f )
141{
142 new(L) lua_function(f);
143 luaL_setmetatable(L, cpp_function);
144}
145
146void set_functions( lua_State* L, const std::vector<lua_cpp::Reg>& functions)
147{
148 luaL_checkversion(L);
149 for (const lua_cpp::Reg& l : functions) { /* fill the table with given functions */
150 if (l.name != nullptr) {
151 push_function(L, l.func);
152 lua_setfield(L, -2, l.name);
153 }
154 }
155}
156
157static int intf_closure_dispatcher( lua_State* L )
158{
159 lua_function * f = static_cast< lua_function *> (luaL_checkudata(L, lua_upvalueindex(1), cpp_function)); //assume the std::function is the first upvalue
160 return (*f)(L);
161}
162
163void push_closure( lua_State* L, const lua_function & f, int nup)
164{
165 push_function(L, f);
166 lua_insert(L, -(1+nup)); //move the function beneath the upvalues
167 lua_pushcclosure(L, &intf_closure_dispatcher, 1+nup);
168}
169
170void set_functions( lua_State* L, const std::vector<lua_cpp::Reg>& functions, int nup )
171{
172 luaL_checkversion(L);
173 luaL_checkstack(L, nup+1, "too many upvalues");
174 for (const lua_cpp::Reg& l : functions) { /* fill the table with given functions */
175 if (l.name == nullptr) {
176 continue;
177 }
178 int i;
179 for (i = 0; i < nup; ++i) /* copy upvalues to the top */
180 lua_pushvalue(L, -nup);
181 push_closure(L, l.func, nup); /* closure with those upvalues */
182 lua_setfield(L, -(nup + 2), l.name);
183 }
184 lua_pop(L, nup); /* remove upvalues */
185}
186
187} // end namespace lua_cpp
#define ERR_LUA
Standard logging facilities (interface).
bool luaW_getglobal(lua_State *L, const std::vector< std::string > &path)
Pushes the value found by following the variadic names (char *), if the value is not nil.
static lg::log_domain log_scripting_lua("scripting/lua")
static int intf_cleanup(lua_State *L)
void register_metatable(lua_State *L)
static int intf_dispatcher(lua_State *L)
void push_function(lua_State *L, const lua_function &f)
Pushes a std::function wrapper object onto the stack.
static int intf_tostring2(lua_State *L)
static int intf_closure_dispatcher(lua_State *L)
void set_functions(lua_State *L, const std::vector< lua_cpp::Reg > &functions)
Analogous to lua_setfuncs, it registers a collection of function wrapper objects into a table,...
static int intf_tostring(lua_State *L)
static int intf_light_userdata_tostring(lua_State *L)
char const * cpp_function
static int intf_thread_tostring(lua_State *L)
std::function< int(lua_State *)> lua_function
void push_closure(lua_State *L, const lua_function &f, int nup)
Pushes a closure which retains a std::function object as its first up-value.
#define i
#define d
#define f