Skip to content

C++ bindings

Header-only C++17 bindings for the consumer side: #include "fznso.hpp", add c/ and cpp/ to your include path, and link nothing.

Implementing a solver in C++ is fznso_export.hpp instead.

23 classs

class fznso::AnnotationRefsource

An annotation attached to a decision, constraint or objective.

methodAnnotationRef(FznsoAnnotationRef raw)
methodAnnotationRef(const A &source)

Borrow an AnnotationSource.

The source must outlive this reference.

Dispatch is static: the vtable is specialised to A, so A’s own methods are called directly.

methodstd::string_view ident() const

The annotation’s identifier.

methodstd::size_t size() const

The number of arguments.

method`Value` operator[](std::size_t index) const

The argument at index.

methodconst FznsoAnnotationRef & raw() const

The underlying handle, for passing back across the interface.

class fznso::AnnotationSourcesource

Produce an AnnotationRef from a type of your own.

The annotation counterpart to ValueSource: implement this to present a downstream annotation type across the interface, and borrow it with AnnotationRef{source}. The reference borrows the source, which must outlive it.

method~AnnotationSource()=default
methodstd::string_view ident() const =

The annotation’s identifier.

methodstd::size_t size() const =

The number of arguments.

method`Value` argument(std::size_t index) const =

The argument at index.

struct fznso::Constraintsource

The index of a constraint within a model.

fieldstd::size_t index
struct fznso::Decisionsource

The index of a decision variable within a model.

fieldstd::size_t index
class fznso::DynSolversource

A solver instance created from a loaded Library.

methodDynSolver(const DynSolver &)=delete
method`DynSolver` & operator=(const DynSolver &)=delete
methodDynSolver(DynSolver &&other) noexcept
method~DynSolver()
method`Value` option_get(std::string_view name) const

The current value of a named option.

methodstd::optional< std::string > option_set(std::string_view name, const Value &value)

Set a named option. Returns the solver’s message on failure.

method`Value` statistic(std::string_view name) const

The current value of a named solver-level statistic, or absent if unknown.

Only statistics the library declares with the solver flag set are readable here; those carrying only the solution flag are read from a Solution instead.

method`Status` run(const M &model, OnSolution &&on_solution, OnMessage &&on_message, ShouldStop &&should_stop)

Run the solver over model, reporting solutions and messages, and polling should_stop to decide whether to keep going.

model may be any Model; passing a concrete type lets ref() be resolved statically. The handlers are called directly, not through a std::function.

should_stop returns true to abandon the search, which yields Status::Kind::Incomplete. It is how a caller ends a run it has already started: return true from inside on_solution for “one solution is enough”, or flip an atomic from another thread to cancel a long search. Unlike the handlers it may be polled concurrently from several threads, so it must be thread-safe — reading a std::atomic<bool>, the usual implementation, already is.

method`Status` run(const M &model, OnSolution &&on_solution, OnMessage &&on_message)

Run the solver to completion, reporting solutions and messages.

Passes no stop predicate, so the solver is told it will never be cancelled.

method`Status` run(const M &model, OnSolution &&on_solution)

Run the solver to completion, ignoring any messages.

Passes neither a message sink nor a stop predicate, so the solver knows to skip building diagnostics and to skip polling.

class fznso::FloatRangessource

The ranges of a float set, borrowed from the value that produced them.

methodFloatRanges(Value value)
methodstd::size_t size() const
methodbool empty() const
method`Range`< double > operator[](std::size_t index) const
class fznso::IntRangessource

The ranges of an integer set, borrowed from the value that produced them.

methodIntRanges(Value value)
methodstd::size_t size() const
methodbool empty() const
method`Range`< std::int64_t > operator[](std::size_t index) const
class fznso::LayeredModelsource

An in-memory Model, built up in layers.

Decisions and constraints are added to the top layer, which can be pushed and popped until it is marked permanent.

methodLayeredModel()
methodLayeredModel(const LayeredModel &)=delete
method`LayeredModel` & operator=(const LayeredModel &)=delete
method`Decision` add_decision(FznsoType type, OwnedValue domain, std::optional< std::string > name=std::nullopt, bool defined=false, bool in_solution=true, std::vector< OwnedAnnotation > annotations={})

Add a decision variable to the top layer.

type says what the variable is — build one with decision_type — and domain which values it may take, which may be absent.

