Skip to content

Entry points

A solver library exports exactly thirteen symbols, each named fznso_<name>_… after the library’s own name: fznso_gecode_solver_run for a solver named gecode.

All thirteen are mandatory. A loader looks every one of them up when opening a library, and a missing symbol fails the load.

These describe the library itself. They take no solver instance, and may be called before one exists.

SymbolReturnsPurpose
fznso_<name>_abi_versionuint32_tThe protocol revision this solver was built against. Checked first; see ABI stability.
fznso_<name>_constraint_listFznsoConstraintListConstraint identifiers and argument types the solver accepts.
fznso_<name>_decision_listFznsoTypeListDecision-variable types the solver can create.
fznso_<name>_objective_listFznsoObjectiveListObjective strategies the solver can pursue.
fznso_<name>_option_listFznsoOptionListOptions the solver accepts, with types and defaults.
fznso_<name>_statistic_listFznsoStatisticListStatistics the solver can report, and whether each comes from a solution, the solver, or both.

The five lists are a solver’s declaration of what it can do. An application must not use a name that does not appear in them, and a solver must not accept one. Where a name has a registry entry, declaring it means committing to the registry’s meaning.

Every list is static: the arrays stay valid for as long as the library is loaded, and the caller never frees them.

FznsoConstraintType carries an arg_types array, and those types are the solver’s own answer, not a copy of the registry’s signature. A solver may declare an argument type narrower than the registry’s — usually to say an argument is only handled as a parameter:

registry int_in(var int: x, var set of int: values)
a solver int_in(var int: x, set of int: values)

Narrowing applies to every part of a type (var int to int, opt int to int), and is the type qualifier relation read as “accepts fewer things”. That is still the same constraint, restricted to what the solver’s propagator can do — so the obligation lands on the application: having read arg_types, it must not pass a decision variable where the solver asked for a parameter.

SymbolPurpose
fznso_<name>_solver_createCreate an instance. Returns FznsoSolver *, never null.
fznso_<name>_solver_freeDestroy an instance and release everything it holds.
fznso_<name>_solver_option_setSet one option. Returns false on failure.
fznso_<name>_solver_option_getRead one option’s current value.
fznso_<name>_solver_statisticRead one statistic from the instance.
fznso_<name>_solver_read_errorHand the most recent error message to a callback.
fznso_<name>_solver_runSearch. See Running a search.

An instance is created, configured, run any number of times, and freed. Options may be changed between runs. Only fznso_<name>_solver_run is expected to take a long time.

Two entry points can fail in a way that carries a message: option_set returns false, and run returns FznsoError. Neither returns the reason directly. The caller then calls fznso_<name>_solver_read_error, which passes the message to a callback rather than returning it, so the message need only stay valid for the duration of that callback.

This is the only error channel. The on_message callback during a run is not for failures.

fznso_<name>_solver_create cannot fail. It returns a valid instance even when the solver cannot work, for example because a licence is missing. The instance records the failure and reports it through the channel above: the next option_set returns false, or the next run returns FznsoError. The application therefore always has an instance to call fznso_<name>_solver_read_error on, and to free.

Copy the template and fill it in; it is not a library to link against.

  • ABI entry points — the full contract on each, generated from the template
  • c/fznso_solver_template.c — the C template, generated by cbindgen
  • FZNSO_EXPORT_SOLVER — C++, which writes all thirteen for you
  • fznso_export! — Rust, likewise