#include "ckiwi.h" #include #include #include using namespace kiwi; namespace { 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; } static const constexpr KiwiErr kKiwiErrUnhandledCxxException { KiwiErrUnknown, "An unhandled C++ exception occurred." }; static const constexpr KiwiErr kKiwiErrNullObjectArg0 { KiwiErrNullObject, "null object passed as argument #0 (self)" }; static 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; } else if (!ref) { return &kKiwiErrNullObjectArg1; } return wrap_err([&]() { f(ptr, ref); }); } } // namespace extern "C" { KiwiVarRef kiwi_var_new(const char* name) { return make_var_cref(name ? name : ""); } void kiwi_var_del(KiwiVarRef var) { VariableRef(var).release(); } KiwiVarRef kiwi_var_clone(KiwiVarRef var) { return VariableRef(var).clone(); } const char* kiwi_var_name(KiwiVarRef var) { const VariableRef self(var); return self ? self->name().c_str() : "()"; } void kiwi_var_set_name(KiwiVarRef var, const char* name) { VariableRef self(var); if (self) self->setName(name); } double kiwi_var_value(KiwiVarRef var) { const VariableRef self(var); return self ? self->value() : std::numeric_limits::quiet_NaN(); } void kiwi_var_set_value(KiwiVarRef var, double value) { VariableRef self(var); if (self) self->setValue(value); } bool kiwi_var_eq(KiwiVarRef var, KiwiVarRef other) { ConstVariableRef self(var); // const defect in upstream const VariableRef other_ref(other); return self && other_ref && self->equals(other_ref); } void kiwi_expression_del_vars(KiwiExpression* expr) { if (!expr) return; for (auto* t = expr->terms; t != expr->terms + expr->term_count; ++t) { VariableRef(t->var).release(); } } KiwiConstraintRef kiwi_constraint_new( const KiwiExpression* lhs, const KiwiExpression* rhs, enum KiwiRelOp op, double strength ) { 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) { for (auto* t = lhs->terms; t != lhs->terms + lhs->term_count; ++t) { ConstVariableRef var(t->var); if (var) terms.emplace_back(var.cref(), t->coefficient); } } if (rhs) { for (auto* t = rhs->terms; t != rhs->terms + rhs->term_count; ++t) { ConstVariableRef var(t->var); if (var) terms.emplace_back(var.cref(), -t->coefficient); } } return make_constraint_cref( Expression(std::move(terms), (lhs ? lhs->constant : 0.0) - (rhs ? rhs->constant : 0.0)), static_cast(op), strength ); } void kiwi_constraint_del(KiwiConstraintRef constraint) { ConstraintRef(constraint).release(); } KiwiConstraintRef kiwi_constraint_clone(KiwiConstraintRef constraint) { return ConstraintRef(constraint).clone(); } double kiwi_constraint_strength(KiwiConstraintRef constraint) { const ConstraintRef self(constraint); return self ? self->strength() : std::numeric_limits::quiet_NaN(); } enum KiwiRelOp kiwi_constraint_op(KiwiConstraintRef constraint) { const ConstraintRef self(constraint); return self ? static_cast(self->op()) : KiwiRelOp::KIWI_OP_EQ; } bool kiwi_constraint_violated(KiwiConstraintRef constraint) { const ConstraintRef self(constraint); return self ? self->violated() : 0; } int kiwi_constraint_expression(KiwiConstraintRef constraint, KiwiExpression* out, int out_size) { const ConstraintRef self(constraint); if (!self) return 0; const auto& expr = self->expression(); const auto& terms = expr.terms(); const int n_terms = terms.size(); if (!out || out_size < n_terms) return n_terms; auto* p = out->terms; for (const auto& t : terms) { *p = KiwiTerm {make_var_cref(t.variable()), t.coefficient()}; ++p; } out->term_count = p - out->terms; out->constant = expr.constant(); return n_terms; } struct KiwiSolver { unsigned error_mask; Solver solver; }; KiwiSolver* kiwi_solver_new(unsigned error_mask) { return new KiwiSolver {error_mask}; } void kiwi_solver_del(KiwiSolver* s) { if (s) delete s; } const KiwiErr* kiwi_solver_add_constraint(KiwiSolver* s, KiwiConstraintRef constraint) { return wrap_err(s, ConstraintRef(constraint), [](auto* s, const auto c) { s->solver.addConstraint(c); }); } const KiwiErr* kiwi_solver_remove_constraint(KiwiSolver* s, KiwiConstraintRef constraint) { return wrap_err(s, ConstraintRef(constraint), [](auto* s, const auto c) { s->solver.removeConstraint(c); }); } bool kiwi_solver_has_constraint(const KiwiSolver* s, KiwiConstraintRef constraint) { ConstraintRef c(constraint); if (!s || !c) return 0; return s->solver.hasConstraint(c); } const KiwiErr* kiwi_solver_add_edit_var(KiwiSolver* s, KiwiVarRef var, double strength) { return wrap_err(s, VariableRef(var), [strength](auto* s, const auto v) { s->solver.addEditVariable(v, strength); }); } const KiwiErr* kiwi_solver_remove_edit_var(KiwiSolver* s, KiwiVarRef var) { return wrap_err(s, VariableRef(var), [](auto* s, const auto v) { s->solver.removeEditVariable(v); }); } bool kiwi_solver_has_edit_var(const KiwiSolver* s, KiwiVarRef var) { VariableRef v(var); if (!s || !v) return 0; return s->solver.hasEditVariable(v); } const KiwiErr* kiwi_solver_suggest_value(KiwiSolver* s, KiwiVarRef var, double value) { return wrap_err(s, VariableRef(var), [value](auto* s, const auto v) { s->solver.suggestValue(v, value); }); } void kiwi_solver_update_vars(KiwiSolver* s) { if (s) s->solver.updateVariables(); } void kiwi_solver_reset(KiwiSolver* s) { if (s) s->solver.reset(); } void kiwi_solver_dump(const KiwiSolver* s) { if (s) s->solver.dump(); } char* kiwi_solver_dumps(const KiwiSolver* s) { if (!s) return nullptr; const auto str = s->solver.dumps(); // upstream library defect const auto buf_size = str.size() + 1; auto* buf = static_cast(std::malloc(buf_size)); if (!buf) return nullptr; std::memcpy(buf, str.c_str(), str.size() + 1); return buf; } } // extern "C"