This tutorial and its program shows the usage of input and output files and command line parameters. With the use of input files its no longer necessary to recompile the whole program when you want to change a parameter and its no longer necessary to write a huge number of parameters on the command line.
With a carefully designed order of parsing the command line options and reading the input file, it is even possible that the parameters on the command line overwrite the parameters in the input file.
The input file has a C-like syntax. You can derive the structure from the following example:
string title "testing blending maps" string author "Philipp Frauenfelder, 2001" int level 1 int polynomial 15 // Viertelkreis // string uex "(y*(1-x)*(x-y))" // string fex "(-(-2+2*x-2*y+(-1+x)*(x-y)*y))" // Dreieck (0,-1), (1,0), (0,1) string uex "(x*(1-x-y)*(x-1-y))" string fex "(-4 + 4*x + x*(1 - x - y)*(-1 + x - y))" array string bcform { 1 "(0)" 2 "(1)" } // 0 means FREE, 1 means DIRICHLET, 2 means NEUMANN array int bctype { 1 1 2 2 } string meshouteps "blendmesh.eps" string meshoutdx "blendmesh.dx" string dataoutnumeric "blendnumeric.data" string parameterout "blendoutput2.concepts" double a 1.0 /* just a parameter */ bool subdivide false
The results from the program are stored in a similar structure. At the end of a program, it is possible to write the input and output data into one file. The output file has the same syntax and structure as the input file and can therefore be used as an input file to reproduce the results at a later time. See the results section for an example.
The first few lines in a code for concepts are filled with the include files. I put the system includes first here.
#include <cmath> #include <cstdlib> #include <cstring> #include <fstream> #include <iostream> #include <unistd.h>
Since there are no real computations in the code, no more include files from Concepts are needed.
#include "basics.hh" #include "toolbox.hh"
int main(int argc, char** argv) {
l and p are just two variables. If debug is set to true, more information is printed to screen. A big try block to catch exceptions. More and more runtime errors in Concepts are reported by throwing an exception. try {
concepts::InputParser input(true); concepts::InOutParameters& inputParameters = input.inputParameters(); inputParameters.addInt("level", l); inputParameters.addInt("polynomial", p); inputParameters.addBool("debug", debug); inputParameters.addString("parameterout", "inputoutput.out");
concepts::InOutParameters& outputParameters = input.outputParameters();
table which is able to nicely format the content of the different arrays (e.g. for later processing with Gnuplot). outputParameters.addArrayDouble("error"); concepts::ResultsTable table; table.addMap(concepts::ResultsTable::DOUBLE, "error", outputParameters); std::string inputfile;
Here, we start with the command line parsing. See the man page for getopt for more information.
In the second line of the following code fragement, the string defines what command line arguments are allowed and if they take a parameter (:) or not. The switch statement has an entry for every command line argument listed in the string. There, the parameter is available as optarg. The default target of the switch clause prints some usage information (ie. help for the command line arguments).
The parameters are processed in the order they appear on the command line. When first specifying an input file with -f, the values in the file can be overridden with additional command line arguments after -f.
int opt; while ((opt = getopt(argc, argv, "-f:l:p:d")) != EOF) switch(opt) { case 'l': inputParameters.addInt("level", std::atoi(optarg)); break; case 'p': inputParameters.addInt("polynomial", std::atoi(optarg)); break; case 'f': inputfile = std::string(optarg); input.parse(inputfile); break; case 'd': inputParameters.addBool("debug", true); break; default: std::cout << "Usage: " << argv[0] << " [-l LEVEL] [-p DEGREE] [-f FILE] [-d]" << std::endl << "where" << std::endl << " FILE: name of the input file" << std::endl << " LEVEL: level of refinement" << std::endl << " DEGREE: polynomial degree" << std::endl << " -d: print the matrices" << std::endl << "Options given after the input file override the values " << "read from the" << std::endl << "input file." << std::endl; std::exit(1); break; }
std::cout << '[' << argv[0] << "]" << std::endl; std::cout << "--" << std::endl; std::cout << "Parameters:" << std::endl << " input file = " << inputfile << std::endl << inputParameters; std::cout << "--" << std::endl;
l = inputParameters.getInt("level"); p = inputParameters.getInt("polynomial");
Here are some dummy computations to fill the output area with content.
outputParameters.addInt("nelm", 10); for (int i = 0; i < 10; ++i) outputParameters.addArrayDouble("error", i, 1.0/(1<<i));
Finally, the input and output data are written to disk with some more information about the user and the system in the header of the file.
std::cout << "--" << std::endl << "Writing gathered data to disk: " << inputParameters.getString("parameterout") << std::endl; std::ofstream *ofs = new std::ofstream (inputParameters.getString("parameterout").c_str()); *ofs << "/* program:\t" << argv[0] << std::endl << " * command:\t"; for (int i = 0; i < argc; ++i) *ofs << argv[i] << " "; *ofs << std::endl << " * input file:\t" << inputfile << std::endl; *ofs << " */" << std::endl << input; delete ofs;
std::cout << table << std::endl;
ofs = new std::ofstream("inputoutput.gnuplot");
*ofs << std::setprecision(20);
table.print<concepts::ResultsTable::GNUPLOT>(*ofs);
delete ofs;
}
parameterout does not exist and the library will throw and exception which is caught here. catch (concepts::ExceptionBase& e) { std::cout << e << std::endl; return 1; } return 0; }
The output of the program called without parameters:
[inputoutput] -- Parameters: input file = string author "(empty)" string comment "(empty)" string parameterout "inputoutput.out" string title "(empty)" int level 0 int polynomial 1 bool debug false -- -- Writing gathered data to disk: inputoutput.out ResultsTable( error error 0 1 1 0.5 2 0.25 3 0.125 4 0.0625 5 0.03125 6 0.015625 7 0.0078125 8 0.00390625 9 0.00195312 )
The program creates the following output files:
inputoutput.out: /* program: inputoutput command: inputoutput input file: */ string author "(empty)" string comment "(empty)" string parameterout "inputoutput.out" string title "(empty)" int level 0 int polynomial 1 bool debug false end // output starts here int nelm 10 array double error { 0 1 1 0.5 2 0.25 3 0.125 4 0.0625 5 0.03125 6 0.015625 7 0.0078125 8 0.00390625 9 0.00195312 }
inputoutput.gnuplot: # error error
0 1
1 0.5
2 0.25
3 0.125
4 0.0625
5 0.03125
6 0.015625
7 0.0078125
8 0.00390625
9 0.001953125
Note the end keyword at the end of the input part and right before the output part. When reading in this file as input file, the parsing stops right there, ie. the previous output data is not read in.
#include <cmath> #include <cstdlib> #include <cstring> #include <fstream> #include <iostream> #include <unistd.h> #include "basics.hh" #include "toolbox.hh" int main(int argc, char** argv) { uint l = 0; uint p = 1; bool debug = false; try { // ********************************************************** input data ** concepts::InputParser input(true); concepts::InOutParameters& inputParameters = input.inputParameters(); inputParameters.addInt("level", l); inputParameters.addInt("polynomial", p); inputParameters.addBool("debug", debug); inputParameters.addString("parameterout", "inputoutput.out"); concepts::InOutParameters& outputParameters = input.outputParameters(); outputParameters.addArrayDouble("error"); concepts::ResultsTable table; table.addMap(concepts::ResultsTable::DOUBLE, "error", outputParameters); std::string inputfile; int opt; while ((opt = getopt(argc, argv, "-f:l:p:d")) != EOF) switch(opt) { case 'l': inputParameters.addInt("level", std::atoi(optarg)); break; case 'p': inputParameters.addInt("polynomial", std::atoi(optarg)); break; case 'f': inputfile = std::string(optarg); input.parse(inputfile); break; case 'd': inputParameters.addBool("debug", true); break; default: std::cout << "Usage: " << argv[0] << " [-l LEVEL] [-p DEGREE] [-f FILE] [-d]" << std::endl << "where" << std::endl << " FILE: name of the input file" << std::endl << " LEVEL: level of refinement" << std::endl << " DEGREE: polynomial degree" << std::endl << " -d: print the matrices" << std::endl << "Options given after the input file override the values " << "read from the" << std::endl << "input file." << std::endl; std::exit(1); break; } // ***************************************************** show parameters ** std::cout << '[' << argv[0] << "]" << std::endl; std::cout << "--" << std::endl; std::cout << "Parameters:" << std::endl << " input file = " << inputfile << std::endl << inputParameters; std::cout << "--" << std::endl; l = inputParameters.getInt("level"); p = inputParameters.getInt("polynomial"); // ******************************************************** computations ** outputParameters.addInt("nelm", 10); for (int i = 0; i < 10; ++i) outputParameters.addArrayDouble("error", i, 1.0/(1<<i)); // ************************************** output of input data and other ** std::cout << "--" << std::endl << "Writing gathered data to disk: " << inputParameters.getString("parameterout") << std::endl; std::ofstream *ofs = new std::ofstream (inputParameters.getString("parameterout").c_str()); *ofs << "/* program:\t" << argv[0] << std::endl << " * command:\t"; for (int i = 0; i < argc; ++i) *ofs << argv[i] << " "; *ofs << std::endl << " * input file:\t" << inputfile << std::endl; *ofs << " */" << std::endl << input; delete ofs; std::cout << table << std::endl; ofs = new std::ofstream("inputoutput.gnuplot"); *ofs << std::setprecision(20); table.print<concepts::ResultsTable::GNUPLOT>(*ofs); delete ofs; } catch (concepts::ExceptionBase& e) { std::cout << e << std::endl; return 1; } return 0; }