#include "ckiwi.h" #include #include using namespace kiwi; namespace { template class alignas(T) SharedRef { private: CS ref_; public: T* operator&() { return reinterpret_cast(&ref_); } T* operator->() { return reinterpret_cast(&ref_); } operator T&() { return *reinterpret_cast(&ref_); } operator CS() const { return ref_; } T& instance() { return *reinterpret_cast(&ref_); } void destroy() { instance().~T(); ref_ = {0}; } SharedRef(CS ref) : ref_(ref) {} template SharedRef(Args&&... args) { new (&ref_) T(std::forward(args)...); } static_assert(sizeof(CS) >= sizeof(T), "SharedRef cannot wrap T (size)"); }; using ConstraintRef = SharedRef; using VariableRef = SharedRef; KiwiErr make_error(KiwiErrKind kind, const std::exception& ex) { constexpr auto max_n = sizeof(KiwiErr::message) - 1; const auto n = std::min(std::strlen(ex.what()), max_n); KiwiErr err {kind}; std::memcpy(err.message, ex.what(), n); if (n == max_n) err.message[max_n] = 0; return err; } KiwiErr make_error(KiwiErrKind kind) { return KiwiErr {kind, {0}}; } template inline KiwiErr wrap_err(F&& f) { try { f(); } catch (const UnsatisfiableConstraint& err) { return make_error(KiwiErrUnsatisfiableConstraint, err); } catch (const UnknownConstraint& err) { return make_error(KiwiErrUnknownConstraint, err); } catch (const DuplicateConstraint& err) { return make_error(KiwiErrDuplicateConstraint, err); } catch (const UnknownEditVariable& err) { return make_error(KiwiErrUnknownEditVariable, err); } catch (const DuplicateEditVariable& err) { return make_error(KiwiErrDuplicateEditVariable, err); } catch (const BadRequiredStrength& err) { return make_error(KiwiErrBadRequiredStrength, err); } catch (const InternalSolverError& err) { return make_error(KiwiErrInternalSolverError, err); } catch (std::bad_alloc&) { return make_error(KiwiErrAlloc); } catch (const std::exception& err) { return make_error(KiwiErrUnknown, err); } catch (...) { return make_error(KiwiErrUnknown); } return make_error(KiwiErrNone); } } // namespace extern "C" { KiwiVarRef kiwi_var_new(const char* name) { return VariableRef(name); } void kiwi_var_del(KiwiVarRef var) { VariableRef(var).destroy(); } const char* kiwi_var_name(KiwiVarRef var) { return VariableRef(var)->name().c_str(); } void kiwi_var_set_name(KiwiVarRef var, const char* name) { VariableRef(var)->setName(name); } double kiwi_var_value(KiwiVarRef var) { return VariableRef(var)->value(); } void kiwi_var_set_value(KiwiVarRef var, double value) { VariableRef(var)->setValue(value); } int kiwi_var_eq(KiwiVarRef var, KiwiVarRef other) { return VariableRef(var)->equals(VariableRef(other)); } KiwiConstraintRef kiwi_constraint_new( const KiwiExpression* expression, enum KiwiRelOp op, double strength ) { if (strength < 0.0) { strength = kiwi::strength::required; } std::vector terms; terms.reserve(expression->term_count); for (auto* t = expression->terms; t != expression->terms + expression->term_count; ++t) { terms.emplace_back(VariableRef(t->var), t->coefficient); } return ConstraintRef( Expression(std::move(terms), expression->constant), static_cast(op), strength ); } void kiwi_constraint_del(KiwiConstraintRef constraint) { ConstraintRef(constraint).destroy(); } double kiwi_constraint_strength(KiwiConstraintRef constraint) { return ConstraintRef(constraint)->strength(); } enum KiwiRelOp kiwi_constraint_op(KiwiConstraintRef constraint) { return static_cast(ConstraintRef(constraint)->op()); } int kiwi_constraint_violated(KiwiConstraintRef constraint) { return ConstraintRef(constraint)->violated(); } int kiwi_constraint_expression( KiwiConstraintRef constraint, KiwiExpression* out, int out_size ) { const auto& expr = ConstraintRef(constraint).instance().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 {VariableRef(t.variable()), t.coefficient()}; ++p; } out->term_count = p - out->terms; out->constant = expr.constant(); return n_terms; } KiwiSolverRef kiwi_solver_new() { return KiwiSolverRef {new (std::nothrow) Solver()}; } void kiwi_solver_del(KiwiSolverRef s) { delete reinterpret_cast(s.impl_); } KiwiErr kiwi_solver_add_constraint(KiwiSolverRef s, KiwiConstraintRef constraint) { return wrap_err([=]() { reinterpret_cast(s.impl_)->addConstraint(ConstraintRef(constraint)); }); } KiwiErr kiwi_solver_remove_constraint(KiwiSolverRef s, KiwiConstraintRef constraint) { return wrap_err([=]() { reinterpret_cast(s.impl_)->removeConstraint(ConstraintRef(constraint)); }); } int kiwi_solver_has_constraint(KiwiSolverRef s, KiwiConstraintRef constraint) { return reinterpret_cast(s.impl_)->hasConstraint(ConstraintRef(constraint)); } KiwiErr kiwi_solver_add_edit_var(KiwiSolverRef s, KiwiVarRef var, double strength) { return wrap_err([=]() { reinterpret_cast(s.impl_)->addEditVariable(VariableRef(var), strength); }); } KiwiErr kiwi_solver_remove_edit_var(KiwiSolverRef s, KiwiVarRef var) { return wrap_err([=]() { reinterpret_cast(s.impl_)->removeEditVariable(VariableRef(var)); }); } int kiwi_solver_has_edit_var(KiwiSolverRef s, KiwiVarRef var) { return reinterpret_cast(s.impl_)->hasEditVariable(VariableRef(var)); } KiwiErr kiwi_solver_suggest_value(KiwiSolverRef s, KiwiVarRef var, double value) { return wrap_err([=]() { reinterpret_cast(s.impl_)->suggestValue(VariableRef(var), value); }); } void kiwi_solver_update_vars(KiwiSolverRef s) { reinterpret_cast(s.impl_)->updateVariables(); } void kiwi_solver_reset(KiwiSolverRef s) { reinterpret_cast(s.impl_)->reset(); } void kiwi_solver_dump(KiwiSolverRef s) { reinterpret_cast(s.impl_)->dump(); } char* kiwi_solver_dumps(KiwiSolverRef s, void* (*alloc)(size_t)) { const auto val = reinterpret_cast(s.impl_)->dumps(); const auto buf_size = val.size() + 1; auto* buf = static_cast(alloc ? alloc(buf_size) : malloc(buf_size)); if (!buf) return nullptr; std::memcpy(buf, val.c_str(), val.size() + 1); return buf; } } // extern "C"