method`Constraint` add_constraint(std::string ident, std::vector< OwnedValue > arguments, std::optional< Decision > defines=std::nullopt, std::vector< OwnedAnnotation > annotations={})

Add a constraint to the top layer.

methodvoid set_objective(std::string ident, OwnedValue argument={}, std::vector< OwnedAnnotation > annotations={})

Set the objective. Pass an empty identifier for a satisfaction problem.

methodvoid push_layer()

Push a new, empty layer.

methodvoid pop_layer()

Pop the top layer. Throws if it is permanent.

methodvoid mark_permanent()

Mark every current layer as permanent, so it can never be popped.

methodvoid mark_redundant(std::size_t layer)

Mark a permanent layer as redundant, letting the solver discard it.

methodvoid set_unchanged(std::size_t count)

Declare how many layers are unchanged since the last run.

methodstd::size_t layer_count() const override

The number of layers currently in the model.

methodstd::size_t layer_permanent() const override

How many layers are permanently committed, and so can never be popped.

methodstd::size_t layer_unchanged() const override

How many layers are unchanged since the last run.

methodstd::size_t layer_redundant_count() const override

How many permanent layers have been marked redundant.

methodstd::size_t layer_redundant_index(std::size_t n) const override

The layer index of the n-th redundant layer.

methodstd::size_t decision_count() const override

The total number of decision variables.

methodstd::size_t decision_layer_end(std::size_t layer) const override

One past the last decision index belonging to layers 0..=layer.

methodFznsoType decision_type(Decision d) const override

The type of a decision variable.

What the variable is, as opposed to decision_domain, which says which values it may take: an absent domain is equally a var bool and an unbounded var int, and an integer range list is equally a var int and the upper bound of a var set of int. Build one with decision_type.

method`Value` decision_domain(Decision d) const override

The domain of a decision variable.

methodstd::optional< std::string_view > decision_name(Decision d) const override

The name of a decision variable, if it has one.

methodbool decision_defined(Decision d) const override

Whether a decision variable is functionally defined by a constraint.

methodbool decision_in_solution(Decision d) const override

Whether a solution may be asked for a decision variable’s value.

A solver must give every such variable a value in every solution it reports, and is free to leave the others open. Having a name does not make a variable needed: names exist only for debugging.

methodstd::size_t decision_annotation_count(Decision d) const override

The number of annotations on a decision variable.

method`AnnotationRef` decision_annotation(Decision d, std::size_t index) const override

The index-th annotation on a decision variable.

methodstd::size_t constraint_count() const override

The total number of constraints.

methodstd::size_t constraint_layer_end(std::size_t layer) const override

One past the last constraint index belonging to layers 0..=layer.

methodstd::string_view constraint_ident(Constraint c) const override

The identifier of a constraint.

methodstd::size_t constraint_argument_count(Constraint c) const override

The number of arguments of a constraint.

method`Value` constraint_argument(Constraint c, std::size_t index) const override

The index-th argument of a constraint.

methodstd::optional< `Decision` > constraint_defines(Constraint c) const override

The decision variable this constraint functionally defines, if any.

methodstd::size_t constraint_annotation_count(Constraint c) const override

The number of annotations on a constraint.

method`AnnotationRef` constraint_annotation(Constraint c, std::size_t index) const override

The index-th annotation on a constraint.

methodstd::string_view objective_ident() const override

The objective identifier, or empty for a satisfaction problem.

method`Value` objective_arg() const override

The objective’s argument.

methodstd::size_t objective_annotation_count() const override

The number of annotations on the objective.

method`AnnotationRef` objective_annotation(std::size_t index) const override

The index-th annotation on the objective.

class fznso::Librarysource

A dynamically loaded solver library.

The library owns every symbol a DynSolver created from it uses, so it must outlive them.

methodstd::vector< std::filesystem::path > search_paths()

The directories searched for solver libraries, in search order: $FZNSO_SOLVER_PATH, then the per-user directory, then the system ones.

Solvers live in their own fznso directory rather than on the normal library path: a shim is usually named after the solver it wraps, so libgecode.so would otherwise collide with real Gecode, and a dedicated directory is what makes discover possible at all.

methodstd::vector< `Discovered` > discover()

List the solver libraries on the search path, without loading any.

Directories are visited in search_paths order and, within a directory, the highest version of a solver comes first. Nothing is opened, so the ABI version is not checked here — find skips a candidate built for another ABI.

methodstd::unique_ptr< `Library` > find(std::string_view name)

