260 lines
7.2 KiB
C++
260 lines
7.2 KiB
C++
#ifndef LUAKIWI_INT_H_
|
|
#define LUAKIWI_INT_H_
|
|
|
|
#include <kiwi/kiwi.h>
|
|
|
|
#include <cstring>
|
|
|
|
#include "luacompat.h"
|
|
|
|
#define ARR_COUNT(x) ((int)(sizeof(x) / sizeof((x)[0])))
|
|
|
|
#if defined(__GNUC__) && !defined(LKIWI_NO_BUILTIN)
|
|
#define l_likely(x) (__builtin_expect(((x) != 0), 1))
|
|
#define l_unlikely(x) (__builtin_expect(((x) != 0), 0))
|
|
#else
|
|
#define l_likely(x) (x)
|
|
#define l_unlikely(x) (x)
|
|
#endif
|
|
|
|
// Lua 5.1 compatibility for missing lua_arith.
|
|
static void compat_arith_unm(lua_State* L) {
|
|
#if defined(LUA_VERSION_NUM) && LUA_VERSION_NUM == 501
|
|
lua_Number n = lua_tonumber(L, -1);
|
|
if (n != 0 || lua_isnumber(L, -1)) {
|
|
lua_pop(L, 1);
|
|
lua_pushnumber(L, -n);
|
|
} else {
|
|
if (!luaL_callmeta(L, -1, "__unm"))
|
|
luaL_error(L, "attempt to perform arithmetic on a %s value", luaL_typename(L, -1));
|
|
lua_replace(L, -2);
|
|
}
|
|
#else
|
|
lua_arith(L, LUA_OPUNM);
|
|
#endif
|
|
}
|
|
|
|
// This version supports placeholders.
|
|
static void setfuncs(lua_State* L, const luaL_Reg* l, int nup) {
|
|
luaL_checkstack(L, nup, "too many upvalues");
|
|
for (; l->name != NULL; l++) { /* fill the table with given functions */
|
|
if (l->func == NULL) /* place holder? */
|
|
lua_pushboolean(L, 0);
|
|
else {
|
|
int i;
|
|
for (i = 0; i < nup; i++) /* copy upvalues to the top */
|
|
lua_pushvalue(L, -nup);
|
|
lua_pushcclosure(L, l->func, nup); /* closure with those upvalues */
|
|
}
|
|
lua_setfield(L, -(nup + 2), l->name);
|
|
}
|
|
lua_pop(L, nup); /* remove upvalues */
|
|
}
|
|
|
|
#define newlib(L, l) (lua_newtable((L)), setfuncs((L), (l), 0))
|
|
|
|
namespace {
|
|
|
|
using namespace kiwi;
|
|
|
|
enum KiwiErrKind {
|
|
KiwiErrNone,
|
|
KiwiErrUnsatisfiableConstraint = 1,
|
|
KiwiErrUnknownConstraint,
|
|
KiwiErrDuplicateConstraint,
|
|
KiwiErrUnknownEditVariable,
|
|
KiwiErrDuplicateEditVariable,
|
|
KiwiErrBadRequiredStrength,
|
|
KiwiErrInternalSolverError,
|
|
KiwiErrAlloc,
|
|
KiwiErrNullObject,
|
|
KiwiErrUnknown,
|
|
};
|
|
|
|
struct KiwiTerm {
|
|
Variable* var;
|
|
double coefficient;
|
|
};
|
|
|
|
struct KiwiExpression {
|
|
double constant;
|
|
int term_count;
|
|
Constraint* owner;
|
|
KiwiTerm terms[1];
|
|
};
|
|
|
|
// This structure was initially designed for LuaJIT FFI. It works OK for C++
|
|
// though it certainly isn't idiomatic.
|
|
struct KiwiErr {
|
|
enum KiwiErrKind kind;
|
|
const char* message;
|
|
bool must_free;
|
|
};
|
|
|
|
struct KiwiSolver {
|
|
unsigned error_mask;
|
|
Solver solver;
|
|
};
|
|
|
|
const KiwiErr* new_error(const KiwiErr* base, const std::exception& ex) {
|
|
if (!std::strcmp(ex.what(), base->message))
|
|
return base;
|
|
|
|
const auto msg_n = std::strlen(ex.what()) + 1;
|
|
|
|
auto* mem = static_cast<char*>(std::malloc(sizeof(KiwiErr) + msg_n));
|
|
if (!mem) {
|
|
return base;
|
|
}
|
|
|
|
const auto* err = new (mem) KiwiErr {base->kind, mem + sizeof(KiwiErr), true};
|
|
std::memcpy(const_cast<char*>(err->message), ex.what(), msg_n);
|
|
return err;
|
|
}
|
|
|
|
const constexpr KiwiErr kKiwiErrUnhandledCxxException {
|
|
KiwiErrUnknown,
|
|
"An unhandled C++ exception occurred."};
|
|
|
|
template<typename F>
|
|
inline const KiwiErr* wrap_err(F&& f) {
|
|
try {
|
|
f();
|
|
} catch (const UnsatisfiableConstraint&) {
|
|
static const constexpr KiwiErr err {
|
|
KiwiErrUnsatisfiableConstraint,
|
|
"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."};
|
|
return &err;
|
|
|
|
} catch (const DuplicateConstraint&) {
|
|
static const constexpr KiwiErr err {
|
|
KiwiErrDuplicateConstraint,
|
|
"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."};
|
|
return &err;
|
|
|
|
} catch (const DuplicateEditVariable&) {
|
|
static const constexpr KiwiErr err {
|
|
KiwiErrDuplicateEditVariable,
|
|
"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."};
|
|
return &err;
|
|
|
|
} catch (const InternalSolverError& ex) {
|
|
static const constexpr KiwiErr base {
|
|
KiwiErrInternalSolverError,
|
|
"An internal solver error occurred."};
|
|
return new_error(&base, ex);
|
|
} catch (std::bad_alloc&) {
|
|
static const constexpr KiwiErr err {KiwiErrAlloc, "A memory allocation failed."};
|
|
return &err;
|
|
} catch (const std::exception& ex) {
|
|
return new_error(&kKiwiErrUnhandledCxxException, ex);
|
|
} catch (...) {
|
|
return &kKiwiErrUnhandledCxxException;
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
template<typename P, typename R, typename F>
|
|
inline const KiwiErr* wrap_err(P& s, F&& f) {
|
|
return wrap_err([&]() { f(s); });
|
|
}
|
|
|
|
template<typename P, typename R, typename F>
|
|
inline const KiwiErr* wrap_err(P& s, R& ref, F&& f) {
|
|
return wrap_err([&]() { f(s, ref); });
|
|
}
|
|
|
|
inline Variable* kiwi_var_retain(Variable* var) {
|
|
alignas(Variable) unsigned char buf[sizeof(Variable)];
|
|
new (buf) Variable(*var);
|
|
return var;
|
|
}
|
|
|
|
inline Constraint* kiwi_constraint_retain(Constraint* c) {
|
|
alignas(Constraint) unsigned char buf[sizeof(Constraint)];
|
|
new (buf) Constraint(*c);
|
|
return c;
|
|
}
|
|
|
|
inline void kiwi_constraint_new(
|
|
const KiwiExpression* lhs,
|
|
const KiwiExpression* rhs,
|
|
RelationalOperator op,
|
|
double strength,
|
|
Constraint* mem
|
|
) {
|
|
if (strength < 0.0) {
|
|
strength = kiwi::strength::required;
|
|
}
|
|
|
|
std::vector<Term> terms;
|
|
terms.reserve((lhs ? lhs->term_count : 0) + (rhs ? rhs->term_count : 0));
|
|
|
|
if (lhs) {
|
|
for (auto* t = lhs->terms; t != lhs->terms + lhs->term_count; ++t) {
|
|
terms.emplace_back(*t->var, t->coefficient);
|
|
}
|
|
}
|
|
if (rhs) {
|
|
for (auto* t = rhs->terms; t != rhs->terms + rhs->term_count; ++t) {
|
|
terms.emplace_back(*t->var, -t->coefficient);
|
|
}
|
|
}
|
|
new (mem) Constraint(
|
|
Expression(std::move(terms), (lhs ? lhs->constant : 0.0) - (rhs ? rhs->constant : 0.0)),
|
|
static_cast<RelationalOperator>(op),
|
|
strength
|
|
);
|
|
}
|
|
|
|
inline const KiwiErr* kiwi_solver_add_constraint(Solver& s, const Constraint& constraint) {
|
|
return wrap_err(s, constraint, [](auto& solver, const auto& c) { solver.addConstraint(c); });
|
|
}
|
|
|
|
inline const KiwiErr* kiwi_solver_remove_constraint(Solver& s, const Constraint& constraint) {
|
|
return wrap_err(s, constraint, [](auto& solver, const auto& c) {
|
|
solver.removeConstraint(c);
|
|
});
|
|
}
|
|
|
|
inline const KiwiErr* kiwi_solver_add_edit_var(Solver& s, const Variable& var, double strength) {
|
|
return wrap_err(s, var, [strength](auto& solver, const auto& v) {
|
|
solver.addEditVariable(v, strength);
|
|
});
|
|
}
|
|
|
|
inline const KiwiErr* kiwi_solver_remove_edit_var(Solver& s, const Variable& var) {
|
|
return wrap_err(s, var, [](auto& solver, const auto& v) { solver.removeEditVariable(v); });
|
|
}
|
|
|
|
inline const KiwiErr* kiwi_solver_suggest_value(Solver& s, const Variable& var, double value) {
|
|
return wrap_err(s, var, [value](auto& solver, const auto& v) {
|
|
solver.suggestValue(v, value);
|
|
});
|
|
}
|
|
|
|
} // namespace
|
|
|
|
// Local Variables:
|
|
// mode: c++
|
|
// End:
|
|
|
|
#endif // LUAKIWI_INT_H_
|