Visual Servoing Platform  version 3.6.1 under development (2024-07-27)
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 <nlohmann/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 }
64 template<>
65 nlohmann::json convertCommandLineArgument<std::string>(const std::string &arg)
66 {
67  nlohmann::json j = arg;
68  return j;
69 }
70 
72 
136 class VISP_EXPORT vpJsonArgumentParser
137 {
138 public:
155  vpJsonArgumentParser(const std::string &description, const std::string &jsonFileArgumentName, const std::string &nestSeparator);
156 
164  std::string help() const;
165 
166 
181  template<typename T>
182  vpJsonArgumentParser &addArgument(const std::string &name, T &parameter, const bool required = true, const std::string &help = "No description")
183  {
184  const auto getter = [name, this](nlohmann::json &j, bool create) -> nlohmann::json * {
185  size_t pos = 0;
186  nlohmann::json *f = &j;
187  std::string token;
188  std::string name_copy = name;
189 
190  while ((pos = name_copy.find(nestSeparator)) != std::string::npos) {
191  token = name_copy.substr(0, pos);
192 
193  name_copy.erase(0, pos + nestSeparator.length());
194  if (create && !f->contains(token)) {
195  (*f)[token] = {};
196  }
197  else if (!f->contains(token)) {
198  return nullptr;
199  }
200  f = &(f->at(token));
201  }
202  if (create && !f->contains(name_copy)) {
203  (*f)[name_copy] = {};
204  }
205  else if (!f->contains(name_copy)) {
206  return nullptr;
207  }
208  f = &(f->at(name_copy));
209  return f;
210  };
211 
212  parsers[name] = [&parameter, required, getter, name](nlohmann::json &j) {
213  const nlohmann::json *field = getter(j, false);
214  const bool fieldHasNoValue = field == nullptr || (field != nullptr && field->is_null());
215  if (required && fieldHasNoValue) {
216  std::stringstream ss;
217  ss << "Argument " << name << " is required, but no value was provided" << std::endl;
218  throw vpException(vpException::badValue, ss.str());
219  }
220  else if (!fieldHasNoValue) {
221  field->get_to(parameter);
222  }
223  };
224 
225  updaters[name] = [getter](nlohmann::json &j, const std::string &s) {
226  nlohmann::json *field = getter(j, true);
227  *field = convertCommandLineArgument<T>(s);
228  };
229 
230  helpers[name] = [help, parameter, required]() -> std::string {
231  std::stringstream ss;
232  nlohmann::json repr = parameter;
233  ss << help << std::endl << "Default: " << repr;
234  if (required) {
235  ss << std::endl << "Required";
236  }
237  else {
238  ss << std::endl << "Optional";
239  }
240  return ss.str();
241  };
242 
243  nlohmann::json *exampleField = getter(exampleJson, true);
244  *exampleField = parameter;
245 
246  return *this;
247  }
248 
255  void parse(int argc, const char *argv[]);
256 
257 
258 private:
259  std::string description; // Program description
260  std::string jsonFileArgumentName; // Name of the argument that points to the json file: ./program --config settings.json. Here jsonFileArgumentName == "--config"
261  std::string nestSeparator; // JSON nesting delimiter character. Used to access JSON nested objects from a single string
262  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)
263  std::map<std::string, std::function<void(nlohmann::json &, const std::string &)>> updaters; // Update the base json document with command line arguments
264  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
265  nlohmann::json exampleJson; // Example JSON argument file: displayed when user calls for help
266 };
267 
268 END_VISP_NAMESPACE
269 
270 #endif // VISP_HAVE_NLOHMANN_JSON
271 
272 #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.