Load a solver by name from the search path, choosing the newest version.

Candidates are tried in discover order: directory precedence first (a solver in $FZNSO_SOLVER_PATH or the per-user directory overrides a system one even if older), then newest version among equals. A candidate built for a different ABI is skipped and the search continues. Throws std::runtime_error if no matching solver loads.

methodstd::unique_ptr< `Library` > find_version(std::string_view name, std::string_view version)

Load a solver by name and version from the search path.

version selects by whole dotted components, so "6" matches 6.2.1; an empty string behaves like find. The newest match is chosen.

methodLibrary(const char *path)

Dynamically load a solver library, resolving every entry point it must export.

The entry points are fznso_<name>_..., where <name> is recovered from the file name by stripping a leading lib and everything from the first .; this is the only supported naming, so the file name and the exported symbols must agree.

A solver’s name has to satisfy both C and the platform’s library naming at once, so:

  1. It must be a valid C identifier: ASCII letters, digits and underscores, not starting with a digit. Lowercase is the convention. No -, no ., nothing non-ASCII — those cannot appear in a symbol.
  2. It must not begin with lib, which could not be told apart from the platform prefix: libssat reads back as libssat from liblibssat.so but as ssat from libssat.dll.
  3. The file’s base name must be <name>, optionally prefixed with lib, followed by any version and extension components. Everything from the first . is ignored, so <name> must not contain a ..

The version goes between the name and the extension on every platform (libgecode.6.2.1.so, libgecode.6.2.1.dylib, gecode.6.2.1.dll), rather than following each platform’s native library convention, so one rule covers all three. Nothing links against a solver — it is opened by path from a directory off the linker search path — so the ELF libfoo.so.MAJOR soname scheme buys nothing here; the native form is still parsed, so a distribution-packaged libgecode.so.6 also loads.

Several versions of one solver can sit side by side (libgecode.6.so, libgecode.7.so). They export the same symbols, but libraries are opened privately (RTLD_LOCAL, and Windows resolves per module), so several can be loaded at once; pass the exact path, or use find_version, to choose one.

Throws std::runtime_error if the file name yields an unusable name, or if the library cannot be opened or is missing a symbol.

methodLibrary(const Library &)=delete
method`Library` & operator=(const Library &)=delete
method~Library()
method`DynSolver` create_solver() const

Create a solver instance.

methodFznsoConstraintList constraint_types() const
methodFznsoTypeList decision_types() const
methodFznsoObjectiveList objectives() const
methodFznsoOptionList options() const
methodFznsoStatisticList statistics() const
struct fznso::Library::AbiMismatchsource

Thrown when a solver reports an ABI version this header cannot use.

A subclass of std::runtime_error, so existing handlers still catch it.

struct fznso::Library::Discoveredsource

A solver library found on the search path, reported without loading it.

fieldstd::string name
fieldstd::string version

Parsed from the file name; empty if none.

fieldstd::filesystem::path path
class fznso::Modelsource

The read-only view of a model that a solver is given.

This is the interface, not an implementation: subclass it to expose a model you already hold in some other form, or use LayeredModel to build one. A model is organised into layers, matching the incremental-solving concept in the interface.

method~Model()=default
methodstd::size_t layer_count() const =

The number of layers currently in the model.

methodstd::size_t layer_permanent() const =

How many layers are permanently committed, and so can never be popped.

methodstd::size_t layer_unchanged() const =

How many layers are unchanged since the last run.

methodstd::size_t layer_redundant_count() const =

How many permanent layers have been marked redundant.

methodstd::size_t layer_redundant_index(std::size_t n) const =

The layer index of the n-th redundant layer.

methodstd::size_t decision_count() const =

The total number of decision variables.

methodstd::size_t decision_layer_end(std::size_t layer) const =

One past the last decision index belonging to layers 0..=layer.

methodFznsoType decision_type(Decision decision) const =

The type of a decision variable.

What the variable is, as opposed to decision_domain, which says which values it may take: an absent domain is equally a var bool and an unbounded var int, and an integer range list is equally a var int and the upper bound of a var set of int. Build one with decision_type.

method`Value` decision_domain(Decision decision) const =

The domain of a decision variable.

methodstd::optional< std::string_view > decision_name(Decision decision) const =

The name of a decision variable, if it has one.

methodbool decision_defined(Decision decision) const =

Whether a decision variable is functionally defined by a constraint.

