Visual Servoing Platform  version 3.6.1 under development (2024-11-14)
vpJsonArgumentParser.h
1 /*
2  * ViSP, open source Visual Servoing Platform software.
3  * Copyright (C) 2005 - 2024 by Inria. All rights reserved.
4  *
5  * This software is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  * See the file LICENSE.txt at the root directory of this source
10  * distribution for additional information about the GNU GPL.
11  *
12  * For using ViSP with software that can not be combined with the GNU
13  * GPL, please contact Inria about acquiring a ViSP Professional
14  * Edition License.
15  *
16  * See https://visp.inria.fr for more information.
17  *
18  * This software was developed at:
19  * Inria Rennes - Bretagne Atlantique
20  * Campus Universitaire de Beaulieu
21  * 35042 Rennes Cedex
22  * France
23  *
24  * If you have questions regarding the use of this file, please contact
25  * Inria at visp@inria.fr
26  *
27  * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
28  * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
29  *
30  * Description:
31  * An argument parser that can both use JSON files and command line arguments as inputs.
32  */
33 
34 #ifndef VP_JSON_ARGUMENT_PARSER_H
35 #define VP_JSON_ARGUMENT_PARSER_H
36 
37 #include <visp3/core/vpConfig.h>
38 
39 #if defined(VISP_HAVE_NLOHMANN_JSON)
40 #include VISP_NLOHMANN_JSON(json.hpp)
41 #include <visp3/core/vpException.h>
42 #include <sstream>
43 
44 
52 template<typename T>
53 nlohmann::json convertCommandLineArgument(const std::string &arg)
54 {
55  nlohmann::json j = nlohmann::json::parse(arg);
56  return j;
57 }
58 
65 template<>
66 nlohmann::json convertCommandLineArgument<std::string>(const std::string &arg)
67 {
68  nlohmann::json j = arg;
69  return j;
70 }
71 
72 BEGIN_VISP_NAMESPACE
73 
137 class VISP_EXPORT vpJsonArgumentParser
138 {
139 public:
140 
142  {
143  WITH_FIELD = 0,
144  FLAG = 1
145  };
146 
163  vpJsonArgumentParser(const std::string &description, const std::string &jsonFileArgumentName, const std::string &nestSeparator);
164 
172  std::string help() const;
173 
188  template<typename T>
189  vpJsonArgumentParser &addArgument(const std::string &name, T &parameter, const bool required = true, const std::string &help = "No description")
190  {
191  argumentType[name] = WITH_FIELD;
192  const auto getter = [name, this](nlohmann::json &j, bool create) -> nlohmann::json * {
193  size_t pos = 0;
194  nlohmann::json *f = &j;
195  std::string token;
196  std::string name_copy = name;
197 
198  while ((pos = name_copy.find(nestSeparator)) != std::string::npos) {
199  token = name_copy.substr(0, pos);
200 
201  name_copy.erase(0, pos + nestSeparator.length());
202  if (create && !f->contains(token)) {
203  (*f)[token] = {};
204  }
205  else if (!f->contains(token)) {
206  return nullptr;
207  }
208  f = &(f->at(token));
209  }
210  if (create && !f->contains(name_copy)) {
211  (*f)[name_copy] = {};
212  }
213  else if (!f->contains(name_copy)) {
214  return nullptr;
215  }
216  f = &(f->at(name_copy));
217  return f;
218  };
219 
220  parsers[name] = [&parameter, required, getter, name](nlohmann::json &j) {
221  const nlohmann::json *field = getter(j, false);
222  const bool fieldHasNoValue = field == nullptr || (field != nullptr && field->is_null());
223  if (required && fieldHasNoValue) {
224  std::stringstream ss;
225  ss << "Argument " << name << " is required, but no value was provided" << std::endl;
226  throw vpException(vpException::badValue, ss.str());
227  }
228  else if (!fieldHasNoValue) {
229  field->get_to(parameter);
230  }
231  };
232 
233  updaters[name] = [getter](nlohmann::json &j, const std::string &s) {
234  nlohmann::json *field = getter(j, true);
235  *field = convertCommandLineArgument<T>(s);
236  };
237 
238  helpers[name] = [help, parameter, required]() -> std::string {
239  std::stringstream ss;
240  nlohmann::json repr = parameter;
241  ss << help << std::endl << "Default: " << repr;
242  if (required) {
243  ss << std::endl << "Required";
244  }
245  else {
246  ss << std::endl << "Optional";
247  }
248  return ss.str();
249  };
250 
251  nlohmann::json *exampleField = getter(exampleJson, true);
252  *exampleField = parameter;
253 
254  return *this;
255  }
256 
266  vpJsonArgumentParser &addFlag(const std::string &name, bool &parameter, const std::string &help = "No description");
267 
274  void parse(int argc, const char *argv[]);
275 
276 private:
277  std::string description; // Program description
278  std::string jsonFileArgumentName; // Name of the argument that points to the json file: ./program --config settings.json. Here jsonFileArgumentName == "--config"
279  std::string nestSeparator; // JSON nesting delimiter character. Used to access JSON nested objects from a single string
280  std::map<std::string, std::function<void(nlohmann::json &)>> parsers; // Functions that update the variables with the values contained in the JSON document (should be used after calling updaters)
281  std::map<std::string, vpJsonArgumentType> argumentType; // Update the base json document with command line arguments
282  std::map<std::string, std::function<void(nlohmann::json &, const std::string &)>> updaters; // Update the base json document with command line arguments
283  std::map<std::string, std::function<std::string()>> helpers; // Functions that output the usage and description of command line arguments: used when the help flag is given as argument
284  nlohmann::json exampleJson; // Example JSON argument file: displayed when user calls for help
285 };
286 
287 END_VISP_NAMESPACE
288 
289 #endif // VISP_HAVE_NLOHMANN_JSON
290 
291 #endif
error that can be emitted by ViSP classes.
Definition: vpException.h:60
@ badValue
Used to indicate that a value is not in the allowed range.
Definition: vpException.h:73
Command line argument parsing with support for JSON files. If a JSON file is supplied,...
vpJsonArgumentParser & addArgument(const std::string &name, T &parameter, const bool required=true, const std::string &help="No description")
Add an argument that can be provided by the user, either via command line or through the json file.