The Battle for Wesnoth  1.19.0-dev
lua_cpp_function.hpp
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 
16 /**
17  * This namespace makes the possibility to push not just C style functions,
18  * but CPP style functions to lua, if they are cast as a std::function.
19  * Using this, for example, C++ method functions may be std::bind'ed and
20  * then pushed into the lua environment and called like usual.
21  *
22  * They are represented as user data with a call operator, which uses a
23  * dispatcher implemented as a C-style function to retrieve the boost
24  * function and execute it. Thus effectively all that we have to provide
25  * is a "value type" user data (full userdata, not light userdata) in lua
26  * which wraps the std::function type and implements a garbage collector.
27  *
28  *
29  * -- Why? --
30  *
31  * There are a few basic approaches to connect lua and C++, with various
32  * degrees of power. Lua, being a C library, has no concept of C++ specific
33  * types, even when compiled as C++. Lua has only two functions which
34  * introduce C functions to the scripting environment:
35  * lua_pushcfunction, and lua_pushcclosure. (The helper library only provides
36  * functions which use these.) These functions can only accept C-style function
37  * pointers of type int (lua_State*). Boost bind cannot be used to make a match
38  * to this type signature, nor can C++ method functions be used.
39  *
40  * In many cases C-style functions are sufficient, but if one ever wants to
41  * refer to an instance of a class or a member variable (which one does as most
42  * of our project is written in C++ object oriented style), it's not enough.
43  *
44  * The basic lua-provided approach to this is to pass objects as "userdata".
45  * Userdata comes in two flavors, "regular" and "light", for representing
46  * value and reference types respectively. Lightuserdata is meant to hold a
47  * pointer to an object -- full userdata is meant to hold an instance of an
48  * object which is either copied with memcpy or constructed using placement new.
49  * Proper memory management may require using lua's __gc metamethod to call a
50  * destructor. In the normal idiom, every class which is passed to lua this
51  * way should have C-function shim provided for each method which may be called
52  * by lua -- the object's "this" is retrieved from the userdata type on the
53  * stack, and used to call the appopriate method. A metatable is defined, which
54  * may be the same as the "lua module" placed in the global namespace which may
55  * also provide access to a constructor.
56  *
57  * This approach is often quite good for small objects. Wesnoth uses full userdata
58  * to represent "rng" objects used in lua random map generation, to represent lua
59  * "private units", to represent vconfigs and translatable strings. Wesnoth uses
60  * lightuserdata for many AI related objects.
61  *
62  * However it is not ideal for "large" objects like the game engine itself or its
63  * helpers. In this case full translation to userdata is out of the question --
64  * in case of lightuserdata, it is problematic because the lua api is not actually
65  * trying to directly represent a wesnoth engine object (which doesn't exist!)
66  * the wesnoth table in lua instead has a medley of callbacks implemented variously
67  * by grabbing from the gamemap, the unit map, the game_config namespace, etc. etc.
68  * for instance even the wesnoth.game_config table is not backed up by any one object,
69  * instead its callbacks may variously alter game_config namespace or the current tod
70  * manager object etc. etc.
71  *
72  * Circa 2012, the solution was to implement every callback function in the wesnoth
73  * table simply as a C function, which grabs whatever engine features it needs from
74  * a collection of pointers with external linkage in resources.hpp. The pointers
75  * must be reset by the play controller object whenever it is created or destroyed...
76  * or reset (replay controller), and always in a particular order because eventually
77  * all of the objects themselves are also grabbing these pointers, leading to segfaults
78  * if they are initialized in the wrong order, or left with danging pointers...
79  * in short it was very messy. While the organization of everything as pure C functions
80  * allows us to flexibly organize the wesnoth table using subtables holding functions
81  * as we like, (which wouldn't be possible if it were based on a lightuserdata), the
82  * requirement to base it all on externally-linked pointer variables comes at great cost.
83  * Further it means that we can never hope to have a correct deep-copy constructor of the
84  * gamestate, meaning that features like "replay moves since my last turn" or "AI based
85  * on exploratory simulations" are much more difficult to produce if not impossible.
86  *
87  * The lua cpp functions code permits us to refactor this by (1) passing all the engine
88  * resources needed by the lua environment, to the "lua kernel" at construction time,
89  * which holds them as private variables (2) declaring all callbacks which need these
90  * as private member functions (3) using boost bind to bind them to the lua kernel and
91  * this code to push the result into the scripting environment, at construction time
92  * of the lua kernel. Now there is no longer any question about dangling pointers in lua,
93  * or issues about making deep copies, since a lua state may be copied and the pointers
94  * in the kernel rebound, and the pointers in lua all die when the kernel is destroyed.
95  *
96  * More generally, this code makes it easy to assemble "mixins" for lua using the member
97  * functions of several different classes, if this is desired.
98  *
99  * The implementation details are also extremely simple -- whereas there are many popular
100  * lua -> C++ binding utilities like LuaPlus, luabind, etc. many of these only truly
101  * support automatic generation of bindings for *all* of the methods of an object, rely
102  * on the userdata approach, and require substantial template metaprogramming which will
103  * turn up in any associated stacktraces. This technique used here essentially delegates
104  * all of the templating work to boost, and is implemented in only a few hundred lines.
105  *
106  *
107  * -- Caveats: --
108  *
109  * Essentially, we provide C++ versions of the lua library calls 'lua_pushcfunction',
110  * 'lua_setfuncs', 'lua_pushcclosure'.
111  *
112  * - They are "C++" versions in that they take std::function<int (lua_State*)> rather
113  * than int(lua_State*).
114  * - While for lua, "lua_pushcfunction(L, f)" is essentially the same as
115  * "lua_pushcclosure(L, f, 0)", for the functions below that is not the case.
116  * lua_cpp::push_function generates a userdata "helper" object with a _call operator,
117  * not technically a function. lua_cpp::push_closure generates a true lua closure.
118  * Therefore push_closure is the most general and most compatible form -- push_function
119  * is slightly simpler and more efficient though.
120  * - Similarly lua_cpp::set_functions(L, l) differs from lua_cpp::set_functions(L,l,nups).
121  * - Closures created by lua_cpp::push_closure are not *exactly* the same as lua closures,
122  * in that the first upvalue is used by the implementation. A closure created with two
123  * upvalues will find them at upvalue indices 2 and 3, and should not touch upvalue 1.
124  */
125 
126 #pragma once
127 
128 #include <functional>
129 
130 #include <vector>
131 
132 struct lua_State;
133 
134 namespace lua_cpp {
135 
136 typedef std::function<int(lua_State*)> lua_function;
137 
138 typedef struct {
139  const char * name;
141 } Reg;
142 
143 void register_metatable ( lua_State* L );
144 
145 /**
146  * Pushes a std::function wrapper object onto the stack. It does
147  * not support up-values. If you need that use push_closure (a little slower).
148  *
149  * NOTE: This object has type userdata, not function. Its metatable has a call operator.
150  * If this is not sufficient for your needs then use push_closure.
151  */
152 void push_function( lua_State* L, const lua_function & f );
153 
154 /**
155  * Analogous to lua_setfuncs, it registers a collection of function wrapper
156  * objects into a table, using push_function.
157  *
158  * The note above applies.
159  */
160 void set_functions( lua_State* L, const std::vector<lua_cpp::Reg>& functions);
161 
162 /**
163  * Analogous to lua_setfuncs, it registers a collection of function wrapper
164  * objects into a table, using push_function.
165  *
166  * The note above applies.
167  */
168 template<int N>
169 void set_functions( lua_State* L, const lua_cpp::Reg(& functions)[N])
170 {
171  std::vector<lua_cpp::Reg> l;
172  l.reserve(N);
173  for(int i = 0; i < N; i++) {
174  l.push_back(functions[i]);
175  }
176  set_functions(L, l);
177 }
178 
179 /**
180  * Pushes a closure which retains a std::function object as its first up-value.
181  * Note that this is *NOT* strictly compatible with the lua c function push_closure --
182  * if you request additional upvalues they will be indexed starting at 2 rather than 1.
183  *
184  * Note that unlike push_function above this results in a function and not userdata
185  * being pushed on the stack.
186  */
187 void push_closure( lua_State* L, const lua_function & f, int nup);
188 
189 /**
190  * Analogous to lua_setfuncs and set_functions above, but pushes closures.
191  *
192  * NOTE: set_functions(L, l, 0) is NOT the same as set_functions(L, l), as
193  * the latter produces userdata and the former doesn't.
194  */
195 void set_functions( lua_State* L, const std::vector<lua_cpp::Reg>& functions, int nup);
196 
197 /**
198  * Analogous to lua_setfuncs and set_functions above, but pushes closures.
199  *
200  * NOTE: set_functions(L, l, 0) is NOT the same as set_functions(L, l), as
201  * the latter produces userdata and the former doesn't.
202  */
203 template<int N>
204 void set_functions( lua_State* L, const lua_cpp::Reg(& functions)[N], int nup)
205 {
206  std::vector<lua_cpp::Reg> l;
207  l.reserve(N);
208  for(int i = 0; i < N; i++) {
209  l.push_back(functions[i]);
210  }
211  set_functions(L, l, nup);
212 }
213 
214 } // end namespace lua_cpp_func
std::size_t i
Definition: function.cpp:968
void register_metatable(lua_State *L)
void push_function(lua_State *L, const lua_function &f)
Pushes a std::function wrapper object onto the stack.
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,...
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.
lua_function func
const char * name
#define f