methodbool decision_in_solution(Decision decision) const =

Whether a solution may be asked for a decision variable’s value.

A solver must give every such variable a value in every solution it reports, and is free to leave the others open. Having a name does not make a variable needed: names exist only for debugging.

methodstd::size_t decision_annotation_count(Decision decision) const =

The number of annotations on a decision variable.

method`AnnotationRef` decision_annotation(Decision decision, std::size_t index) const =

The index-th annotation on a decision variable.

methodstd::size_t constraint_count() const =

The total number of constraints.

methodstd::size_t constraint_layer_end(std::size_t layer) const =

One past the last constraint index belonging to layers 0..=layer.

methodstd::string_view constraint_ident(Constraint constraint) const =

The identifier of a constraint.

methodstd::size_t constraint_argument_count(Constraint constraint) const =

The number of arguments of a constraint.

method`Value` constraint_argument(Constraint constraint, std::size_t index) const =

The index-th argument of a constraint.

methodstd::optional< `Decision` > constraint_defines(Constraint constraint) const =

The decision variable this constraint functionally defines, if any.

methodstd::size_t constraint_annotation_count(Constraint constraint) const =

The number of annotations on a constraint.

method`AnnotationRef` constraint_annotation(Constraint constraint, std::size_t index) const =

The index-th annotation on a constraint.

methodstd::string_view objective_ident() const =

The objective identifier, or empty for a satisfaction problem.

method`Value` objective_arg() const =

The objective’s argument.

methodstd::size_t objective_annotation_count() const =

The number of annotations on the objective.

method`AnnotationRef` objective_annotation(std::size_t index) const =

The index-th annotation on the objective.

class fznso::ModelRefAdaptersource

Presents a model received across the interface as a Model.

This is what a solver implemented in C++ reads its input through.

methodModelRefAdapter(FznsoModelRef raw)
methodstd::size_t layer_count() const override

The number of layers currently in the model.

methodstd::size_t layer_permanent() const override

How many layers are permanently committed, and so can never be popped.

methodstd::size_t layer_unchanged() const override

How many layers are unchanged since the last run.

methodstd::size_t layer_redundant_count() const override

How many permanent layers have been marked redundant.

methodstd::size_t layer_redundant_index(std::size_t n) const override

The layer index of the n-th redundant layer.

methodstd::size_t decision_count() const override

The total number of decision variables.

methodstd::size_t decision_layer_end(std::size_t layer) const override

One past the last decision index belonging to layers 0..=layer.

methodFznsoType decision_type(Decision d) const override

The type of a decision variable.

What the variable is, as opposed to decision_domain, which says which values it may take: an absent domain is equally a var bool and an unbounded var int, and an integer range list is equally a var int and the upper bound of a var set of int. Build one with decision_type.

method`Value` decision_domain(Decision d) const override

The domain of a decision variable.

methodstd::optional< std::string_view > decision_name(Decision d) const override

The name of a decision variable, if it has one.

methodbool decision_defined(Decision d) const override

Whether a decision variable is functionally defined by a constraint.

methodbool decision_in_solution(Decision d) const override

Whether a solution may be asked for a decision variable’s value.

A solver must give every such variable a value in every solution it reports, and is free to leave the others open. Having a name does not make a variable needed: names exist only for debugging.

methodstd::size_t decision_annotation_count(Decision d) const override

The number of annotations on a decision variable.

method`AnnotationRef` decision_annotation(Decision d, std::size_t index) const override

The index-th annotation on a decision variable.

methodstd::size_t constraint_count() const override

The total number of constraints.

methodstd::size_t constraint_layer_end(std::size_t layer) const override

One past the last constraint index belonging to layers 0..=layer.

methodstd::string_view constraint_ident(Constraint c) const override

The identifier of a constraint.

methodstd::size_t constraint_argument_count(Constraint c) const override

The number of arguments of a constraint.

method`Value` constraint_argument(Constraint c, std::size_t index) const override

The index-th argument of a constraint.

methodstd::optional< `Decision` > constraint_defines(Constraint c) const override

The decision variable this constraint functionally defines, if any.

methodstd::size_t constraint_annotation_count(Constraint c) const override

The number of annotations on a constraint.

method`AnnotationRef` constraint_annotation(Constraint c, std::size_t index) const override

The index-th annotation on a constraint.

methodstd::string_view objective_ident() const override

The objective identifier, or empty for a satisfaction problem.

