The Battle for Wesnoth  1.19.7+dev
lua_cpp_function.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2014 - 2024
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  lua_function * d = static_cast< lua_function *> (luaL_checkudata(L, 1, cpp_function));
63  // d is not null, if it was null then checkudata raised a lua error and a longjump was executed.
64  std::stringstream result;
65  result << "c++ function: " << std::hex << d;
66  lua_pushstring(L, result.str().c_str());
67  return 1;
68 }
69 
70 void register_metatable ( lua_State* L )
71 {
72  luaL_newmetatable(L, cpp_function);
73  lua_pushcfunction(L, intf_dispatcher);
74  lua_setfield(L, -2, "__call");
75  lua_pushcfunction(L, intf_cleanup);
76  lua_setfield(L, -2, "__gc");
77  lua_pushcfunction(L, intf_tostring);
78  lua_setfield(L, -2, "__tostring");
79  lua_pushstring(L, "function");
80  lua_setfield(L, -2, "__metatable");
81  lua_pushvalue(L, -1); //make a copy of this table, set it to be its own __index table
82  lua_setfield(L, -2, "__index");
83 
84  lua_pop(L, 1);
85 }
86 
87 void push_function( lua_State* L, const lua_function & f )
88 {
89  new(L) lua_function(f);
90  luaL_setmetatable(L, cpp_function);
91 }
92 
93 void set_functions( lua_State* L, const std::vector<lua_cpp::Reg>& functions)
94 {
95  luaL_checkversion(L);
96  for (const lua_cpp::Reg& l : functions) { /* fill the table with given functions */
97  if (l.name != nullptr) {
98  push_function(L, l.func);
99  lua_setfield(L, -2, l.name);
100  }
101  }
102 }
103 
104 static int intf_closure_dispatcher( lua_State* L )
105 {
106  lua_function * f = static_cast< lua_function *> (luaL_checkudata(L, lua_upvalueindex(1), cpp_function)); //assume the std::function is the first upvalue
107  return (*f)(L);
108 }
109 
110 void push_closure( lua_State* L, const lua_function & f, int nup)
111 {
112  push_function(L, f);
113  lua_insert(L, -(1+nup)); //move the function beneath the upvalues
114  lua_pushcclosure(L, &intf_closure_dispatcher, 1+nup);
115 }
116 
117 void set_functions( lua_State* L, const std::vector<lua_cpp::Reg>& functions, int nup )
118 {
119  luaL_checkversion(L);
120  luaL_checkstack(L, nup+1, "too many upvalues");
121  for (const lua_cpp::Reg& l : functions) { /* fill the table with given functions */
122  if (l.name == nullptr) {
123  continue;
124  }
125  int i;
126  for (i = 0; i < nup; ++i) /* copy upvalues to the top */
127  lua_pushvalue(L, -nup);
128  push_closure(L, l.func, nup); /* closure with those upvalues */
129  lua_setfield(L, -(nup + 2), l.name);
130  }
131  lua_pop(L, nup); /* remove upvalues */
132 }
133 
134 } // end namespace lua_cpp
std::size_t i
Definition: function.cpp:1029
Standard logging facilities (interface).
#define ERR_LUA
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_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