The Battle for Wesnoth  1.19.27+dev
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 
26 static 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 
32 namespace lua_cpp {
33 
34 char const * cpp_function = "CPP_Function";
35 
36 static 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 
46 static 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 
60 static 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 
67 static 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 
77 static 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 
87 static 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 
94 void 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 
140 void push_function( lua_State* L, const lua_function & f )
141 {
142  new(L) lua_function(f);
143  luaL_setmetatable(L, cpp_function);
144 }
145 
146 void 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 
157 static 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 
163 void 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 
170 void 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
std::size_t i
Definition: function.cpp:1031
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.
#define ERR_LUA
static lg::log_domain log_scripting_lua("scripting/lua")
logger & info()
Definition: log.cpp:351
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 d
#define f