method`Value` objective_arg() const override

The objective’s argument.

methodstd::size_t objective_annotation_count() const override

The number of annotations on the objective.

method`AnnotationRef` objective_annotation(std::size_t index) const override

The index-th annotation on the objective.

struct fznso::OwnedAnnotationsource

An owned annotation: the counterpart to AnnotationRef, which only borrows.

Its arguments are OwnedValues.

It is itself an AnnotationSource, so an AnnotationRef borrowing it is just AnnotationRef{owned}.

fieldstd::string ident_
fieldstd::vector< `OwnedValue` > arguments_
methodOwnedAnnotation()=default
methodOwnedAnnotation(std::string ident, std::vector< OwnedValue > arguments)
methodstd::string_view ident() const override

The annotation’s identifier.

methodstd::size_t size() const override

The number of arguments.

method`Value` argument(std::size_t index) const override

The argument at index.

struct fznso::OwnedValuesource

An owned value: the counterpart to Value, which only borrows.

Holds any of the shapes a value can take. Because it owns its storage, it can outlive whatever it was built from — which is what a model needs, since it is queried long after it is built. Construct one from a Value to copy a borrowed value into owned storage.

It is itself a ValueSource, so a Value borrowing it is just Value{owned}.

fieldstd::variant< `Absent`, bool, std::int64_t, double, std::string, `Decision`, `Constraint`, std::vector< `Range`< std::int64_t > >, std::vector< `Range`< double > >, std::vector< `OwnedValue` > > payload
methodOwnedValue()
methodOwnedValue(bool v)
methodOwnedValue(std::int64_t v)
methodOwnedValue(double v)
methodOwnedValue(std::string v)
methodOwnedValue(const char *v)
methodOwnedValue(Decision v)
methodOwnedValue(Constraint v)
methodOwnedValue(std::vector< Range< std::int64_t > > v)
methodOwnedValue(std::vector< Range< double > > v)
methodOwnedValue(std::vector< OwnedValue > v)
methodOwnedValue(std::optional< T > v)

An optional value: std::nullopt is absent, a present value takes the kind of T.

methodOwnedValue(const Value &value)

Copy a borrowed Value into owned storage, recursively.

methodFznsoValueKind kind() const override

Which payload this value holds. Every other method must agree with it.

methodbool as_bool() const override
methodstd::int64_t as_int() const override
methoddouble as_float() const override
methodstd::string_view as_string() const override

The string payload; the returned view must outlive the value.

method`Decision` as_decision() const override
method`Constraint` as_constraint() const override
methodstd::size_t size() const override

The number of list elements or set ranges; zero for anything else.

A string’s length is taken from as_string, so a string source need not implement this — only list and set sources do.

method`Range`< std::int64_t > int_range(std::size_t i) const override
method`Range`< double > float_range(std::size_t i) const override
method`Value` element(std::size_t i) const override

The list element at index; the returned value must outlive this one.

method`OwnedValue` int_range(std::int64_t min, std::int64_t max)

An integer set covering a single inclusive range.

method`OwnedValue` float_range(double min, double max)

A float set covering a single inclusive range.

struct fznso::OwnedValue::Absentsource

The absent value.

struct fznso::Rangesource

An inclusive [min, max] range.

fieldT min
fieldT max
class fznso::Solutionsource

A solution reported by a solver.

methodSolution(FznsoSolutionRef raw)
method`Value` operator[](Decision decision) const

The value assigned to a decision variable.

method`Value` statistic(std::string_view name) const

A named statistic for this solution.

struct fznso::Statussource

How a run finished.

enumenum Kind
Complete

The solver explored the whole search space.

Incomplete

The solver stopped early; better solutions may exist.

Error

The solver failed; error explains why.

field`Kind` kind
fieldstd::string error

Set only when kind is Error.

methodbool complete() const
methodbool failed() const
class fznso::Typesource

Builds a FznsoType from a base type and the qualifiers you name.

FznsoType is a C struct, so it cannot carry the builder methods itself; this holds one and converts back implicitly, so it can be used anywhere a FznsoType is expected, including the constexpr capability tables a solver declares. Every flag starts clear and is only set by the method that names it, which is what keeps positional bools out of the call:

