Skip to content

Lifetimes & ownership

These four rules apply to every declaration in the interface. They are stated here once so that individual functions need only name their own preconditions.

FznsoModel, FznsoValue, FznsoSolution, FznsoAnnotation and FznsoSolver are handles, meaningful only to the side that created one. The receiver passes a handle back to the callbacks it arrived with, and never inspects or dereferences it.

A handle need not address memory at all. A small payload such as a decision index is commonly stored in the pointer word rather than behind it, so that passing one across the interface never requires an allocation, and a null handle is a legitimate value rather than an error.

Reach the contents through the methods table of the matching …Ref struct (FznsoValueRef, FznsoModelRef, FznsoSolutionRef, FznsoAnnotationRef), which pairs the handle with the callbacks that can read it.

Most methods entries are meaningful only in a particular state:

  • a get_* on a value requires kind() to return the matching FznsoValueKind;
  • an index argument requires a value below the matching length callback.

An implementation is not required to check. Violating a precondition may read nonsense, or abort the process. Read kind, or the relevant length, first, and call only what the answer permits.

Every pointer, string and handle crossing the interface is borrowed. The receiver never frees it, and must not keep it past the window in which it is valid:

WhatValid for
A capability list (fznso_<name>_constraint_list and friends)as long as the library is loaded
A value, string or annotation read from a model, solution or annotationas long as that handle
A solutionthe duration of the on_solution callback only
The scope and value passed to on_messagethe duration of that callback only
The model passed to fznso_<name>_solver_runthe duration of that call
A value from fznso_<name>_solver_option_get or …_solver_statisticuntil the next call that can change the solver

Copy anything that has to outlive its window. A consumer that wants to keep a solution must copy the values out before returning from on_solution; a solver that wants to keep part of a model must copy it before returning from run.

Strings are a common trap: FznsoStr and the bytes behind FznsoValueMethods::as_string are not null-terminated, and are not yours. Use the length, and copy before storing.

An application rejects a solver whose fznso_<name>_abi_version differs from its own, before calling any other entry point. There is no forward or backward compatibility between revisions. See ABI stability.