1 // sol2
2 
3 // The MIT License (MIT)
4 
5 // Copyright (c) 2013-2018 Rapptz, ThePhD and contributors
6 
7 // Permission is hereby granted, free of charge, to any person obtaining a copy of
8 // this software and associated documentation files (the "Software"), to deal in
9 // the Software without restriction, including without limitation the rights to
10 // use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
11 // the Software, and to permit persons to whom the Software is furnished to do so,
12 // subject to the following conditions:
13 
14 // The above copyright notice and this permission notice shall be included in all
15 // copies or substantial portions of the Software.
16 
17 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
19 // FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
20 // COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
21 // IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
22 // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23 
24 #ifndef SOL_CONTAINER_USERTYPE_METATABLE_HPP
25 #define SOL_CONTAINER_USERTYPE_METATABLE_HPP
26 
27 #include "stack.hpp"
28 #include "container_traits.hpp"
29 #include <unordered_map>
30 
31 namespace sol {
32 
33 	template <typename X>
34 	struct container_usertype_metatable {
35 		typedef std::remove_pointer_t<meta::unqualified_t<X>> T;
36 		typedef container_traits<T> traits;
37 		typedef container_detail::container_traits_default<T> default_traits;
38 
real_index_get_traitssol::container_usertype_metatable39 		static int real_index_get_traits(std::true_type, lua_State* L) {
40 			return traits::index_get(L);
41 		}
42 
real_index_get_traitssol::container_usertype_metatable43 		static int real_index_get_traits(std::false_type, lua_State* L) {
44 			return default_traits::index_get(L);
45 		}
46 
real_index_callsol::container_usertype_metatable47 		static int real_index_call(lua_State* L) {
48 			typedef usertype_detail::map_t<std::string, lua_CFunction> call_map;
49 			static const call_map calls{
50 				{ "at", &at_call },
51 				{ "get", &real_get_call },
52 				{ "set", &real_set_call },
53 				{ "size", &real_length_call },
54 				{ "add", &real_add_call },
55 				{ "empty", &real_empty_call },
56 				{ "insert", &real_insert_call },
57 				{ "clear", &real_clear_call },
58 				{ "find", &real_find_call },
59 				{ "erase", &real_erase_call },
60 				{ "pairs", &pairs_call },
61 				{ "next", &next_call },
62 			};
63 			auto maybenameview = stack::unqualified_check_get<string_view>(L, 2);
64 			if (maybenameview) {
65 				const string_view& nameview = *maybenameview;
66 #if defined(SOL_UNORDERED_MAP_COMPATIBLE_HASH) && SOL_UNORDERED_MAP_COMPATIBLE_HASH
67 				auto it = calls.find(nameview, string_view_hash(), std::equal_to<string_view>());
68 #else
69 				std::string name(nameview.data(), nameview.size());
70 				auto it = calls.find(name);
71 #endif
72 				if (it != calls.cend()) {
73 					return stack::push(L, it->second);
74 				}
75 			}
76 			return real_index_get_traits(container_detail::has_traits_index_get<traits>(), L);
77 		}
78 
real_at_traitssol::container_usertype_metatable79 		static int real_at_traits(std::true_type, lua_State* L) {
80 			return traits::at(L);
81 		}
82 
real_at_traitssol::container_usertype_metatable83 		static int real_at_traits(std::false_type, lua_State* L) {
84 			return default_traits::at(L);
85 		}
86 
real_at_callsol::container_usertype_metatable87 		static int real_at_call(lua_State* L) {
88 			return real_at_traits(container_detail::has_traits_at<traits>(), L);
89 		}
90 
real_get_traitssol::container_usertype_metatable91 		static int real_get_traits(std::true_type, lua_State* L) {
92 			return traits::get(L);
93 		}
94 
real_get_traitssol::container_usertype_metatable95 		static int real_get_traits(std::false_type, lua_State* L) {
96 			return default_traits::get(L);
97 		}
98 
real_get_callsol::container_usertype_metatable99 		static int real_get_call(lua_State* L) {
100 			return real_get_traits(container_detail::has_traits_get<traits>(), L);
101 		}
102 
real_set_traitssol::container_usertype_metatable103 		static int real_set_traits(std::true_type, lua_State* L) {
104 			return traits::set(L);
105 		}
106 
real_set_traitssol::container_usertype_metatable107 		static int real_set_traits(std::false_type, lua_State* L) {
108 			return default_traits::set(L);
109 		}
110 
real_set_callsol::container_usertype_metatable111 		static int real_set_call(lua_State* L) {
112 			return real_set_traits(container_detail::has_traits_set<traits>(), L);
113 		}
114 
real_index_set_traitssol::container_usertype_metatable115 		static int real_index_set_traits(std::true_type, lua_State* L) {
116 			return traits::index_set(L);
117 		}
118 
real_index_set_traitssol::container_usertype_metatable119 		static int real_index_set_traits(std::false_type, lua_State* L) {
120 			return default_traits::index_set(L);
121 		}
122 
real_new_index_callsol::container_usertype_metatable123 		static int real_new_index_call(lua_State* L) {
124 			return real_index_set_traits(container_detail::has_traits_index_set<traits>(), L);
125 		}
126 
real_pairs_traitssol::container_usertype_metatable127 		static int real_pairs_traits(std::true_type, lua_State* L) {
128 			return traits::pairs(L);
129 		}
130 
real_pairs_traitssol::container_usertype_metatable131 		static int real_pairs_traits(std::false_type, lua_State* L) {
132 			return default_traits::pairs(L);
133 		}
134 
real_pairs_callsol::container_usertype_metatable135 		static int real_pairs_call(lua_State* L) {
136 			return real_pairs_traits(container_detail::has_traits_pairs<traits>(), L);
137 		}
138 
real_ipairs_traitssol::container_usertype_metatable139 		static int real_ipairs_traits(std::true_type, lua_State* L) {
140 			return traits::ipairs(L);
141 		}
142 
real_ipairs_traitssol::container_usertype_metatable143 		static int real_ipairs_traits(std::false_type, lua_State* L) {
144 			return default_traits::ipairs(L);
145 		}
146 
real_ipairs_callsol::container_usertype_metatable147 		static int real_ipairs_call(lua_State* L) {
148 			return real_ipairs_traits(container_detail::has_traits_ipairs<traits>(), L);
149 		}
150 
real_next_traitssol::container_usertype_metatable151 		static int real_next_traits(std::true_type, lua_State* L) {
152 			return traits::next(L);
153 		}
154 
real_next_traitssol::container_usertype_metatable155 		static int real_next_traits(std::false_type, lua_State* L) {
156 			return default_traits::next(L);
157 		}
158 
real_next_callsol::container_usertype_metatable159 		static int real_next_call(lua_State* L) {
160 			return real_next_traits(container_detail::has_traits_next<traits>(), L);
161 		}
162 
real_size_traitssol::container_usertype_metatable163 		static int real_size_traits(std::true_type, lua_State* L) {
164 			return traits::size(L);
165 		}
166 
real_size_traitssol::container_usertype_metatable167 		static int real_size_traits(std::false_type, lua_State* L) {
168 			return default_traits::size(L);
169 		}
170 
real_length_callsol::container_usertype_metatable171 		static int real_length_call(lua_State* L) {
172 			return real_size_traits(container_detail::has_traits_size<traits>(), L);
173 		}
174 
real_add_traitssol::container_usertype_metatable175 		static int real_add_traits(std::true_type, lua_State* L) {
176 			return traits::add(L);
177 		}
178 
real_add_traitssol::container_usertype_metatable179 		static int real_add_traits(std::false_type, lua_State* L) {
180 			return default_traits::add(L);
181 		}
182 
real_add_callsol::container_usertype_metatable183 		static int real_add_call(lua_State* L) {
184 			return real_add_traits(container_detail::has_traits_add<traits>(), L);
185 		}
186 
real_insert_traitssol::container_usertype_metatable187 		static int real_insert_traits(std::true_type, lua_State* L) {
188 			return traits::insert(L);
189 		}
190 
real_insert_traitssol::container_usertype_metatable191 		static int real_insert_traits(std::false_type, lua_State* L) {
192 			return default_traits::insert(L);
193 		}
194 
real_insert_callsol::container_usertype_metatable195 		static int real_insert_call(lua_State* L) {
196 			return real_insert_traits(container_detail::has_traits_insert<traits>(), L);
197 		}
198 
real_clear_traitssol::container_usertype_metatable199 		static int real_clear_traits(std::true_type, lua_State* L) {
200 			return traits::clear(L);
201 		}
202 
real_clear_traitssol::container_usertype_metatable203 		static int real_clear_traits(std::false_type, lua_State* L) {
204 			return default_traits::clear(L);
205 		}
206 
real_clear_callsol::container_usertype_metatable207 		static int real_clear_call(lua_State* L) {
208 			return real_clear_traits(container_detail::has_traits_clear<traits>(), L);
209 		}
210 
real_empty_traitssol::container_usertype_metatable211 		static int real_empty_traits(std::true_type, lua_State* L) {
212 			return traits::empty(L);
213 		}
214 
real_empty_traitssol::container_usertype_metatable215 		static int real_empty_traits(std::false_type, lua_State* L) {
216 			return default_traits::empty(L);
217 		}
218 
real_empty_callsol::container_usertype_metatable219 		static int real_empty_call(lua_State* L) {
220 			return real_empty_traits(container_detail::has_traits_empty<traits>(), L);
221 		}
222 
real_erase_traitssol::container_usertype_metatable223 		static int real_erase_traits(std::true_type, lua_State* L) {
224 			return traits::erase(L);
225 		}
226 
real_erase_traitssol::container_usertype_metatable227 		static int real_erase_traits(std::false_type, lua_State* L) {
228 			return default_traits::erase(L);
229 		}
230 
real_erase_callsol::container_usertype_metatable231 		static int real_erase_call(lua_State* L) {
232 			return real_erase_traits(container_detail::has_traits_erase<traits>(), L);
233 		}
234 
real_find_traitssol::container_usertype_metatable235 		static int real_find_traits(std::true_type, lua_State* L) {
236 			return traits::find(L);
237 		}
238 
real_find_traitssol::container_usertype_metatable239 		static int real_find_traits(std::false_type, lua_State* L) {
240 			return default_traits::find(L);
241 		}
242 
real_find_callsol::container_usertype_metatable243 		static int real_find_call(lua_State* L) {
244 			return real_find_traits(container_detail::has_traits_find<traits>(), L);
245 		}
246 
add_callsol::container_usertype_metatable247 		static int add_call(lua_State* L) {
248 			return detail::typed_static_trampoline<decltype(&real_add_call), (&real_add_call)>(L);
249 		}
250 
erase_callsol::container_usertype_metatable251 		static int erase_call(lua_State* L) {
252 			return detail::typed_static_trampoline<decltype(&real_erase_call), (&real_erase_call)>(L);
253 		}
254 
insert_callsol::container_usertype_metatable255 		static int insert_call(lua_State* L) {
256 			return detail::typed_static_trampoline<decltype(&real_insert_call), (&real_insert_call)>(L);
257 		}
258 
clear_callsol::container_usertype_metatable259 		static int clear_call(lua_State* L) {
260 			return detail::typed_static_trampoline<decltype(&real_clear_call), (&real_clear_call)>(L);
261 		}
262 
empty_callsol::container_usertype_metatable263 		static int empty_call(lua_State* L) {
264 			return detail::typed_static_trampoline<decltype(&real_empty_call), (&real_empty_call)>(L);
265 		}
266 
find_callsol::container_usertype_metatable267 		static int find_call(lua_State* L) {
268 			return detail::typed_static_trampoline<decltype(&real_find_call), (&real_find_call)>(L);
269 		}
270 
length_callsol::container_usertype_metatable271 		static int length_call(lua_State* L) {
272 			return detail::typed_static_trampoline<decltype(&real_length_call), (&real_length_call)>(L);
273 		}
274 
pairs_callsol::container_usertype_metatable275 		static int pairs_call(lua_State* L) {
276 			return detail::typed_static_trampoline<decltype(&real_pairs_call), (&real_pairs_call)>(L);
277 		}
278 
ipairs_callsol::container_usertype_metatable279 		static int ipairs_call(lua_State* L) {
280 			return detail::typed_static_trampoline<decltype(&real_ipairs_call), (&real_ipairs_call)>(L);
281 		}
282 
next_callsol::container_usertype_metatable283 		static int next_call(lua_State* L) {
284 			return detail::typed_static_trampoline<decltype(&real_next_call), (&real_next_call)>(L);
285 		}
286 
at_callsol::container_usertype_metatable287 		static int at_call(lua_State* L) {
288 			return detail::typed_static_trampoline<decltype(&real_at_call), (&real_at_call)>(L);
289 		}
290 
get_callsol::container_usertype_metatable291 		static int get_call(lua_State* L) {
292 			return detail::typed_static_trampoline<decltype(&real_get_call), (&real_get_call)>(L);
293 		}
294 
set_callsol::container_usertype_metatable295 		static int set_call(lua_State* L) {
296 			return detail::typed_static_trampoline<decltype(&real_set_call), (&real_set_call)>(L);
297 		}
298 
index_callsol::container_usertype_metatable299 		static int index_call(lua_State* L) {
300 			return detail::typed_static_trampoline<decltype(&real_index_call), (&real_index_call)>(L);
301 		}
302 
new_index_callsol::container_usertype_metatable303 		static int new_index_call(lua_State* L) {
304 			return detail::typed_static_trampoline<decltype(&real_new_index_call), (&real_new_index_call)>(L);
305 		}
306 	};
307 
308 	namespace stack {
309 		namespace stack_detail {
310 			template <typename T, bool is_shim = false>
311 			struct metatable_setup {
312 				lua_State* L;
313 
metatable_setupsol::stack::stack_detail::metatable_setup314 				metatable_setup(lua_State* L)
315 				: L(L) {
316 				}
317 
operator ()sol::stack::stack_detail::metatable_setup318 				void operator()() {
319 					typedef container_usertype_metatable<std::conditional_t<is_shim,
320 						as_container_t<std::remove_pointer_t<T>>,
321 						std::remove_pointer_t<T>>>
322 						meta_cumt;
323 					static const char* metakey = is_shim ? &usertype_traits<as_container_t<std::remove_pointer_t<T>>>::metatable()[0] : &usertype_traits<T>::metatable()[0];
324 					static const std::array<luaL_Reg, 19> reg = { {
325 						{ "__pairs", &meta_cumt::pairs_call },
326 						{ "__ipairs", &meta_cumt::ipairs_call },
327 						{ "__len", &meta_cumt::length_call },
328 						{ "__index", &meta_cumt::index_call },
329 						{ "__newindex", &meta_cumt::new_index_call },
330 						{ "pairs", &meta_cumt::pairs_call },
331 						{ "next", &meta_cumt::next_call },
332 						{ "at", &meta_cumt::at_call },
333 						{ "get", &meta_cumt::get_call },
334 						{ "set", &meta_cumt::set_call },
335 						{ "size", &meta_cumt::length_call },
336 						{ "empty", &meta_cumt::empty_call },
337 						{ "clear", &meta_cumt::clear_call },
338 						{ "insert", &meta_cumt::insert_call },
339 						{ "add", &meta_cumt::add_call },
340 						{ "find", &meta_cumt::find_call },
341 						{ "erase", &meta_cumt::erase_call },
342 						std::is_pointer<T>::value ? luaL_Reg{ nullptr, nullptr } : luaL_Reg{ "__gc", &detail::usertype_alloc_destruct<T> },
343 						{ nullptr, nullptr }
344 					} };
345 
346 					if (luaL_newmetatable(L, metakey) == 1) {
347 						luaL_setfuncs(L, reg.data(), 0);
348 					}
349 					lua_setmetatable(L, -2);
350 				}
351 			};
352 		} // namespace stack_detail
353 
354 		template <typename T>
355 		struct pusher<as_container_t<T>> {
356 			typedef meta::unqualified_t<T> C;
357 
push_lvaluesol::stack::pusher358 			static int push_lvalue(std::true_type, lua_State* L, const C& cont) {
359 				stack_detail::metatable_setup<C*, true> fx(L);
360 				return pusher<detail::as_pointer_tag<const C>>{}.push_fx(L, fx, detail::ptr(cont));
361 			}
362 
push_lvaluesol::stack::pusher363 			static int push_lvalue(std::false_type, lua_State* L, const C& cont) {
364 				stack_detail::metatable_setup<C, true> fx(L);
365 				return pusher<detail::as_value_tag<C>>{}.push_fx(L, fx, cont);
366 			}
367 
push_rvaluesol::stack::pusher368 			static int push_rvalue(std::true_type, lua_State* L, C&& cont) {
369 				stack_detail::metatable_setup<C, true> fx(L);
370 				return pusher<detail::as_value_tag<C>>{}.push_fx(L, fx, std::move(cont));
371 			}
372 
push_rvaluesol::stack::pusher373 			static int push_rvalue(std::false_type, lua_State* L, const C& cont) {
374 				return push_lvalue(std::is_lvalue_reference<T>(), L, cont);
375 			}
376 
pushsol::stack::pusher377 			static int push(lua_State* L, const as_container_t<T>& as_cont) {
378 				return push_lvalue(std::is_lvalue_reference<T>(), L, as_cont.source);
379 			}
380 
pushsol::stack::pusher381 			static int push(lua_State* L, as_container_t<T>&& as_cont) {
382 				return push_rvalue(meta::all<std::is_rvalue_reference<T>, meta::neg<std::is_lvalue_reference<T>>>(), L, std::forward<T>(as_cont.source));
383 			}
384 		};
385 
386 		template <typename T>
387 		struct pusher<as_container_t<T*>> {
388 			typedef std::add_pointer_t<meta::unqualified_t<std::remove_pointer_t<T>>> C;
389 
pushsol::stack::pusher390 			static int push(lua_State* L, T* cont) {
391 				stack_detail::metatable_setup<C> fx(L);
392 				return pusher<detail::as_pointer_tag<T>>{}.push_fx(L, fx, cont);
393 			}
394 		};
395 
396 		template <typename T>
397 		struct pusher<T, std::enable_if_t<meta::all<is_container<meta::unqualified_t<T>>, meta::neg<is_lua_reference<meta::unqualified_t<T>>>>::value>> {
398 			typedef meta::unqualified_t<T> C;
399 
pushsol::stack::pusher400 			static int push(lua_State* L, const T& cont) {
401 				stack_detail::metatable_setup<C> fx(L);
402 				return pusher<detail::as_value_tag<T>>{}.push_fx(L, fx, cont);
403 			}
404 
pushsol::stack::pusher405 			static int push(lua_State* L, T&& cont) {
406 				stack_detail::metatable_setup<C> fx(L);
407 				return pusher<detail::as_value_tag<T>>{}.push_fx(L, fx, std::move(cont));
408 			}
409 		};
410 
411 		template <typename T>
412 		struct pusher<T*, std::enable_if_t<meta::all<is_container<meta::unqualified_t<T>>, meta::neg<is_lua_reference<meta::unqualified_t<T>>>>::value>> {
413 			typedef std::add_pointer_t<meta::unqualified_t<std::remove_pointer_t<T>>> C;
414 
pushsol::stack::pusher415 			static int push(lua_State* L, T* cont) {
416 				stack_detail::metatable_setup<C> fx(L);
417 				return pusher<detail::as_pointer_tag<T>>{}.push_fx(L, fx, cont);
418 			}
419 		};
420 
421 		template <typename T, typename C>
422 		struct checker<as_container_t<T>, type::userdata, C> {
423 			template <typename Handler>
checksol::stack::checker424 			static bool check(lua_State* L, int index, Handler&& handler, record& tracking) {
425 				return stack::check<T>(L, index, std::forward<Handler>(handler), tracking);
426 			}
427 		};
428 
429 		template <typename T>
430 		struct getter<as_container_t<T>> {
getsol::stack::getter431 			static decltype(auto) get(lua_State* L, int index, record& tracking) {
432 				return stack::unqualified_get<T>(L, index, tracking);
433 			}
434 		};
435 
436 		template <typename T>
437 		struct getter<as_container_t<T>*> {
getsol::stack::getter438 			static decltype(auto) get(lua_State* L, int index, record& tracking) {
439 				return stack::unqualified_get<T*>(L, index, tracking);
440 			}
441 		};
442 	} // namespace stack
443 
444 } // namespace sol
445 
446 #endif // SOL_CONTAINER_USERTYPE_METATABLE_HPP
447