31 #include <visp3/core/vpConfig.h>
32 #include <visp3/io/vpJsonArgumentParser.h>
33 #include <visp3/core/vpException.h>
36 #if defined(VISP_HAVE_NLOHMANN_JSON)
39 using json = nlohmann::json;
42 const std::string &nestSeparator) :
43 description(description),
44 jsonFileArgumentName(jsonFileArgumentName),
45 nestSeparator(nestSeparator)
47 if (jsonFileArgumentName.empty()) {
51 if (nestSeparator.empty()) {
55 helpers[jsonFileArgumentName] = []() -> std::string {
56 return "Path to the JSON configuration file. Values in this files are loaded, and can be overridden by command line arguments.\nOptional";
64 ss <<
"Program description: " << description << std::endl;
65 ss <<
"Arguments: " << std::endl;
66 unsigned spacesBetweenArgAndDescription = 0;
67 for (
const auto &helper : helpers) {
68 if (helper.first.size() > spacesBetweenArgAndDescription) {
69 spacesBetweenArgAndDescription =
static_cast<unsigned int>(helper.first.size());
72 spacesBetweenArgAndDescription += 4;
74 for (
const auto &helper : helpers) {
75 std::stringstream argss(helper.second());
78 while (getline(argss, line,
'\n')) {
79 const unsigned lineSpace = first ? spacesBetweenArgAndDescription -
static_cast<unsigned>(helper.first.size()) : spacesBetweenArgAndDescription;
80 const std::string spaceBetweenArgAndDescription(lineSpace,
' ');
82 ss <<
"\t" << helper.first << spaceBetweenArgAndDescription << line << std::endl;
85 ss <<
"\t" << spaceBetweenArgAndDescription << line << std::endl;
92 ss <<
"Example JSON configuration file: " << std::endl << std::endl;
93 ss << exampleJson.dump(2) << std::endl;
99 argumentType[name] =
FLAG;
100 const auto getter = [name,
this](nlohmann::json &j,
bool create) -> nlohmann::json * {
102 nlohmann::json *f = &j;
104 std::string name_copy = name;
106 while ((pos = name_copy.find(nestSeparator)) != std::string::npos) {
107 token = name_copy.substr(0, pos);
109 name_copy.erase(0, pos + nestSeparator.length());
110 if (create && !f->contains(token)) {
113 else if (!f->contains(token)) {
118 if (create && !f->contains(name_copy)) {
119 (*f)[name_copy] = {};
121 else if (!f->contains(name_copy)) {
124 f = &(f->at(name_copy));
128 parsers[name] = [¶meter, getter, name](nlohmann::json &j) {
129 const nlohmann::json *field = getter(j,
false);
130 const bool fieldHasNoValue = ((field ==
nullptr) || (field !=
nullptr && field->is_null()));
131 if (!fieldHasNoValue && (field->type() == json::value_t::boolean && (*field) ==
true)) {
132 parameter = !parameter;
136 updaters[name] = [getter](nlohmann::json &j,
const std::string &) {
137 nlohmann::json *field = getter(j,
true);
141 helpers[name] = [
help, parameter]() -> std::string {
142 std::stringstream ss;
143 nlohmann::json repr = parameter;
144 ss <<
help << std::endl <<
"Default: " << repr;
148 nlohmann::json *exampleField = getter(exampleJson,
true);
149 *exampleField = parameter;
157 const std::vector<std::string> arguments(argv + 1, argv + argc);
158 std::vector<unsigned> ignoredArguments;
159 const auto jsonFileArgumentPos = std::find(arguments.begin(), arguments.end(), jsonFileArgumentName);
161 if (jsonFileArgumentPos != arguments.end()) {
162 ignoredArguments.push_back(
static_cast<unsigned>(jsonFileArgumentPos - arguments.begin() + 1));
163 ignoredArguments.push_back(
static_cast<unsigned>(jsonFileArgumentPos - arguments.begin() + 2));
165 if (jsonFileArgumentPos == arguments.end() - 1) {
168 const std::string jsonFileName = *(jsonFileArgumentPos + 1);
169 std::ifstream jsonFile(jsonFileName);
170 if (!jsonFile.good()) {
171 std::stringstream ss;
172 ss <<
"Could not open JSON file " << jsonFileName <<
"! Make sure it exists and is readable" << std::endl;
175 j = json::parse(jsonFile);
179 for (
int i = 1; i < argc; ++i) {
180 const std::string arg = argv[i];
181 bool stop_for_loop =
false;
182 if (std::find(ignoredArguments.begin(), ignoredArguments.end(), i) != ignoredArguments.end()) {
183 stop_for_loop =
true;
185 if (!stop_for_loop) {
186 if (arg ==
"-h" || arg ==
"--help") {
187 std::cout <<
help() << std::endl;
191 if (parsers.find(arg) != parsers.end()) {
194 updaters[arg](j, std::string(argv[i + 1]));
198 std::stringstream ss;
199 ss <<
"Argument " << arg <<
" was passed but no value was provided" << std::endl;
203 else if (argumentType[arg] ==
FLAG) {
204 updaters[arg](j, std::string());
208 std::cerr <<
"Unknown parameter when parsing: " << arg << std::endl;
214 for (
const auto &parser : parsers) {
error that can be emitted by ViSP classes.
@ badValue
Used to indicate that a value is not in the allowed range.
Command line argument parsing with support for JSON files. If a JSON file is supplied,...
std::string help() const
Generate a help message, containing the description of the arguments, their default value and whether...
void parse(int argc, const char *argv[])
Parse the arguments.
vpJsonArgumentParser(const std::string &description, const std::string &jsonFileArgumentName, const std::string &nestSeparator)
Create a new argument parser, that can take into account both a JSON configuration file and command l...
vpJsonArgumentParser & addFlag(const std::string &name, bool ¶meter, const std::string &help="No description")
Add an argument that acts as a flag when specified on the command line. When this flag is specified,...