Parsing correction (issue #4) using Boost_Program_Options.

Dependencies: Boost (boost::program_options, boost::variant, boost::any).

Test is ok: required arguments have to be given in command line
(go check t-eoParserBoost: e.g.
    $> ./test/eo/t-eoParserBoost --alpha6=6 --alpha4).
This commit is contained in:
Adèle Harrissart 2014-10-05 22:26:56 +02:00
commit 8c078b685e
6 changed files with 932 additions and 0 deletions

View file

@ -73,6 +73,19 @@ if(ENABLE_CMAKE_TESTING)
include(CTest REQUIRED)
endif(ENABLE_CMAKE_TESTING)
# required by t-eoParserBoost.cpp
find_package( Boost 1.36.0 )
if(Boost_FOUND)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS_DEBUG} -L ${Boost_INCLUDE_DIRS} -lboost_program_options -Wreorder")
if (UNIX OR CMAKE_COMPILER_IS_GNUCXX)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=gnu++11")
endif (UNIX OR CMAKE_COMPILER_IS_GNUCXX)
if (APPLE)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mmacosx-version-min=10.7")
endif (APPLE)
endif(Boost_FOUND)
######################################################################################
### 4) Build examples ?
######################################################################################

View file

@ -33,6 +33,13 @@ set(EOUTILS_SOURCES
eoSignal.cpp
)
if(Boost_FOUND)
message("add eoParserBoost.cpp to EOUTILS_SOURCES (see: /src/eo/utils/CMakeLists.txt)")
set(EOUTILS_SOURCES ${EOUTILS_SOURCES} eoParserBoost.cpp) # new parser using Boost
else(Boost_FOUND)
message("Boost not found. WARNING: eoParserBoost can not be used. (Warning message from /eo/src/utils/CMakeLists.txt)")
endif(Boost_FOUND)
add_library(eoutils STATIC ${EOUTILS_SOURCES})
install(TARGETS eoutils ARCHIVE DESTINATION ${LIB} COMPONENT libraries)

View file

