📜 ⬆️ ⬇️

Confetti - simple and fast configuration of your project

If you are writing a project a little more than average, then you usually encounter settings and configuration. There are quite a few solutions for C / C ++, I want to tell you about one more rather simple and beautiful solution from the Company mail @ Ru, which I used in my project

I myself used different config parsers, in recent projects I used re2c (the config was similar to the nginx config). Re2c even has a little in common with Candy - it is code generation:
no configuration files and structures need to be codified; Mag Confetty will do everything for you.

Unfortunately, there is no documentation, otherwise there would not be this article. Interested, you are welcome ...

')
Install the source
c GitHub github.com/mailru/confetti
The main requirement is the installation of YACC.

In the example folder there is an example of use. Using an example and building a config is intuitive. The whole point is to consistently perform several steps:
// 1. example.cfgtmpl:
../confetti -i example.cfgtmpl -n my_product -f my_product.cfg

// 2. h :
../confetti -i example.cfgtmpl -n my_product -h my_product_cfg.h
../confetti -i example.cfgtmpl -n my_product -c my_product_cfg.c
gcc -Wall -g -O0 -Werror -std=gnu99 -I. -c my_product_cfg.c

// 2. :
../confetti -i example.cfgtmpl -H prscfg.h
../confetti -i example.cfgtmpl -p prscfg.c
gcc -Wall -g -O0 -Werror -std=gnu99 -I. -c prscfg.c

// 4.
gcc -Wall -g -O0 -Werror -std=gnu99 -I. -c example.c
gcc -o example example.o my_product_cfg.o prscfg.o


If we look at an example, it will be clear how to implement it in a project. The following are the implementation steps for my project.

1. created a folder in the sources of your project

cd config.src

2. rewrote the executable file

cp confetty / usr / local / bin

3) corrected make file

applied to my project:
CONFETTI=/usr/local/bin/confetti
NAME=tarantool_proxy
CFG=tarantool_proxy.cfgtmpl

test_OBJS=tarantool_proxy_cfg.o tnt_config.o prscfg.o

all: $(NAME).cfg test

.SUFFIXES: .oc

.co:
$(CC) $(CFLAGS) $(INCLUDE) -c $<

test: $(test_OBJS)
$(CC) -o $@ $(test_OBJS) $(LIB)

tarantool_proxy: $(test_OBJS)
$(CC) -o $@ $(test_OBJS) $(LIB)

$(NAME).cfg: $(CFG)
$(CONFETTI) -i $< -n $(NAME) -f $(NAME).cfg
$(CONFETTI) -i $< -n $(NAME) -h $(NAME)_cfg.h
$(CONFETTI) -i $< -n $(NAME) -c $(NAME)_cfg.c

prscfg.c: $(CFG)
$(CONFETTI) -i $< -p $@

prscfg.h: $(CFG)
$(CONFETTI) -i $< -H $@

prscfg.c: prscfg.h $(NAME)_cfg.h

$(NAME)_cfg.c: prscfg.h $(NAME)_cfg.h

clean:
rm -f $(NAME).cfg $(NAME)_cfg.c $(NAME)_cfg.h
rm -f prscfg.c prscfg.h
rm -f test
rm *.o

install:
cp $(NAME).def.cfg ../cfg/$(NAME).cfg
cp tarantool_proxy_cfg.o ..
cp prscfg.o ..
cp *.h ..

Please note added the install target, which copies the generated files to the sources of my project.

3) created a template configuration file

from example.cfgtmpl
in the block% {} replaced the file name with the name that should be in the project tarantool_proxy_cfg.h:
% {
#include <prscfg.h>
#include <tarantool_proxy_cfg.h>

void out_warning ( ConfettyError r, char * format, ... ) ;
% } :

4. created your own configuration

and checked it out:
confetti -i tarantool_proxy.cfgtmpl -n tarantool_proxy -f tarantool_proxy.cfg

I added the missing part of the tarantool_proxy.cfgtmpl config file for my own:
pid = "/usr/local/var/tarantool_proxy.pid"
log = "/usr/local/var/log/tarantool_proxy.log"

daemon = 1

pool_size = 4096

# count of threads
threads = 4

#listen
host = "localhost"
port = 33013

# server connections
server = [

hostname = "localhost"
port = 33013
namespace = [
key = NULL , required
]
]

namespace = [
type = NULL , required
]

5. after generating the configuration file

tarantool_proxy.cfg rewrote it into the default configuration file: tarantool_proxy.def.cfg and filled it with the necessary data (some of them):

server [ 0 ] . hostname = "host2"
server [ 0 ] . port = 33013

server [ 1 ] . hostname = "host1"
server [ 1 ] . port = 33023


namespace [ 1 ] . type = "str"
namespace [ 0 ] . type = "int"


server [ 0 ] . namespace [ 0 ] . key = "345"
server [ 0 ] . namespace [ 1 ] . key = "abc"

