Modify main.cpp to contain the following code.
#include <iostream>
#include "strom.hpp"
using namespace strom;
// static data member initializations
std::string Strom::_program_name = "strom";
unsigned Strom::_major_version = 1;
unsigned Strom::_minor_version = 0;
const double Node::_smallest_edge_length = 1.0e-12;
int main(int argc, const char * argv[]) {
Strom strom;
try {
strom.processCommandLineOptions(argc, argv);
strom.run();
}
catch(std::exception & x) {
std::cerr << "Exception: " << x.what() << std::endl;
std::cerr << "Aborted." << std::endl;
}
catch(...) {
std::cerr << "Exception of unknown type!\n";
}
return 0;
}
The main function now creates a Strom
object, then passes argc
and argv
off to the Strom
object’s processCommandLineOptions
function, and finally calls the Strom
object’s run
method. Any exceptions thrown are caught and their error messsages displayed to the user.
The main.cpp file will not change very much from now on. Any new options will be added to the Strom
class instead.
Some data members (_program_name
, _major_version
, and _minor_version
) of the Strom
class were declared static. We must therefore initialize these in main.cpp, as it is our only source code file, just like we have been doing for Node::_smallest_edge_length
.
The extra capability we’ve added to our program requires us to:
program_options
library,Navigate to where you previously (in step 5) installed the Boost headers (~/Documents/libraries/boost_1_71_0) and enter the following command:
./bootstrap.sh --with-toolset=gcc --with-libraries=program_options,filesystem,system
The bootstrap.sh script will fairly quickly return, instructing you to type ./b2
to perform the actual compilation.
./b2 cxxflags="-std=c++11" -d 2
Note that I’ve added cxxflags="-std=c++11"
to ensure that the Boost libraries are compiled under the same C++ dialect used to compile the main strom program. This will ensure that the libraries can be linked to the strom executable. I’ve also specified -d 2
to specify debugging level 2, which causes the Boost build system to display the compile and link commands it is using (this helps when trying to diagnose what happened if the compiling or linking fails).
Assuming the compilation was successful, you should now see something similar to the following output:
The Boost C++ Libraries were successfully built!
The following directory should be added to compiler include paths:
/home/plewis/Documents/libraries/boost_1_71_0
The following directory should be added to linker library paths:
/home/plewis/Documents/libraries/boost_1_71_0/stage/lib
Copy the files libboost_program_options.a, libboost_filesystem.a, and libboost_system.a to the ~/lib/static folder.
Here is the meson.build file we’ve been building up, with the highlighted lines either added or modified to accommodate program options:
project('strom', 'cpp',
default_options : ['cpp_std=c++11','cpp_link_args=-static','prefix=/home/paul/Documents/strom/distr'],
version : '1.0')
cpp = meson.get_compiler('cpp')
<span style="color:#0000ff"><strong>lib_program_options = cpp.find_library('boost_program_options', dirs: ['/home/paul/lib/static'], required: true)</strong></span>
lib_ncl = cpp.find_library('ncl', dirs: ['/home/paul/lib/static'], required: true)
incl_ncl = include_directories('/home/paul/include')
incl_boost = include_directories('/home/paul/Documents/libraries/boost_1_71_0')
<span style="color:#0000ff"><strong>executable('strom', 'main.cpp', install: true, install_dir: '.', dependencies: [lib_ncl,lib_program_options], include_directories: [incl_ncl,incl_boost])</strong></span>
install_data('test.tre', install_dir: '.')
Note the meson variable lib_program_options
stores the location of the libboost_program_options.a, and this variable is added to the lib_ncl
variable to form a list that is supplied to the dependencies
argument to the executables command.
The last line in the meson.build file copies the test.tre file to the distr directory so that it will be available in the same directory as the strom executable when you test the program.
If you run the program now by typing simply ./strom
, from your ~/Documents/strom/distr directory, you should see an error message:
Exception: the option '--treefile' is required but missing
Why did we get this error message? Try running the program like this from the command line:
./strom --treefile test.tre
Because the name of the tree file is no longer hard-coded in the main function, the program forces you to supply these file names.
Here is the output you should see if everything is working correctly:
Note: configuration file (strom.conf) not found
Starting...
storing read block: TAXA
storing read block: TREES
Read 14 trees from file
Topology 1 seen in these 4 trees:
10 11 12 13
Topology 2 seen in these 2 trees:
8 9
Topology 3 seen in these 8 trees:
0 1 2 3 4 5 6 7
Topologies sorted by sample frequency:
topology frequency
3 8
1 4
2 2
Finished!
If the lines in your output are flooded with lines such as “Creating Node object” and “Destroying Node object”, then you failed to heed the advise in the section “Lost at sea” at the very bottom of the Test the TreeSummary class page.
You may have wondered about this line:
Note: configuration file (strom.conf) not found
The Boost program_options
library makes it possible to store command line options inside a configuration file rather than entering them from the command line. If you create a file named strom.conf inside the install directory (~/Documents/strom/distr) containing these two lines…
# this is a comment
treefile = test.tre
you should be able to run strom without specifying any command line options. You may wish to create the strom.conf file inside your src directory and add a line to your meson.build file to copy it to the install directory.
Note that in the config file the convention is to use key/value pairs, whereas on the command line, --
is the convention. Be sure to use one key/value pair per line, and note that lines starting with #
are ignored (this feature can be used to insert comments into your configuration files).