CYAML
LibCYAML: Tutorial

This document is intended for C developers wishing to make use of LibCYAML.

Overview

If you want to use LibCYAML you'll need to have two things:

  1. A consistent structure to the sort of YAML you want to load/save.
  2. Some C data structure to load/save to/from.

LibCYAML's aim is to make this as simple as possible for the programmer. However, LibCYAML knows nothing about either your data structure or the "shape" of the YAML you want to load. You provide this information by defining "schemas", and passing them to LibCYAML.

Note: If you need to handle arbitrary "free-form" YAML (e.g. for a tool to convert between YAML and JSON), then LibCYAML would not be much help. In such a case, I'd recommend using libyaml directly.

A simple example: loading YAML

Let's say you want to load the following YAML document:

name: Fibonacci
data:
- 1
- 1
- 2
- 3
- 5
- 8

And you want to load it into the following C data structure:

struct numbers {
char *name;
int *data;
unsigned data_count;
};

Then we need to define a CYAML schema to describe these to LibCYAML.

Note: Use the doxygen API documentation, or else the documentation in cyaml.h in conjunction with this guide.

At the top level of the YAML is a mapping with two fields, "name" and "data". The the first field is just a simple scalar value (it's neither a mapping nor a sequence). The second field has a sequence value.

We'll start by defining the CYAML schema for the "data" sequence, since since that's the "deepest" non-scalar type. The reason for starting here will become clear later.

/* CYAML value schema for entries of the data sequence. */
static const cyaml_schema_value_t data_entry = {
};

Here we're making a cyaml_schema_value_t for the entries in the sequence. There are various CYAML_VALUE_{TYPE} macros to assist with this. Here we're using CYAML_VALUE_INT, because the value is a signed integer. The parameters passed to the macro are enum cyaml_flag, and the C data type of the value.

Now we can write the schema for the mapping. First we'll construct an array of cyaml_schema_field_t entries that describe each field in the mapping.

/* CYAML mapping schema fields array for the top level mapping. */
static const cyaml_schema_field_t top_mapping_schema[] = {
struct numbers, name,
struct numbers, data, &data_entry,
};

There are CYAML_FIELD_{TYPE} helper macros to construct the mapping field entries. The array must be terminated by a CYAML_FIELD_END entry. The helper macro parameters are specific to each CYAML_FIELD_{TYPE} macro.

The entry for the name field is of type string pointer. You can consult the documentation for the CYAML_FIELD_{TYPE} macros to see what the parameters mean.

Note: The field for the sequence takes a pointer to the sequence entry data type that we defined earlier as data_entry.

Finally we can define the schema for the top level value that gets passed to the LibCYAML.

/* CYAML value schema for the top level mapping. */
static const cyaml_schema_value_t top_schema = {
struct numbers, top_mapping_schema),
};

In this case our top level value is a mapping type. One of the parameters needed for mappings is the array of field definitions. In this case we're passing the top_mapping_schema that we defined above.

/* Create our CYAML configuration. */
static const cyaml_config_t config = {
.log_level = CYAML_LOG_WARNING, /* Logging errors and warnings only. */
.log_fn = cyaml_log, /* Use the default logging function. */
.mem_fn = cyaml_mem, /* Use the default memory allocator. */
};
/* Where to store the loaded data */
struct numbers *n;
/* Load the file into p */
cyaml_err_t err = cyaml_load_file(argv[ARG_PATH_IN], &config,
&top_schema, (cyaml_data_t **)&n, NULL);
if (err != CYAML_OK) {
/* Handle error */
}
/* Use the data. */
printf("%s:\n", n->name);
for (unsigned i = 0; i < n->data_count; i++) {
printf(" - %i\n", n->data[i]);
}
/* Free the data */
err = cyaml_free(&config, &top_schema, n, 0);
if (err != CYAML_OK) {
/* Handle error */
}

And that's it, the YAML is loaded into the custom C data structure.

You can find the code for this in the "numerical" example in the examples directory.

CYAML_VALUE_INT
#define CYAML_VALUE_INT(_flags, _type)
Definition: cyaml.h:559
cyaml_log
void cyaml_log(cyaml_log_t level, void *ctx, const char *fmt, va_list args)
cyaml_config
Definition: cyaml.h:1359
CYAML_VALUE_MAPPING
#define CYAML_VALUE_MAPPING(_flags, _type, _fields)
Definition: cyaml.h:1053
cyaml_data_t
void cyaml_data_t
Definition: cyaml.h:1306
CYAML_OK
@ CYAML_OK
Definition: cyaml.h:516
CYAML_FLAG_POINTER
@ CYAML_FLAG_POINTER
Definition: cyaml.h:142
cyaml_mem
void * cyaml_mem(void *ctx, void *ptr, size_t size)
CYAML_FIELD_END
#define CYAML_FIELD_END
Definition: cyaml.h:1284
CYAML_LOG_WARNING
@ CYAML_LOG_WARNING
Definition: cyaml.h:1313
cyaml_load_file
cyaml_err_t cyaml_load_file(const char *path, const cyaml_config_t *config, const cyaml_schema_value_t *schema, cyaml_data_t **data_out, unsigned *seq_count_out)
cyaml_config::log_level
cyaml_log_t log_level
Definition: cyaml.h:1412
CYAML_FLAG_DEFAULT
@ CYAML_FLAG_DEFAULT
Definition: cyaml.h:133
cyaml_schema_field
Definition: cyaml.h:410
CYAML_FIELD_SEQUENCE
#define CYAML_FIELD_SEQUENCE(_key, _flags, _structure, _member, _entry, _min, _max)
Definition: cyaml.h:1159
cyaml_free
cyaml_err_t cyaml_free(const cyaml_config_t *config, const cyaml_schema_value_t *schema, cyaml_data_t *data, unsigned seq_count)
CYAML_UNLIMITED
#define CYAML_UNLIMITED
Definition: cyaml.h:1290
cyaml_schema_value
Definition: cyaml.h:302
cyaml_err_t
enum cyaml_err cyaml_err_t
CYAML_FIELD_STRING_PTR
#define CYAML_FIELD_STRING_PTR(_key, _flags, _structure, _member, _min, _max)
Definition: cyaml.h:1034