// `list of var int`
FznsoType arg = `fznso::Type`{FznsoTypeBaseInt}.`list`(true).`decision`(true);
// `var set of int`, or plain `var int`, depending on `of_sets`
FznsoType var = `fznso::Type`{FznsoTypeBaseInt}.`decision`(true).`set`(of_sets);
methodconstexpr Type(FznsoTypeBase base)
methodconstexpr `Type` list(bool list_of) const

Whether this type is a list of its element type.

methodconstexpr `Type` decision(bool decision) const

Whether this type is a decision variable rather than a parameter.

methodconstexpr `Type` set(bool set_of) const

Whether this type is over sets of its base type rather than single values.

methodconstexpr `Type` opt(bool opt) const

Whether this type also permits the absent value.

methodconstexpr operator FznsoType() const
class fznso::Valuesource

A borrowed value handed across the interface.

The typed accessors are only valid for the matching kind(); prefer variant(), which resolves the kind and the payload together.

The converting constructors borrow their argument — the value points at it and reads it back on demand, without copying — so the argument must outlive the value. This is how a value already held elsewhere is handed across the interface. A default-constructed value is absent.

A type that decides its shape at runtime is handed over by deriving from ValueSource and constructing a Value from it. To build an owned value instead — one that outlives the storage it came from — use OwnedValue.

methodValue()

The absent value.

methodValue(FznsoValueRef raw)
methodValue(const bool &value)

Borrow a boolean.

methodValue(const std::int64_t &value)

Borrow a 64-bit integer.

methodValue(const double &value)

Borrow a 64-bit float.

methodValue(const std::string &value)

Borrow a string.

methodValue(Decision value)

Reference a decision variable (carried in the handle, nothing borrowed).

methodValue(Constraint value)

Reference a constraint (carried in the handle, nothing borrowed).

methodValue(const std::vector< Range< std::int64_t > > &ranges)

Borrow an integer set as an ordered list of inclusive ranges.

methodValue(const std::vector< Range< double > > &ranges)

Borrow a float set as an ordered list of inclusive ranges.

methodValue(const std::vector< T > &items)

Borrow a list, whose elements are themselves scalars or ValueSources.

methodValue(const V &source)

Borrow a ValueSource, deciding the value’s shape at runtime.

Dispatch is static: the vtable is specialised to V, so V’s own kind() and accessors are called directly.

methodFznsoValueKind kind() const

Which payload this value holds.

methodbool as_bool() const
methodstd::int64_t as_int() const
methoddouble as_float() const
method`Decision` as_decision() const
method`Constraint` as_constraint() const
methodstd::string_view as_string() const

The string payload. Not null-terminated; the length comes from len.

methodstd::size_t size() const

The number of list elements, set ranges, or string bytes.

methodbool empty() const
method`Value` operator[](std::size_t index) const

The list element at index.

Indexing a value means list indexing; the ranges of a set are reached through int_range/float_range instead, so that [] has one meaning.

method`Range`< std::int64_t > int_range(std::size_t index) const

The integer-set range at index.

method`Range`< double > float_range(std::size_t index) const

The float-set range at index.

method`ValueVariant` variant() const

Resolve this value into its payload as a variant.

methodconst FznsoValueRef & raw() const

The underlying handle, for passing back across the interface.

class fznso::ValueListsource

The elements of a list value, borrowed from the value that produced them.

methodValueList(Value value)
methodstd::size_t size() const
methodbool empty() const
method`Value` operator[](std::size_t index) const
class fznso::ValueSourcesource

Produce a Value from a type that decides its shape at runtime.

Implement this when a single downstream type can hold any kind — an interpreter’s expression, a variant AST node — and you want to hand it across the interface without copying its payload into a OwnedValue. Construct a Value from it; the value borrows this source, which must outlive it.

Implement kind() and the accessors your kinds use; the rest abort if called, since a well-formed value only has its matching accessor invoked.

method~ValueSource()=default
methodFznsoValueKind kind() const =

Which payload this value holds. Every other method must agree with it.

methodbool as_bool() const
methodstd::int64_t as_int() const
methoddouble as_float() const
methodstd::string_view as_string() const

The string payload; the returned view must outlive the value.

method`Decision` as_decision() const
method`Constraint` as_constraint() const
methodstd::size_t size() const

The number of list elements or set ranges; zero for anything else.

A string’s length is taken from as_string, so a string source need not implement this — only list and set sources do.

method`Range`< std::int64_t > int_range(std::size_t) const
method`Range`< double > float_range(std::size_t) const
method`Value` element(std::size_t) const

The list element at index; the returned value must outlive this one.