C bindings
Note
The C bindings are experimental and still under development. The interface is incomplete, and function signatures and behavior may change in future releases.
The C binding exposes PUQ quantities through <snt/c/puq.h> and DIPL parsing
through <snt/c/dip.h>. It uses opaque handles: C applications hold pointers to SNT objects and
operate on them through functions, while the implementation remains in C++.
Getting started
Build and install SciNumTools with ENABLE_BINDING_C=ON (the default)
and the PUQ and DIP modules enabled. See Installation for the
source build instructions. In a consuming CMake project, link to snt-c:
cmake_minimum_required(VERSION 3.22)
project(snt_c_example LANGUAGES C CXX)
find_package(snt CONFIG REQUIRED)
add_executable(snt-c-example main.c)
target_link_libraries(snt-c-example PRIVATE snt-c)
set_target_properties(snt-c-example PROPERTIES LINKER_LANGUAGE CXX)
The C++ linker supplies the runtime needed by the underlying C++ libraries; the application source can remain C.
Quantities and units
Use snt_puq_quantity_eval to evaluate a PUEL expression,
snt_puq_quantity_convert to create a quantity in the requested units, and
snt_puq_quantity_format to write its textual representation into a buffer.
This complete example converts metres to centimetres:
#include <stdio.h>
#include <snt/c/puq.h>
int main(void) {
snt_puq_error error = {0, NULL};
snt_puq_quantity* quantity = NULL;
snt_puq_quantity* converted = NULL;
char output[128];
if (snt_puq_quantity_eval("2.5*m", &quantity, &error) != 0) {
fprintf(stderr, "PUQ evaluation failed: %s\n", error.message);
return 1;
}
if (snt_puq_quantity_convert(quantity, "cm", &converted, &error) != 0) {
fprintf(stderr, "PUQ conversion failed: %s\n", error.message);
snt_puq_quantity_free(quantity);
return 1;
}
if (snt_puq_quantity_format(converted, output, sizeof(output), &error) != 0) {
fprintf(stderr, "PUQ formatting failed: %s\n", error.message);
snt_puq_quantity_free(converted);
snt_puq_quantity_free(quantity);
return 1;
}
puts(output);
snt_puq_quantity_free(converted);
snt_puq_quantity_free(quantity);
return 0;
}
DIPL parameters
Create a parser with snt_dip_parser_create, add definitions with
snt_dip_parser_add_string or snt_dip_parser_add_file, and call
snt_dip_parser_parse. Then retrieve a value as text with
snt_dip_parser_get. Pass a node path such as
answer or project.name without a leading ?:
#include <stdio.h>
#include <snt/c/dip.h>
int main(void) {
const char* source = "answer int = 42\nmessage str = \"hello\"\n";
snt_dip_error error = {0, NULL};
snt_dip* dip = NULL;
char value[128];
if (snt_dip_parser_create(&dip, &error) ||
snt_dip_parser_add_string(dip, source, &error) ||
snt_dip_parser_parse(dip, &error)) {
fprintf(stderr, "DIPL error: %s\n", error.message);
snt_dip_parser_free(dip);
return 1;
}
if (snt_dip_parser_get(dip, "answer", value, sizeof(value), &error)) {
fprintf(stderr, "DIPL lookup error: %s\n", error.message);
snt_dip_parser_free(dip);
return 1;
}
printf("answer = %s\n", value);
snt_dip_parser_free(dip);
return 0;
}
To load a DIPfile manifest, replace the individual input calls with
snt_dip_parser_add_project(dip, "DIPfile", &error) before parsing. See
DIPfile projects for the format.
Registering schemas
Use snt_dip_parser_add_schema_string or snt_dip_parser_add_schema_file
with an explicit schema name and a body without a $schema wrapper:
snt_dip* parser = NULL;
snt_dip_error error = {0};
if (snt_dip_parser_create(&parser, &error) == 0) {
if (snt_dip_parser_add_schema_string(parser, "settings", "value int = 42", &error) == 0 &&
snt_dip_parser_add_string(parser, "physics : settings", &error) == 0) {
int status = snt_dip_parser_parse(parser, &error);
/* Handle status and inspect physics.value. */
}
snt_dip_parser_free(parser);
}
The file variant takes (parser, name, path, error). Both functions use the
same return codes and error structure as the other parser functions.
Overriding values
snt_dip_parser_add_override_string(parser, body, error) accepts unwrapped
path = value modifications before parsing. Use
snt_dip_parser_add_override_file(parser, path, error) to read the body from
a file. Registration is atomic and file provenance retains the source path.
After parsing or loading DIPH5, call
snt_dip_parser_is_overridden(parser, path, &result, error) to inspect the
override flag. result is 1 for an overridden value and 0 otherwise.
Environment persistence
Use snt_dip_environment_save and snt_dip_environment_load with a DIP
handle, a const char* filename, and an snt_dip_error object. They
return zero on success, using the same error convention as parsing.
See Environment persistence for the
DIPH5 format and current limitations.
Generating static parameters
After parsing or loading an environment, call
snt_dip_environment_generate with an snt_dip_export_format and an
output path. It follows the normal C binding error convention:
snt_dip_error error = {0};
if (snt_dip_environment_generate(
dip, SNT_DIP_EXPORT_CPP, "parameters.hpp", &error) != 0) {
fprintf(stderr, "%s\n", error.message);
}
The supported C ABI formats are SNT_DIP_EXPORT_CPP,
SNT_DIP_EXPORT_C, SNT_DIP_EXPORT_FORTRAN, SNT_DIP_EXPORT_RUST,
SNT_DIP_EXPORT_JULIA, SNT_DIP_EXPORT_JSON, and
SNT_DIP_EXPORT_YAML. See Static parameter generation for the generated representations.
Generating reports
After parsing or loading an environment, call
snt_dip_environment_generate_report. Use SNT_DIP_REPORT_TEX for TeX or
SNT_DIP_REPORT_PDF for PDF:
snt_dip_error error = {0};
if (snt_dip_environment_generate_report(
dip, SNT_DIP_REPORT_TEX, "report.tex", "DIPfile",
"introduction.tex", NULL, &error) != 0) {
fprintf(stderr, "%s\n", error.message);
}
Other format values are SNT_DIP_REPORT_MD, SNT_DIP_REPORT_RST,
SNT_DIP_REPORT_HTML, SNT_DIP_REPORT_TYP, SNT_DIP_REPORT_TXT, and
SNT_DIP_REPORT_JSON. They use Brief++ renderers without an external
tool. JSON is a briefpp/1 document tree. Pass a null introduction path
for these formats; a LaTeX introduction is accepted only for TeX and PDF.
The input label, introduction path, and compiler name may be null. A null
compiler uses pdflatex for PDF output. The introduction file is a trusted
LaTeX fragment without a preamble. PDF generation returns an error if the
configured compiler is unavailable or fails. See Generating DIP
reports for report contents and DIPH5
limits.
To set cover fields, use snt_dip_environment_generate_report_with_options:
snt_dip_report_options options = {0};
options.title = "Mock Heat Flow Study";
options.author = "Example Research Team";
options.date = "2026-09-28";
options.version = "1.0 demo";
snt_dip_environment_generate_report_with_options(
dip, SNT_DIP_REPORT_PDF, "report.pdf", &options, &error);
The default title is DIP parameter report; an empty author appears as
Not specified. Date and version default to the local generation date and
SNT build version. See the CreateReport example for the cover and linked contents page.
Errors and ownership
Operations returning int return zero on success and nonzero on error.
Pass an snt_puq_error or snt_dip_error to receive the error code and message. The message is
owned by the library; copy it if it must survive a later error on the same
thread.
Release every created quantity with snt_puq_quantity_free and every parser
with snt_dip_parser_free. Conversion creates a separate quantity, so both the
original and converted handles must be released. The free functions also
accept null pointers.
Output buffers belong to the caller. Their capacity must include space for the terminating null character; insufficient capacity is reported as an error.