Better exception safety for Lua binding
This commit is contained in:
@@ -65,7 +65,7 @@ PointerAlignment: Left
|
|||||||
ReferenceAlignment: Left # New in v13. int &name ==> int& name
|
ReferenceAlignment: Left # New in v13. int &name ==> int& name
|
||||||
ReflowComments: false
|
ReflowComments: false
|
||||||
SeparateDefinitionBlocks: Always # New in v14.
|
SeparateDefinitionBlocks: Always # New in v14.
|
||||||
SortIncludes: false
|
SortIncludes: true
|
||||||
SortUsingDeclarations: true
|
SortUsingDeclarations: true
|
||||||
SpaceAfterCStyleCast: false
|
SpaceAfterCStyleCast: false
|
||||||
SpaceAfterLogicalNot: false
|
SpaceAfterLogicalNot: false
|
||||||
|
|||||||
@@ -17,14 +17,6 @@ extern "C" {
|
|||||||
|
|
||||||
#if !defined(LUA_VERSION_NUM) || LUA_VERSION_NUM == 501
|
#if !defined(LUA_VERSION_NUM) || LUA_VERSION_NUM == 501
|
||||||
|
|
||||||
#define LUA_OPADD 0
|
|
||||||
#define LUA_OPSUB 1
|
|
||||||
#define LUA_OPMUL 2
|
|
||||||
#define LUA_OPDIV 3
|
|
||||||
#define LUA_OPMOD 4
|
|
||||||
#define LUA_OPPOW 5
|
|
||||||
#define LUA_OPUNM 6
|
|
||||||
|
|
||||||
static int lua_absindex(lua_State* L, int i) {
|
static int lua_absindex(lua_State* L, int i) {
|
||||||
if (i < 0 && i > LUA_REGISTRYINDEX)
|
if (i < 0 && i > LUA_REGISTRYINDEX)
|
||||||
i += lua_gettop(L) + 1;
|
i += lua_gettop(L) + 1;
|
||||||
|
|||||||
@@ -141,49 +141,57 @@ template<typename F>
|
|||||||
inline const KiwiErr* wrap_err(F&& f) {
|
inline const KiwiErr* wrap_err(F&& f) {
|
||||||
static const constexpr KiwiErr kKiwiErrUnhandledCxxException {
|
static const constexpr KiwiErr kKiwiErrUnhandledCxxException {
|
||||||
KiwiErrUnknown,
|
KiwiErrUnknown,
|
||||||
"An unhandled C++ exception occurred."};
|
"An unhandled C++ exception occurred."
|
||||||
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
f();
|
f();
|
||||||
} catch (const UnsatisfiableConstraint&) {
|
} catch (const UnsatisfiableConstraint&) {
|
||||||
static const constexpr KiwiErr err {
|
static const constexpr KiwiErr err {
|
||||||
KiwiErrUnsatisfiableConstraint,
|
KiwiErrUnsatisfiableConstraint,
|
||||||
"The constraint cannot be satisfied."};
|
"The constraint cannot be satisfied."
|
||||||
|
};
|
||||||
return &err;
|
return &err;
|
||||||
} catch (const UnknownConstraint&) {
|
} catch (const UnknownConstraint&) {
|
||||||
static const constexpr KiwiErr err {
|
static const constexpr KiwiErr err {
|
||||||
KiwiErrUnknownConstraint,
|
KiwiErrUnknownConstraint,
|
||||||
"The constraint has not been added to the solver."};
|
"The constraint has not been added to the solver."
|
||||||
|
};
|
||||||
return &err;
|
return &err;
|
||||||
|
|
||||||
} catch (const DuplicateConstraint&) {
|
} catch (const DuplicateConstraint&) {
|
||||||
static const constexpr KiwiErr err {
|
static const constexpr KiwiErr err {
|
||||||
KiwiErrDuplicateConstraint,
|
KiwiErrDuplicateConstraint,
|
||||||
"The constraint has already been added to the solver."};
|
"The constraint has already been added to the solver."
|
||||||
|
};
|
||||||
return &err;
|
return &err;
|
||||||
|
|
||||||
} catch (const UnknownEditVariable&) {
|
} catch (const UnknownEditVariable&) {
|
||||||
static const constexpr KiwiErr err {
|
static const constexpr KiwiErr err {
|
||||||
KiwiErrUnknownEditVariable,
|
KiwiErrUnknownEditVariable,
|
||||||
"The edit variable has not been added to the solver."};
|
"The edit variable has not been added to the solver."
|
||||||
|
};
|
||||||
return &err;
|
return &err;
|
||||||
|
|
||||||
} catch (const DuplicateEditVariable&) {
|
} catch (const DuplicateEditVariable&) {
|
||||||
static const constexpr KiwiErr err {
|
static const constexpr KiwiErr err {
|
||||||
KiwiErrDuplicateEditVariable,
|
KiwiErrDuplicateEditVariable,
|
||||||
"The edit variable has already been added to the solver."};
|
"The edit variable has already been added to the solver."
|
||||||
|
};
|
||||||
return &err;
|
return &err;
|
||||||
|
|
||||||
} catch (const BadRequiredStrength&) {
|
} catch (const BadRequiredStrength&) {
|
||||||
static const constexpr KiwiErr err {
|
static const constexpr KiwiErr err {
|
||||||
KiwiErrBadRequiredStrength,
|
KiwiErrBadRequiredStrength,
|
||||||
"A required strength cannot be used in this context."};
|
"A required strength cannot be used in this context."
|
||||||
|
};
|
||||||
return &err;
|
return &err;
|
||||||
|
|
||||||
} catch (const InternalSolverError& ex) {
|
} catch (const InternalSolverError& ex) {
|
||||||
static const constexpr KiwiErr base {
|
static const constexpr KiwiErr base {
|
||||||
KiwiErrInternalSolverError,
|
KiwiErrInternalSolverError,
|
||||||
"An internal solver error occurred."};
|
"An internal solver error occurred."
|
||||||
|
};
|
||||||
return new_error(&base, ex);
|
return new_error(&base, ex);
|
||||||
} catch (std::bad_alloc&) {
|
} catch (std::bad_alloc&) {
|
||||||
static const constexpr KiwiErr err {KiwiErrAlloc, "A memory allocation failed."};
|
static const constexpr KiwiErr err {KiwiErrAlloc, "A memory allocation failed."};
|
||||||
@@ -208,9 +216,12 @@ inline const KiwiErr* wrap_err(P&& s, R&& ref, F&& f) {
|
|||||||
|
|
||||||
template<typename T, typename... Args>
|
template<typename T, typename... Args>
|
||||||
inline T* make_unmanaged(Args... args) {
|
inline T* make_unmanaged(Args... args) {
|
||||||
auto* o = new T(std::forward<Args>(args)...);
|
auto* p = new (std::nothrow) T(std::forward<Args>(args)...);
|
||||||
o->m_refcount = 1;
|
if (lk_unlikely(!p))
|
||||||
return o;
|
return nullptr;
|
||||||
|
|
||||||
|
p->m_refcount = 1;
|
||||||
|
return p;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
@@ -238,30 +249,35 @@ inline ConstraintData* kiwi_constraint_new(
|
|||||||
strength = kiwi::strength::required;
|
strength = kiwi::strength::required;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<Term> terms;
|
try {
|
||||||
terms.reserve(static_cast<std::size_t>(
|
std::vector<Term> terms;
|
||||||
(lhs && lhs->term_count > 0 ? lhs->term_count : 0)
|
|
||||||
+ (rhs && rhs->term_count > 0 ? rhs->term_count : 0)
|
|
||||||
));
|
|
||||||
|
|
||||||
if (lhs) {
|
terms.reserve(static_cast<decltype(terms)::size_type>(
|
||||||
for (int i = 0; i < lhs->term_count; ++i) {
|
(lhs && lhs->term_count > 0 ? lhs->term_count : 0)
|
||||||
const auto& t = lhs->terms[i];
|
+ (rhs && rhs->term_count > 0 ? rhs->term_count : 0)
|
||||||
terms.emplace_back(Variable(t.var), t.coefficient);
|
));
|
||||||
}
|
|
||||||
}
|
|
||||||
if (rhs) {
|
|
||||||
for (int i = 0; i < rhs->term_count; ++i) {
|
|
||||||
const auto& t = rhs->terms[i];
|
|
||||||
terms.emplace_back(Variable(t.var), -t.coefficient);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return make_unmanaged<ConstraintData>(
|
if (lhs) {
|
||||||
Expression(std::move(terms), (lhs ? lhs->constant : 0.0) - (rhs ? rhs->constant : 0.0)),
|
for (int i = 0; i < lhs->term_count; ++i) {
|
||||||
static_cast<RelationalOperator>(op),
|
const auto& t = lhs->terms[i];
|
||||||
strength
|
terms.emplace_back(Variable(t.var), t.coefficient);
|
||||||
);
|
}
|
||||||
|
}
|
||||||
|
if (rhs) {
|
||||||
|
for (int i = 0; i < rhs->term_count; ++i) {
|
||||||
|
const auto& t = rhs->terms[i];
|
||||||
|
terms.emplace_back(Variable(t.var), -t.coefficient);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return make_unmanaged<ConstraintData>(
|
||||||
|
Expression(std::move(terms), (lhs ? lhs->constant : 0.0) - (rhs ? rhs->constant : 0.0)),
|
||||||
|
static_cast<RelationalOperator>(op),
|
||||||
|
strength
|
||||||
|
);
|
||||||
|
|
||||||
|
} catch (...) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
inline const KiwiErr* kiwi_solver_add_constraint(Solver& s, ConstraintData* constraint) {
|
inline const KiwiErr* kiwi_solver_add_constraint(Solver& s, ConstraintData* constraint) {
|
||||||
|
|||||||
@@ -1,9 +1,7 @@
|
|||||||
#include "ljkiwi.hpp"
|
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
#include "luacompat.h"
|
|
||||||
#include "luakiwi-int.h"
|
#include "luakiwi-int.h"
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
@@ -13,9 +11,7 @@ namespace {
|
|||||||
|
|
||||||
enum TypeId { NOTYPE, VAR = 1, TERM, EXPR, CONSTRAINT, SOLVER, ERROR, NUMBER };
|
enum TypeId { NOTYPE, VAR = 1, TERM, EXPR, CONSTRAINT, SOLVER, ERROR, NUMBER };
|
||||||
|
|
||||||
const int ERR_KIND_TAB = NUMBER + 1;
|
enum { ERR_KIND_TAB = NUMBER + 1, VAR_SUB_FN, MEM_ERR_MSG, CONTEXT_TAB_MAX };
|
||||||
const int VAR_SUB_FN = ERR_KIND_TAB + 1;
|
|
||||||
const int CONTEXT_TAB_MAX = VAR_SUB_FN + 1;
|
|
||||||
|
|
||||||
constexpr const char* const lkiwi_error_kinds[] = {
|
constexpr const char* const lkiwi_error_kinds[] = {
|
||||||
"KiwiErrNone",
|
"KiwiErrNone",
|
||||||
@@ -218,13 +214,19 @@ inline KiwiSolver* get_solver(lua_State* L, int idx) {
|
|||||||
return static_cast<KiwiSolver*>(check_arg(L, idx, SOLVER));
|
return static_cast<KiwiSolver*>(check_arg(L, idx, SOLVER));
|
||||||
}
|
}
|
||||||
|
|
||||||
// note this expects the 2nd upvalue to have the variable weak table
|
VariableData** var_new(lua_State* L) {
|
||||||
VariableData* var_new(lua_State* L, VariableData* var) {
|
auto** varp = static_cast<VariableData**>(lua_newuserdata(L, sizeof(VariableData*)));
|
||||||
*static_cast<VariableData**>(lua_newuserdata(L, sizeof(VariableData*))) = var;
|
|
||||||
|
|
||||||
push_type(L, VAR);
|
push_type(L, VAR);
|
||||||
lua_setmetatable(L, -2);
|
lua_setmetatable(L, -2);
|
||||||
|
return varp;
|
||||||
|
}
|
||||||
|
|
||||||
|
// note this expects the 2nd upvalue to have the variable weak table
|
||||||
|
VariableData* var_register(lua_State* L, VariableData* var) {
|
||||||
|
if (lk_unlikely(!var)) {
|
||||||
|
lua_rawgeti(L, lua_upvalueindex(1), MEM_ERR_MSG);
|
||||||
|
lua_error(L);
|
||||||
|
}
|
||||||
#if defined(LUA_VERSION_NUM) && LUA_VERSION_NUM == 501
|
#if defined(LUA_VERSION_NUM) && LUA_VERSION_NUM == 501
|
||||||
// a true compatibility shim has performance implications here
|
// a true compatibility shim has performance implications here
|
||||||
lua_pushlightuserdata(L, var);
|
lua_pushlightuserdata(L, var);
|
||||||
@@ -260,10 +262,13 @@ inline ConstraintData* constraint_new(
|
|||||||
double strength
|
double strength
|
||||||
) {
|
) {
|
||||||
auto** c = static_cast<ConstraintData**>(lua_newuserdata(L, sizeof(ConstraintData*)));
|
auto** c = static_cast<ConstraintData**>(lua_newuserdata(L, sizeof(ConstraintData*)));
|
||||||
*c = kiwi_constraint_new(lhs, rhs, op, strength);
|
|
||||||
|
|
||||||
push_type(L, CONSTRAINT);
|
push_type(L, CONSTRAINT);
|
||||||
lua_setmetatable(L, -2);
|
lua_setmetatable(L, -2);
|
||||||
|
|
||||||
|
if (lk_unlikely(!(*c = kiwi_constraint_new(lhs, rhs, op, strength)))) {
|
||||||
|
lua_rawgeti(L, lua_upvalueindex(1), MEM_ERR_MSG);
|
||||||
|
lua_error(L);
|
||||||
|
}
|
||||||
return *c;
|
return *c;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -540,11 +545,15 @@ constexpr const struct luaL_Reg kiwi_var_m[] = {
|
|||||||
{"eq", lkiwi_eq},
|
{"eq", lkiwi_eq},
|
||||||
{"le", lkiwi_le},
|
{"le", lkiwi_le},
|
||||||
{"ge", lkiwi_ge},
|
{"ge", lkiwi_ge},
|
||||||
{0, 0}};
|
{0, 0}
|
||||||
|
};
|
||||||
|
|
||||||
int lkiwi_var_new(lua_State* L) {
|
int lkiwi_var_new(lua_State* L) {
|
||||||
const char* name = luaL_optstring(L, 1, "");
|
const char* name = luaL_optstring(L, 1, "");
|
||||||
var_new(L, make_unmanaged<VariableData>(name));
|
|
||||||
|
auto* varp = var_new(L);
|
||||||
|
var_register(L, *varp = make_unmanaged<VariableData>(name));
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -662,8 +671,10 @@ int lkiwi_term_m_index(lua_State* L) {
|
|||||||
#else
|
#else
|
||||||
lua_rawgetp(L, lua_upvalueindex(2), term->var);
|
lua_rawgetp(L, lua_upvalueindex(2), term->var);
|
||||||
#endif
|
#endif
|
||||||
if (lua_isnil(L, -1))
|
if (lua_isnil(L, -1)) {
|
||||||
var_new(L, term->var);
|
auto* varp = var_new(L);
|
||||||
|
var_register(L, *varp = retain_unmanaged(term->var));
|
||||||
|
}
|
||||||
return 1;
|
return 1;
|
||||||
} else if (len == 11 && memcmp("coefficient", k, len) == 0) {
|
} else if (len == 11 && memcmp("coefficient", k, len) == 0) {
|
||||||
lua_pushnumber(L, term->coefficient);
|
lua_pushnumber(L, term->coefficient);
|
||||||
@@ -692,7 +703,8 @@ constexpr const struct luaL_Reg kiwi_term_m[] = {
|
|||||||
{"eq", lkiwi_eq},
|
{"eq", lkiwi_eq},
|
||||||
{"le", lkiwi_le},
|
{"le", lkiwi_le},
|
||||||
{"ge", lkiwi_ge},
|
{"ge", lkiwi_ge},
|
||||||
{0, 0}};
|
{0, 0}
|
||||||
|
};
|
||||||
|
|
||||||
int lkiwi_term_new(lua_State* L) {
|
int lkiwi_term_new(lua_State* L) {
|
||||||
auto* var = get_var(L, 1);
|
auto* var = get_var(L, 1);
|
||||||
@@ -908,7 +920,8 @@ constexpr const struct luaL_Reg kiwi_expr_m[] = {
|
|||||||
{"eq", lkiwi_eq},
|
{"eq", lkiwi_eq},
|
||||||
{"le", lkiwi_le},
|
{"le", lkiwi_le},
|
||||||
{"ge", lkiwi_ge},
|
{"ge", lkiwi_ge},
|
||||||
{0, 0}};
|
{0, 0}
|
||||||
|
};
|
||||||
|
|
||||||
int lkiwi_expr_new(lua_State* L) {
|
int lkiwi_expr_new(lua_State* L) {
|
||||||
int nterms = lua_gettop(L) - 1;
|
int nterms = lua_gettop(L) - 1;
|
||||||
@@ -1060,7 +1073,8 @@ constexpr const struct luaL_Reg kiwi_constraint_m[] = {
|
|||||||
{"expression", lkiwi_constraint_expression},
|
{"expression", lkiwi_constraint_expression},
|
||||||
{"add_to", lkiwi_constraint_add_to},
|
{"add_to", lkiwi_constraint_add_to},
|
||||||
{"remove_from", lkiwi_constraint_remove_from},
|
{"remove_from", lkiwi_constraint_remove_from},
|
||||||
{0, 0}};
|
{0, 0}
|
||||||
|
};
|
||||||
|
|
||||||
int lkiwi_constraint_new(lua_State* L) {
|
int lkiwi_constraint_new(lua_State* L) {
|
||||||
const auto* lhs = get_expr_opt(L, 1);
|
const auto* lhs = get_expr_opt(L, 1);
|
||||||
@@ -1133,7 +1147,8 @@ constexpr const struct luaL_Reg lkiwi_constraints[] = {
|
|||||||
{"pair_ratio", lkiwi_constraints_pair_ratio},
|
{"pair_ratio", lkiwi_constraints_pair_ratio},
|
||||||
{"pair", lkiwi_constraints_pair},
|
{"pair", lkiwi_constraints_pair},
|
||||||
{"single", lkiwi_constraints_single},
|
{"single", lkiwi_constraints_single},
|
||||||
{0, 0}};
|
{0, 0}
|
||||||
|
};
|
||||||
|
|
||||||
void lkiwi_mod_constraints_new(lua_State* L, int ctx_i) {
|
void lkiwi_mod_constraints_new(lua_State* L, int ctx_i) {
|
||||||
luaL_newlibtable(L, lkiwi_constraints);
|
luaL_newlibtable(L, lkiwi_constraints);
|
||||||
@@ -1192,7 +1207,8 @@ int lkiwi_error_m_tostring(lua_State* L) {
|
|||||||
|
|
||||||
constexpr const struct luaL_Reg lkiwi_error_m[] = {
|
constexpr const struct luaL_Reg lkiwi_error_m[] = {
|
||||||
{"__tostring", lkiwi_error_m_tostring},
|
{"__tostring", lkiwi_error_m_tostring},
|
||||||
{0, 0}};
|
{0, 0}
|
||||||
|
};
|
||||||
|
|
||||||
int lkiwi_error_mask(lua_State* L) {
|
int lkiwi_error_mask(lua_State* L) {
|
||||||
int invert = lua_toboolean(L, 2);
|
int invert = lua_toboolean(L, 2);
|
||||||
@@ -1444,7 +1460,8 @@ constexpr const struct luaL_Reg kiwi_solver_m[] = {
|
|||||||
{"set_error_mask", lkiwi_solver_set_error_mask},
|
{"set_error_mask", lkiwi_solver_set_error_mask},
|
||||||
{"__tostring", lkiwi_solver_m_tostring},
|
{"__tostring", lkiwi_solver_m_tostring},
|
||||||
{"__gc", lkiwi_solver_m_gc},
|
{"__gc", lkiwi_solver_m_gc},
|
||||||
{0, 0}};
|
{0, 0}
|
||||||
|
};
|
||||||
|
|
||||||
int lkiwi_solver_new(lua_State* L) {
|
int lkiwi_solver_new(lua_State* L) {
|
||||||
lua_Integer error_mask;
|
lua_Integer error_mask;
|
||||||
@@ -1541,7 +1558,8 @@ constexpr const struct luaL_Reg lkiwi[] = {
|
|||||||
{"eq", lkiwi_eq},
|
{"eq", lkiwi_eq},
|
||||||
{"le", lkiwi_le},
|
{"le", lkiwi_le},
|
||||||
{"ge", lkiwi_ge},
|
{"ge", lkiwi_ge},
|
||||||
{0, 0}};
|
{0, 0}
|
||||||
|
};
|
||||||
|
|
||||||
int no_member_mt_index(lua_State* L) {
|
int no_member_mt_index(lua_State* L) {
|
||||||
luaL_error(L, "attempt to access non-existent member '%s'", lua_tostring(L, 2));
|
luaL_error(L, "attempt to access non-existent member '%s'", lua_tostring(L, 2));
|
||||||
@@ -1619,6 +1637,8 @@ extern "C" LJKIWI_EXPORT int luaopen_ljkiwi(lua_State* L) {
|
|||||||
int ctx_i = lua_gettop(L);
|
int ctx_i = lua_gettop(L);
|
||||||
|
|
||||||
compat_init(L, ctx_i);
|
compat_init(L, ctx_i);
|
||||||
|
lua_pushliteral(L, "kiwi library memory allocation error");
|
||||||
|
lua_rawseti(L, ctx_i, MEM_ERR_MSG);
|
||||||
|
|
||||||
no_member_mt_new(L);
|
no_member_mt_new(L);
|
||||||
register_type(L, "kiwi.Var", ctx_i, VAR, kiwi_var_m);
|
register_type(L, "kiwi.Var", ctx_i, VAR, kiwi_var_m);
|
||||||
|
|||||||
Reference in New Issue
Block a user