📜 ⬆️ ⬇️

Configuration files Libconfig library

Introduction


Somehow being in search of how to fasten configuration ini files or json to my server, I went through the options, but for some reason they were inconvenient or too simple, or bicycles. And although I love the xml configuration, but sometimes it is overly huge files and inconvenient for a small number of settings to write a lot of text. Once I asked a friend a question on this topic, he then threw the library to me. She reminds json mixed with yaml.



The library has two interfaces: functional and object. They are very similar, since the object uses internally a functional implementation, but there are some differences discussed in this post.
')

Setup and connection
Configuration file
Use in C
Use in C ++

Setup and connection


The library is in many repositories, so the installation is simple:
$ sudo aptitude install libconfig8 libconfig8-dev libconfig++ libconfig++-dev 


In the C ++ source code, it is only connected via the switch:
 #include <libconfig.h++> 

or
 #include <libconfig.hh> 

or for C
 #include <libconfig.h> 


Configuration file


The config file is the following structure:
 # Example application configuration file version = "1.0"; application: { window: { title = "My Application"; size = { w = 640; h = 480; }; pos = { x = 350; y = 250; }; }; list = ( ( "abc", 123, true ), 1.234, ( /* an empty list */) ); books = ( { title = "Treasure Island"; author = "Robert Louis Stevenson"; price = 29.95; qty = 5; }, { title = "Snow Crash"; author = "Neal Stephenson"; price = 9.99; qty = 8; } ); misc: { pi = 3.141592654; bigint = 9223372036854775807L; columns = [ "Last Name", "First Name", "MI" ]; bitmask = 0x1FC3; }; }; 


The main types of entries in the config are the following types:
Element (Setting)
This is the minimum significant part of the configuration structure and look like a key value:
 name = value; 

or
 name : value 

Groups

Groups can contain any number of elements, but each element must contain a unique key within the group. It is written in braces:
 { settings... } 

Arrays

They contain any number of elements, even zero, but all elements consist only of values ​​and must have the same scalar type within the array. Writes in square brackets:
 [ value, value ... ] 

Lists

Lists contain zero or more scalar type elements, arrays, groups, or lists. It is written in parentheses:
 ( value, value ... ) 

Integer values ​​(integers)

They are written in the usual decimal way (± 0-9) or hexadecimal form (0xA-f). But integer values ​​are limited to a range of -2147483648..2147483647 (32bit), but if large ranges are needed, then an 'L' is added at the end.
 3578934 897893450934L 

Fractional numbers with floating point (floats)

It is also recorded in the usual way.
 3.1415 

The exponential entry is standard with 'e'.
Boolean values

Values ​​are written as 'true' or 'false' and case-insensitive (without quotes, of course).
Strings

Written in double quotes as
 "     " 
.
The following options will eventually give the same string value:
 "  " "  " 

 "  " /*  */ "  " //  " " 
.
Comments

In the config, there are three possible types in C ++:

External connections (Includes)

This is, in general, the most delicious yummy.
 # file: quote.cfg quote = "Criticism may not be agreeable, but it is necessary." " It fulfils the same function as pain in the human" " body. It calls attention to an unhealthy state of" " things.\n" "\t--Winston Churchill"; 

 # file: test.cfg info: { name = "Winston Churchill"; @include "quote.cfg" country = "UK"; }; 


With API


In this part, I will not begin to describe all the functions, only the main ones, since they are generally similar, and the main nuances.

Description of the functions used below

 #include <stdio.h> #include <stdlib.h> #include <libconfig.h> /*      'example.cfg'     */ int main(int argc, char **argv) { /*   . */ config_t cfg; config_setting_t *setting; const char *str; config_init(&cfg); /*   */ /*  .  ,    */ if(! config_read_file(&cfg, "example.cfg")) { fprintf(stderr, "%s:%d - %s\n", config_error_file(&cfg), config_error_line(&cfg), config_error_text(&cfg)); config_destroy(&cfg); return(EXIT_FAILURE); } /*    "name". */ if(config_lookup_string(&cfg, "name", &str)) printf("Store name: %s\n\n", str); else fprintf(stderr, "No 'name' setting in configuration file.\n"); /*     . */ setting = config_lookup(&cfg, "inventory.books"); if(setting != NULL) { int count = config_setting_length(setting); int i; printf("%-30s %-30s %-6s %s\n", "TITLE", "AUTHOR", "PRICE", "QTY"); for(i = 0; i < count; ++i) { config_setting_t *book = config_setting_get_elem(setting, i); /*    ,      . */ const char *title, *author; double price; int qty; if(!(config_setting_lookup_string(book, "title", &title) && config_setting_lookup_string(book, "author", &author) && config_setting_lookup_float(book, "price", &price) && config_setting_lookup_int(book, "qty", &qty))) continue; printf("%-30s %-30s $%6.2f %3d\n", title, author, price, qty); } putchar('\n'); } /*     . */ setting = config_lookup(&cfg, "inventory.movies"); if(setting != NULL) { unsigned int count = config_setting_length(setting); unsigned int i; printf("%-30s %-10s %-6s %s\n", "TITLE", "MEDIA", "PRICE", "QTY"); for(i = 0; i < count; ++i) { config_setting_t *movie = config_setting_get_elem(setting, i); /*    ,     . */ const char *title, *media; double price; int qty; if(!(config_setting_lookup_string(movie, "title", &title) && config_setting_lookup_string(movie, "media", &media) && config_setting_lookup_float(movie, "price", &price) && config_setting_lookup_int(movie, "qty", &qty))) continue; printf("%-30s %-10s $%6.2f %3d\n", title, media, price, qty); } putchar('\n'); } config_destroy(&cfg); /*   ,      */ return(EXIT_SUCCESS); } 


