A trivial BARS application
A bare bones example of a BARS application can be found in programs/example
.
This app can:
- parse options
- read configuration file
- check if an option has an acceptable value.
$ example
int option value: 1
string option value: test
$ example --test-int-param 23
int option value: 23
string option value: test
$ example --test-int-param -1
ERROR: ExampleIntParameter can't be -1
$ echo 'ExampleIntParameter 100' > /tmp/cfgexample; example --config /tmp/cfgexample
int option value: 100
string option value:
It consists of the following files:
main.cc
- application logic.opts.h
,opts.cc
- option parsing, configuration parsing, global variables and sanity checks.CMakeLists.txt
- build system commands. This file binds your app to the BARS build system. Do not touch it, unless you know what you are doing.
Let's go through each of these files in order.
main.cc
Right now main.cc looks pretty simple.
//program headers
#include "opts.h"
//system headers
#include "iostream"
//BARS dependencies
#include "BARS.h"
using namespace BARS;
int main(int argc, char** argv)
{
// Init should be called at the beggining of all BARS programms
App::Init(argc, argv, "example.rc", parseOpts, readRC, checkParams);
std::cout << "int option value: " << gExampleIntParameter << std::endl;
std::cout << "string option value: " << gExampleStringParameter << std::endl;
return 0;
}
App::Init
is a BARS function that sets up your application. It does the following:
- Parses arguments with the
parseOpts
function. - Reads a configuration file (either default one, or the one provided by the user) with
readRC
- Checks if the app can run with
checkParams
. This includes:- Checking if all the necessary parameters have been provided.
- Checking if all the required files exist.
- If some parameters are not set with an option or a configuration file entry, initialize them with a default value. For example, if
--out
flag was not provided, application output can be set to stdout.
The functions parseOpts
, readRC
and checkParams
are defined in the opts.cc
file. Let's take a look at it.
opts.cc
Here's opts.cc
from the example application.
#include "opts.h"
#include "iostream"
#include "getopt.h"
#include "cstdlib"
#include "stdlib.h"
#include "TEnv.h"
#include "BARS.h"
using namespace BARS;
int gExampleIntParameter = -1;
std::string gExampleStringParameter;
/**
* Read resource file, fill parameters.
*/
void readRC(const char* rcpath)
{
TEnv env;
if (-1 == env.ReadFile(App::RCPath, kEnvAll)) {
std::cerr << "ERROR: failed to read configuration file at '" << App::RCPath << "'" << std::endl;
exit(1);
}
gExampleIntParameter = env.GetValue("ExampleIntParameter", gExampleIntParameter);
gExampleStringParameter = env.GetValue("ExampleStringParameter", gExampleStringParameter.c_str());
}
/**
* Parse options passed to the application.
*/
void parseOpts(int argc, char** argv)
{
int c = -1;
while (1) {
static struct option long_options[] = {
{"out", required_argument, NULL, 'o'},
{"season", required_argument, NULL, 's'},
{"run", required_argument, NULL, 'r'},
{"config", required_argument, NULL, 'c'},
{"iterations", required_argument, NULL, 'i'},
{"prodid", required_argument, NULL, 'p'},
{"in", required_argument, NULL, 0},
{"cluster", required_argument, NULL, 2},
{"test-int-param", required_argument, NULL, 3},
{"test-str-param", required_argument, NULL, 4},
{ NULL, 0, NULL, 0 }
};
/* getopt_long stores the option index here. */
int option_index = 0;
c = getopt_long (argc, argv, "", long_options, &option_index);
/* Detect the end of the options. */
if (c == -1)
break;
switch (c) {
case 0:
App::Input = optarg;
break;
case 2:
App::Cluster = atoi(optarg);
break;
case 3:
gExampleIntParameter = atoi(optarg);
break;
case 4:
gExampleStringParameter = optarg;
break;
case 's':
App::Season = atoi(optarg);
break;
case 'r':
App::Run = atoi(optarg);
break;
case 'c':
App::RCPath = optarg;
break;
case 'p':
App::ProdId = optarg;
break;
case 'i':
App::Iterations = atoi(optarg);
break;
case 'o':
App::Output = optarg;
break;
case '?':
/* getopt_long already printed an error message. */
break;
default:
abort ();
}
}
}
void checkParams()
{
if (-1 == gExampleIntParameter) {
std::cerr << "ERROR: ExampleIntParameter can't be -1" << std::endl;
exit(1);
}
}
Global variables
The variables corresponsing to the application options are defined and initialized on top (don't forget to create extern
s in opts.h
!). The example application has 2 of them.
int gExampleIntParameter = -1;
std::string gExampleStringParameter;
Each option is prefixed with a small-case g
.
BARS::App
namespace has global variables for commonly used options (App::Input
for input, App::Cluster
for cluster # etc). See here for BARS::App
documentation.
Parsing configuration
After option declarations goes the readRC
function, used to parse an app configuration file:
void readRC(const char* rcpath)
{
TEnv env;
if (-1 == env.ReadFile(App::RCPath, kEnvAll)) {
std::cerr << "ERROR: failed to read configuration file at '" << App::RCPath << "'" << std::endl;
exit(1);
}
gExampleIntParameter = env.GetValue("ExampleIntParameter", gExampleIntParameter)
gExampleStringParameter = env.GetValue("ExampleStringParameter", gExampleStringParameter.c_str());
}
Each BARS application comes with a configuration file. It is a standard ROOT config file, as defined at here.
A default configuration file can be found at $BARSSYS/resources/
. Users can supply their own with --config /path/to/my/config.rc
. Path to the configuration file is stored in App::RCPath
.
Parsing options
The next part of opts.cc
is the function parseOpts
. It parses options provided to the application and assigns their value to option variables defined on top of the opts.cc
. Option parsing is executed after configuration parsing, so options provided to a BARS application override the values provided by the configuration file.
parseOpts
is a textbook function for argument parsing in Linux (see man getopt_long
for reference).It starts with a struct defining all options for the application,
static struct option long_options[] = {
{"test-int-param", required_argument, NULL, 3},
{"test-str-param", required_argument, NULL, 4},
...
...
...
{ NULL, 0, NULL, 0 }
};
followed by the switch processing every option:
switch (c) {
case 3:
gExampleIntParameter = atoi(optarg);
break;
case 4:
gExampleStringParameter = optarg;
break;
...
...
...
default:
abort ();
}
Adding new options this way should be self-evident. Note that an example application has a lot of commonly used options already parsed for the developer's comfort.
Checking parameters
Finally, the last function in opts.cc
is checkParams
void checkParams()
{
if (-1 == gExampleIntParameter) {
std::cerr << "ERROR: ExampleIntParameter can't be -1" << std::endl;
exit(1);
}
}
It's the last function in App::Init
and exits the application if some of the parameters have unacceptable values or are missing.