vsg  1.0.4
VulkanSceneGraph library
CommandLine.h
1 #pragma once
2 
3 /* <editor-fold desc="MIT License">
4 
5 Copyright(c) 2018 Robert Osfield
6 
7 Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
8 
9 The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
10 
11 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
12 
13 </editor-fold> */
14 
15 #include <vsg/core/Export.h>
16 #include <vsg/core/type_name.h>
17 #include <vsg/io/Options.h>
18 #include <vsg/io/stream.h>
19 
20 #include <vector>
21 
22 namespace vsg
23 {
24 
25  template<typename T>
26  constexpr std::size_t type_num_elements(T) noexcept { return 1; }
27  template<typename T>
28  constexpr std::size_t type_num_elements(const t_vec2<T>&) noexcept { return 2; }
29  template<typename T>
30  constexpr std::size_t type_num_elements(const t_vec3<T>&) noexcept { return 3; }
31  template<typename T>
32  constexpr std::size_t type_num_elements(const t_vec4<T>&) noexcept { return 4; }
33  template<typename T>
34  constexpr std::size_t type_num_elements(const t_mat4<T>&) noexcept { return 16; }
35  template<typename T, typename R>
36  constexpr std::size_t type_num_elements(const std::pair<T, R>&) noexcept { return 2; }
37 
38  // forward declare
39  class Options;
40 
43  class VSG_DECLSPEC CommandLine
44  {
45  public:
46  CommandLine(int* argc, char** argv);
47 
48  int& argc() { return *_argc; }
49  char** argv() { return _argv; }
50 
51  char* operator[](int i) { return _argv[i]; }
52 
53  template<typename T>
54  bool read(int& i, T& v)
55  {
56  const int num_args = *_argc;
57  if (i >= num_args) return false;
58 
59  if constexpr (std::is_same_v<T, std::string>)
60  {
61  v = _argv[i++];
62  return true;
63  }
64  else
65  {
66  std::size_t num_elements = type_num_elements(v);
67 
68  _istr.clear();
69  if (num_elements == 1)
70  {
71  _istr.str(_argv[i]);
72  ++i;
73  }
74  else
75  {
76  std::string str;
77  for (; num_elements > 0 && i < num_args; --num_elements, ++i)
78  {
79  str += ' ';
80  str += _argv[i];
81  }
82 
83  _istr.str(str);
84  }
85  _istr >> v;
86 
87  return (!_istr.fail());
88  }
89  }
90 
91  void remove(int i, int num)
92  {
93  if (i >= *_argc) return;
94 
95  int source = i + num;
96  if (source >= *_argc)
97  {
98  // removed section is at end of argv so just reset argc to i
99  *_argc = i;
100  return;
101  }
102 
103  // shift all the remaining entries down to fill the removed space
104  for (; source < *_argc; ++i, ++source)
105  {
106  _argv[i] = _argv[source];
107  }
108 
109  *_argc -= num;
110  }
111 
112  template<typename... Args>
113  bool read(const std::string& match, Args&... args)
114  {
115  for (int i = 1; i < *_argc; ++i)
116  {
117  if (match == _argv[i])
118  {
119  int start = i;
120  ++i;
121 
122  // match any parameters
123  bool result = (read(i, args) && ...);
124 
125  if (result)
126  {
127  remove(start, i - start);
128  }
129  else
130  {
131  std::string parameters = ((match + " ") + ... + type_name(args));
132  std::string errorMessage = std::string("Failed to match command line required parameters for ") + parameters;
133  _errorMessages.push_back(errorMessage);
134  }
135 
136  return result;
137  }
138  }
139  return false;
140  }
141 
142  template<typename... Args>
143  bool read(std::initializer_list<std::string> matches, Args&... args)
144  {
145  bool result = false;
146  for (auto str : matches) result = read(str, args...) | result;
147  return result;
148  }
149 
150  template<typename T, typename... Args>
151  T value(T defaultValue, const std::string& match, Args&... args)
152  {
153  T v{defaultValue};
154  read(match, args..., v);
155  return v;
156  }
157 
158  template<typename T, typename... Args>
159  T value(T defaultValue, std::initializer_list<std::string> matches, Args&... args)
160  {
161  T v{defaultValue};
162  read(matches, args..., v);
163  return v;
164  }
165 
166  template<typename T>
167  bool readAndAssign(const std::string& match, Options* options)
168  {
169  if constexpr (std::is_same_v<T, void>)
170  {
171  if (options && read(std::string("--") + match))
172  {
173  options->setValue(match, true);
174  return true;
175  }
176  }
177  else
178  {
179  T v;
180  if (options && read(std::string("--") + match, v))
181  {
182  options->setValue(match, v);
183  return true;
184  }
185  }
186  return false;
187  }
188 
189  bool read(Options* options);
190 
191  using Messages = std::vector<std::string>;
192  bool errors() const { return !_errorMessages.empty(); }
193 
194  Messages& getErrorMessages() { return _errorMessages; }
195  const Messages& getErrorMessages() const { return _errorMessages; }
196 
197  int writeErrorMessages(std::ostream& out) const
198  {
199  if (_errorMessages.empty()) return 1;
200  for (auto message : _errorMessages) out << message << std::endl;
201  return 0;
202  }
203 
204  protected:
205  int* _argc;
206  char** _argv;
207  std::istringstream _istr;
208  Messages _errorMessages;
209  };
210 
211  // specialize handling of bool parameter
212  template<>
213  inline bool CommandLine::read(int& i, bool& v)
214  {
215  const int num_args = *_argc;
216  if (i >= num_args) return false;
217 
218  const char* str = _argv[i];
219  if (!str) return false;
220 
221  if (std::strcmp(str, "true") == 0 || std::strcmp(str, "True") == 0 || std::strcmp(str, "TRUE") == 0 || std::strcmp(str, "1") == 0)
222  {
223  v = true;
224  ++i;
225  return true;
226  }
227 
228  if (std::strcmp(str, "false") == 0 || std::strcmp(str, "False") == 0 || std::strcmp(str, "FALSE") == 0 || std::strcmp(str, "0") == 0)
229  {
230  v = false;
231  ++i;
232  return true;
233  }
234  return false;
235  }
236 
237  // specialize matching of bool parameters
238  template<>
239  inline bool CommandLine::read(const std::string& match, bool& v)
240  {
241  for (int i = 1; i < *_argc; ++i)
242  {
243  if (match == _argv[i])
244  {
245  int start = i;
246  ++i;
247 
248  // match any parameters
249  if (!read(i, v))
250  {
251  v = true;
252  }
253 
254  remove(start, i - start);
255 
256  return true;
257  }
258  }
259  return false;
260  }
261 
262 } // namespace vsg
Definition: CommandLine.h:44
void setValue(const std::string &key, const T &value)
Definition: Value.h:135
Class for passing IO related options to vsg::read/write calls.
Definition: Options.h:33