A brief description of the functionality

Full description in the documentation .

config_t - type of configuration file (this is not yet a record). Roughly speaking, the main container.
config_setting_t is a configuration item object. The example uses a pointer returned by the container to the item being searched for.
int config_read_file (config_t * config, const char * filename) - the function reads the configuration file filename into memory and fills an object of type config_t . You can not read from the file, but immediately “feed” the string in config_read_string () or give the file descriptor in config_read ()
int config_lookup_string (const config_t * config, const char * path, const char ** value) - searches and returns the value as a pointer to the string value , along the specified path path inside the config config .
config_setting_t * config_lookup (const config_t * config, const char * path) - looks for an entry inside the config on the specified internal path and returns it.
config_setting_t * config_setting_get_elem (const config_setting_t * setting, unsigned int index) - used for arrays, lists to return from it items with such a number in order
int config_setting_lookup_string (const config_setting_t * setting, const char * name, const char ** value) -
returns the value of the child name element relative to the given setting
When it is necessary to obtain a value in a specific specified entry, then functions like
int config_setting_get_int (const config_setting_t * setting)

C ++ API


Same example, but in C ++. Full documentation on the site

 #include <iostream> #include <iomanip> #include <cstdlib> #include <libconfig.h++> using namespace std; using namespace libconfig; // ,    'example.cfg'     int main(int argc, char **argv) { Config cfg; //  .     //   ++   ,    try { cfg.readFile("example.cfg"); } catch(const FileIOException &fioex) { std::cerr << "I/O error while reading file." << std::endl; return(EXIT_FAILURE); } catch(const ParseException &pex) { std::cerr << "Parse error at " << pex.getFile() << ":" << pex.getLine() << " - " << pex.getError() << std::endl; return(EXIT_FAILURE); } //   . try { string name = cfg.lookup("name"); cout << "Store name: " << name << endl << endl; } catch(const SettingNotFoundException &nfex) { cerr << "No 'name' setting in configuration file." << endl; } const Setting& root = cfg.getRoot(); //     . try { const Setting &books = root["inventory"]["books"]; int count = books.getLength(); cout << setw(30) << left << "TITLE" << " " << setw(30) << left << "AUTHOR" << " " << setw(6) << left << "PRICE" << " " << "QTY" << endl; for(int i = 0; i < count; ++i) { const Setting &book = books[i]; //    ,     . string title, author; double price; int qty; if(!(book.lookupValue("title", title) && book.lookupValue("author", author) && book.lookupValue("price", price) && book.lookupValue("qty", qty))) continue; cout << setw(30) << left << title << " " << setw(30) << left << author << " " << '$' << setw(6) << right << price << " " << qty << endl; } cout << endl; } catch(const SettingNotFoundException &nfex) { // Ignore. } //     . try { const Setting &movies = root["inventory"]["movies"]; int count = movies.getLength(); cout << setw(30) << left << "TITLE" << " " << setw(10) << left << "MEDIA" << " " << setw(6) << left << "PRICE" << " " << "QTY" << endl; for(int i = 0; i < count; ++i) { const Setting &movie = movies[i]; //   ,    . string title, media; double price; int qty; if(!(movie.lookupValue("title", title) && movie.lookupValue("media", media) && movie.lookupValue("price", price) && movie.lookupValue("qty", qty))) continue; cout << setw(30) << left << title << " " << setw(10) << left << media << " " << '$' << setw(6) << right << price << " " << qty << endl; } cout << endl; } catch(const SettingNotFoundException &nfex) { // Ignore. } return(EXIT_SUCCESS); } 

There is the same principle as in the functional style, only before receiving data from the config it is necessary to get the root element cfg.getRoot (); and only then from him turn to the other elements. It is also necessary to be attentive to the fact that almost all errors are thrown exceptions

Conclusion


In addition to reading convenient configs, the API also provides the functionality for creating config elements and writing to the media.

All documentation [ru] on the library website.

Examples come with the source code. They can be downloaded by the following command in the console:
 $ apt-get source libconfig++ 

or from the source for a direct link.

Documentation is presented in HTML and PDF formats.
GNU LGPL license.

Source: https://habr.com/ru/post/148948/


All Articles