Visual Servoing Platform  version 3.6.1 under development (2024-04-27)
vpJsonArgumentParser.h
1 /*
2  * ViSP, open source Visual Servoing Platform software.
3  * Copyright (C) 2005 - 2023 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 _vpJsonArgumentParser_h_
35 #define _vpJsonArgumentParser_h_
36 #include <visp3/core/vpConfig.h>
37 
38 #if defined(VISP_HAVE_NLOHMANN_JSON)
39 #include <nlohmann/json.hpp>
40 #include <visp3/core/vpException.h>
41 #include <sstream>
42 
43 
51 template<typename T>
52 nlohmann::json convertCommandLineArgument(const std::string &arg)
53 {
54  nlohmann::json j = nlohmann::json::parse(arg);
55  return j;
56 }
63 template<>
64 nlohmann::json convertCommandLineArgument<std::string>(const std::string &arg)
65 {
66  nlohmann::json j = arg;
67  return j;
68 }
132 class VISP_EXPORT vpJsonArgumentParser
133 {
134 public:
151  vpJsonArgumentParser(const std::string &description, const std::string &jsonFileArgumentName, const std::string &nestSeparator);
152 
160  std::string help() const;
161 
162 
177  template<typename T>
178  vpJsonArgumentParser &addArgument(const std::string &name, T &parameter, const bool required = true, const std::string &help = "No description")
179  {
180  const auto getter = [name, this](nlohmann::json &j, bool create) -> nlohmann::json *{
181  size_t pos = 0;
182  nlohmann::json *f = &j;
183  std::string token;
184  std::string name_copy = name;
185 
186  while ((pos = name_copy.find(nestSeparator)) != std::string::npos) {
187  token = name_copy.substr(0, pos);
188 
189  name_copy.erase(0, pos + nestSeparator.length());
190  if (create && !f->contains(token)) {
191  (*f)[token] = {};
192  }
193  else if (!f->contains(token)) {
194  return nullptr;
195  }
196  f = &(f->at(token));
197  }
198  if (create && !f->contains(name_copy)) {
199  (*f)[name_copy] = {};
200  }
201  else if (!f->contains(name_copy)) {
202  return nullptr;
203  }
204  f = &(f->at(name_copy));
205  return f;
206  };
207 
208  parsers[name] = [&parameter, required, getter, name](nlohmann::json &j) {
209  const nlohmann::json *field = getter(j, false);
210  const bool fieldHasNoValue = field == nullptr || (field != nullptr && field->is_null());
211  if (required && fieldHasNoValue) {
212  std::stringstream ss;
213  ss << "Argument " << name << " is required, but no value was provided" << std::endl;
214  throw vpException(vpException::badValue, ss.str());
215  }
216  else if (!fieldHasNoValue) {
217  field->get_to(parameter);
218  }
219  };
220 
221  updaters[name] = [getter](nlohmann::json &j, const std::string &s) {
222  nlohmann::json *field = getter(j, true);
223  *field = convertCommandLineArgument<T>(s);
224  };
225 
226  helpers[name] = [help, parameter, required]() -> std::string {
227  std::stringstream ss;
228  nlohmann::json repr = parameter;
229  ss << help << std::endl << "Default: " << repr;
230  if (required) {
231  ss << std::endl << "Required";
232  }
233  else {
234  ss << std::endl << "Optional";
235  }
236  return ss.str();
237  };
238 
239  nlohmann::json *exampleField = getter(exampleJson, true);
240  *exampleField = parameter;
241 
242  return *this;
243  }
244 
251  void parse(int argc, const char *argv[]);
252 
253 
254 private:
255  std::string description; // Program description
256  std::string jsonFileArgumentName; // Name of the argument that points to the json file: ./program --config settings.json. Here jsonFileArgumentName == "--config"
257  std::string nestSeparator; // JSON nesting delimiter character. Used to access JSON nested objects from a single string
258  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)
259  std::map<std::string, std::function<void(nlohmann::json &, const std::string &)>> updaters; // Update the base json document with command line arguments
260  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
261  nlohmann::json exampleJson; // Example JSON argument file: displayed when user calls for help
262 };
263 
264 #endif // VISP_HAVE_NLOHMANN_JSON
265 
266 #endif
error that can be emitted by ViSP classes.
Definition: vpException.h:59
@ badValue
Used to indicate that a value is not in the allowed range.
Definition: vpException.h:85
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.