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 void register_metatable ( lua_State* L )
78 {
79  luaL_newmetatable(L, cpp_function);
80  lua_pushcfunction(L, intf_dispatcher);
81  lua_setfield(L, -2, "__call");
82  lua_pushcfunction(L, intf_cleanup);
83  lua_setfield(L, -2, "__gc");
84  lua_pushcfunction(L, intf_tostring);
85  lua_setfield(L, -2, "__tostring");
86  lua_pushstring(L, "function");
87  lua_setfield(L, -2, "__metatable");
88  lua_pushvalue(L, -1); //make a copy of this table, set it to be its own __index table
89  lua_setfield(L, -2, "__index");
90 
91  lua_pop(L, 1);
92 
93  // Also set the metatable for regular functions
94  lua_pushcfunction(L, intf_tostring2);
95  lua_createtable(L, 0, 1);
96  lua_pushvalue(L, -2);
97  lua_setfield(L, -2, "__tostring");
98  lua_pushstring(L, "function");
99  lua_setfield(L, -2, "__metatable");
100  lua_setmetatable(L, -2);
101 }
102 
103 void push_function( lua_State* L, const lua_function & f )
104 {
105  new(L) lua_function(f);
106  luaL_setmetatable(L, cpp_function);
107 }
108 
109 void set_functions( lua_State* L, const std::vector<lua_cpp::Reg>& functions)
110 {
111  luaL_checkversion(L);
112  for (const lua_cpp::Reg& l : functions) { /* fill the table with given functions */
113  if (l.name != nullptr) {
114  push_function(L, l.func);
115  lua_setfield(L, -2, l.name);
116  }
117  }
118 }
119 
120 static int intf_closure_dispatcher( lua_State* L )
121 {
122  lua_function * f = static_cast< lua_function *> (luaL_checkudata(L, lua_upvalueindex(1), cpp_function)); //assume the std::function is the first upvalue
123  return (*f)(L);
124 }
125 
126 void push_closure( lua_State* L, const lua_function & f, int nup)
127 {
128  push_function(L, f);
129  lua_insert(L, -(1+nup)); //move the function beneath the upvalues
130  lua_pushcclosure(L, &intf_closure_dispatcher, 1+nup);
131 }
132 
133 void set_functions( lua_State* L, const std::vector<lua_cpp::Reg>& functions, int nup )
134 {
135  luaL_checkversion(L);
136  luaL_checkstack(L, nup+1, "too many upvalues");
137  for (const lua_cpp::Reg& l : functions) { /* fill the table with given functions */
138  if (l.name == nullptr) {
139  continue;
140  }
141  int i;
142  for (i = 0; i < nup; ++i) /* copy upvalues to the top */
143  lua_pushvalue(L, -nup);
144  push_closure(L, l.func, nup); /* closure with those upvalues */
145  lua_setfield(L, -(nup + 2), l.name);
146  }
147  lua_pop(L, nup); /* remove upvalues */
148 }
149 
150 } // end namespace lua_cpp
std::size_t i
Definition: function.cpp:1031
Standard logging facilities (interface).
#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)
char const * cpp_function
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