Better exception safety for Lua binding

This commit is contained in:
2024-02-26 12:15:33 -06:00
parent ae5e4b3419
commit 94a8bdca79
4 changed files with 92 additions and 64 deletions

View File

@@ -65,7 +65,7 @@ PointerAlignment: Left
ReferenceAlignment: Left # New in v13. int &name ==> int& name
ReflowComments: false
SeparateDefinitionBlocks: Always # New in v14.
SortIncludes: false
SortIncludes: true
SortUsingDeclarations: true
SpaceAfterCStyleCast: false
SpaceAfterLogicalNot: false

View File

@@ -17,14 +17,6 @@ extern "C" {
#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) {
if (i < 0 && i > LUA_REGISTRYINDEX)
i += lua_gettop(L) + 1;

View File

@@ -141,49 +141,57 @@ template<typename F>
inline const KiwiErr* wrap_err(F&& f) {
static const constexpr KiwiErr kKiwiErrUnhandledCxxException {
KiwiErrUnknown,
"An unhandled C++ exception occurred."};
"An unhandled C++ exception occurred."
};
try {
f();
} catch (const UnsatisfiableConstraint&) {
static const constexpr KiwiErr err {
KiwiErrUnsatisfiableConstraint,
"The constraint cannot be satisfied."};
"The constraint cannot be satisfied."
};
return &err;
} catch (const UnknownConstraint&) {
static const constexpr KiwiErr err {
KiwiErrUnknownConstraint,
"The constraint has not been added to the solver."};
"The constraint has not been added to the solver."
};
return &err;
} catch (const DuplicateConstraint&) {
static const constexpr KiwiErr err {
KiwiErrDuplicateConstraint,
"The constraint has already been added to the solver."};
"The constraint has already been added to the solver."
};
return &err;
} catch (const UnknownEditVariable&) {
static const constexpr KiwiErr err {
KiwiErrUnknownEditVariable,
"The edit variable has not been added to the solver."};
"The edit variable has not been added to the solver."
};
return &err;
} catch (const DuplicateEditVariable&) {
static const constexpr KiwiErr err {
KiwiErrDuplicateEditVariable,
"The edit variable has already been added to the solver."};
"The edit variable has already been added to the solver."
};
return &err;
} catch (const BadRequiredStrength&) {
static const constexpr KiwiErr err {
KiwiErrBadRequiredStrength,
"A required strength cannot be used in this context."};
"A required strength cannot be used in this context."
};
return &err;
} catch (const InternalSolverError& ex) {
static const constexpr KiwiErr base {
KiwiErrInternalSolverError,
"An internal solver error occurred."};
"An internal solver error occurred."
};
return new_error(&base, ex);
} catch (std::bad_alloc&) {
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>
inline T* make_unmanaged(Args... args) {
auto* o = new T(std::forward<Args>(args)...);
o->m_refcount = 1;
return o;
auto* p = new (std::nothrow) T(std::forward<Args>(args)...);
if (lk_unlikely(!p))
return nullptr;
p->m_refcount = 1;
return p;
}
template<typename T>
@@ -238,30 +249,35 @@ inline ConstraintData* kiwi_constraint_new(
strength = kiwi::strength::required;
}
std::vector<Term> terms;
terms.reserve(static_cast<std::size_t>(
(lhs && lhs->term_count > 0 ? lhs->term_count : 0)
+ (rhs && rhs->term_count > 0 ? rhs->term_count : 0)
));
try {
std::vector<Term> terms;
if (lhs) {
for (int i = 0; i < lhs->term_count; ++i) {
const auto& t = lhs->terms[i];
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);
}
}
terms.reserve(static_cast<decltype(terms)::size_type>(
(lhs && lhs->term_count > 0 ? lhs->term_count : 0)
+ (rhs && rhs->term_count > 0 ? rhs->term_count : 0)
));
return make_unmanaged<ConstraintData>(
Expression(std::move(terms), (lhs ? lhs->constant : 0.0) - (rhs ? rhs->constant : 0.0)),
static_cast<RelationalOperator>(op),
strength
);
if (lhs) {
for (int i = 0; i < lhs->term_count; ++i) {
const auto& t = lhs->terms[i];
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) {

View File

@@ -1,9 +1,7 @@
#include "ljkiwi.hpp"
#include <math.h>
#include <stdlib.h>
#include <string.h>
#include "luacompat.h"
#include "luakiwi-int.h"
namespace {
@@ -13,9 +11,7 @@ namespace {
enum TypeId { NOTYPE, VAR = 1, TERM, EXPR, CONSTRAINT, SOLVER, ERROR, NUMBER };
const int ERR_KIND_TAB = NUMBER + 1;
const int VAR_SUB_FN = ERR_KIND_TAB + 1;
const int CONTEXT_TAB_MAX = VAR_SUB_FN + 1;
enum { ERR_KIND_TAB = NUMBER + 1, VAR_SUB_FN, MEM_ERR_MSG, CONTEXT_TAB_MAX };
constexpr const char* const lkiwi_error_kinds[] = {
"KiwiErrNone",
@@ -218,13 +214,19 @@ inline KiwiSolver* get_solver(lua_State* L, int idx) {
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) {
*static_cast<VariableData**>(lua_newuserdata(L, sizeof(VariableData*))) = var;
VariableData** var_new(lua_State* L) {
auto** varp = static_cast<VariableData**>(lua_newuserdata(L, sizeof(VariableData*)));
push_type(L, VAR);
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
// a true compatibility shim has performance implications here
lua_pushlightuserdata(L, var);
@@ -260,10 +262,13 @@ inline ConstraintData* constraint_new(
double strength
) {
auto** c = static_cast<ConstraintData**>(lua_newuserdata(L, sizeof(ConstraintData*)));
*c = kiwi_constraint_new(lhs, rhs, op, strength);
push_type(L, CONSTRAINT);
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;
}
@@ -540,11 +545,15 @@ constexpr const struct luaL_Reg kiwi_var_m[] = {
{"eq", lkiwi_eq},
{"le", lkiwi_le},
{"ge", lkiwi_ge},
{0, 0}};
{0, 0}
};
int lkiwi_var_new(lua_State* L) {
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;
}
@@ -662,8 +671,10 @@ int lkiwi_term_m_index(lua_State* L) {
#else
lua_rawgetp(L, lua_upvalueindex(2), term->var);
#endif
if (lua_isnil(L, -1))
var_new(L, term->var);
if (lua_isnil(L, -1)) {
auto* varp = var_new(L);
var_register(L, *varp = retain_unmanaged(term->var));
}
return 1;
} else if (len == 11 && memcmp("coefficient", k, len) == 0) {
lua_pushnumber(L, term->coefficient);
@@ -692,7 +703,8 @@ constexpr const struct luaL_Reg kiwi_term_m[] = {
{"eq", lkiwi_eq},
{"le", lkiwi_le},
{"ge", lkiwi_ge},
{0, 0}};
{0, 0}
};
int lkiwi_term_new(lua_State* L) {
auto* var = get_var(L, 1);
@@ -908,7 +920,8 @@ constexpr const struct luaL_Reg kiwi_expr_m[] = {
{"eq", lkiwi_eq},
{"le", lkiwi_le},
{"ge", lkiwi_ge},
{0, 0}};
{0, 0}
};
int lkiwi_expr_new(lua_State* L) {
int nterms = lua_gettop(L) - 1;
@@ -1060,7 +1073,8 @@ constexpr const struct luaL_Reg kiwi_constraint_m[] = {
{"expression", lkiwi_constraint_expression},
{"add_to", lkiwi_constraint_add_to},
{"remove_from", lkiwi_constraint_remove_from},
{0, 0}};
{0, 0}
};
int lkiwi_constraint_new(lua_State* L) {
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", lkiwi_constraints_pair},
{"single", lkiwi_constraints_single},
{0, 0}};
{0, 0}
};
void lkiwi_mod_constraints_new(lua_State* L, int ctx_i) {
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[] = {
{"__tostring", lkiwi_error_m_tostring},
{0, 0}};
{0, 0}
};
int lkiwi_error_mask(lua_State* L) {
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},
{"__tostring", lkiwi_solver_m_tostring},
{"__gc", lkiwi_solver_m_gc},
{0, 0}};
{0, 0}
};
int lkiwi_solver_new(lua_State* L) {
lua_Integer error_mask;
@@ -1541,7 +1558,8 @@ constexpr const struct luaL_Reg lkiwi[] = {
{"eq", lkiwi_eq},
{"le", lkiwi_le},
{"ge", lkiwi_ge},
{0, 0}};
{0, 0}
};
int no_member_mt_index(lua_State* L) {
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);
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);
register_type(L, "kiwi.Var", ctx_i, VAR, kiwi_var_m);