2020-07-15 21:29:27 +08:00
|
|
|
// sol3
|
2018-02-20 10:15:26 +08:00
|
|
|
|
2017-09-13 14:46:56 +08:00
|
|
|
// The MIT License (MIT)
|
2013-11-25 17:56:27 +08:00
|
|
|
|
2020-08-13 22:42:37 +08:00
|
|
|
// Copyright (c) 2013-2020 Rapptz, ThePhD and contributors
|
2013-11-25 17:56:27 +08:00
|
|
|
|
|
|
|
// Permission is hereby granted, free of charge, to any person obtaining a copy of
|
|
|
|
// this software and associated documentation files (the "Software"), to deal in
|
|
|
|
// the Software without restriction, including without limitation the rights to
|
|
|
|
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
|
|
|
// the Software, and to permit persons to whom the Software is furnished to do so,
|
|
|
|
// subject to the following conditions:
|
|
|
|
|
|
|
|
// The above copyright notice and this permission notice shall be included in all
|
|
|
|
// copies or substantial portions of the Software.
|
|
|
|
|
|
|
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
|
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
|
|
|
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
|
|
|
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
|
|
|
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
|
|
|
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
|
|
|
|
|
|
#ifndef SOL_STACK_HPP
|
|
|
|
#define SOL_STACK_HPP
|
|
|
|
|
2020-08-10 05:17:06 +08:00
|
|
|
#include <sol/trampoline.hpp>
|
|
|
|
#include <sol/stack_core.hpp>
|
|
|
|
#include <sol/stack_reference.hpp>
|
|
|
|
#include <sol/stack_check.hpp>
|
|
|
|
#include <sol/stack_get.hpp>
|
|
|
|
#include <sol/stack_check_get.hpp>
|
|
|
|
#include <sol/stack_push.hpp>
|
|
|
|
#include <sol/stack_pop.hpp>
|
|
|
|
#include <sol/stack_field.hpp>
|
|
|
|
#include <sol/stack_probe.hpp>
|
2018-12-20 12:17:15 +08:00
|
|
|
|
2013-12-10 06:01:52 +08:00
|
|
|
#include <cstring>
|
2016-03-25 03:45:44 +08:00
|
|
|
#include <array>
|
2013-11-25 17:56:27 +08:00
|
|
|
|
2016-06-20 05:59:40 +08:00
|
|
|
namespace sol {
|
2017-08-06 07:20:28 +08:00
|
|
|
namespace detail {
|
2020-08-10 05:17:06 +08:00
|
|
|
using typical_chunk_name_t = char[SOL_ID_SIZE_I_];
|
|
|
|
using typical_file_chunk_name_t = char[SOL_FILE_ID_SIZE_I_];
|
2017-09-07 03:09:51 +08:00
|
|
|
|
2017-08-06 07:20:28 +08:00
|
|
|
inline const std::string& default_chunk_name() {
|
2017-08-07 14:27:08 +08:00
|
|
|
static const std::string name = "";
|
2017-08-06 07:20:28 +08:00
|
|
|
return name;
|
|
|
|
}
|
2017-08-07 14:07:21 +08:00
|
|
|
|
|
|
|
template <std::size_t N>
|
|
|
|
const char* make_chunk_name(const string_view& code, const std::string& chunkname, char (&basechunkname)[N]) {
|
|
|
|
if (chunkname.empty()) {
|
|
|
|
auto it = code.cbegin();
|
|
|
|
auto e = code.cend();
|
|
|
|
std::size_t i = 0;
|
2017-08-07 19:54:43 +08:00
|
|
|
static const std::size_t n = N - 4;
|
2017-08-07 14:07:21 +08:00
|
|
|
for (i = 0; i < n && it != e; ++i, ++it) {
|
|
|
|
basechunkname[i] = *it;
|
|
|
|
}
|
2017-08-07 19:54:43 +08:00
|
|
|
if (it != e) {
|
|
|
|
for (std::size_t c = 0; c < 3; ++i, ++c) {
|
2017-08-07 19:59:13 +08:00
|
|
|
basechunkname[i] = '.';
|
2017-08-07 19:54:43 +08:00
|
|
|
}
|
|
|
|
}
|
2017-08-07 14:07:21 +08:00
|
|
|
basechunkname[i] = '\0';
|
|
|
|
return &basechunkname[0];
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
return chunkname.c_str();
|
|
|
|
}
|
|
|
|
}
|
2019-01-05 18:48:51 +08:00
|
|
|
|
|
|
|
inline void clear_entries(stack_reference r) {
|
|
|
|
stack::push(r.lua_state(), lua_nil);
|
|
|
|
while (lua_next(r.lua_state(), -2)) {
|
|
|
|
absolute_index key(r.lua_state(), -2);
|
|
|
|
auto pn = stack::pop_n(r.lua_state(), 1);
|
|
|
|
stack::set_field<false, true>(r.lua_state(), key, lua_nil, r.stack_index());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
inline void clear_entries(const reference& registry_reference) {
|
|
|
|
auto pp = stack::push_pop(registry_reference);
|
|
|
|
stack_reference ref(registry_reference.lua_state(), -1);
|
|
|
|
clear_entries(ref);
|
|
|
|
}
|
2017-09-13 14:46:56 +08:00
|
|
|
} // namespace detail
|
2017-08-06 07:20:28 +08:00
|
|
|
|
2016-06-20 05:59:40 +08:00
|
|
|
namespace stack {
|
|
|
|
namespace stack_detail {
|
2017-09-13 14:46:56 +08:00
|
|
|
template <typename T>
|
2016-06-20 05:59:40 +08:00
|
|
|
inline int push_as_upvalues(lua_State* L, T& item) {
|
|
|
|
typedef std::decay_t<T> TValue;
|
2017-12-20 17:58:32 +08:00
|
|
|
static const std::size_t itemsize = sizeof(TValue);
|
|
|
|
static const std::size_t voidsize = sizeof(void*);
|
|
|
|
static const std::size_t voidsizem1 = voidsize - 1;
|
|
|
|
static const std::size_t data_t_count = (sizeof(TValue) + voidsizem1) / voidsize;
|
2016-06-20 05:59:40 +08:00
|
|
|
typedef std::array<void*, data_t_count> data_t;
|
|
|
|
|
2020-07-15 21:29:27 +08:00
|
|
|
data_t data { {} };
|
2016-06-20 05:59:40 +08:00
|
|
|
std::memcpy(&data[0], std::addressof(item), itemsize);
|
|
|
|
int pushcount = 0;
|
|
|
|
for (auto&& v : data) {
|
|
|
|
pushcount += push(L, lightuserdata_value(v));
|
|
|
|
}
|
|
|
|
return pushcount;
|
2016-06-11 09:04:48 +08:00
|
|
|
}
|
|
|
|
|
2017-09-13 14:46:56 +08:00
|
|
|
template <typename T>
|
2017-05-10 01:24:56 +08:00
|
|
|
inline std::pair<T, int> get_as_upvalues(lua_State* L, int index = 2) {
|
2017-12-20 17:58:32 +08:00
|
|
|
static const std::size_t data_t_count = (sizeof(T) + (sizeof(void*) - 1)) / sizeof(void*);
|
2016-06-20 05:59:40 +08:00
|
|
|
typedef std::array<void*, data_t_count> data_t;
|
2020-07-15 21:29:27 +08:00
|
|
|
data_t voiddata { {} };
|
2016-06-20 05:59:40 +08:00
|
|
|
for (std::size_t i = 0, d = 0; d < sizeof(T); ++i, d += sizeof(void*)) {
|
2016-07-08 04:52:39 +08:00
|
|
|
voiddata[i] = get<lightuserdata_value>(L, upvalue_index(index++));
|
2016-06-20 05:59:40 +08:00
|
|
|
}
|
|
|
|
return std::pair<T, int>(*reinterpret_cast<T*>(static_cast<void*>(voiddata.data())), index);
|
2016-06-11 09:04:48 +08:00
|
|
|
}
|
|
|
|
|
2019-01-14 10:46:53 +08:00
|
|
|
template <typename Fx, typename... Args>
|
|
|
|
static decltype(auto) eval(types<>, std::index_sequence<>, lua_State*, int, record&, Fx&& fx, Args&&... args) {
|
|
|
|
return std::forward<Fx>(fx)(std::forward<Args>(args)...);
|
|
|
|
}
|
2017-09-13 14:46:56 +08:00
|
|
|
|
2019-01-14 10:46:53 +08:00
|
|
|
template <typename Fx, typename Arg, typename... Args, std::size_t I, std::size_t... Is, typename... FxArgs>
|
2020-07-15 21:29:27 +08:00
|
|
|
static decltype(auto) eval(
|
|
|
|
types<Arg, Args...>, std::index_sequence<I, Is...>, lua_State* L, int start, record& tracking, Fx&& fx, FxArgs&&... fxargs) {
|
|
|
|
return eval(types<Args...>(),
|
|
|
|
std::index_sequence<Is...>(),
|
|
|
|
L,
|
|
|
|
start,
|
|
|
|
tracking,
|
|
|
|
std::forward<Fx>(fx),
|
|
|
|
std::forward<FxArgs>(fxargs)...,
|
|
|
|
stack_detail::unchecked_get<Arg>(L, start + tracking.used, tracking));
|
2019-01-14 10:46:53 +08:00
|
|
|
}
|
2016-07-29 12:57:47 +08:00
|
|
|
|
2020-07-15 21:29:27 +08:00
|
|
|
template <bool checkargs = detail::default_safe_function_calls, std::size_t... I, typename R, typename... Args, typename Fx, typename... FxArgs>
|
2016-06-20 05:59:40 +08:00
|
|
|
inline decltype(auto) call(types<R>, types<Args...> ta, std::index_sequence<I...> tai, lua_State* L, int start, Fx&& fx, FxArgs&&... args) {
|
2020-07-15 21:29:27 +08:00
|
|
|
static_assert(meta::all<meta::is_not_move_only<Args>...>::value,
|
|
|
|
"One of the arguments being bound is a move-only type, and it is not being taken by reference: this will break your code. Please take "
|
|
|
|
"a reference and std::move it manually if this was your intention.");
|
2019-03-10 23:09:52 +08:00
|
|
|
if constexpr (checkargs) {
|
2020-07-15 21:29:27 +08:00
|
|
|
argument_handler<types<R, Args...>> handler {};
|
2019-03-10 23:09:52 +08:00
|
|
|
multi_check<Args...>(L, start, handler);
|
|
|
|
}
|
2020-07-15 21:29:27 +08:00
|
|
|
record tracking {};
|
2019-02-11 04:02:40 +08:00
|
|
|
if constexpr (std::is_void_v<R>) {
|
|
|
|
eval(ta, tai, L, start, tracking, std::forward<Fx>(fx), std::forward<FxArgs>(args)...);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
return eval(ta, tai, L, start, tracking, std::forward<Fx>(fx), std::forward<FxArgs>(args)...);
|
|
|
|
}
|
2016-06-20 05:59:40 +08:00
|
|
|
}
|
2017-09-13 14:46:56 +08:00
|
|
|
} // namespace stack_detail
|
2016-06-20 05:59:40 +08:00
|
|
|
|
2016-06-20 07:13:01 +08:00
|
|
|
template <typename T>
|
|
|
|
int set_ref(lua_State* L, T&& arg, int tableindex = -2) {
|
|
|
|
push(L, std::forward<T>(arg));
|
|
|
|
return luaL_ref(L, tableindex);
|
2016-06-20 05:59:40 +08:00
|
|
|
}
|
|
|
|
|
2019-02-11 04:02:40 +08:00
|
|
|
template <bool check_args = detail::default_safe_function_calls, typename R, typename... Args, typename Fx, typename... FxArgs>
|
2016-06-20 05:59:40 +08:00
|
|
|
inline decltype(auto) call(types<R> tr, types<Args...> ta, lua_State* L, int start, Fx&& fx, FxArgs&&... args) {
|
2019-02-11 04:02:40 +08:00
|
|
|
using args_indices = std::make_index_sequence<sizeof...(Args)>;
|
|
|
|
if constexpr (std::is_void_v<R>) {
|
|
|
|
stack_detail::call<check_args>(tr, ta, args_indices(), L, start, std::forward<Fx>(fx), std::forward<FxArgs>(args)...);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
return stack_detail::call<check_args>(tr, ta, args_indices(), L, start, std::forward<Fx>(fx), std::forward<FxArgs>(args)...);
|
|
|
|
}
|
2016-06-20 05:59:40 +08:00
|
|
|
}
|
|
|
|
|
2019-02-11 04:02:40 +08:00
|
|
|
template <bool check_args = detail::default_safe_function_calls, typename R, typename... Args, typename Fx, typename... FxArgs>
|
2016-06-20 05:59:40 +08:00
|
|
|
inline decltype(auto) call(types<R> tr, types<Args...> ta, lua_State* L, Fx&& fx, FxArgs&&... args) {
|
2020-07-15 21:29:27 +08:00
|
|
|
if constexpr (std::is_void_v<R>) {
|
2019-02-11 04:02:40 +08:00
|
|
|
call<check_args>(tr, ta, L, 1, std::forward<Fx>(fx), std::forward<FxArgs>(args)...);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
return call<check_args>(tr, ta, L, 1, std::forward<Fx>(fx), std::forward<FxArgs>(args)...);
|
|
|
|
}
|
2016-06-20 05:59:40 +08:00
|
|
|
}
|
|
|
|
|
2019-02-11 04:02:40 +08:00
|
|
|
template <bool check_args = detail::default_safe_function_calls, typename R, typename... Args, typename Fx, typename... FxArgs>
|
2016-06-20 05:59:40 +08:00
|
|
|
inline decltype(auto) call_from_top(types<R> tr, types<Args...> ta, lua_State* L, Fx&& fx, FxArgs&&... args) {
|
2019-02-11 04:02:40 +08:00
|
|
|
using expected_count_t = meta::count_for_pack<lua_size, Args...>;
|
|
|
|
if constexpr (std::is_void_v<R>) {
|
|
|
|
call<check_args>(tr,
|
|
|
|
ta,
|
|
|
|
L,
|
2019-02-11 18:50:35 +08:00
|
|
|
(std::max)(static_cast<int>(lua_gettop(L) - expected_count_t::value), static_cast<int>(0)),
|
2019-02-11 04:02:40 +08:00
|
|
|
std::forward<Fx>(fx),
|
|
|
|
std::forward<FxArgs>(args)...);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
return call<check_args>(tr,
|
|
|
|
ta,
|
|
|
|
L,
|
|
|
|
(std::max)(static_cast<int>(lua_gettop(L) - expected_count_t::value), static_cast<int>(0)),
|
|
|
|
std::forward<Fx>(fx),
|
|
|
|
std::forward<FxArgs>(args)...);
|
2017-08-22 03:25:43 +08:00
|
|
|
}
|
2016-06-11 09:04:48 +08:00
|
|
|
}
|
|
|
|
|
2020-07-15 21:29:27 +08:00
|
|
|
template <bool check_args = detail::default_safe_function_calls, bool clean_stack = true, typename Ret0, typename... Ret, typename... Args,
|
|
|
|
typename Fx, typename... FxArgs>
|
2019-02-11 04:02:40 +08:00
|
|
|
inline int call_into_lua(types<Ret0, Ret...> tr, types<Args...> ta, lua_State* L, int start, Fx&& fx, FxArgs&&... fxargs) {
|
|
|
|
if constexpr (std::is_void_v<Ret0>) {
|
|
|
|
call<check_args>(tr, ta, L, start, std::forward<Fx>(fx), std::forward<FxArgs>(fxargs)...);
|
|
|
|
if constexpr (clean_stack) {
|
|
|
|
lua_settop(L, 0);
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
(void)tr;
|
2020-07-15 21:29:27 +08:00
|
|
|
decltype(auto) r
|
|
|
|
= call<check_args>(types<meta::return_type_t<Ret0, Ret...>>(), ta, L, start, std::forward<Fx>(fx), std::forward<FxArgs>(fxargs)...);
|
2019-02-11 04:02:40 +08:00
|
|
|
using R = meta::unqualified_t<decltype(r)>;
|
|
|
|
using is_stack = meta::any<is_stack_based<R>, std::is_same<R, absolute_index>, std::is_same<R, ref_index>, std::is_same<R, raw_index>>;
|
|
|
|
if constexpr (clean_stack && !is_stack::value) {
|
|
|
|
lua_settop(L, 0);
|
|
|
|
}
|
|
|
|
return push_reference(L, std::forward<decltype(r)>(r));
|
2017-08-22 03:25:43 +08:00
|
|
|
}
|
2016-06-11 09:04:48 +08:00
|
|
|
}
|
2016-06-20 05:59:40 +08:00
|
|
|
|
2017-12-08 10:46:43 +08:00
|
|
|
template <bool check_args = detail::default_safe_function_calls, bool clean_stack = true, typename Fx, typename... FxArgs>
|
2016-06-20 05:59:40 +08:00
|
|
|
inline int call_lua(lua_State* L, int start, Fx&& fx, FxArgs&&... fxargs) {
|
2019-02-11 04:02:40 +08:00
|
|
|
using traits_type = lua_bind_traits<meta::unqualified_t<Fx>>;
|
|
|
|
using args_list = typename traits_type::args_list;
|
|
|
|
using returns_list = typename traits_type::returns_list;
|
2017-08-22 03:25:43 +08:00
|
|
|
return call_into_lua<check_args, clean_stack>(returns_list(), args_list(), L, start, std::forward<Fx>(fx), std::forward<FxArgs>(fxargs)...);
|
2016-06-11 09:04:48 +08:00
|
|
|
}
|
2016-06-20 05:59:40 +08:00
|
|
|
|
2018-01-29 11:21:13 +08:00
|
|
|
inline call_syntax get_call_syntax(lua_State* L, const string_view& key, int index) {
|
2018-12-20 12:17:15 +08:00
|
|
|
if (lua_gettop(L) < 1) {
|
2016-11-16 11:45:34 +08:00
|
|
|
return call_syntax::dot;
|
|
|
|
}
|
2018-01-29 11:21:13 +08:00
|
|
|
luaL_getmetatable(L, key.data());
|
2016-07-29 01:33:08 +08:00
|
|
|
auto pn = pop_n(L, 1);
|
2016-11-16 11:45:34 +08:00
|
|
|
if (lua_compare(L, -1, index, LUA_OPEQ) != 1) {
|
|
|
|
return call_syntax::dot;
|
2016-06-20 05:59:40 +08:00
|
|
|
}
|
2016-11-16 11:45:34 +08:00
|
|
|
return call_syntax::colon;
|
2016-06-11 09:04:48 +08:00
|
|
|
}
|
|
|
|
|
2020-07-15 21:29:27 +08:00
|
|
|
inline void script(
|
|
|
|
lua_State* L, lua_Reader reader, void* data, const std::string& chunkname = detail::default_chunk_name(), load_mode mode = load_mode::any) {
|
2018-08-10 23:17:31 +08:00
|
|
|
detail::typical_chunk_name_t basechunkname = {};
|
|
|
|
const char* chunknametarget = detail::make_chunk_name("lua_Reader", chunkname, basechunkname);
|
|
|
|
if (lua_load(L, reader, data, chunknametarget, to_string(mode).c_str()) || lua_pcall(L, 0, LUA_MULTRET, 0)) {
|
|
|
|
lua_error(L);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-07-15 21:29:27 +08:00
|
|
|
inline void script(
|
|
|
|
lua_State* L, const string_view& code, const std::string& chunkname = detail::default_chunk_name(), load_mode mode = load_mode::any) {
|
|
|
|
|
2017-09-07 03:09:51 +08:00
|
|
|
detail::typical_chunk_name_t basechunkname = {};
|
2017-08-07 14:07:21 +08:00
|
|
|
const char* chunknametarget = detail::make_chunk_name(code, chunkname, basechunkname);
|
|
|
|
if (luaL_loadbufferx(L, code.data(), code.size(), chunknametarget, to_string(mode).c_str()) || lua_pcall(L, 0, LUA_MULTRET, 0)) {
|
2016-06-23 01:26:27 +08:00
|
|
|
lua_error(L);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-08-06 07:20:28 +08:00
|
|
|
inline void script_file(lua_State* L, const std::string& filename, load_mode mode = load_mode::any) {
|
|
|
|
if (luaL_loadfilex(L, filename.c_str(), to_string(mode).c_str()) || lua_pcall(L, 0, LUA_MULTRET, 0)) {
|
2016-06-23 01:26:27 +08:00
|
|
|
lua_error(L);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-09-13 14:46:56 +08:00
|
|
|
inline void luajit_exception_handler(lua_State* L, int (*handler)(lua_State*, lua_CFunction) = detail::c_trampoline) {
|
2018-12-23 04:36:42 +08:00
|
|
|
#if defined(SOL_LUAJIT) && (!defined(SOL_EXCEPTIONS_SAFE_PROPAGATION) || !(SOL_EXCEPTIONS_SAFE_PROPAGATION))
|
2017-09-13 07:15:23 +08:00
|
|
|
if (L == nullptr) {
|
|
|
|
return;
|
|
|
|
}
|
2020-08-10 05:17:06 +08:00
|
|
|
#if SOL_IS_ON(SOL_SAFE_STACK_CHECK_I_)
|
2018-12-20 12:17:15 +08:00
|
|
|
luaL_checkstack(L, 1, detail::not_enough_stack_space_generic);
|
|
|
|
#endif // make sure stack doesn't overflow
|
2016-06-20 05:59:40 +08:00
|
|
|
lua_pushlightuserdata(L, (void*)handler);
|
2016-07-29 01:33:08 +08:00
|
|
|
auto pn = pop_n(L, 1);
|
2016-06-20 05:59:40 +08:00
|
|
|
luaJIT_setmode(L, -1, LUAJIT_MODE_WRAPCFUNC | LUAJIT_MODE_ON);
|
2016-02-27 15:59:47 +08:00
|
|
|
#else
|
2016-06-20 05:59:40 +08:00
|
|
|
(void)L;
|
|
|
|
(void)handler;
|
2016-02-27 15:43:53 +08:00
|
|
|
#endif
|
2016-06-20 05:59:40 +08:00
|
|
|
}
|
2016-02-27 15:43:53 +08:00
|
|
|
|
2016-06-20 05:59:40 +08:00
|
|
|
inline void luajit_exception_off(lua_State* L) {
|
2017-11-12 07:33:56 +08:00
|
|
|
#if defined(SOL_LUAJIT)
|
2017-09-13 07:15:23 +08:00
|
|
|
if (L == nullptr) {
|
|
|
|
return;
|
|
|
|
}
|
2016-06-20 05:59:40 +08:00
|
|
|
luaJIT_setmode(L, -1, LUAJIT_MODE_WRAPCFUNC | LUAJIT_MODE_OFF);
|
2016-02-27 15:59:47 +08:00
|
|
|
#else
|
2016-06-20 05:59:40 +08:00
|
|
|
(void)L;
|
2016-02-27 15:43:53 +08:00
|
|
|
#endif
|
2016-06-20 05:59:40 +08:00
|
|
|
}
|
2017-09-13 14:46:56 +08:00
|
|
|
} // namespace stack
|
|
|
|
} // namespace sol
|
2013-11-25 17:56:27 +08:00
|
|
|
|
2014-05-29 14:38:02 +08:00
|
|
|
#endif // SOL_STACK_HPP
|