server [ 1 ] . namespace [ 0 ] . key = "xyz"
server [ 1 ] . namespace [ 1 ] . key = "345"


The values ​​specified for the template file are used by default.
Further, I used this file as a duplicate, since the tarantool_proxy.cfg file is constantly being rewritten by the confetti program.

6. created on the basis of example.c own tester file config.


7. do make until everything goes away

It will be surprising if it works out the first time :)

We ./test
==========Accepted: 11; Skipped: 0===========
pid => '/usr/local/var/tarantool_proxy.pid'
log => '/usr/local/var/log/tarantool_proxy.log'
daemon => '1'
pool_size => '4096'
threads => '4'
host => 'localhost'
port => '33013'
server[0].hostname => 'localhost'
server[0].port => '33013'
server[0].namespace[0].key => '345'
server[0].namespace[1].key => 'abc'
server[1].hostname => 'tfn24'
server[1].port => '33023'
server[1].namespace[0].key => 'xyz'
server[1].namespace[1].key => '345'
namespace[0].type => 'int'
namespace[1].type => 'str'
==========DIRECT=========
pid=/usr/local/var/tarantool_proxy.pid
daemon=1
keys
==========Destroy=========
: ./test
==========Accepted: 11; Skipped: 0===========
pid => '/usr/local/var/tarantool_proxy.pid'
log => '/usr/local/var/log/tarantool_proxy.log'
daemon => '1'
pool_size => '4096'
threads => '4'
host => 'localhost'
port => '33013'
server[0].hostname => 'localhost'
server[0].port => '33013'
server[0].namespace[0].key => '345'
server[0].namespace[1].key => 'abc'
server[1].hostname => 'tfn24'
server[1].port => '33023'
server[1].namespace[0].key => 'xyz'
server[1].namespace[1].key => '345'
namespace[0].type => 'int'
namespace[1].type => 'str'
==========DIRECT=========
pid=/usr/local/var/tarantool_proxy.pid
daemon=1
keys
==========Destroy=========
./test
==========Accepted: 11; Skipped: 0===========
pid => '/usr/local/var/tarantool_proxy.pid'
log => '/usr/local/var/log/tarantool_proxy.log'
daemon => '1'
pool_size => '4096'
threads => '4'
host => 'localhost'
port => '33013'
server[0].hostname => 'localhost'
server[0].port => '33013'
server[0].namespace[0].key => '345'
server[0].namespace[1].key => 'abc'
server[1].hostname => 'tfn24'
server[1].port => '33023'
server[1].namespace[0].key => 'xyz'
server[1].namespace[1].key => '345'
namespace[0].type => 'int'
namespace[1].type => 'str'
==========DIRECT=========
pid=/usr/local/var/tarantool_proxy.pid
daemon=1
keys
==========Destroy=========

As for the “DIRECT” block, this is a direct access test:
printf ( "========== DIRECT ========== \ n " ) ;
printf ( "pid =% s \ n " , cfg. pid ) ;
printf ( "daemon =% d \ n " , cfg. daemon ) ;

or access to array elements:
tarantool_proxy_namespace ** it = cfg. namespace ;
while ( * it ! = NULL ) {
printf ( "namespace type =% s \ n " , ( * it ) -> type ) ;
++ it ;
}

So, what now remains:

  1. Make make install, which will overwrite all the necessary files in the directory of your project.
    The config.src directory remains a test polygon.
  2. In your own project, include #include {name} _cfg.h where {name} is the name you chose when generating the config file (in my project tarantool_proxy)
  3. Declare source configuration and assign default values
    tarantool_proxy cfg ;
    char * key , * value ;
    fill_default_tarantool_proxy ( & cfg ) ;

  4. Declare the configuration file and read it:
    int nAccepted , nSkipped ;
    FILE * fh = fopen ( filename , "r" ) ;

    if ( ! fh ) {
    fprintf ( stderr , "Could not open file% s \ n " , argv [ 1 ] ) ;
    return 1 ;
    }

    useStdout = 1 ;
    parse_cfg_file_tarantool_proxy ( & cfg , fh , 1 , & nAccepted , & nSkipped ) ;
    printf ( "========== Accepted:% d; Skipped:% d ============ \ n " , nAccepted , nSkipped ) ;
    fclose ( fh ) ;


    If we use arrays, we declare iterators:
    tarantool_proxy_iterator_t * i ;
    i = tarantool_proxy_iterator_init ( ) ;

    while ( ( key = tarantool_proxy_iterator_next ( i , & cfg , & value ) ) ! = NULL ) {
    if ( value ) {
    printf ( "% s => '% s' \ n " , key , value ) ;
    free ( value ) ;
    } else {
    printf ( "% s => (null) \ n " , key ) ;
    }
    }



Or, we use “direct” access to data structures, as mentioned above.
That's all, the reconfiguration of the project (changing the structure of the config) now takes no more than 5 minutes.

I hope that someone will save a lot of time.
Thanks to the author Teodor Sigaev

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


All Articles