@ -0,0 +1,555 @@
//-----------------------------------------------------------------------------
// eoParserBoost.cpp
// (c) Thales group
/*
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Contact: http://eodev.sourceforge.net
Authors:
*/
//-----------------------------------------------------------------------------
#ifdef _MSC_VER
#pragma warning(disable:4786) // to avoid long name warnings
#endif
#include "eoParserBoost.h"
eoParserBoost::eoParserBoost(unsigned _argc, char ** _argv, std::string _programDescription) throw(std::bad_alloc):
programName(_argv[0]),
programDescription(_programDescription),
nb_args(_argc),
desc("Allowed options") // Root node of the sections tree.
// The child nodes will been saved into the "sections_map".
// For the moment the tree depth level == 2.
{
unsigned i, j, k, l;
for (i = 1; i < _argc; i++)
{ // First check if a configuration file name (specified by symbol '@') has been given by command-line (e.g. @myFile.param).
if ('@' == _argv[i][0])
{ // Copy this file name (in order to process it later).
j = strlen(_argv[i]+1) + 1;
configFile_name = new char[j];
strcpy(configFile_name, _argv[i]+1);
--nb_args;
break; // If several configuration file names have been given only the first one will be treated. As a consequence the others will be ignored.
}
else
configFile_name = NULL;
}
args = new char*[nb_args];
try
{
k = 0;
for (i = 0; i < _argc; i++)
{ // Then copy all the arguments which have been given by command-line (in order to process them later) except for the configuration file name (already copied).
if ('@' != _argv[i][0])
{
j = strlen(_argv[i]) + 1;
args[k] = new char[j];
strcpy(args[k++], _argv[i]);
}
}
}
catch (...)
{ // Clean up any allocated memory because the destructor will never be called.
for (l = 0; l < i; l++)
delete[] args[l];
delete[] args; // Safe: assert(nb_args > 0);
throw std::bad_alloc(); // Translate any exception to bad_alloc().
}
}
eoParserBoost::~eoParserBoost()
{
unsigned i;
for (i = 0; i < nb_args; i++)
delete[] args[i];
delete[] args; // Safe: assert(nb_args > 0);
if (configFile_name != NULL)
delete[] configFile_name;
}
std::string eoParserBoost::checkIfExists(const std::string _longName, const std::string _shortHand)
{
if (_longName == "")
{
std::string msg = "Exception: blank parameter longname forbidden";
throw std::invalid_argument(msg);
}
for (std::map<std::string, my_map>::iterator it = info_map.begin(); it != info_map.end(); it++)
{ // For all sections...
my_map m_m = it->second;
for (my_map::iterator i = m_m.begin(); i != m_m.end(); i++)
{ // ... check that the parameter longname doesn't already exist.
if (i->first == _longName)
{
std::string msg = std::string("Exception: two parameters with the same longname is forbidden (see parameters called \"") + _longName + std::string("\").");
throw std::invalid_argument(msg);
}
}
}
std::stringstream ss;
ss << _longName << "," << _shortHand; // By following the POSIX/GNU standards Boost will only consider the first char of the shorthand string given as an argument.
// e.g. 'option_name, oxxxxxxx' will be registered as 'option_name, o'
return ss.str();
}
void eoParserBoost::manageSectionsMaps(const std::string _section)
{
if (!sections_map.count(_section))
{ // Add a new node to the "sections_map" and an other one to the "info_map" (which contains basic information about the parameters).
po::options_description o_d(_section);
sections_map.insert({_section, o_d});
my_map m_m;
info_map.insert({_section, m_m});
}
}
void eoParserBoost::addPositionalOption(const std::string _longName, const std::string _shortHand, unsigned _position)
{
// First check that this position is not already reserved.
if (pod.name_for_position(_position) != "")
{
std::string msg = std::string("Exception: position already reserved (see parameters named \"")
+ pod.name_for_position(_position)
+ std::string("\" and \"")
+ _longName + std::string("\").");
throw std::invalid_argument(msg);
}
// WARNING from Boost Tutorial : "The positional_options_description class only specifies translation from position to name,
// and the option name should still be registered with an instance of the options_description class."
pod.add(_longName.c_str(), _position);
// N.B.: the help message for positional options has to be manually written.
positional_options_help_message << " -" << _shortHand
<< " [ " << _longName << " ] "
<< "position = " << _position << "\n";
}
void eoParserBoost::saveInfo(const std::string _longName,
const std::string _shortHand,
const std::string _description,
const std::string _section,
int _position)
{
info_map.find(_section)->second
.insert({_longName, {_shortHand, _description, _position}});
}
template <class ValueType>
ValueType eoParserBoost::createParam(ValueType _value,
const std::string _longName,
const std::string _description,
const std::string _shortHand,
const std::string _section,
bool _implicit,
int _position)
{
std::string str = checkIfExists(_longName, _shortHand);
if (_position > 0)
addPositionalOption(_longName, _shortHand, static_cast<unsigned>(_position));
// Add the parameter to the right section into the "sections_map".
// If the section name is empty, the parameter belongs to the miscellaneous section called "General".
std::string section = _section;
if (section == "")
section = "General";
manageSectionsMaps(section);
if (_implicit)
sections_map.find(section)->second
.add_options()
(str.c_str(), po::value<ValueType>()->implicit_value(_value), _description.c_str());
else
sections_map.find(section)->second
.add_options()
(str.c_str(), po::value<ValueType>()->default_value(_value), _description.c_str());
// Save now the information about parameter shorthand and longname, parameter section, description and position
// that later we want to recover easily in order to save these settings in an output file.
// (see function writeSettings())
saveInfo(_longName, _shortHand, _description, section, _position);
return _value;
}
template <class ValueType>
std::pair< ValueType, ValueType > eoParserBoost::createParam(const std::pair< ValueType, ValueType > _values,
const std::string _longName,
const std::string _description,
const std::string _shortHand,
const std::string _section,
int _position)
{
std::string str = checkIfExists(_longName, _shortHand);
if (_position > 0)
addPositionalOption(_longName, _shortHand, static_cast<unsigned>(_position));
std::string section = _section;
if (section == "")
section = "General";
manageSectionsMaps(section);
sections_map.find(section)->second
.add_options()
(str.c_str(), po::value<ValueType>()->default_value(_values.first)->implicit_value(_values.second), _description.c_str());
saveInfo(_longName, _shortHand, _description, section, _position);
return _values;
}
template <class ValueType>
void eoParserBoost::createParam(const std::string _longName,
const std::string _description,
const std::string _shortHand,
const std::string _section,
int _position)
{
std::string str = checkIfExists(_longName, _shortHand);
if (_position > 0)
addPositionalOption(_longName, _shortHand, static_cast<unsigned>(_position));
std::string section = _section;
if (section == "")
section = "General";
manageSectionsMaps(_section);
sections_map.find(_section)->second
.add_options()
(str.c_str(), po::value<ValueType>()->required(), _description.c_str());
saveInfo(_longName, _shortHand, _description, section, -1);
}
void eoParserBoost::printParsedOptions(po::parsed_options& _parsedOptions)
{
std::stringstream msg;
msg << "\nPrinting parsed_options:\n";
unsigned n = 0;
for (opt_vect::iterator it = _parsedOptions.options.begin(); it != _parsedOptions.options.end(); it++)
{
++n;
po::basic_option<char>& opt = *it;
msg << "string_key[string]: " << opt.string_key << '\t'
<< "position_key[int]: " << opt.position_key << '\t'
<< "unregistered[bool]: " << opt.unregistered << '\t'
<< "case_insensitive[bool]:" << opt.case_insensitive << '\t';
str_vect o_tokens = opt.original_tokens;
for (str_vect::iterator i = o_tokens.begin(); i != o_tokens.end(); i++)
{
const char* c = (*i).c_str();
msg << "original_tokens: " << c << '\t';
}
str_vect val = opt.value;
for (str_vect::iterator j = val.begin(); j != val.end(); j++)
{
const char* cv = (*j).c_str();
msg << "value: " << cv << '\t';
}
msg << '\n';
}
msg << "number of options: " << n << "\n\n";
#ifndef NDEBUG
eo::log << eo::setlevel(eo::debug) << msg.str(); // Reminder: this is only a test display function.
#endif
}
po::parsed_options eoParserBoost::parseConfigFile()
{
std::ifstream ifs(configFile_name);
ifs.peek();
// Check if the file exists first:
if (!ifs)
{
std::string msg = std::string("Could not open configuration file: ") + configFile_name;
throw std::runtime_error(msg);
}
else
{
std::string str;
std::vector<std::string> arguments;
while (ifs >> str)
{
while (str[0] == ' ')
str = str.substr(1, str.length()); // Ignore blanks at the beginning of the string.
if (str[0] == '#')
{ // This part has been commented so skip the rest of the line.
std::string tempStr;
getline(ifs, tempStr);
}
else
arguments.push_back(str);
}
return po::command_line_parser(arguments)
.options(desc)
#ifdef ALLOW_UNENREGISTERED
.allow_unregistered() // DANGER: allows unenregistered parameters!
// As a consequence if the flag is set to true and unenregistered parameters are found,
// these unenregistered parameters won't be noticed and the whole configuration file
// will still be treated without throwing exception.
#endif
.run();
}
}
void eoParserBoost::processParams(bool _configureHelpOption)
{
if (_configureHelpOption)
createParam(std::pair< bool, bool > (false, true), "help", "Print this message.", "h", "");
for (std::map< std::string, po::options_description >::iterator it = sections_map.begin(); it != sections_map.end(); it++)
desc.add(it->second); // Add all derived sections to the root.
try
{
if (configFile_name != NULL) // If a configuration file name has been given in command line...
{ // ...it has to be processed first if we want command-line to have highest priority.
po::parsed_options parsed_options_from_file = parseConfigFile();
//printParsedOptions(parsed_options_from_file); // test display function
po::store(parsed_options_from_file, vm);
}
po::parsed_options parsed_options_from_cmd = po::command_line_parser(nb_args, args)
.options(desc)
.positional(pod)
#ifdef ALLOW_UNENREGISTERED
.allow_unregistered() // DANGER: allows unenregistered parameters!
// As a consequence if the flag is set to true and unenregistered parameters are found,
// these unenregistered parameters won't be noticed and the whole configuration file
// will still be treated without throwing exception.
#endif
.run();
//printParsedOptions(parsed_options_from_cmd); // test display function
po::store(parsed_options_from_cmd, vm);
po::notify(vm);
}
catch (const po::error &e)
{
if (vm.count("help") && (vm["help"].as<bool>()))
{ // Display help if requested (by command-line) with '--help' or '-h'.
std::cout << "-help specified" << std::endl;
printHelp(); // Print help before throwing exception !
}
throw; // Throw exception even if help is requested because it can tell a little bit more to the user.
// But do not exit() : destructors won't be called! It has to be done directly into the source code of the program (see test file t-eoParserBoost.cpp).
}
if (vm.count("help") && (vm["help"].as<bool>()))
{ // Display help if requested (by command-line) with '--help' or '-h'.
std::cout << "-help specified" << std::endl;
printHelp();
}
}
void eoParserBoost::printHelp()
{
std::cout << programName << std::endl;
std::cout << programDescription << std::endl;
std::cout << desc << std::endl;
std::string msg = positional_options_help_message.str();
if (msg != "")
std::cout << "Positional options:\n" << msg << std::endl;
std::cout << "@param_file defines a file where the parameters are stored\n" << std::endl;
}
template <class ValueType>
void eoParserBoost::getValue(const std::string _longName, ValueType& _val)
{
if (vm.count(_longName))
_val = vm[_longName].as<ValueType>();
else
{ // Throw an exception if the parameter doesn't exist or if it's an undeclared implicit parameter.
std::string msg = _longName + std::string(" does not exist or this implicit parameter has not been declared (see getValue()).");
throw std::invalid_argument(msg);
}
}
template <class ValueType>
ValueType eoParserBoost::getValue(const std::string _longName)
{
if (vm.count(_longName))
return vm[_longName].as<ValueType>();
std::string msg = _longName + std::string(" does not exist or this implicit parameter has not been declared (see getValue()).");
throw std::invalid_argument(msg); // Throw an exception if the parameter doesn't exist or if it's an undeclared implicit parameter.
}
std::pair<bool, my_variant> eoParserBoost::valueCast(const boost::any& _val)
{
if (_val.type() == typeid(int))
return std::make_pair(true, boost::any_cast<int>(_val));
if (_val.type() == typeid(short))
return std::make_pair(true, boost::any_cast<short>(_val));
if (_val.type() == typeid(long))
return std::make_pair(true, boost::any_cast<long>(_val));
if (_val.type() == typeid(unsigned int))
return std::make_pair(true, boost::any_cast<unsigned int>(_val));
if (_val.type() == typeid(unsigned short))
return std::make_pair(true, boost::any_cast<unsigned short>(_val));
if (_val.type() == typeid(unsigned long))
return std::make_pair(true, boost::any_cast<unsigned long>(_val));
if (_val.type() == typeid(char))
return std::make_pair(true, boost::any_cast<char>(_val));
if (_val.type() == typeid(float))
return std::make_pair(true, boost::any_cast<float>(_val));
if (_val.type() == typeid(double))
return std::make_pair(true, boost::any_cast<double>(_val));
if (_val.type() == typeid(long double))
return std::make_pair(true, boost::any_cast<long double>(_val));
if (_val.type() == typeid(bool))
return std::make_pair(true, boost::any_cast<bool>(_val));
if (_val.type() == typeid(std::string))
return std::make_pair(true, boost::any_cast<std::string>(_val));
if (_val.type() == typeid(char*))
return std::make_pair(true, boost::any_cast<char*>(_val));
/*
if (_val.type() == typeid(std::vector< int >))
return std::make_pair(true, boost::any_cast< std::vector< int > >(_val));
if (_val.type() == typeid(std::vector< short >))
return std::make_pair(true, boost::any_cast< std::vector< short > >(_val));
if (_val.type() == typeid(std::vector< long >))
return std::make_pair(true, boost::any_cast< std::vector< long > >(_val));
if (_val.type() == typeid(std::vector< unsigned int >))
return std::make_pair(true, boost::any_cast< std::vector< unsigned int > >(_val));
if (_val.type() == typeid(std::vector< unsigned short >))
return std::make_pair(true, boost::any_cast< std::vector< unsigned short > >(_val));
if (_val.type() == typeid(std::vector< unsigned long >))
return std::make_pair(true, boost::any_cast< std::vector< unsigned long > >(_val));
if (_val.type() == typeid(std::vector< char >))
return std::make_pair(true, boost::any_cast< std::vector< char > >(_val));
if (_val.type() == typeid(std::vector< float >))
return std::make_pair(true, boost::any_cast< std::vector< float > >(_val));
if (_val.type() == typeid(std::vector< double >))
return std::make_pair(true, boost::any_cast< std::vector< double > >(_val));
if (_val.type() == typeid(std::vector< long double >))
return std::make_pair(true, boost::any_cast< std::vector< long double > >(_val));
if (_val.type() == typeid(std::vector< bool >))
return std::make_pair(true, boost::any_cast< std::vector< bool > >(_val));
if (_val.type() == typeid(std::vector< str::string >))
return std::make_pair(true, boost::any_cast< std::vector< std::string > >(_val));
if (_val.type() == typeid(std::vector< char* >))
return std::make_pair(true, boost::any_cast< std::vector< char* > >(_val));
*/
else
return std::make_pair<bool, std::string>(false, "Unexpected type"); // Type is not referenced. Cast failed.
}
void eoParserBoost::writeSettings(std::ofstream& _ofs)
{
for (std::map<std::string, my_map>::iterator it = info_map.begin(); it != info_map.end(); it++)
{ // Print all parameters section by section.
std::string section = it->first;
_ofs << "#### " << section << " ####\n";
my_map m_m = it->second;
for (my_map::iterator i = m_m.begin(); i != m_m.end(); i++)
{
std::string longName = i->first;
std::string shortHand = boost::get<std::string>((i->second)[0]);
std::string description = boost::get<std::string>((i->second)[1]);
int position = boost::get<int>((i->second)[2]);
const po::variable_value& var = vm.find(longName)->second;
/*********************************************************************************************************************************
* writing convention for the parameter value : commented or not commented ?
*
* - default (commented == TRUE)
* | - not declared (commented == TRUE) WARNING: value has no type, cast will fail
* value -| - implicit |
* | | + declared in command-line or in configuration file (commented == FALSE)
* + not default -|
* |
* + not implicit => value has been changed by configuration file or in command-line (commented == FALSE)
*
*********************************************************************************************************************************/
if ( var.defaulted() || (!var.defaulted() && !vm.count(longName)) )
_ofs << "# --" << longName;
else
_ofs << "--" << longName;
if (vm.count(longName))
{ // Case where it is not an undeclared implicit parameter, e.g. we are sure that the value has a type
const boost::any& val = var.value();
if (!val.empty())
{
_ofs << "=";
_ofs << valueCast(val).second;
}
}
if ( (shortHand != "") && (description == "") && (position <= 0) )
_ofs << "\t" << "# -" << shortHand.substr(0,1);
if ( (shortHand != "") && (description != "") && (position <= 0) )
_ofs << "\t" << "# -" << shortHand.substr(0,1) << ": " << description;
if ( (shortHand != "") && (description != "") && (position > 0) )
_ofs << "\t" << "# -" << shortHand.substr(0,1) << ": " << description << ", position: " << position;
if ( (shortHand == "") && (description != "") && (position <= 0) )
_ofs << "\t" << "# " << description;
if ( (shortHand == "") && (description == "") && (position > 0) )
_ofs << "\t" << "# position: " << position;;
_ofs << "\n";
}
_ofs << "\n";
}
}
void eoParserBoost::writeSettings(const std::string _myFile)
{
std::ofstream ofs(_myFile);
std::string msg;
// Check first if the output file exists :
if (!ofs.is_open())
{
msg = std::string("Could not open the output file: ") + _myFile;
throw std::runtime_error(msg);
}
else
{
writeSettings(ofs);
ofs.close();
msg = std::string("Settings save in ") + _myFile;
}
#ifndef NDEBUG
eo::log << eo::setlevel(eo::debug) << msg;
#endif
}

