Configuration files

Introduction

The class ConfigStream is useful to parse configuration files in text format. One may search for a word in the configuration file, extract the value of a field or benefit from markup substitution.

ConfigStream derives from ExtStream. Therefore, it is advocated to read the section "Extended streams" of this documentation.

Example

The following example shows how to use a few methods of ConfigStream.

file.cfg:

# This files demonstrates how flexible configuration files
# read by ConfigStream may be.

# One may want to put a statement at the beginning of its configuration file,
# to describe its content.
Configuration file associated with version <version_number>.

[Domain]

# Dimensions.
Nx = 5, Ny = 6
# First cell.
x_min: -1.5	y_min: 5.2
# Steps.
Delta_x 3.1 # Experiment #2
Delta_y 9.7

[Files]

user: mallet
Home: /home/<user>
Data: <Home>/data/
Programs: <Home>/programs/version-<version_number>/

date: 2004-10-02, version_number: 1.5

read_config.cpp:

#include "Talos.hxx"
using namespace Talos;

int main()
{
  ConfigStream file("file.cfg");

  cout << "Reading " << file.GetFileName() << "..." << endl;

  // Domain variables.
  int Nx, Ny;
  double x_min, y_min;
  float dx, dy;
  // Parses the configuration file.
  file.Find("[Domain]"); // Searches for section [Domain].
  file.GetValue("Nx", Nx);
  file.GetValue("Ny", Ny);
  file.GetValue("x_min", x_min);
  file.GetValue("y_min", y_min);
  file.GetValue("Delta_x", dx);
  file.GetValue("Delta_y", dy);

  cout << "Domain: (" << x_min << ", " << dx << ", " << Nx << ") x ("
       << y_min << ", " << dy << ", " << Ny << ")." << endl;

  // Data files.
  string data_dir, programs_dir; // Directories.
  string note;
  file.FindFromBeginning("[Domain]"); // Searches for section [Files].
  file.GetValue("Data", data_dir); // With nested markup substitutions.
  // Markup <version> is defined after "Programs", but it still works:
  file.GetValue("Programs", programs_dir);

  cout << "Data directory: " << data_dir << endl;
  cout << "Programs directory: " << programs_dir << endl;

  // First statement.
  file.Rewind();
  cout << file.GetLine() << endl;

  return 0;
}

Result:

Reading file.cfg...
Domain: (-1.5, 3.1, 5) x (5.2, 9.7, 6).
Data directory: /home/mallet/data/
Programs directory: /home/mallet/programs/version-1.5/
Configuration file associated with version 1.5.

makefile (launch make read_config):

CC	=	g++
INCPATH	=	Talos # Put the path to Talos.
LINK	=	g++

TARGETS	=	read read_config read_configs exceptions dates

all: $(TARGETS)

$(TARGETS): % : %.o
	$(LINK) -o $@ $<

%.o : %.cpp
	$(CC) -I$(INCPATH) -c -o $@ $<

clean:
	rm -f $(TARGETS) $(TARGETS:%=%.o)

Comments

ConfigStream derives from ExtStream. It is advocated to read the section "Extended streams" of this documentation to understand how ExtStream works. ConfigStream essentially behaves in the same way as ExtStream except that it performs markup substitution.

If a markup substitution fails, an exception is raised.

Multiple configuration files

Dealing with multiple configuration files at the same time is pretty easy thanks to ConfigStreams. An instance of ConfigStreams is associated with multiple files, but it has exactly the same abilities as an instance of ConfigStream.

If file.cfg is split in two files:

domain.cfg:

# This files demonstrates how flexible configuration files
# read by ConfigStreams may be.

# One may want to put a statement at the beginning of its configuration file,
# to describe its content.
Configuration file associated with version <version_number>.

[Domain]

# Dimensions.
Nx = 5, Ny = 6
# First cell.
x_min: -1.5	y_min: 5.2
# Steps.
Delta_x 3.1 # Experiment #2
Delta_y 9.7

paths.cfg:

[Files]

user: mallet
Home: /home/<user>
Data: <Home>/data/
Programs: <Home>/programs/version-<version_number>/

date: 2004-10-02, version_number: 1.5

read_configs.cpp (compilation: make read_configs):

#include "Talos.hxx"
using namespace Talos;

int main()
{
  ConfigStreams file("domain.cfg", "paths.cfg");

  cout << "Reading " << file.GetStreams()[0]->GetFileName() << "..." << endl;
  cout << "and " << file.GetStreams()[1]->GetFileName() << "..." << endl;

  // Domain variables.
  int Nx, Ny;
  double x_min, y_min;
  float dx, dy;
  // Parses the configuration file.
  file.Find("[Domain]"); // Searches for section [Domain].
  file.GetValue("Nx", Nx);
  file.GetValue("Ny", Ny);
  file.GetValue("x_min", x_min);
  file.GetValue("y_min", y_min);
  file.GetValue("Delta_x", dx);
  file.GetValue("Delta_y", dy);

  cout << "Domain: (" << x_min << ", " << dx << ", " << Nx << ") x ("
       << y_min << ", " << dy << ", " << Ny << ")." << endl;

  // Data files.
  string data_dir, programs_dir; // Directories.
  string note;
  file.FindFromBeginning("[Domain]"); // Searches for section [Files].
  file.GetValue("Data", data_dir); // With nested markup substitutions.
  // Markup <version> is defined after "Programs", but it still works:
  file.GetValue("Programs", programs_dir);

  cout << "Data directory: " << data_dir << endl;
  cout << "Programs directory: " << programs_dir << endl;

  // First statement.
  file.Rewind();
  cout << file.GetLine() << endl;

  return 0;
}

Result:

Reading domain.cfg...
and paths.cfg...
Domain: (-1.5, 3.1, 5) x (5.2, 9.7, 6).
Data directory: /home/mallet/data/
Programs directory: /home/mallet/programs/version-1.5/
Configuration file associated with version 1.5.

Only the two first lines (in main()) have changed. The constructor takes two files and, to get the file names, one has to call GetStreams which returns a vector of pointers to the instances of ConfigStream (refer to the methods documentation for details). Otherwise, the whole is managed as if only one file was parsed.

Markup substitution also works across files.