Files
ljkiwi/ckiwi/ckiwi.cpp

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"