View file

@ -0,0 +1,283 @@
/* (c) Thales group
This library is free software; you can redistribute it and/or modify it under
the terms of the GNU Lesser General Public License as published by the Free
Software Foundation; either version 2 of the License, or (at your option) any
later version.
This library is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License along
with this library; if not, write to the Free Software Foundation, Inc., 59
Temple Place, Suite 330, Boston, MA 02111-1307 USA
Contact: http://eodev.sourceforge.net
Authors:
*/
#ifndef EO_PARSER_BOOST_H
#define EO_PARSER_BOOST_H
#include "eoParam.h"
#include <iostream>
#include <new> // std::bad_alloc()
#include <exception>
#include <fstream>
#include <sstream>
#include <vector>
#include <typeinfo> // For operator typeid().
#include "eoLogger.h" // Required by eo::log (see eoParserBoost::printParsedOptions()).
#include "../eoObject.h"
//#include "../eoPersistent.h"
#include <boost/program_options/options_description.hpp>
#include <boost/program_options/parsers.hpp>
#include <boost/program_options/variables_map.hpp>
#include <boost/variant.hpp>
namespace po = boost::program_options;
typedef std::vector< po::basic_option<char> > opt_vect; // Used by eoParserBoost::printParsedOptions() ; in order to manipulate po::parsed_options easily.
typedef std::vector< std::basic_string<char> > str_vect; // Used by eoParserBoost::printParsedOptions() ; in order to manipulate po::parsed_options easily.
typedef boost::variant< int,
short,
long,
unsigned int,
unsigned short,
unsigned long,
char,
float,
double,
long double,
bool,
char*,
std::string > my_variant; // For output writing (value cast)
// and also for collecting all kind of information about the parameters (see "my_map")
typedef std::map< std::string, std::vector< my_variant > > my_map; // In order to save basic information about an option
// and collect them easily at the end for a complete data serialization
// (see eoParserBoost::writeSettings()).
// Key value: parameter longname ; mapped value : parameter shorthand, description and position.
/**
eoParserBoost: command line parser and configuration file reader/writer
This class is inspired by eoParser. Contrary to eoParser
this class respects the POSIX/GNU standards.
Dependencies : this class uses the Boost Program Options library :
$> sudo apt-get install libboost-dev-all
this class also requires c++11 : compile with flag -std=gnu++11
*/
class eoParserBoost: public eoObject//, public eoPersistent
{
public:
/**
* Constructor.
* A complete constructor that reads the command-line arguments.
* A configuration file can also be given for treatement, but it has to
* be specified as follows :
* $> ./myEo @param.rc will then load using the parameter file param.rc
*
* @param _argc command line arguments count
* @param _argv command line parameters
* @param _programDescription description of the work the program does
*/
eoParserBoost(unsigned _argc, char **_argv , std::string _programDescription = "") throw(std::bad_alloc);
/**
* Destructor.
*/
~eoParserBoost();
/**
* Construct a option and set its default OR implicit value.
*
* @param _defaultValue The value (default or implicit value)
* @param _longName Long name of the argument
* @param _description Description of the parameter. What is useful for.
* @param _shortHand Short name of the argument (Optional).
* By convention only one char (the 1st) will be considered.
* @param _section Name of the section to which the parameter belongs.
* Allows to improve the output file writing but also to organize
* the help indications which are printed on the terminal (--help).
* @param _implicit Tells if the value is a default value or an implicit value.
* @param _position The option POSITION
*/
template <class ValueType>
ValueType createParam(ValueType _value,
const std::string _longName,
const std::string _description,
const std::string _shortHand,
const std::string _section,
bool _implicit,
int _position = -1);
/**
* Construct a option and set its default AND implicit value.
*
* @param _values The combinaison of the default and implicit values.
* The first member is the default value, the second is the implicit value.
* @param _longName Long name of the argument
* @param _description Description of the parameter. What is useful for.
* @param _shortHand Short name of the argument (Optional).
* By convention only one char (the 1st) will be considered.
* @param _section Name of the section to which the parameter belongs.
* Allows to improve the output file writing but also to organize
* the help indications printed on the terminal (--help).
* @param _position The option POSITION
*/
template <class ValueType>
std::pair< ValueType, ValueType > createParam(std::pair< ValueType, ValueType > _values,
const std::string _longName,
const std::string _description,
const std::string _shortHand,
const std::string _section,
int _position = -1);
/**
* Construct a parameter whose value has to be given in command-line
* (or from a configuration file).
*
* @param _longName Long name of the argument
* @param _description Description of the parameter. What is useful for.
* @param _shortHand Short name of the argument (Optional).
* By convention only one char (the 1st) will be considered.
* @param _section Name of the section to which the parameter belongs.
* Allows to improve the output file writing but also to organize
* the help indications which are printed on the terminal (--help).
* @param _position The option POSITION
*/
template <class ValueType>
void createParam(const std::string _longName,
const std::string _description,
const std::string _shortHand,
const std::string _section,
int _position = -1);
/**
* Process all the parameter values (parameters created from the source code,
* and modified in the command line or by means of a configuration file).
*
* @param _configureHelpOption Allows to configure automatically the help option
* using the command-line specification --help or -h.
*/
void processParams(bool _configureHelpOption);
/**
* Cast the parameter value and copy it into the argument given as a reference.
*/
template <class ValueType>
void getValue(const std::string _longName, ValueType& _val);
/**
* Cast the parameter value as given by the ValueType type.
*/
template <class ValueType>
ValueType getValue(const std::string _longName);
/**
* Write and save processed settings in a file (data serialization).
* Motivation: save settings given in command-line + configuration file
* N.B. This output file can also be reused as configuration file for the next program execution.
*/
void writeSettings(const std::string _myFile);
std::string className(void) const { return "eoParserBoost"; }
protected:
std::string programName;
std::string programDescription;
char* configFile_name; // In order to register the configuration file name (given in command line).
// N.B. The 'char*' type is required type by the Boost parser function.
unsigned nb_args; // In order to register argc (given in command line).
char** args; // In order to register argv (given in command line).
// N.B. The 'char**' type is required type by the Boost parser function.
// The options decription component
// Describes the allowed options and what to do with the option values
po::options_description desc;
// The positional options description component
po::positional_options_description pod;
// The storage component
// Used to store all parameters that are processed
po::variables_map vm;
// Contains all the options descriptions related to their own section.
// Key value : the section ; mapped value : the po::options_description object.
// For the moment: tree depth level = 2.
std::map< std::string, po::options_description > sections_map;
// In order to save basic information (section, short- and long- names and description) that we can't access easily
// with po::options_description or with po::positional_options_description.
// Purpose: enable the complete data serialization by saving these settings in an output file (see function writeSettings()).
// Key value : section ; mapped value : information about the parameter (parameter longname, shorthand, description, and position).
std::map< std::string, my_map > info_map;
// The help message for positional options has to be manually written.
std::stringstream positional_options_help_message;
/**
* Throws an exception if the parameter longname already exists.
*/
std::string checkIfExists(const std::string _longName, const std::string _shortHand);
/**
* Add a new key to the map related to the new section.
*/
void manageSectionsMaps(const std::string _section);
/**
* Add a positional parameter into the positional options description.
*/
void addPositionalOption(const std::string _longName, const std::string _shortHand, unsigned _position);
/**
* Save basic information about an parameter.
*/
void saveInfo(const std::string _longName,
const std::string _shortHand,
const std::string _description,
const std::string _section,
int _position);
/**
* Parse the argument values which are contained into the configuration file.
*/
po::parsed_options parseConfigFile();
/**
* Print the parsed options - test display function
* In order to test the Boost parsing treatement.
*/
void printParsedOptions(po::parsed_options& _parsedOptions);
/**
* Print information about all the processed parameters.
* This is the called function by the command-line option '--help' or '-h'.
*/
void printHelp();
/**
* Return the right value cast.
*/
std::pair<bool, my_variant> valueCast(const boost::any& _val);
/**
* Print settings on an output stream.
*/
void writeSettings(std::ofstream& ofs);
};
#endif // EO_PARSER_BOOST_H

