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:
parent
34be39f0aa
commit
8c078b685e
6 changed files with 932 additions and 0 deletions
|
|
@ -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 ?
|
||||
######################################################################################
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
||||
|
|
|
|||
555
src/eo/utils/eoParserBoost.cpp
Normal file
555
src/eo/utils/eoParserBoost.cpp
Normal 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
|
||||
}
|
||||
283
src/eo/utils/eoParserBoost.h
Normal file
283
src/eo/utils/eoParserBoost.h
Normal 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
|
||||
|
|
@ -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)
|
||||
|
|
|
|||
69
test/eo/t-eoParserBoost.cpp
Normal file
69
test/eo/t-eoParserBoost.cpp
Normal 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;
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue