RCFProto
 All Classes Functions Typedefs
CommandLine.hpp
1 
2 //******************************************************************************
3 // RCF - Remote Call Framework
4 //
5 // Copyright (c) 2005 - 2013, Delta V Software. All rights reserved.
6 // http://www.deltavsoft.com
7 //
8 // RCF is distributed under dual licenses - closed source or GPL.
9 // Consult your particular license for conditions of use.
10 //
11 // If you have not purchased a commercial license, you are using RCF
12 // under GPL terms.
13 //
14 // Version: 2.0
15 // Contact: support <at> deltavsoft.com
16 //
17 //******************************************************************************
18 
19 #ifndef INCLUDE_UTIL_COMMANDLINE_HPP
20 #define INCLUDE_UTIL_COMMANDLINE_HPP
21 
22 #include <exception>
23 #include <iostream>
24 #include <map>
25 #include <sstream>
26 #include <stdexcept>
27 #include <vector>
28 
29 //*****************************************
30 // Command line parsing utility
31 
32 namespace RCF {
33 
34  class I_CommandLineOption {
35  public:
36  virtual ~I_CommandLineOption() {}
37  virtual void notify_begin() = 0;
38  virtual void notify( std::string) = 0;
39  virtual void notify_end() = 0;
40  virtual std::string getName() = 0;
41  virtual std::string getDefaultValue() = 0;
42  virtual std::string getHelpString() = 0;
43  };
44 
45  class CommandLine
46  {
47  private:
48  CommandLine()
49  {}
50 
51  public:
52  static CommandLine &getSingleton()
53  {
54  static CommandLine commandLine;
55  return commandLine;
56  }
57 
58  void parse(int argc, char **argv, bool exitOnHelp = true)
59  {
60  parse(argc, const_cast<const char **>(argv), exitOnHelp);
61  }
62 
63  void parse(int argc, const char **argv, bool exitOnHelp = true)
64  {
65  mOptionValues.clear();
66  mArgs.clear();
67  int i=1;
68 
69  // If there is no "help" command line option registered, and the only command line option found is "help",
70  // then we print out the helpstrings for all known command line options, and exit.
71  if (argc == 2 && isKey(argv[1]) && toKey(argv[1]) == "help")
72  {
73  if (mOptions.find( "help" ) == mOptions.end())
74  {
75  std::cout << "Available command line switches:\n";
76  for (OptionIterator it = mOptions.begin(); it != mOptions.end(); it++)
77  {
78  if ((*it).second)
79  {
80  I_CommandLineOption *option = (*it).second;
81  std::cout << "-" << option->getName() << "\n";
82  std::cout << "\tDescription: " << option->getHelpString() << "\n";
83  std::cout << "\tDefault: " << option->getDefaultValue() << "\n";
84  }
85  }
86  if (exitOnHelp)
87  {
88  exit(0);
89  }
90  }
91  }
92 
93  // Parse the command line
94  while (i < argc)
95  {
96  std::string arg1 = argv[i];
97  i++;
98  std::string arg2 = (i<argc) ? argv[i] : "";
99  i++;
100  if (isKey(arg1))
101  {
102  if (!isKey(arg2) )
103  {
104  mOptionValues[ toKey(arg1) ].push_back( arg2 );
105  }
106  else
107  {
108  mOptionValues[ toKey(arg1) ].push_back( "" );
109  i--;
110  }
111  if (mOptions.find( toKey(arg1) ) == mOptions.end())
112  {
113  // This isn't very useful.
114  //std::cout << "Unrecognized option \"" << arg1 << "\"; type \"-help\" to list all options.\n";
115  }
116  }
117  else
118  {
119  mArgs.push_back( arg1 );
120  i--;
121  }
122  }
123 
124  // Notify the registered I_CommandLineOption objects
125  for (OptionIterator iter = mOptions.begin(); iter != mOptions.end(); iter++)
126  {
127  if ((*iter).second)
128  {
129  (*iter).second->notify_begin();
130  }
131  }
132 
133  for (OptionValueIterator iter = mOptionValues.begin(); iter != mOptionValues.end(); iter++)
134  {
135  OptionIterator jter = mOptions.find((*iter).first);
136  if (jter != mOptions.end())
137  {
138  for (ValueIterator kter = (*iter).second.begin(); kter != (*iter).second.end(); kter++)
139  {
140  jter->second->notify( *kter );
141  }
142  }
143  }
144 
145  for (OptionIterator iter = mOptions.begin(); iter != mOptions.end(); iter++)
146  {
147  if ((*iter).second)
148  {
149  (*iter).second->notify_end();
150  }
151  }
152  }
153 
154  void registerOption(I_CommandLineOption *option)
155  {
156  mOptions[ option->getName() ] = option;
157  }
158 
159  void unregisterOption(I_CommandLineOption *option)
160  {
161  OptionIterator it = mOptions.find( option->getName() );
162  if (it != mOptions.end() && (*it).second == option)
163  {
164  mOptions.erase( it );
165  }
166  }
167 
168  void clear()
169  {
170  mOptions.clear();
171  mOptionValues.clear();
172  mArgs.clear();
173  }
174 
175  template<typename T>
176  T get(std::string name)
177  {
178  T t;
179  lexical_cast( mOptionValues[name][0], t );
180  return t;
181  }
182 
183  const std::vector<std::string> &getArguments()
184  {
185  return mArgs;
186  }
187 
188  const std::vector<std::string> &getValues(std::string name)
189  {
190  return mOptionValues[name];
191  }
192 
193  template<typename T>
194  T lexical_cast(std::string strValue, T* = NULL)
195  {
196  T t;
197  //t = boost::lexical_cast<T, std::string>(strValue);
198  lexical_cast(strValue, t);
199  return t;
200  }
201 
202  private:
203 
204  std::map< std::string, I_CommandLineOption *> mOptions;
205  std::map<std::string, std::vector<std::string> > mOptionValues;
206  std::vector<std::string> mArgs;
207 
208  typedef std::map< std::string, I_CommandLineOption *>::iterator OptionIterator;
209  typedef std::map<std::string, std::vector<std::string> >::iterator OptionValueIterator;
210  typedef std::vector<std::string>::iterator ValueIterator;
211 
212  bool isKey(const std::string &arg)
213  {
214  bool startsWithDash = (arg.size() > 1 && arg[0] == '-' );
215  bool startsWithDoubleDash = (arg.size() > 2 && arg[0] == '-' && arg[1] == '-');
216  return startsWithDash || startsWithDoubleDash;
217  }
218 
219  std::string toKey(const std::string &arg)
220  {
221  assert( isKey(arg) );
222 
223  bool startsWithDash = (arg.size() > 1 && arg[0] == '-' );
224  bool startsWithDoubleDash = (arg.size() > 2 && arg[0] == '-' && arg[1] == '-');
225 
226  if (startsWithDoubleDash)
227  {
228  return arg.substr(2);
229  }
230  else if (startsWithDash)
231  {
232  return arg.substr(1);
233  }
234  else
235  {
236  assert(0 && "invalid command line option syntax");
237  return "";
238  }
239  }
240 
241  void lexical_cast( const std::string &strValue, bool &value )
242  {
243  if (strValue == "1" || strValue == "true" || strValue == "")
244  value = true;
245  else
246  value = false;
247  }
248 
249  void lexical_cast( const std::string &strValue, int &value )
250  {
251  value = atoi(strValue.c_str());
252  }
253 
254  void lexical_cast( const std::string &strValue, unsigned int &value )
255  {
256  value = static_cast<unsigned int>(atoi(strValue.c_str()));
257  }
258 
259  void lexical_cast( const std::string &strValue, std::string &value )
260  {
261  value = strValue;
262  }
263 
264  };
265 
266  template<typename T>
267  class CommandLineOption : public I_CommandLineOption {
268  public:
269 
270  CommandLineOption(std::string name, T default_value, std::string helpString) :
271  name(name), default_value(default_value), helpString(helpString)
272  {
273  CommandLine::getSingleton().registerOption(this);
274  }
275 
276  ~CommandLineOption()
277  {
278  CommandLine::getSingleton().unregisterOption(this);
279  }
280 
281  operator T() const
282  {
283  return get();
284  }
285 
286  //const std::vector<std::string> &getValues() const
287  const std::vector<T> &getValues() const
288  {
289  return values;
290  }
291 
292  T get() const
293  {
294  return (values.empty()) ? default_value : values[0];
295  }
296 
297  void set(T t)
298  {
299  values.clear();
300  values.push_back( t );
301  }
302 
303  virtual void on_notify_end()
304  {}
305 
306  private:
307 
308  void notify_begin()
309  {
310  values.clear();
311  }
312 
313  void notify_end()
314  {
315  on_notify_end();
316  }
317 
318  void notify( std::string value )
319  {
320  //values.push_back( CommandLine::getSingleton().template lexical_cast<T>(value) );
321  values.push_back( CommandLine::getSingleton().lexical_cast(value, (T *) 0));
322  }
323 
324  std::string getName()
325  {
326  return name;
327  }
328 
329  std::string getHelpString()
330  {
331  return helpString;
332  }
333 
334  std::string getDefaultValue()
335  {
336  std::ostringstream ostr;
337  ostr << default_value ;
338  return ostr.str();
339  }
340 
341  private:
342  std::string name;
343  T default_value;
344  std::vector<T> values;
345  std::string helpString;
346  };
347 
348  template<typename T>
349  inline std::istream &operator>>(std::istream &is, CommandLineOption<T> &option)
350  {
351  T t;
352  is >> t;
353  option.set(t);
354  return is;
355  }
356 
357  template<typename T>
358  inline RCF::MemOstream &operator<<(RCF::MemOstream &os, CommandLineOption<T> &option)
359  {
360  os << option.get();
361  return os;
362  }
363 
364 } // namespace RCF
365 
366 #endif // ! INCLUDE_UTIL_COMMANDLINE_HPP