42 const std::string &nestSeparator) :
43 m_description(description),
44 m_jsonFileArgumentName(jsonFileArgumentName),
45 m_nestSeparator(nestSeparator)
47 if (m_jsonFileArgumentName.empty()) {
48 throw vpException(vpException::badValue,
"The JSON file argument must not be empty");
51 if (m_nestSeparator.empty()) {
52 throw vpException(vpException::badValue,
"You must provide a JSON nesting delimiter to be able to parse JSON");
55 m_helpers[m_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: " << m_description << std::endl;
65 ss <<
"Arguments: " << std::endl;
66 unsigned spacesBetweenArgAndDescription = 0;
67 for (
const auto &helper : m_helpers) {
68 if (helper.first.size() > spacesBetweenArgAndDescription) {
69 spacesBetweenArgAndDescription =
static_cast<unsigned int>(helper.first.size());
72 spacesBetweenArgAndDescription += 4;
74 for (
const auto &helper : m_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 << m_exampleJson.dump(2) << std::endl;
99 m_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(m_nestSeparator)) != std::string::npos) {
107 token = name_copy.substr(0, pos);
109 name_copy.erase(0, pos + m_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 m_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 m_updaters[name] = [getter](nlohmann::json &j,
const std::string &) {
137 nlohmann::json *field = getter(j,
true);
141 m_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(m_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(), m_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 (m_parsers.find(arg) != m_parsers.end()) {
194 m_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 (m_argumentType[arg] ==
FLAG) {
204 m_updaters[arg](j, std::string());
208 std::cerr <<
"Unknown parameter when parsing: " << arg << std::endl;
214 for (
const auto &parser : m_parsers) {
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,...