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