#include #include #include 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 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)...); return cref; } template inline decltype(auto) make_var_cref(Args&&... args) { return make_cref(std::forward(args)...); } template inline decltype(auto) make_constraint_cref(Args&&... args) { return make_cref(std::forward(args)...); } template class SharedRef { private: R& cref_; public: explicit SharedRef(R& cref) : cref_(cref) {} static_assert( sizeof(R) >= sizeof(T), // NOLINT(bugprone-sizeof-expression) "SharedRef CS too small for T" ); R clone() const { return make_cref(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; using VariableRef = SharedRef; using ConstVariableRef = const SharedRef; 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(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(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 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 inline const KiwiErr* wrap_err(P ptr, F&& f) { if (!ptr) { return &kKiwiErrNullObjectArg0; } return wrap_err([&]() { f(ptr); }); } template 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 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(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(&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