View file

@ -79,6 +79,11 @@ if(ENABLE_CXX11_RANDOM)
endif(ENABLE_CXX11_RANDOM)
if(Boost_FOUND)
message("add t-eoParserBoost to TEST_LIST (see: /test/eo/CMakeLists.txt)")
set (TEST_LIST ${TEST_LIST} t-eoParserBoost) # test the new parser with Boost
endif(Boost_FOUND)
foreach (test ${TEST_LIST})
set ("T_${test}_SOURCES" "${test}.cpp")
endforeach (test)

View file

@ -0,0 +1,69 @@
#include <iostream>
#include <vector>
#include "../../src/eo/utils/eoParserBoost.cpp"
int main(int ac, char** av)
{
eoParserBoost parser(ac, av, "Test program.");
unsigned int alpha1 = parser.createParam(10, "alpha1", "A default parameter", "a1", "Default values", false); // WARNING: "integer" is given and but type "unsigned int" is expected.
int alpha2 = parser.createParam(10, "alpha2", "A default parameter", "a2", "Default values", false); // N.B. allows the same string shorthand ! But an exception will be thrown when using it...
unsigned alpha3 = 10;
parser.createParam(alpha3, "alpha3", "A default parameter", "c", "Default values", false);
unsigned int alpha4 = parser.createParam<unsigned>(10, "alpha4", "A implicit parameter", "d", "Implicit values", true);
std::pair< unsigned, unsigned > alpha5 = parser.createParam(std::pair< unsigned, unsigned > (10, 20), "alpha5", "A implicit default parameter", "e", "Implicit and default values"); // Must return the implicit and default values.
parser.createParam(std::pair< std::string, std::string > ("", ""), "blank_option", "A implicit default parameter with empty values", "k", "Implicit and default options");
// Requested value : if no value is given in command-line or by means of a configuration file, an exception will be thrown.
parser.createParam<unsigned int>("alpha6", "A parameter requested value", "f", "Requested values");
// Positional option
unsigned int alpha7 = parser.createParam((unsigned)10, "alpha7", "A positional default parameter", "g", "Positional options", false, 1);
//parser.createParam((unsigned)10, "alpha8", "A default parameter", "h", "Positional options", false, 1); // Get an exception: same position as the former parameter.
parser.createParam((unsigned)5, "gui.alpha9", "A positional implicit parameter", "i", "Positional options", true, 3);
parser.createParam(std::pair< std::string, std::string > ("abc", "defg"), "My_string", "A positional implicit default parameter", "j", "Positional options", 17);
std::cout << "alpha1: " << alpha1 << std::endl;
std::cout << "alpha2: " << alpha2 << std::endl;
std::cout << "alpha3: " << alpha3 << std::endl;
std::cout << "alpha4: " << alpha4 << std::endl;
std::cout << "alpha5 [default value]: " << alpha5.first << std::endl;
std::cout << "alpha5 [implicit value]: " << alpha5.second << std::endl;
std::cout << "alpha7: " << alpha7 << std::endl;
parser.processParams(true); // Set boolean to true in order to require the help configuration.
bool userNeedsHelp = false;
parser.getValue("help", userNeedsHelp);
if (userNeedsHelp)
exit(1); // Exit if help is requested. It has to be done here.
alpha1 = 0;
try
{
parser.getValue(std::string("alpha1"), alpha1); // Get an exception: the registered type (with createParam()) is different from the one which is given as a reference.
std::cout << "alpha1: " << alpha1 << std::endl;
}
catch(std::exception& e) // Catch Boost exception.
{
std::cout << "Catched exception: " << e.what() << std::endl;
}
alpha2 = 0;
parser.getValue("alpha2", alpha2); // default value
std::cout << "alpha2: " << alpha2 << std::endl;
unsigned int alpha6;
parser.getValue("alpha6", alpha6); // requested value
std::cout << "alpha6: " << alpha6 << std::endl;
alpha4 = parser.getValue<unsigned int>("alpha4"); // implicit value
std::cout << "alpha4: " << alpha4 << std::endl;
parser.writeSettings("o.txt");
return 0;
}