259 lines
6.7 KiB
C++
259 lines
6.7 KiB
C++
#include "ckiwi.h"
|
|
|
|
#include <kiwi/kiwi.h>
|
|
|
|
#include <cstring>
|
|
|
|
using namespace kiwi;
|
|
|
|
namespace {
|
|
template<class T, class CS>
|
|
class alignas(T) SharedRef {
|
|
private:
|
|
CS ref_;
|
|
|
|
public:
|
|
T* operator&() {
|
|
return reinterpret_cast<T*>(&ref_);
|
|
}
|
|
|
|
T* operator->() {
|
|
return reinterpret_cast<T*>(&ref_);
|
|
}
|
|
|
|
operator T&() {
|
|
return *reinterpret_cast<T*>(&ref_);
|
|
}
|
|
|
|
operator CS() const {
|
|
return ref_;
|
|
}
|
|
|
|
T& instance() {
|
|
return *reinterpret_cast<T*>(&ref_);
|
|
}
|
|
|
|
void destroy() {
|
|
instance().~T();
|
|
ref_ = {0};
|
|
}
|
|
|
|
SharedRef<T, CS>(CS ref) : ref_(ref) {}
|
|
|
|
template<typename... Args>
|
|
SharedRef<T, CS>(Args&&... args) {
|
|
new (&ref_) T(std::forward<Args>(args)...);
|
|
}
|
|
|
|
static_assert(sizeof(CS) >= sizeof(T), "SharedRef cannot wrap T (size)");
|
|
};
|
|
|
|
using ConstraintRef = SharedRef<Constraint, KiwiConstraintRef>;
|
|
using VariableRef = SharedRef<Variable, KiwiVarRef>;
|
|
|
|
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<typename F>
|
|
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<Term> 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<RelationalOperator>(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<KiwiRelOp>(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<Solver*>(s.impl_);
|
|
}
|
|
|
|
KiwiErr kiwi_solver_add_constraint(KiwiSolverRef s, KiwiConstraintRef constraint) {
|
|
return wrap_err([=]() {
|
|
reinterpret_cast<Solver*>(s.impl_)->addConstraint(ConstraintRef(constraint));
|
|
});
|
|
}
|
|
|
|
KiwiErr kiwi_solver_remove_constraint(KiwiSolverRef s, KiwiConstraintRef constraint) {
|
|
return wrap_err([=]() {
|
|
reinterpret_cast<Solver*>(s.impl_)->removeConstraint(ConstraintRef(constraint));
|
|
});
|
|
}
|
|
|
|
int kiwi_solver_has_constraint(KiwiSolverRef s, KiwiConstraintRef constraint) {
|
|
return reinterpret_cast<Solver*>(s.impl_)->hasConstraint(ConstraintRef(constraint));
|
|
}
|
|
|
|
KiwiErr kiwi_solver_add_edit_var(KiwiSolverRef s, KiwiVarRef var, double strength) {
|
|
return wrap_err([=]() {
|
|
reinterpret_cast<Solver*>(s.impl_)->addEditVariable(VariableRef(var), strength);
|
|
});
|
|
}
|
|
|
|
KiwiErr kiwi_solver_remove_edit_var(KiwiSolverRef s, KiwiVarRef var) {
|
|
return wrap_err([=]() {
|
|
reinterpret_cast<Solver*>(s.impl_)->removeEditVariable(VariableRef(var));
|
|
});
|
|
}
|
|
|
|
int kiwi_solver_has_edit_var(KiwiSolverRef s, KiwiVarRef var) {
|
|
return reinterpret_cast<Solver*>(s.impl_)->hasEditVariable(VariableRef(var));
|
|
}
|
|
|
|
KiwiErr kiwi_solver_suggest_value(KiwiSolverRef s, KiwiVarRef var, double value) {
|
|
return wrap_err([=]() {
|
|
reinterpret_cast<Solver*>(s.impl_)->suggestValue(VariableRef(var), value);
|
|
});
|
|
}
|
|
|
|
void kiwi_solver_update_vars(KiwiSolverRef s) {
|
|
reinterpret_cast<Solver*>(s.impl_)->updateVariables();
|
|
}
|
|
|
|
void kiwi_solver_reset(KiwiSolverRef s) {
|
|
reinterpret_cast<Solver*>(s.impl_)->reset();
|
|
}
|
|
|
|
void kiwi_solver_dump(KiwiSolverRef s) {
|
|
reinterpret_cast<Solver*>(s.impl_)->dump();
|
|
}
|
|
|
|
char* kiwi_solver_dumps(KiwiSolverRef s, void* (*alloc)(size_t)) {
|
|
const auto val = reinterpret_cast<Solver*>(s.impl_)->dumps();
|
|
const auto buf_size = val.size() + 1;
|
|
auto* buf = static_cast<char*>(alloc ? alloc(buf_size) : malloc(buf_size));
|
|
if (!buf)
|
|
return nullptr;
|
|
std::memcpy(buf, val.c_str(), val.size() + 1);
|
|
return buf;
|
|
}
|
|
|
|
} // extern "C"
|