conf2struct takes a configuration file that describes a
configuration file in the libconfig format, and generates a C parser that will read a configuration file directly into a C structure. The goal is to accept any file that is valid for
libconfig, which in particular means using
conf2struct does not introduce restrictions to what the configuration file should look like.
conf2struct manageѕ optional settings and default values.
(A bit of history:
libconfig, which provides a C API. There are two downsides
to that: with the increase of the number of settings, the
code to read the configuration file grew and became harder
to read, while being all mostly boilerplate code; also,
every time it reads a setting
libconfig traverses a tree
doing string compares on the setting names, which is very
inefficient for settings that get used very often: those
setting need to be copied to a structure. Put both problems
together, and you get this program as a solution by taking a
conf2struct is published on github.
The format of the target configuration file is itself described as a configuration file which is somewhat similar to a schema file.
The following settings are located at the root of the configuration file:
header: output file name for the struct declarations
parser: output file for the C code
config: description of the target configuration file. This contains a
nameentry which will prefix all symbols, and a
itemsdeclaration equivalent to that of a group (see below).
The following entries are mandatory for each setting:
namespecifies the name of the setting
typespecifies the type of the setting. It is one of the libconfig types:
array. Type can also be specified as
runtimeif you want to add runtime data to the struct. The C type will then be specified by the additionnal
c2swill do nothing with these types (no init, no alloc, no printing).
Scalar types take the additional following entries:
defaultspecifies its default value. If the setting has no default, then it is mandatory.
optionalspecifies that a setting can be undefined. Undefined strings will point to NULL, and optional settings receive an additional
<id>_is_presentboolean in the final struct.
Group entries must contain an
items setting which contains
a list of named settings. Groups can contain items of any type.
List entries must contain an
items setting which contains
the list of setting expected in each item of the list.
Lists can contain anything, including other lists.
Note this does not support items of varying types, as is possible in libconfig. This libconfig feature feels counter-intuitive when targeting a struct for storage.
Settings of type
array must contain an
setting which specifies the type of the scalars contained in
Lists and arrays both get converted to a static array. A
<name>_len is added containing the number of
elements in the array.
Assuming the prefix for our configuration is
foo, the prototype for the parser produced is:
int foo_parse_file( const char* filename, struct foo_items* config, const char** errmsg);
filenameis the name of the file, passed directly to libconfig.
configis the root configuration struct as output by
conf2struct. The parser will fill that structure and allocate memory for groups, lists and arrays as required.
errmsgis a text string that explains what went wrong if parsing failed (e.g. a mandatory option is missing).
config_parser() returns 0 on failure, 1 on success.
conf2struct also builds a pretty-printer that takes a
struct as input.
eg_conf.cfg documents the
configuration to build a parser for the libconfig
parser.c shows a very simple parser that
also reports errors, using this example. A simple:
will produce the parser in
eg_conf.cfg), which parses
prints the result directly from the in-memory struct.
conf2struct is written in Perl, and the Perl
libconfig appears to be very bad at
reporting parsing errors (that, or the documentation is very
bad). The small
program can be used to validate configuration files: it will
provide the exact parse error location, if any.
validation parameters, e.g.
max_lengthfor arrays and lists, and strings, which can be verified to check the validity of the configuration.
generator of a command-line parser to override the configuration file.
x2struct does a similar job, but for C++, added constraints to the target configuration file (while I did not want users to have to change existing configuration files), and I didn’t see how to extend it to get useful error messages, default values, and setting validation.