skin this cat
This commit is contained in:
331
luakiwi/kiwibridge.cpp
Normal file
331
luakiwi/kiwibridge.cpp
Normal file
@@ -0,0 +1,331 @@
|
||||
#include <kiwi/kiwi.h>
|
||||
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
|
||||
namespace {
|
||||
|
||||
using namespace kiwi;
|
||||
|
||||
enum KiwiErrKind {
|
||||
KiwiErrNone,
|
||||
KiwiErrUnsatisfiableConstraint = 1,
|
||||
KiwiErrUnknownConstraint,
|
||||
KiwiErrDuplicateConstraint,
|
||||
KiwiErrUnknownEditVariable,
|
||||
KiwiErrDuplicateEditVariable,
|
||||
KiwiErrBadRequiredStrength,
|
||||
KiwiErrInternalSolverError,
|
||||
KiwiErrAlloc,
|
||||
KiwiErrNullObject,
|
||||
KiwiErrUnknown,
|
||||
};
|
||||
|
||||
enum KiwiRelOp { KIWI_OP_LE, KIWI_OP_GE, KIWI_OP_EQ };
|
||||
|
||||
typedef struct KiwiVarRefType* KiwiVarRef;
|
||||
typedef struct KiwiConstraintRefType* KiwiConstraintRef;
|
||||
|
||||
struct KiwiTerm {
|
||||
Variable* var;
|
||||
double coefficient;
|
||||
};
|
||||
|
||||
struct KiwiExpression {
|
||||
double constant;
|
||||
int term_count;
|
||||
Constraint* owner;
|
||||
KiwiTerm terms[1]; // LuaJIT: struct KiwiTerm terms_[?];
|
||||
};
|
||||
|
||||
struct KiwiErr {
|
||||
enum KiwiErrKind kind;
|
||||
const char* message;
|
||||
bool must_free;
|
||||
};
|
||||
|
||||
struct KiwiSolver {
|
||||
unsigned error_mask;
|
||||
Solver solver;
|
||||
};
|
||||
|
||||
template<typename T, typename R, typename... Args>
|
||||
inline R make_cref(Args&&... args) {
|
||||
static_assert(
|
||||
sizeof(R) >= sizeof(T), // NOLINT(bugprone-sizeof-expression)
|
||||
"to_cref: R too small for T"
|
||||
);
|
||||
static_assert(alignof(R) >= alignof(T), "to_cref: R alignment too small for T");
|
||||
|
||||
R cref;
|
||||
new (&cref) T(std::forward<Args>(args)...);
|
||||
return cref;
|
||||
}
|
||||
|
||||
template<typename... Args>
|
||||
inline decltype(auto) make_var_cref(Args&&... args) {
|
||||
return make_cref<Variable, KiwiVarRef>(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template<typename... Args>
|
||||
inline decltype(auto) make_constraint_cref(Args&&... args) {
|
||||
return make_cref<Constraint, KiwiConstraintRef>(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template<class T, class R>
|
||||
class SharedRef {
|
||||
private:
|
||||
R& cref_;
|
||||
|
||||
public:
|
||||
explicit SharedRef<T, R>(R& cref) : cref_(cref) {}
|
||||
|
||||
static_assert(
|
||||
sizeof(R) >= sizeof(T), // NOLINT(bugprone-sizeof-expression)
|
||||
"SharedRef<T,CS> CS too small for T"
|
||||
);
|
||||
|
||||
R clone() const {
|
||||
return make_cref<T, R>(cref());
|
||||
}
|
||||
|
||||
void release() {
|
||||
if (cref_) {
|
||||
ptr()->~T();
|
||||
cref_ = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
T* ptr() {
|
||||
T* p;
|
||||
void* s = &cref_;
|
||||
std::memcpy(&p, &s, sizeof p); // NOLINT(bugprone-sizeof-expression)
|
||||
return p;
|
||||
}
|
||||
|
||||
const T* const_ptr() const {
|
||||
const T* p;
|
||||
const void* s = &cref_;
|
||||
std::memcpy(&p, &s, sizeof p); // NOLINT(bugprone-sizeof-expression)
|
||||
return p;
|
||||
}
|
||||
|
||||
const T& cref() const {
|
||||
return *const_ptr();
|
||||
}
|
||||
|
||||
T* operator&() const {
|
||||
return ptr();
|
||||
}
|
||||
|
||||
T* operator->() {
|
||||
return ptr();
|
||||
}
|
||||
|
||||
const T* operator->() const {
|
||||
return const_ptr();
|
||||
}
|
||||
|
||||
operator const T&() const {
|
||||
return cref();
|
||||
}
|
||||
|
||||
explicit operator bool() const {
|
||||
return cref_;
|
||||
}
|
||||
};
|
||||
|
||||
using ConstraintRef = SharedRef<Constraint, KiwiConstraintRef>;
|
||||
using VariableRef = SharedRef<Variable, KiwiVarRef>;
|
||||
using ConstVariableRef = const SharedRef<const Variable, const KiwiVarRef>;
|
||||
|
||||
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."};
|
||||
|
||||
const constexpr KiwiErr kKiwiErrNullObjectArg0 {
|
||||
KiwiErrNullObject,
|
||||
"null object passed as argument #0 (self)"};
|
||||
|
||||
const constexpr KiwiErr kKiwiErrNullObjectArg1 {
|
||||
KiwiErrNullObject,
|
||||
"null object passed as argument #1"};
|
||||
|
||||
template<typename F>
|
||||
inline const KiwiErr* wrap_err(F&& f) {
|
||||
try {
|
||||
f();
|
||||
} catch (const UnsatisfiableConstraint& ex) {
|
||||
static const constexpr KiwiErr err {
|
||||
KiwiErrUnsatisfiableConstraint,
|
||||
"The constraint cannot be satisfied."};
|
||||
return &err;
|
||||
} catch (const UnknownConstraint& ex) {
|
||||
static const constexpr KiwiErr err {
|
||||
KiwiErrUnknownConstraint,
|
||||
"The constraint has not been added to the solver."};
|
||||
return &err;
|
||||
|
||||
} catch (const DuplicateConstraint& ex) {
|
||||
static const constexpr KiwiErr err {
|
||||
KiwiErrDuplicateConstraint,
|
||||
"The constraint has already been added to the solver."};
|
||||
return &err;
|
||||
|
||||
} catch (const UnknownEditVariable& ex) {
|
||||
static const constexpr KiwiErr err {
|
||||
KiwiErrUnknownEditVariable,
|
||||
"The edit variable has not been added to the solver."};
|
||||
return &err;
|
||||
|
||||
} catch (const DuplicateEditVariable& ex) {
|
||||
static const constexpr KiwiErr err {
|
||||
KiwiErrDuplicateEditVariable,
|
||||
"The edit variable has already been added to the solver."};
|
||||
return &err;
|
||||
|
||||
} catch (const BadRequiredStrength& ex) {
|
||||
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 ptr, F&& f) {
|
||||
if (!ptr) {
|
||||
return &kKiwiErrNullObjectArg0;
|
||||
}
|
||||
return wrap_err([&]() { f(ptr); });
|
||||
}
|
||||
|
||||
template<typename P, typename R, typename F>
|
||||
inline const KiwiErr* wrap_err(P ptr, R& ref, F&& f) {
|
||||
if (!ptr) {
|
||||
return &kKiwiErrNullObjectArg0;
|
||||
}
|
||||
return wrap_err([&]() { f(ptr, ref); });
|
||||
}
|
||||
|
||||
// inline void kiwi_var_del(KiwiVarRef var) { VariableRef(var).release(); }
|
||||
|
||||
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,
|
||||
enum KiwiRelOp 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) {
|
||||
// FIXME FIXME: this should cause a copy!
|
||||
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
|
||||
);
|
||||
}
|
||||
|
||||
int kiwi_constraint_expression(const Constraint* c, KiwiExpression* out, int out_size) {
|
||||
const auto& expr = c->expression();
|
||||
const auto& terms = expr.terms();
|
||||
const int n_terms = terms.size();
|
||||
if (!out || out_size < n_terms)
|
||||
return n_terms;
|
||||
|
||||
// FIXME FIXME FIXME: dangling pointer
|
||||
auto* p = out->terms;
|
||||
for (const auto& t : terms) {
|
||||
Variable* v = const_cast<Variable*>(&t.variable());
|
||||
*p = KiwiTerm {v, t.coefficient()};
|
||||
++p;
|
||||
}
|
||||
out->term_count = p - out->terms;
|
||||
out->constant = expr.constant();
|
||||
|
||||
return n_terms;
|
||||
}
|
||||
|
||||
inline const KiwiErr* kiwi_solver_add_constraint(KiwiSolver* s, const Constraint& constraint) {
|
||||
return wrap_err(s, constraint, [](auto* s, const auto& c) { s->solver.addConstraint(c); });
|
||||
}
|
||||
|
||||
inline const KiwiErr* kiwi_solver_remove_constraint(KiwiSolver* s, const Constraint& constraint) {
|
||||
return wrap_err(s, constraint, [](auto* s, const auto& c) { s->solver.removeConstraint(c); });
|
||||
}
|
||||
|
||||
inline const KiwiErr*
|
||||
kiwi_solver_add_edit_var(KiwiSolver* s, const Variable& var, double strength) {
|
||||
return wrap_err(s, var, [strength](auto* s, const auto& v) {
|
||||
s->solver.addEditVariable(v, strength);
|
||||
});
|
||||
}
|
||||
|
||||
inline const KiwiErr* kiwi_solver_remove_edit_var(KiwiSolver* s, const Variable& var) {
|
||||
return wrap_err(s, var, [](auto* s, const auto& v) { s->solver.removeEditVariable(v); });
|
||||
}
|
||||
|
||||
inline const KiwiErr*
|
||||
kiwi_solver_suggest_value(KiwiSolver* s, const Variable& var, double value) {
|
||||
return wrap_err(s, var, [value](auto* s, const auto& v) { s->solver.suggestValue(v, value); });
|
||||
}
|
||||
|
||||
} // namespace
|
||||
Reference in New Issue
Block a user