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