diff --git a/cmake/Config.cmake b/cmake/Config.cmake index 278298caf..ebc500e2d 100755 --- a/cmake/Config.cmake +++ b/cmake/Config.cmake @@ -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 ? ###################################################################################### diff --git a/src/eo/utils/CMakeLists.txt b/src/eo/utils/CMakeLists.txt index 3176f5446..d84072031 100755 --- a/src/eo/utils/CMakeLists.txt +++ b/src/eo/utils/CMakeLists.txt @@ -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) diff --git a/src/eo/utils/eoParserBoost.cpp b/src/eo/utils/eoParserBoost.cpp new file mode 100644 index 000000000..0dd48e9d5 --- /dev/null +++ b/src/eo/utils/eoParserBoost.cpp @@ -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::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 +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(_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()->implicit_value(_value), _description.c_str()); + else + sections_map.find(section)->second + .add_options() + (str.c_str(), po::value()->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 +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(_position)); + + std::string section = _section; + if (section == "") + section = "General"; + + manageSectionsMaps(section); + + sections_map.find(section)->second + .add_options() + (str.c_str(), po::value()->default_value(_values.first)->implicit_value(_values.second), _description.c_str()); + + saveInfo(_longName, _shortHand, _description, section, _position); + + return _values; +} + +template +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(_position)); + + std::string section = _section; + if (section == "") + section = "General"; + + manageSectionsMaps(_section); + + sections_map.find(_section)->second + .add_options() + (str.c_str(), po::value()->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& 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 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())) + { // 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())) + { // 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 +void eoParserBoost::getValue(const std::string _longName, ValueType& _val) +{ + if (vm.count(_longName)) + _val = vm[_longName].as(); + 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 +ValueType eoParserBoost::getValue(const std::string _longName) +{ + if (vm.count(_longName)) + return vm[_longName].as(); + 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 eoParserBoost::valueCast(const boost::any& _val) +{ + if (_val.type() == typeid(int)) + return std::make_pair(true, boost::any_cast(_val)); + if (_val.type() == typeid(short)) + return std::make_pair(true, boost::any_cast(_val)); + if (_val.type() == typeid(long)) + return std::make_pair(true, boost::any_cast(_val)); + if (_val.type() == typeid(unsigned int)) + return std::make_pair(true, boost::any_cast(_val)); + if (_val.type() == typeid(unsigned short)) + return std::make_pair(true, boost::any_cast(_val)); + if (_val.type() == typeid(unsigned long)) + return std::make_pair(true, boost::any_cast(_val)); + if (_val.type() == typeid(char)) + return std::make_pair(true, boost::any_cast(_val)); + if (_val.type() == typeid(float)) + return std::make_pair(true, boost::any_cast(_val)); + if (_val.type() == typeid(double)) + return std::make_pair(true, boost::any_cast(_val)); + if (_val.type() == typeid(long double)) + return std::make_pair(true, boost::any_cast(_val)); + if (_val.type() == typeid(bool)) + return std::make_pair(true, boost::any_cast(_val)); + if (_val.type() == typeid(std::string)) + return std::make_pair(true, boost::any_cast(_val)); + if (_val.type() == typeid(char*)) + return std::make_pair(true, boost::any_cast(_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(false, "Unexpected type"); // Type is not referenced. Cast failed. +} + +void eoParserBoost::writeSettings(std::ofstream& _ofs) +{ + for (std::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((i->second)[0]); + std::string description = boost::get((i->second)[1]); + int position = boost::get((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 +} diff --git a/src/eo/utils/eoParserBoost.h b/src/eo/utils/eoParserBoost.h new file mode 100644 index 000000000..e8591d243 --- /dev/null +++ b/src/eo/utils/eoParserBoost.h @@ -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 +#include // std::bad_alloc() +#include +#include +#include +#include +#include // For operator typeid(). + +#include "eoLogger.h" // Required by eo::log (see eoParserBoost::printParsedOptions()). +#include "../eoObject.h" +//#include "../eoPersistent.h" + +#include +#include +#include +#include + +namespace po = boost::program_options; + +typedef std::vector< po::basic_option > opt_vect; // Used by eoParserBoost::printParsedOptions() ; in order to manipulate po::parsed_options easily. +typedef std::vector< std::basic_string > 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 + 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 + 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 + 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 + void getValue(const std::string _longName, ValueType& _val); + + /** + * Cast the parameter value as given by the ValueType type. + */ + template + 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 valueCast(const boost::any& _val); + + /** + * Print settings on an output stream. + */ + void writeSettings(std::ofstream& ofs); +}; + +#endif // EO_PARSER_BOOST_H \ No newline at end of file diff --git a/test/eo/CMakeLists.txt b/test/eo/CMakeLists.txt index f4b03c406..acdf052c5 100755 --- a/test/eo/CMakeLists.txt +++ b/test/eo/CMakeLists.txt @@ -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) diff --git a/test/eo/t-eoParserBoost.cpp b/test/eo/t-eoParserBoost.cpp new file mode 100644 index 000000000..321196a52 --- /dev/null +++ b/test/eo/t-eoParserBoost.cpp @@ -0,0 +1,69 @@ +#include +#include + +#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(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("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("alpha4"); // implicit value + std::cout << "alpha4: " << alpha4 << std::endl; + + parser.writeSettings("o.txt"); + + + return 0; +} \ No newline at end of file