-
Notifications
You must be signed in to change notification settings - Fork 1
/
lundi.hpp
247 lines (205 loc) · 7.38 KB
/
lundi.hpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
#pragma once
#include <vector>
#include <functional>
#include <exception>
#include <stdexcept>
#include <string>
#include <utility>
#include <boost/fusion/container/vector.hpp>
#include <boost/fusion/view/reverse_view.hpp>
#include <boost/fusion/algorithm/iteration/for_each.hpp>
#include <boost/fusion/functional/invocation/invoke.hpp>
#include <boost/type_traits/is_base_of.hpp>
// this is required for BOOST_NOEXCEPT and MVSC compatibility
#include <boost/config/suffix.hpp>
#include <istream>
#include <lua.hpp>
#include "lundi/variant.hpp"
#include "lundi/proxy.hpp"
#include "lundi/make_function.hpp"
#include "lundi/version.hpp"
namespace lua {
class exception : public virtual std::exception {
public:
exception(const std::string& s) : s(s) {}
exception(std::string&& s) : s(std::move(s)) {}
char const* what() const BOOST_NOEXCEPT override { return s.data(); }
private:
std::string s;
};
namespace detail {
class function_wrapper {
public:
virtual int operator()(lua_State *state) = 0;
virtual ~function_wrapper() {};
};
struct fetch_parameter {
lua_State *state;
fetch_parameter(lua_State *s) : state(s) {}
void operator()(std::string& t) const {
t = lua_tostring(state, -1);
lua_pop(state, 1);
}
void operator()(int& t) const {
t = lua_tonumber(state, -1);
lua_pop(state, 1);
}
};
template<typename Ret, typename... Args>
class function_wrapper_impl : public function_wrapper {
public:
typedef std::function<Ret(Args...)> FuncType;
template<typename FRet, typename... FArgs>
struct function_invoker {
int operator()(FuncType& func, boost::fusion::vector<FArgs...>& params, lua_State* state) {
variant result = invoke(func, params);
boost::apply_visitor(detail::push_variant(state), result);
return 1;
}
};
template<typename... FArgs>
struct function_invoker<void, FArgs...> {
int operator()(FuncType& func, boost::fusion::vector<FArgs...>& params, lua_State* dummy) {
// void-return means we are not pushing anything back to the stack
invoke(func, params);
// and the number of arguments returned is 0
return 0;
}
};
//TODO: tuple returns
function_wrapper_impl(FuncType const &f) : func_(f) {}
int operator()(lua_State *state) {
boost::fusion::vector<Args...> params;
boost::fusion::reverse_view<decltype(params)> r_params(params);
for_each(r_params, fetch_parameter(state));
return function_invoker<Ret, Args...>()(func_, params, state);
}
private:
FuncType func_;
};
template<typename Ret, typename... Args>
function_wrapper *make_wrapper(std::function<Ret(Args...)> const &function) {
return new function_wrapper_impl<typename variant_friendly<Ret>::type, Args...>(function);
}
inline
int dispatch_to_wrapper(lua_State *state) {
void *light_ud = lua_touserdata(state, lua_upvalueindex(1));
function_wrapper *wrapper = reinterpret_cast<function_wrapper *>(light_ud);
return (*wrapper)(state);
}
template<typename StreamT>
static const char *read_stream(StreamT &stream, std::vector<char> &buffer, size_t &size) {
stream.read(&buffer[0], buffer.size());
size = stream.gcount();
return &buffer[0];
}
template<typename StreamT>
static const char *stream_reader(lua_State *L, void *data, size_t *size) {
auto &info = *reinterpret_cast<std::pair<std::reference_wrapper<StreamT>, std::vector<char>>*>(data);
return read_stream<StreamT>(info.first, info.second, *size);
}
} // detail
template<typename StreamT>
char const *stream_name(StreamT &stream) {
return "<stream>";
}
class state {
lua_State *state_;
std::function<void(std::string const&)> error_func_;
variant peek(int index) {
switch(lua_type(state_, index)) {
case LUA_TNUMBER:
return lua_tonumber(state_, index);
case LUA_TBOOLEAN:
return static_cast<bool>(lua_toboolean(state_, index));
case LUA_TSTRING:
return lua_tostring(state_, index);
// TODO : function?
}
return nil;
}
variant pop() {
variant value = peek(-1);
lua_pop(state_, 1);
return value;
}
// handles error values returned by various C API function
// It isn't intended to be called directly!
void protect (int err) {
if (err != 0)
{
// check if any error message is present
std::string error_msg;
if (!lua_isstring(state_, -1)) {
error_msg = "<no error message>";
}
else {
error_msg = std::string(lua_tostring(state_, -1));
lua_pop(state_, -1); // remove error message
}
if (error_func_)
error_func_(error_msg);
}
}
variant call_r(int nargs) {
protect(lua_pcall(state_, nargs, 1, 0));
return pop();
}
template<typename T, typename... Args>
variant call_r(int nargs, T t, Args... args) {
variant value = t;
boost::apply_visitor(detail::push_variant(state_), value);
return call_r(nargs + 1, args...);
}
void register_wrapper(std::string const &name, detail::function_wrapper *wrapper) {
lua_pushlightuserdata(state_, wrapper);
lua_pushcclosure(state_, detail::dispatch_to_wrapper, 1);
lua_setglobal(state_, name.c_str());
}
std::vector<detail::function_wrapper *> wrappers;
public:
template<typename Functor>
state(Functor&& error_func)
: state_(luaL_newstate())
, error_func_(std::forward<Functor>(error_func)) {
// loads basic, table, I/O, string and math libraries
luaL_openlibs(state_);
}
void set_global(std::string const &name, variant value) {
boost::apply_visitor(detail::push_variant(state_), value);
lua_setglobal(state_, name.c_str());
}
// This overload is used to fix "broken" bool overload with literals
// It might be needed to set up a proper bool type from scratch.
template <std::size_t N>
void set_global(std::string const& name, char const(&value)[N]) { set_global(name, std::string(value)); }
variant get_global(std::string const &name) {
lua_getglobal(state_, name.c_str());
return pop();
}
proxy<variant, state> operator[](std::string name) {
return proxy<variant, state>(std::move(name), *this);
}
template<typename StreamT>
typename boost::enable_if<boost::is_base_of<std::istream, StreamT>, void>::type eval(StreamT &stream) {
using namespace detail;
auto reader_info = std::make_pair(std::ref(stream), std::vector<char>(4096));
protect(lua_load(state_, &stream_reader<StreamT>, &reader_info, stream_name(stream)));
protect(lua_pcall(state_, 0, LUA_MULTRET, 0));
}
void eval(std::string const &program) {
protect(luaL_dostring(state_, program.c_str()));
}
template<typename... Args>
variant call(std::string const &name, Args... args) {
lua_getglobal(state_, name.c_str());
return call_r(0, args...);
}
template<typename FuncType>
void register_function(std::string const &name, FuncType const &func) {
auto wrapper = detail::make_wrapper(make_function(func));
register_wrapper(name, wrapper);
wrappers.push_back(wrapper);
}
};
} // lua