Teuchos_CommandLineProcessor.cpp

Go to the documentation of this file.
00001 // @HEADER
00002 // ***********************************************************************
00003 // 
00004 //                    Teuchos: Common Tools Package
00005 //                 Copyright (2004) Sandia Corporation
00006 // 
00007 // Under terms of Contract DE-AC04-94AL85000, there is a non-exclusive
00008 // license for use of this work by or on behalf of the U.S. Government.
00009 // 
00010 // This library is free software; you can redistribute it and/or modify
00011 // it under the terms of the GNU Lesser General Public License as
00012 // published by the Free Software Foundation; either version 2.1 of the
00013 // License, or (at your option) any later version.
00014 //  
00015 // This library is distributed in the hope that it will be useful, but
00016 // WITHOUT ANY WARRANTY; without even the implied warranty of
00017 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00018 // Lesser General Public License for more details.
00019 //  
00020 // You should have received a copy of the GNU Lesser General Public
00021 // License along with this library; if not, write to the Free Software
00022 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
00023 // USA
00024 // Questions? Contact Michael A. Heroux (maherou@sandia.gov) 
00025 // 
00026 // ***********************************************************************
00027 // @HEADER
00028 
00029 // //////////////////////////////////////////////////
00030 // Teuchos_CommandLineProcessor.cpp
00031 
00032 #include "Teuchos_CommandLineProcessor.hpp"
00033 #include "Teuchos_TestForException.hpp"
00034 
00035 #ifdef HAVE_MPI
00036 #include "mpi.h"
00037 #endif
00038 
00039 namespace {
00040 
00041 inline int my_max( int a, int b ) { return a > b ? a : b; }
00042 
00043 std::string remove_quotes( const std::string& str )
00044 {
00045   if(str[0] != '\"')
00046     return str;
00047   return str.substr(1,str.size()-2);
00048 }
00049 
00050 std::string add_quotes( const std::string& str )
00051 {
00052   if(str[0] == '\"')
00053     return str;
00054   return "\"" + str + "\"";
00055 }
00056 
00057 } // end namespace
00058 
00059 namespace Teuchos {
00060 
00061 CommandLineProcessor::CommandLineProcessor(
00062   bool   throwExceptions
00063   ,bool  recogniseAllOptions
00064   )
00065   :throwExceptions_(throwExceptions)
00066   ,recogniseAllOptions_(recogniseAllOptions)
00067 {}
00068 
00069 // Set up options
00070 
00071 void CommandLineProcessor::setOption(
00072   const char     option_true[]
00073   ,const char    option_false[]
00074   ,bool          *option_val
00075   ,const char    documentation[]
00076   )
00077 {
00078   TEST_FOR_EXCEPT(!(option_val!=NULL));
00079   options_list_[std::string(option_true)]
00080     = opt_val_val_t(OPT_BOOL_TRUE,option_val);
00081   options_list_[std::string(option_false)]
00082     = opt_val_val_t(OPT_BOOL_FALSE,option_val);
00083   options_documentation_list_.push_back(
00084     opt_doc_t(OPT_BOOL_TRUE,option_true,option_false,std::string(documentation?documentation:""),option_val) );
00085 }
00086 
00087 void CommandLineProcessor::setOption(
00088   const char     option_name[]
00089   ,int           *option_val
00090   ,const char    documentation[]
00091   )
00092 {
00093   TEST_FOR_EXCEPT(!(option_val!=NULL));
00094   options_list_[std::string(option_name)]
00095     = opt_val_val_t(OPT_INT,option_val);
00096   options_documentation_list_.push_back(
00097     opt_doc_t(OPT_INT,option_name,"",std::string(documentation?documentation:""),option_val) );
00098 }
00099 
00100 void CommandLineProcessor::setOption(
00101   const char     option_name[]
00102   ,double        *option_val
00103   ,const char    documentation[]
00104   )
00105 {
00106   TEST_FOR_EXCEPT(!(option_val!=NULL));
00107   options_list_[std::string(option_name)]
00108     = opt_val_val_t(OPT_DOUBLE,option_val);
00109   options_documentation_list_.push_back(
00110     opt_doc_t(OPT_DOUBLE,option_name,"",std::string(documentation?documentation:""),option_val) );
00111 }
00112 
00113 void CommandLineProcessor::setOption(
00114   const char     option_name[]
00115   ,std::string   *option_val
00116   ,const char    documentation[]
00117   )
00118 {
00119   TEST_FOR_EXCEPT(!(option_val!=NULL));
00120   options_list_[std::string(option_name)]
00121     = opt_val_val_t(OPT_STRING,option_val);
00122   options_documentation_list_.push_back(
00123     opt_doc_t(OPT_STRING,option_name,"",std::string(documentation?documentation:""),option_val) );
00124 }
00125 
00126 // Parse command line
00127 
00128 CommandLineProcessor::EParseCommandLineReturn
00129 CommandLineProcessor::parse(
00130   int             argc
00131   ,char*          argv[]
00132   ,std::ostream   *errout
00133   ) const
00134 {
00135   std::string        opt_name;
00136   std::string        opt_val_str;
00137   const std::string  help_opt = "help";
00138   const std::string  pause_opt = "pause-for-debugging";
00139 #ifdef HAVE_MPI
00140   int procRank = -1;
00141 #endif
00142   for( int i = 1; i < argc; ++i ) {
00143     bool gov_return = get_opt_val( argv[i], &opt_name, &opt_val_str );
00144     if( !gov_return ) {
00145       if(recogniseAllOptions()) {
00146 #ifdef HAVE_MPI
00147       if (procRank == 0) 
00148 #endif
00149         print_bad_opt(i,argv,errout);
00150         return PARSE_UNRECOGNIZED_OPTION;
00151       }
00152       else {
00153         continue;
00154       }
00155     }
00156     if( opt_name == help_opt ) {
00157       if(errout) printHelpMessage( argv[0], *errout );
00158       return PARSE_HELP_PRINTED;
00159     }
00160     if( opt_name == pause_opt ) {
00161 #ifdef HAVE_MPI
00162       if(procRank < 0 ) MPI_Comm_rank( MPI_COMM_WORLD, &procRank );
00163       if(procRank == 0) {
00164 #endif
00165         std::cerr << "\nType 0 and press enter to continue : ";
00166         int dummy_int = 0;
00167         std::cin >> dummy_int;
00168 #ifdef HAVE_MPI
00169       }
00170       MPI_Barrier(MPI_COMM_WORLD);
00171 #endif
00172       continue;
00173     }
00174     // Lookup the option (we had better find it!)
00175     options_list_t::const_iterator  itr = options_list_.find(opt_name);
00176     if( itr == options_list_.end() ) {
00177 #ifdef HAVE_MPI
00178       if(procRank == 0)
00179 #endif
00180       print_bad_opt(i,argv,errout);
00181       if( recogniseAllOptions() )
00182         return PARSE_UNRECOGNIZED_OPTION;
00183       else
00184         continue;
00185     }
00186     // Changed access to second value of map to not use overloaded arrow operator, 
00187     // otherwise this code will not compile on Janus (HKT, 12/01/2003) 
00188     const opt_val_val_t &opt_val_val = (*itr).second;
00189     switch( opt_val_val.opt_type ) {
00190       case OPT_BOOL_TRUE:
00191         *(any_cast<bool*>(opt_val_val.opt_val)) = true;
00192         break;
00193       case OPT_BOOL_FALSE:
00194         *(any_cast<bool*>(opt_val_val.opt_val)) = false;
00195         break;
00196       case OPT_INT:
00197         *(any_cast<int*>(opt_val_val.opt_val)) = ::atoi(opt_val_str.c_str());
00198         break;
00199       case OPT_DOUBLE:
00200         *(any_cast<double*>(opt_val_val.opt_val)) = ::atof(opt_val_str.c_str());
00201         break;
00202       case OPT_STRING:
00203         *(any_cast<std::string*>(opt_val_val.opt_val)) = remove_quotes(opt_val_str);
00204         break;
00205       case OPT_ENUM_INT:
00206         if( !set_enum_value( i, argv, opt_name, any_cast<int>(opt_val_val.opt_val), remove_quotes(opt_val_str), errout ) )
00207           return PARSE_UNRECOGNIZED_OPTION;
00208         break;
00209       default:
00210         TEST_FOR_EXCEPT(true); // Local programming error only
00211     }
00212   }
00213   return PARSE_SUCCESSFUL;
00214 }
00215 
00216 void CommandLineProcessor::printHelpMessage( const char program_name[], std::ostream &out ) const
00217 {
00218 #ifdef HAVE_MPI
00219   int procRank = 0;
00220   int mpiInitialized;
00221   MPI_Initialized(&mpiInitialized);
00222   if(mpiInitialized) MPI_Comm_rank( MPI_COMM_WORLD, &procRank );
00223   if (procRank == 0) {
00224 #endif
00225   using std::setw;
00226   using std::endl;
00227 
00228   const int opt_type_w = 8;
00229   const char spc_chars[] = "  ";
00230 
00231   // Get the maximum length of an option name
00232   int opt_name_w = 19; // For the 'pause-for-debugging' option
00233   options_documentation_list_t::const_iterator itr;
00234   for( itr = options_documentation_list_.begin(); itr != options_documentation_list_.end(); ++itr ) {
00235     opt_name_w = my_max(opt_name_w,itr->opt_name.length());
00236     if( itr->opt_type )
00237       opt_name_w = my_max(opt_name_w,itr->opt_name_false.length());
00238   }
00239   opt_name_w += 2;
00240 
00241   // Print the help message
00242   out
00243     << "Usage: " << program_name << " [options]\n"
00244     << spc_chars << "options:\n"
00245     << spc_chars
00246     << "--"
00247 #ifdef HAVE_STD_IOS_BASE_FMTFLAGS
00248     << std::left << setw(opt_name_w) << "help"
00249     << std::left << setw(opt_type_w) << " "
00250 #else
00251           << std::setiosflags(std::ios::left) << setw(opt_name_w) << "help"
00252           << std::setiosflags(std::ios::left) << setw(opt_type_w) << " "
00253 #endif
00254     << "Prints this help message"
00255     << endl
00256     << spc_chars
00257     << "--"
00258 #ifdef HAVE_STD_IOS_BASE_FMTFLAGS
00259     << std::left << setw(opt_name_w) << "pause-for-debugging"
00260     << std::left << setw(opt_type_w) << " "
00261 #else
00262           << std::setiosflags(std::ios::left) << setw(opt_name_w) << "pause-for-debugging"
00263           << std::setiosflags(std::ios::left) << setw(opt_type_w) << " "
00264 #endif
00265     << "Pauses for user input to allow attaching a debugger"
00266     << endl;
00267   for( itr = options_documentation_list_.begin(); itr != options_documentation_list_.end(); ++itr ) {
00268     // print top line with option name, type and short documentation string
00269     out
00270       << spc_chars
00271       << "--"
00272 #ifdef HAVE_STD_IOS_BASE_FMTFLAGS
00273       << std::left << setw(opt_name_w) << itr->opt_name
00274       << std::left << setw(opt_type_w) << opt_type_str(itr->opt_type)
00275 #else
00276       << std::setiosflags(std::ios::left) << setw(opt_name_w) << itr->opt_name
00277       << std::setiosflags(std::ios::left) << setw(opt_type_w) << opt_type_str(itr->opt_type)
00278 #endif
00279       << ( itr->documentation.length() ? itr->documentation.c_str() : "No documentation" )
00280       << endl;
00281     // If an enumeration option then the next line is the value options
00282     if( itr->opt_type == OPT_ENUM_INT ) {
00283       out
00284         << spc_chars
00285         << "  "
00286         << setw(opt_name_w) << ""
00287         << setw(opt_type_w) << "";
00288       print_enum_opt_names( any_cast<int>(itr->default_val), out );
00289       out
00290         << endl;
00291     }
00292     // Now print the line that contains the default values
00293     if( itr->opt_type == OPT_BOOL_TRUE ) {
00294       out
00295         << spc_chars
00296         << "--"
00297         << setw(opt_name_w) << itr->opt_name_false;
00298     }
00299     else {
00300       out
00301         << spc_chars
00302         << "  "
00303         << setw(opt_name_w) << " ";
00304     }
00305     out
00306       << setw(opt_type_w) << " "
00307       << "(default: ";
00308     switch( itr->opt_type ) {
00309       case OPT_BOOL_TRUE:
00310         out << "--" << ( (*(any_cast<bool*>(itr->default_val))) ? itr->opt_name : itr->opt_name_false );
00311         break;
00312       case OPT_INT:
00313       case OPT_DOUBLE:
00314       case OPT_STRING:
00315       case OPT_ENUM_INT:
00316         out << "--" << itr->opt_name;
00317         break;
00318       default:
00319         TEST_FOR_EXCEPT(true); // Local programming error only
00320     }
00321     switch( itr->opt_type ) {
00322       case OPT_BOOL_TRUE:
00323         break;
00324       case OPT_INT:
00325         out << "=" << (*(any_cast<int*>(itr->default_val)));
00326         break;
00327       case OPT_DOUBLE:
00328         out <<  "=" << (*(any_cast<double*>(itr->default_val)));
00329         break;
00330       case OPT_STRING:
00331         out <<  "=" << add_quotes(*(any_cast<std::string*>(itr->default_val)));
00332         break;
00333       case OPT_ENUM_INT:
00334         out <<  "=" << add_quotes(enum_opt_default_val_name(itr->opt_name,any_cast<int>(itr->default_val),&out));
00335         break;
00336       default:
00337         TEST_FOR_EXCEPT(true); // Local programming error only
00338     }
00339     out << ")\n";
00340   }
00341   if(throwExceptions_)
00342     TEST_FOR_EXCEPTION( true, HelpPrinted, "Help message was printed" );
00343 #ifdef HAVE_MPI
00344   }
00345 #endif
00346 }
00347 
00348 // private
00349 
00350 void CommandLineProcessor::setEnumOption(
00351   const char    enum_option_name[]
00352   ,int          *enum_option_val
00353   ,const int    num_enum_opt_values
00354   ,const int    enum_opt_values[]
00355   ,const char*  enum_opt_names[]
00356   ,const char   documentation[]
00357   )
00358 {
00359   TEST_FOR_EXCEPT(enum_option_val==NULL);
00360   TEST_FOR_EXCEPT(num_enum_opt_values<=0);
00361   TEST_FOR_EXCEPT(enum_opt_values==NULL);
00362   TEST_FOR_EXCEPT(enum_opt_names==NULL);
00363 
00364   enum_opt_data_list_.push_back(
00365     enum_opt_data_t(enum_option_val,num_enum_opt_values,enum_opt_values,enum_opt_names)
00366     );
00367   const int opt_id = enum_opt_data_list_.size()-1;
00368   options_list_[std::string(enum_option_name)]
00369     = opt_val_val_t(OPT_ENUM_INT,opt_id);
00370   options_documentation_list_.push_back(
00371     opt_doc_t(OPT_ENUM_INT,enum_option_name,"",std::string(documentation?documentation:""),opt_id)
00372     );
00373 }
00374 
00375 bool CommandLineProcessor::set_enum_value(
00376   int                  argv_i
00377   ,char*               argv[]
00378   ,const std::string   &enum_opt_name
00379   ,const int           enum_id
00380   ,const std::string   &enum_str_val
00381   ,std::ostream        *errout
00382   ) const
00383 {
00384   const enum_opt_data_t
00385     &enum_opt_data = enum_opt_data_list_.at(enum_id);
00386   std::vector<std::string>::const_iterator
00387     itr_begin = enum_opt_data.enum_opt_names.begin(),
00388     itr_end   = enum_opt_data.enum_opt_names.end(),
00389     itr       =  std::find( itr_begin, itr_end, enum_str_val );
00390   if( itr == itr_end ) {
00391     const int j = argv_i;
00392 #define CLP_ERR_MSG \
00393       "Error, the value \"" << enum_str_val << "\" for the " \
00394       << j<<(j==1?"st":(j==2?"nd":(j==3?"rd":"th"))) << " option --" \
00395       << enum_opt_name << " was not recognized (use --help)!"
00396     if(errout)
00397       *errout << std::endl << argv[0] << " : " << CLP_ERR_MSG << std::endl;
00398     if( throwExceptions() ) {
00399       TEST_FOR_EXCEPTION( true, std::invalid_argument, CLP_ERR_MSG );
00400     }
00401     else {
00402       return false;
00403     }
00404 #undef CLP_ERR_MSG
00405   }
00406   const int enum_opt_val_index = itr - itr_begin;
00407   *enum_opt_data.enum_option_val = enum_opt_data.enum_opt_values.at(enum_opt_val_index);
00408   return true;
00409 }
00410 
00411 void CommandLineProcessor::print_enum_opt_names(
00412   const int            enum_id
00413   ,std::ostream        &out
00414   ) const
00415 {
00416   const enum_opt_data_t
00417     &enum_opt_data = enum_opt_data_list_.at(enum_id);
00418   typedef std::vector<string>::const_iterator itr_t;
00419   out << "Valid options:";
00420   for( itr_t itr = enum_opt_data.enum_opt_names.begin(); itr != enum_opt_data.enum_opt_names.end() ; ++itr ) {
00421     if( itr != enum_opt_data.enum_opt_names.begin() ) out << ",";
00422     out << " " << add_quotes(*itr);
00423   }
00424 }
00425 
00426 std::string
00427 CommandLineProcessor::enum_opt_default_val_name(
00428   const std::string    &enum_name
00429   ,const int           enum_id
00430   ,std::ostream        *errout
00431   ) const
00432 {
00433   const enum_opt_data_t
00434     &enum_opt_data = enum_opt_data_list_.at(enum_id);
00435   return enum_opt_data.enum_opt_names.at(
00436     find_enum_opt_index(
00437       enum_name,*enum_opt_data.enum_option_val,enum_opt_data,errout
00438       )
00439     );
00440 }
00441 
00442 int CommandLineProcessor::find_enum_opt_index(
00443   const std::string           &enum_opt_name
00444   ,const int                  opt_value
00445   ,const enum_opt_data_t      &enum_data
00446   ,std::ostream               *errout
00447   ) const
00448 {
00449   std::vector<int>::const_iterator
00450     itr_begin = enum_data.enum_opt_values.begin(),
00451     itr_end   = enum_data.enum_opt_values.end(),
00452     itr       =  std::find( itr_begin, itr_end, opt_value );
00453   if( itr == itr_end ) {
00454 #define CLP_ERR_MSG \
00455       ( recogniseAllOptions() ? "Error" : "Warning" ) \
00456       << ", option --" << enum_opt_name << " was given an invalid " \
00457       "initial option value of " << opt_value << "!"
00458     if(errout)
00459       *errout << CLP_ERR_MSG << std::endl;
00460     if( throwExceptions() )
00461       TEST_FOR_EXCEPTION( true, std::invalid_argument, CLP_ERR_MSG );
00462 #undef CLP_ERR_MSG
00463   }
00464   return itr - itr_begin;
00465 }
00466 
00467 bool CommandLineProcessor::get_opt_val(
00468   const char     str[]
00469   ,std::string   *opt_name
00470   ,std::string   *opt_val_str
00471   ) const
00472 {
00473   const int len = strlen(str);
00474   if( len < 3 )
00475     return false; // Can't be an option with '--' followed by at least one char
00476   if( str[0] != '-' || str[1] != '-' )
00477     return false; // Not a recognised option
00478   // Find the '='
00479   int equ_i;
00480   for( equ_i = 2; equ_i < len && str[equ_i] != '='; ++equ_i );
00481   // Set opt_name
00482   opt_name->assign( str + 2, equ_i-2 );
00483   // Set opt_val_str
00484   if( equ_i == len ) {
00485     *opt_val_str = "";
00486   }
00487   else {
00488     opt_val_str->assign( str + equ_i + 1, len - equ_i - 1 );
00489   }
00490   return true;
00491 }
00492 
00493 void CommandLineProcessor::print_bad_opt(
00494   int             argv_i
00495   ,char*          argv[]
00496   ,std::ostream   *errout
00497   ) const
00498 {
00499   const int j = argv_i;
00500 #define CLP_ERR_MSG \
00501     ( recogniseAllOptions() ? "Error" : "Warning" ) \
00502     << ", the " << j<<(j==1?"st":(j==2?"nd":(j==3?"rd":"th"))) \
00503     << " option \'" << argv[argv_i] << "\' was not recognized (use --help)!"
00504   if(errout)
00505     *errout << std::endl << argv[0] << " : " << CLP_ERR_MSG << std::endl;
00506   if( recogniseAllOptions() && throwExceptions() )
00507     TEST_FOR_EXCEPTION( true, UnrecognizedOption, CLP_ERR_MSG );
00508 #undef CLP_ERR_MSG
00509 }
00510 
00511 } // end namespace Teuchos
00512 
00513 

Generated on Thu Sep 18 12:41:17 2008 for Teuchos - Trilinos Tools Package by doxygen 1.3.9.1