Loading...
Searching...
No Matches
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".

name:
data:

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 = {
CYAML_VALUE_INT(CYAML_FLAG_DEFAULT, int),
};
cyaml_schema_value_t
struct cyaml_schema_value cyaml_schema_value_t
CYAML_FLAG_DEFAULT
@ CYAML_FLAG_DEFAULT
Definition cyaml.h:139
CYAML_VALUE_INT
#define CYAML_VALUE_INT(_flags, _type)
Definition cyaml.h:629

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[] = {
CYAML_FIELD_STRING_PTR(
"name", CYAML_FLAG_POINTER, struct numbers, name, 0, CYAML_UNLIMITED),
CYAML_FIELD_SEQUENCE(
"data", CYAML_FLAG_POINTER, struct numbers, data, &data_entry, 0, CYAML_UNLIMITED),
CYAML_FIELD_END
};
CYAML_FIELD_STRING_PTR
#define CYAML_FIELD_STRING_PTR(_key, _flags, _structure, _member, _min, _max)
Definition cyaml.h:1104
cyaml_schema_field_t
struct cyaml_schema_field cyaml_schema_field_t
CYAML_FIELD_SEQUENCE
#define CYAML_FIELD_SEQUENCE(_key, _flags, _structure, _member, _entry, _min, _max)
Definition cyaml.h:1229
CYAML_FLAG_POINTER
@ CYAML_FLAG_POINTER
Definition cyaml.h:148
CYAML_UNLIMITED
#define CYAML_UNLIMITED
Definition cyaml.h:1370
CYAML_FIELD_END
#define CYAML_FIELD_END
Definition cyaml.h:1364

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 = {
CYAML_VALUE_MAPPING(
CYAML_FLAG_POINTER, struct numbers, top_mapping_schema),
};
CYAML_VALUE_MAPPING
#define CYAML_VALUE_MAPPING(_flags, _type, _fields)
Definition cyaml.h:1123

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_fn = cyaml_log, /* Use the default logging function. */
.mem_fn = cyaml_mem, /* Use the default memory allocator. */
.log_level = CYAML_LOG_WARNING, /* Logging errors and warnings only. */
};
/* Where to store the loaded data */
struct numbers *n;
/* Load the file into n */
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 */
}
cyaml_err_t
enum cyaml_err cyaml_err_t
cyaml_data_t
void cyaml_data_t
Definition cyaml.h:1386
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_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_OK
@ CYAML_OK
Definition cyaml.h:586
cyaml_mem
void * cyaml_mem(void *ctx, void *ptr, size_t size)
cyaml_config_t
struct cyaml_config cyaml_config_t
cyaml_log
void cyaml_log(cyaml_log_t level, void *ctx, const char *fmt, va_list args)
CYAML_LOG_WARNING
@ CYAML_LOG_WARNING
Definition cyaml.h:1393

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.