diff --git a/thirdparty/argparse/.stylua.toml b/thirdparty/argparse/.stylua.toml
new file mode 100644
index 0000000000..394e8846d8
--- /dev/null
+++ b/thirdparty/argparse/.stylua.toml
@@ -0,0 +1 @@
+indent_type = "Spaces"
diff --git a/thirdparty/argparse/CMakeLists.txt b/thirdparty/argparse/CMakeLists.txt
index 19d894207d..0f9f183ff8 100644
--- a/thirdparty/argparse/CMakeLists.txt
+++ b/thirdparty/argparse/CMakeLists.txt
@@ -1,14 +1,21 @@
cmake_minimum_required(VERSION 3.12.4)
+if(NOT DEFINED PROJECT_NAME)
+ set(ARGPARSE_IS_TOP_LEVEL ON)
+else()
+ set(ARGPARSE_IS_TOP_LEVEL OFF)
+endif()
+
project(argparse
- VERSION 2.9.0
+ VERSION 3.0.0
DESCRIPTION "A single header argument parser for C++17"
HOMEPAGE_URL "https://github.com/p-ranav/argparse"
LANGUAGES CXX
)
option(ARGPARSE_INSTALL "Include an install target" ON)
-option(ARGPARSE_BUILD_TESTS "Build tests" OFF)
+option(ARGPARSE_BUILD_TESTS "Build tests" ON)
+option(ARGPARSE_BUILD_SAMPLES "Build samples" OFF)
include(GNUInstallDirs)
include(CMakePackageConfigHelpers)
@@ -24,12 +31,12 @@ target_include_directories(argparse INTERFACE
if(ARGPARSE_BUILD_SAMPLES)
add_subdirectory(samples)
endif()
-
-if(ARGPARSE_BUILD_TESTS)
+
+if(ARGPARSE_BUILD_TESTS AND ARGPARSE_IS_TOP_LEVEL)
add_subdirectory(test)
endif()
-
-if(ARGPARSE_INSTALL)
+
+if(ARGPARSE_INSTALL AND ARGPARSE_IS_TOP_LEVEL)
install(TARGETS argparse EXPORT argparseConfig)
install(EXPORT argparseConfig
NAMESPACE argparse::
diff --git a/thirdparty/argparse/README.md b/thirdparty/argparse/README.md
index 5c7957651d..c35b6ede66 100644
--- a/thirdparty/argparse/README.md
+++ b/thirdparty/argparse/README.md
@@ -6,7 +6,7 @@
-
+
## Highlights
@@ -25,6 +25,7 @@
* [Deciding if the value was given by the user](#deciding-if-the-value-was-given-by-the-user)
* [Joining values of repeated optional arguments](#joining-values-of-repeated-optional-arguments)
* [Repeating an argument to increase a value](#repeating-an-argument-to-increase-a-value)
+ * [Mutually Exclusive Group](#mutually-exclusive-group)
* [Negative Numbers](#negative-numbers)
* [Combining Positional and Optional Arguments](#combining-positional-and-optional-arguments)
* [Printing Help](#printing-help)
@@ -45,6 +46,8 @@
* [Positional Arguments with Compound Toggle Arguments](#positional-arguments-with-compound-toggle-arguments)
* [Restricting the set of values for an argument](#restricting-the-set-of-values-for-an-argument)
* [Using `option=value` syntax](#using-optionvalue-syntax)
+* [Developer Notes](#developer-notes)
+ * [Copying and Moving](#copying-and-moving)
* [CMake Integration](#cmake-integration)
* [Building, Installing, and Testing](#building-installing-and-testing)
* [Supported Toolchains](#supported-toolchains)
@@ -95,7 +98,7 @@ int main(int argc, char *argv[]) {
try {
program.parse_args(argc, argv);
}
- catch (const std::runtime_error& err) {
+ catch (const std::exception& err) {
std::cerr << err.what() << std::endl;
std::cerr << program;
return 1;
@@ -137,7 +140,7 @@ program.add_argument("--verbose")
try {
program.parse_args(argc, argv);
}
-catch (const std::runtime_error& err) {
+catch (const std::exception& err) {
std::cerr << err.what() << std::endl;
std::cerr << program;
std::exit(1);
@@ -158,6 +161,31 @@ Here's what's happening:
* Since the argument is actually optional, no error is thrown when running the program without ```--verbose```. Note that by using ```.default_value(false)```, if the optional argument isn’t used, it's value is automatically set to false.
* By using ```.implicit_value(true)```, the user specifies that this option is more of a flag than something that requires a value. When the user provides the --verbose option, it's value is set to true.
+#### Flag
+
+When defining flag arguments, you can use the shorthand `flag()` which is the same as `default_value(false).implicit_value(true)`.
+
+```cpp
+argparse::ArgumentParser program("test");
+
+program.add_argument("--verbose")
+ .help("increase output verbosity")
+ .flag();
+
+try {
+ program.parse_args(argc, argv);
+}
+catch (const std::exception& err) {
+ std::cerr << err.what() << std::endl;
+ std::cerr << program;
+ std::exit(1);
+}
+
+if (program["--verbose"] == true) {
+ std::cout << "Verbosity enabled" << std::endl;
+}
+```
+
#### Requiring optional arguments
There are scenarios where you would like to make an optional argument ***required***. As discussed above, optional arguments either begin with `-` or `--`. You can make these types of arguments required like so:
@@ -203,7 +231,7 @@ program.add_argument("--color")
try {
program.parse_args(argc, argv); // Example: ./main --color orange
}
-catch (const std::runtime_error& err) {
+catch (const std::exception& err) {
std::cerr << err.what() << std::endl;
std::cerr << program;
std::exit(1);
@@ -226,7 +254,7 @@ program.add_argument("--color")
try {
program.parse_args(argc, argv); // Example: ./main --color red --color green --color blue
}
-catch (const std::runtime_error& err) {
+catch (const std::exception& err) {
std::cerr << err.what() << std::endl;
std::cerr << program;
std::exit(1);
@@ -255,6 +283,38 @@ program.parse_args(argc, argv); // Example: ./main -VVVV
std::cout << "verbose level: " << verbosity << std::endl; // verbose level: 4
```
+#### Mutually Exclusive Group
+
+Create a mutually exclusive group using `program.add_mutually_exclusive_group(required = false)`. `argparse`` will make sure that only one of the arguments in the mutually exclusive group was present on the command line:
+
+```cpp
+auto &group = program.add_mutually_exclusive_group();
+group.add_argument("--first");
+group.add_argument("--second");
+```
+
+with the following usage will yield an error:
+
+```console
+foo@bar:/home/dev/$ ./main --first 1 --second 2
+Argument '--second VAR' not allowed with '--first VAR'
+```
+
+The `add_mutually_exclusive_group()` function also accepts a `required` argument, to indicate that at least one of the mutually exclusive arguments is required:
+
+```cpp
+auto &group = program.add_mutually_exclusive_group(true);
+group.add_argument("--first");
+group.add_argument("--second");
+```
+
+with the following usage will yield an error:
+
+```console
+foo@bar:/home/dev/$ ./main
+One of the arguments '--first VAR' or '--second VAR' is required
+```
+
### Negative Numbers
Optional arguments start with ```-```. Can ```argparse``` handle negative numbers? The answer is yes!
@@ -274,7 +334,7 @@ program.add_argument("floats")
try {
program.parse_args(argc, argv);
}
-catch (const std::runtime_error& err) {
+catch (const std::exception& err) {
std::cerr << err.what() << std::endl;
std::cerr << program;
std::exit(1);
@@ -307,7 +367,7 @@ program.add_argument("--verbose")
try {
program.parse_args(argc, argv);
}
-catch (const std::runtime_error& err) {
+catch (const std::exception& err) {
std::cerr << err.what() << std::endl;
std::cerr << program;
std::exit(1);
@@ -388,7 +448,7 @@ Optional arguments:
-h, --help shows help message and exits
-v, --version prints version information and exits
--member ALIAS The alias for the member to pass to.
- --verbose
+ --verbose
Possible things include betingalw, chiz, and res.
```
@@ -407,7 +467,7 @@ program.add_argument("--input_files")
try {
program.parse_args(argc, argv); // Example: ./main --input_files config.yml System.xml
}
-catch (const std::runtime_error& err) {
+catch (const std::exception& err) {
std::cerr << err.what() << std::endl;
std::cerr << program;
std::exit(1);
@@ -436,7 +496,7 @@ program.add_argument("--query_point")
try {
program.parse_args(argc, argv); // Example: ./main --query_point 3.5 4.7 9.2
}
-catch (const std::runtime_error& err) {
+catch (const std::exception& err) {
std::cerr << err.what() << std::endl;
std::cerr << program;
std::exit(1);
@@ -491,7 +551,7 @@ program.add_argument("-c")
try {
program.parse_args(argc, argv); // Example: ./main -abc 1.95 2.47
}
-catch (const std::runtime_error& err) {
+catch (const std::exception& err) {
std::cerr << err.what() << std::endl;
std::cerr << program;
std::exit(1);
@@ -551,7 +611,7 @@ The grammar follows `std::from_chars`, but does not exactly duplicate it. For ex
| 'g' or 'G' | general form (either fixed or scientific) |
| | |
| 'd' | decimal |
-| 'i' | `std::from_chars` grammar with base == 0 |
+| 'i' | `std::from_chars` grammar with base == 10 |
| 'o' | octal (unsigned) |
| 'u' | decimal (unsigned) |
| 'x' or 'X' | hexadecimal (unsigned) |
@@ -604,7 +664,7 @@ program.add_argument("files")
try {
program.parse_args(argc, argv);
}
-catch (const std::runtime_error& err) {
+catch (const std::exception& err) {
std::cerr << err.what() << std::endl;
std::cerr << program;
std::exit(1);
@@ -651,7 +711,7 @@ program.add_argument("files")
try {
program.parse_args(argc, argv);
}
-catch (const std::runtime_error& err) {
+catch (const std::exception& err) {
std::cerr << err.what() << std::endl;
std::cerr << program;
std::exit(1);
@@ -777,7 +837,7 @@ int main(int argc, char *argv[]) {
try {
program.parse_args(argc, argv);
}
- catch (const std::runtime_error& err) {
+ catch (const std::exception& err) {
std::cerr << err.what() << std::endl;
std::cerr << program;
return 1;
@@ -841,6 +901,39 @@ When a help message is requested from a subparser, only the help for that partic
Additionally, every parser has the `.is_subcommand_used("")` and `.is_subcommand_used(subparser)` member functions to check if a subcommand was used.
+Sometimes there may be a need to hide part of the subcommands from the user
+by suppressing information about them in an help message. To do this,
+```ArgumentParser``` contains the method ```.set_suppress(bool suppress)```:
+
+```cpp
+argparse::ArgumentParser program("test");
+
+argparse::ArgumentParser hidden_cmd("hidden");
+hidden_cmd.add_argument("files").remaining();
+hidden_cmd.set_suppress(true);
+
+program.add_subparser(hidden_cmd);
+```
+
+```console
+foo@bar:/home/dev/$ ./main -h
+Usage: test [--help] [--version] {}
+
+Optional arguments:
+ -h, --help shows help message and exits
+ -v, --version prints version information and exits
+
+foo@bar:/home/dev/$ ./main hidden -h
+Usage: hidden [--help] [--version] files
+
+Positional arguments:
+ files [nargs: 0 or more]
+
+Optional arguments:
+ -h, --help shows help message and exits
+ -v, --version prints version information and exits
+```
+
### Getting Argument and Subparser Instances
```Argument``` and ```ArgumentParser``` instances added to an ```ArgumentParser``` can be retrieved with ```.at()```. The default return type is ```Argument```.
@@ -904,7 +997,7 @@ int main(int argc, char *argv[]) {
try {
program.parse_args(argc, argv);
}
- catch (const std::runtime_error& err) {
+ catch (const std::exception& err) {
std::cerr << err.what() << std::endl;
std::cerr << program;
return 1;
@@ -952,7 +1045,7 @@ int main(int argc, char *argv[]) {
try {
program.parse_args(argc, argv);
}
- catch (const std::runtime_error& err) {
+ catch (const std::exception& err) {
std::cerr << err.what() << std::endl;
std::cerr << program;
return 1;
@@ -993,7 +1086,7 @@ program.add_argument("config")
try {
program.parse_args({"./test", "config.json"});
}
-catch (const std::runtime_error& err) {
+catch (const std::exception& err) {
std::cerr << err.what() << std::endl;
std::cerr << program;
std::exit(1);
@@ -1029,7 +1122,7 @@ program.add_argument("--files")
try {
program.parse_args(argc, argv);
}
-catch (const std::runtime_error& err) {
+catch (const std::exception& err) {
std::cerr << err.what() << std::endl;
std::cerr << program;
std::exit(1);
@@ -1060,18 +1153,12 @@ argparse::ArgumentParser program("test");
program.add_argument("input")
.default_value(std::string{"baz"})
- .action([](const std::string& value) {
- static const std::vector choices = { "foo", "bar", "baz" };
- if (std::find(choices.begin(), choices.end(), value) != choices.end()) {
- return value;
- }
- return std::string{ "baz" };
- });
+ .choices("foo", "bar", "baz");
try {
program.parse_args(argc, argv);
}
-catch (const std::runtime_error& err) {
+catch (const std::exception& err) {
std::cerr << err.what() << std::endl;
std::cerr << program;
std::exit(1);
@@ -1083,10 +1170,37 @@ std::cout << input << std::endl;
```console
foo@bar:/home/dev/$ ./main fex
-baz
+Invalid argument "fex" - allowed options: {foo, bar, baz}
```
-## Using `option=value` syntax
+Using choices also works with integer types, e.g.,
+
+```cpp
+argparse::ArgumentParser program("test");
+
+program.add_argument("input")
+ .default_value(0)
+ .choices(0, 1, 2, 3, 4, 5);
+
+try {
+ program.parse_args(argc, argv);
+}
+catch (const std::exception& err) {
+ std::cerr << err.what() << std::endl;
+ std::cerr << program;
+ std::exit(1);
+}
+
+auto input = program.get("input");
+std::cout << input << std::endl;
+```
+
+```console
+foo@bar:/home/dev/$ ./main 6
+Invalid argument "6" - allowed options: {0, 1, 2, 3, 4, 5}
+```
+
+### Using `option=value` syntax
```cpp
#include "argparse.hpp"
@@ -1100,7 +1214,7 @@ int main(int argc, char *argv[]) {
try {
program.parse_args(argc, argv);
}
- catch (const std::runtime_error& err) {
+ catch (const std::exception& err) {
std::cerr << err.what() << std::endl;
std::cerr << program;
return 1;
@@ -1122,6 +1236,12 @@ foo@bar:/home/dev/$ ./test --bar=BAR --foo
--bar: BAR
```
+## Developer Notes
+
+### Copying and Moving
+
+`argparse::ArgumentParser` is intended to be used in a single function - setup everything and parse arguments in one place. Attempting to move or copy invalidates internal references (issue #260). Thus, starting with v3.0, `argparse::ArgumentParser` copy and move constructors are marked as `delete`.
+
## CMake Integration
Use the latest argparse in your CMake project without copying any content.
diff --git a/thirdparty/argparse/conanfile.py b/thirdparty/argparse/conanfile.py
index 743e5f3f77..79d407a1d0 100644
--- a/thirdparty/argparse/conanfile.py
+++ b/thirdparty/argparse/conanfile.py
@@ -2,7 +2,7 @@ from conans import ConanFile
class ArgparseConan(ConanFile):
name = "argparse"
- version = "2.9"
+ version = "3.0"
exports_sources = "include/argparse.hpp"
no_copy_source = True
diff --git a/thirdparty/argparse/include/argparse/argparse.hpp b/thirdparty/argparse/include/argparse/argparse.hpp
index 58582b90ab..0c85127da4 100644
--- a/thirdparty/argparse/include/argparse/argparse.hpp
+++ b/thirdparty/argparse/include/argparse/argparse.hpp
@@ -29,6 +29,8 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#pragma once
+
+#ifndef ARGPARSE_MODULE_USE_STD_MODULE
#include
#include
#include
@@ -53,6 +55,7 @@ SOFTWARE.
#include
#include
#include
+#endif
namespace argparse {
@@ -72,7 +75,7 @@ struct HasContainerTraits<
decltype(std::declval().size())>> : std::true_type {};
template
-static constexpr bool IsContainer = HasContainerTraits::value;
+inline constexpr bool IsContainer = HasContainerTraits::value;
template
struct HasStreamableTraits : std::false_type {};
@@ -84,7 +87,7 @@ struct HasStreamableTraits<
: std::true_type {};
template
-static constexpr bool IsStreamable = HasStreamableTraits::value;
+inline constexpr bool IsStreamable = HasStreamableTraits::value;
constexpr std::size_t repr_max_container_size = 5;
@@ -145,6 +148,7 @@ constexpr bool standard_unsigned_integer = true;
} // namespace
+constexpr int radix_2 = 2;
constexpr int radix_8 = 8;
constexpr int radix_10 = 10;
constexpr int radix_16 = 16;
@@ -180,12 +184,28 @@ constexpr bool starts_with(std::basic_string_view prefix,
}
enum class chars_format {
- scientific = 0x1,
- fixed = 0x2,
- hex = 0x4,
+ scientific = 0xf1,
+ fixed = 0xf2,
+ hex = 0xf4,
+ binary = 0xf8,
general = fixed | scientific
};
+struct ConsumeBinaryPrefixResult {
+ bool is_binary;
+ std::string_view rest;
+};
+
+constexpr auto consume_binary_prefix(std::string_view s)
+ -> ConsumeBinaryPrefixResult {
+ if (starts_with(std::string_view{"0b"}, s) ||
+ starts_with(std::string_view{"0B"}, s)) {
+ s.remove_prefix(2);
+ return {true, s};
+ }
+ return {false, s};
+}
+
struct ConsumeHexPrefixResult {
bool is_hexadecimal;
std::string_view rest;
@@ -211,13 +231,14 @@ inline auto do_from_chars(std::string_view s) -> T {
if (ptr == last) {
return x;
}
- throw std::invalid_argument{"pattern does not match to the end"};
+ throw std::invalid_argument{"pattern '" + std::string(s) +
+ "' does not match to the end"};
}
if (ec == std::errc::invalid_argument) {
- throw std::invalid_argument{"pattern not found"};
+ throw std::invalid_argument{"pattern '" + std::string(s) + "' not found"};
}
if (ec == std::errc::result_out_of_range) {
- throw std::range_error{"not representable"};
+ throw std::range_error{"'" + std::string(s) + "' not representable"};
}
return x; // unreachable
}
@@ -228,25 +249,97 @@ template struct parse_number {
}
};
-template struct parse_number {
+template struct parse_number {
auto operator()(std::string_view s) -> T {
- if (auto [ok, rest] = consume_hex_prefix(s); ok) {
- return do_from_chars(rest);
+ if (auto [ok, rest] = consume_binary_prefix(s); ok) {
+ return do_from_chars(rest);
}
throw std::invalid_argument{"pattern not found"};
}
};
+template struct parse_number {
+ auto operator()(std::string_view s) -> T {
+ if (starts_with("0x"sv, s) || starts_with("0X"sv, s)) {
+ if (auto [ok, rest] = consume_hex_prefix(s); ok) {
+ try {
+ return do_from_chars(rest);
+ } catch (const std::invalid_argument &err) {
+ throw std::invalid_argument("Failed to parse '" + std::string(s) +
+ "' as hexadecimal: " + err.what());
+ } catch (const std::range_error &err) {
+ throw std::range_error("Failed to parse '" + std::string(s) +
+ "' as hexadecimal: " + err.what());
+ }
+ }
+ } else {
+ // Allow passing hex numbers without prefix
+ // Shape 'x' already has to be specified
+ try {
+ return do_from_chars(s);
+ } catch (const std::invalid_argument &err) {
+ throw std::invalid_argument("Failed to parse '" + std::string(s) +
+ "' as hexadecimal: " + err.what());
+ } catch (const std::range_error &err) {
+ throw std::range_error("Failed to parse '" + std::string(s) +
+ "' as hexadecimal: " + err.what());
+ }
+ }
+
+ throw std::invalid_argument{"pattern '" + std::string(s) +
+ "' not identified as hexadecimal"};
+ }
+};
+
template struct parse_number {
auto operator()(std::string_view s) -> T {
auto [ok, rest] = consume_hex_prefix(s);
if (ok) {
- return do_from_chars(rest);
+ try {
+ return do_from_chars(rest);
+ } catch (const std::invalid_argument &err) {
+ throw std::invalid_argument("Failed to parse '" + std::string(s) +
+ "' as hexadecimal: " + err.what());
+ } catch (const std::range_error &err) {
+ throw std::range_error("Failed to parse '" + std::string(s) +
+ "' as hexadecimal: " + err.what());
+ }
}
+
+ auto [ok_binary, rest_binary] = consume_binary_prefix(s);
+ if (ok_binary) {
+ try {
+ return do_from_chars(rest_binary);
+ } catch (const std::invalid_argument &err) {
+ throw std::invalid_argument("Failed to parse '" + std::string(s) +
+ "' as binary: " + err.what());
+ } catch (const std::range_error &err) {
+ throw std::range_error("Failed to parse '" + std::string(s) +
+ "' as binary: " + err.what());
+ }
+ }
+
if (starts_with("0"sv, s)) {
- return do_from_chars(rest);
+ try {
+ return do_from_chars(rest);
+ } catch (const std::invalid_argument &err) {
+ throw std::invalid_argument("Failed to parse '" + std::string(s) +
+ "' as octal: " + err.what());
+ } catch (const std::range_error &err) {
+ throw std::range_error("Failed to parse '" + std::string(s) +
+ "' as octal: " + err.what());
+ }
+ }
+
+ try {
+ return do_from_chars(rest);
+ } catch (const std::invalid_argument &err) {
+ throw std::invalid_argument("Failed to parse '" + std::string(s) +
+ "' as decimal integer: " + err.what());
+ } catch (const std::range_error &err) {
+ throw std::range_error("Failed to parse '" + std::string(s) +
+ "' as decimal integer: " + err.what());
}
- return do_from_chars(rest);
}
};
@@ -261,7 +354,7 @@ template <> inline const auto generic_strtod = strtold;
template inline auto do_strtod(std::string const &s) -> T {
if (isspace(static_cast(s[0])) || s[0] == '+') {
- throw std::invalid_argument{"pattern not found"};
+ throw std::invalid_argument{"pattern '" + s + "' not found"};
}
auto [first, last] = pointer_range(s);
@@ -273,10 +366,11 @@ template inline auto do_strtod(std::string const &s) -> T {
if (ptr == last) {
return x;
}
- throw std::invalid_argument{"pattern does not match to the end"};
+ throw std::invalid_argument{"pattern '" + s +
+ "' does not match to the end"};
}
if (errno == ERANGE) {
- throw std::range_error{"not representable"};
+ throw std::range_error{"'" + s + "' not representable"};
}
return x; // unreachable
}
@@ -287,8 +381,20 @@ template struct parse_number {
throw std::invalid_argument{
"chars_format::general does not parse hexfloat"};
}
+ if (auto r = consume_binary_prefix(s); r.is_binary) {
+ throw std::invalid_argument{
+ "chars_format::general does not parse binfloat"};
+ }
- return do_strtod(s);
+ try {
+ return do_strtod(s);
+ } catch (const std::invalid_argument &err) {
+ throw std::invalid_argument("Failed to parse '" + s +
+ "' as number: " + err.what());
+ } catch (const std::range_error &err) {
+ throw std::range_error("Failed to parse '" + s +
+ "' as number: " + err.what());
+ }
}
};
@@ -297,6 +403,31 @@ template struct parse_number {
if (auto r = consume_hex_prefix(s); !r.is_hexadecimal) {
throw std::invalid_argument{"chars_format::hex parses hexfloat"};
}
+ if (auto r = consume_binary_prefix(s); r.is_binary) {
+ throw std::invalid_argument{"chars_format::hex does not parse binfloat"};
+ }
+
+ try {
+ return do_strtod(s);
+ } catch (const std::invalid_argument &err) {
+ throw std::invalid_argument("Failed to parse '" + s +
+ "' as hexadecimal: " + err.what());
+ } catch (const std::range_error &err) {
+ throw std::range_error("Failed to parse '" + s +
+ "' as hexadecimal: " + err.what());
+ }
+ }
+};
+
+template struct parse_number {
+ auto operator()(std::string const &s) -> T {
+ if (auto r = consume_hex_prefix(s); r.is_hexadecimal) {
+ throw std::invalid_argument{
+ "chars_format::binary does not parse hexfloat"};
+ }
+ if (auto r = consume_binary_prefix(s); !r.is_binary) {
+ throw std::invalid_argument{"chars_format::binary parses binfloat"};
+ }
return do_strtod(s);
}
@@ -308,12 +439,24 @@ template struct parse_number {
throw std::invalid_argument{
"chars_format::scientific does not parse hexfloat"};
}
+ if (auto r = consume_binary_prefix(s); r.is_binary) {
+ throw std::invalid_argument{
+ "chars_format::scientific does not parse binfloat"};
+ }
if (s.find_first_of("eE") == std::string::npos) {
throw std::invalid_argument{
"chars_format::scientific requires exponent part"};
}
- return do_strtod(s);
+ try {
+ return do_strtod(s);
+ } catch (const std::invalid_argument &err) {
+ throw std::invalid_argument("Failed to parse '" + s +
+ "' as scientific notation: " + err.what());
+ } catch (const std::range_error &err) {
+ throw std::range_error("Failed to parse '" + s +
+ "' as scientific notation: " + err.what());
+ }
}
};
@@ -323,12 +466,24 @@ template struct parse_number {
throw std::invalid_argument{
"chars_format::fixed does not parse hexfloat"};
}
+ if (auto r = consume_binary_prefix(s); r.is_binary) {
+ throw std::invalid_argument{
+ "chars_format::fixed does not parse binfloat"};
+ }
if (s.find_first_of("eE") != std::string::npos) {
throw std::invalid_argument{
"chars_format::fixed does not parse exponent part"};
}
- return do_strtod(s);
+ try {
+ return do_strtod(s);
+ } catch (const std::invalid_argument &err) {
+ throw std::invalid_argument("Failed to parse '" + s +
+ "' as fixed notation: " + err.what());
+ } catch (const std::range_error &err) {
+ throw std::range_error("Failed to parse '" + s +
+ "' as fixed notation: " + err.what());
+ }
}
};
@@ -347,6 +502,65 @@ std::string join(StrIt first, StrIt last, const std::string &separator) {
return value.str();
}
+template struct can_invoke_to_string {
+ template
+ static auto test(int)
+ -> decltype(std::to_string(std::declval()), std::true_type{});
+
+ template static auto test(...) -> std::false_type;
+
+ static constexpr bool value = decltype(test(0))::value;
+};
+
+template struct IsChoiceTypeSupported {
+ using CleanType = typename std::decay::type;
+ static const bool value = std::is_integral::value ||
+ std::is_same::value ||
+ std::is_same::value ||
+ std::is_same::value;
+};
+
+template
+std::size_t get_levenshtein_distance(const StringType &s1,
+ const StringType &s2) {
+ std::vector> dp(
+ s1.size() + 1, std::vector(s2.size() + 1, 0));
+
+ for (std::size_t i = 0; i <= s1.size(); ++i) {
+ for (std::size_t j = 0; j <= s2.size(); ++j) {
+ if (i == 0) {
+ dp[i][j] = j;
+ } else if (j == 0) {
+ dp[i][j] = i;
+ } else if (s1[i - 1] == s2[j - 1]) {
+ dp[i][j] = dp[i - 1][j - 1];
+ } else {
+ dp[i][j] = 1 + std::min({dp[i - 1][j], dp[i][j - 1], dp[i - 1][j - 1]});
+ }
+ }
+ }
+
+ return dp[s1.size()][s2.size()];
+}
+
+template
+std::string_view
+get_most_similar_string(const std::map &map,
+ const std::string_view input) {
+ std::string_view most_similar{};
+ std::size_t min_distance = std::numeric_limits::max();
+
+ for (const auto &entry : map) {
+ std::size_t distance = get_levenshtein_distance(entry.first, input);
+ if (distance < min_distance) {
+ min_distance = distance;
+ most_similar = entry.first;
+ }
+ }
+
+ return most_similar;
+}
+
} // namespace details
enum class nargs_pattern { optional, any, at_least_one };
@@ -404,7 +618,15 @@ public:
}
template Argument &default_value(T &&value) {
+ m_num_args_range = NArgsRange{0, m_num_args_range.get_max()};
m_default_value_repr = details::repr(value);
+
+ if constexpr (std::is_convertible_v) {
+ m_default_value_str = std::string{std::string_view{value}};
+ } else if constexpr (details::can_invoke_to_string::value) {
+ m_default_value_str = std::to_string(value);
+ }
+
m_default_value = std::forward(value);
return *this;
}
@@ -424,8 +646,18 @@ public:
return *this;
}
+ // This is shorthand for:
+ // program.add_argument("foo")
+ // .default_value(false)
+ // .implicit_value(true)
+ Argument &flag() {
+ default_value(false);
+ implicit_value(true);
+ return *this;
+ }
+
template
- auto action(F &&callable, Args &&... bound_args)
+ auto action(F &&callable, Args &&...bound_args)
-> std::enable_if_t,
Argument &> {
using action_type = std::conditional_t<
@@ -465,6 +697,9 @@ public:
} else if constexpr (is_one_of(Shape, 'u') &&
details::standard_unsigned_integer) {
action(details::parse_number());
+ } else if constexpr (is_one_of(Shape, 'b') &&
+ details::standard_unsigned_integer) {
+ action(details::parse_number());
} else if constexpr (is_one_of(Shape, 'o') &&
details::standard_unsigned_integer) {
action(details::parse_number());
@@ -506,10 +741,12 @@ public:
m_num_args_range = NArgsRange{0, 1};
break;
case nargs_pattern::any:
- m_num_args_range = NArgsRange{0, (std::numeric_limits::max)()};
+ m_num_args_range =
+ NArgsRange{0, (std::numeric_limits::max)()};
break;
case nargs_pattern::at_least_one:
- m_num_args_range = NArgsRange{1, (std::numeric_limits::max)()};
+ m_num_args_range =
+ NArgsRange{1, (std::numeric_limits::max)()};
break;
}
return *this;
@@ -520,6 +757,82 @@ public:
return nargs(nargs_pattern::any);
}
+ template void add_choice(T &&choice) {
+ static_assert(details::IsChoiceTypeSupported::value,
+ "Only string or integer type supported for choice");
+ static_assert(std::is_convertible_v ||
+ details::can_invoke_to_string::value,
+ "Choice is not convertible to string_type");
+ if (!m_choices.has_value()) {
+ m_choices = std::vector{};
+ }
+
+ if constexpr (std::is_convertible_v) {
+ m_choices.value().push_back(
+ std::string{std::string_view{std::forward(choice)}});
+ } else if constexpr (details::can_invoke_to_string::value) {
+ m_choices.value().push_back(std::to_string(std::forward(choice)));
+ }
+ }
+
+ Argument &choices() {
+ if (!m_choices.has_value()) {
+ throw std::runtime_error("Zero choices provided");
+ }
+ return *this;
+ }
+
+ template
+ Argument &choices(T &&first, U &&...rest) {
+ add_choice(std::forward(first));
+ choices(std::forward(rest)...);
+ return *this;
+ }
+
+ void find_default_value_in_choices_or_throw() const {
+
+ const auto &choices = m_choices.value();
+
+ if (m_default_value.has_value()) {
+ if (std::find(choices.begin(), choices.end(), m_default_value_str) ==
+ choices.end()) {
+ // provided arg not in list of allowed choices
+ // report error
+
+ std::string choices_as_csv =
+ std::accumulate(choices.begin(), choices.end(), std::string(),
+ [](const std::string &a, const std::string &b) {
+ return a + (a.empty() ? "" : ", ") + b;
+ });
+
+ throw std::runtime_error(
+ std::string{"Invalid default value "} + m_default_value_repr +
+ " - allowed options: {" + choices_as_csv + "}");
+ }
+ }
+ }
+
+ template
+ void find_value_in_choices_or_throw(Iterator it) const {
+
+ const auto &choices = m_choices.value();
+
+ if (std::find(choices.begin(), choices.end(), *it) == choices.end()) {
+ // provided arg not in list of allowed choices
+ // report error
+
+ std::string choices_as_csv =
+ std::accumulate(choices.begin(), choices.end(), std::string(),
+ [](const std::string &a, const std::string &b) {
+ return a + (a.empty() ? "" : ", ") + b;
+ });
+
+ throw std::runtime_error(std::string{"Invalid argument "} +
+ details::repr(*it) + " - allowed options: {" +
+ choices_as_csv + "}");
+ }
+ }
+
template
Iterator consume(Iterator start, Iterator end,
std::string_view used_name = {}) {
@@ -529,6 +842,14 @@ public:
m_is_used = true;
m_used_name = used_name;
+ if (m_choices.has_value()) {
+ // Check each value in (start, end) and make sure
+ // it is in the list of allowed choices/options
+ for (auto it = start; it != end; ++it) {
+ find_value_in_choices_or_throw(it);
+ }
+ }
+
const auto num_args_max = m_num_args_range.get_max();
const auto num_args_min = m_num_args_range.get_min();
std::size_t dist = 0;
@@ -599,6 +920,34 @@ public:
throw_nargs_range_validation_error();
}
}
+
+ if (m_choices.has_value()) {
+ // Make sure the default value (if provided)
+ // is in the list of choices
+ find_default_value_in_choices_or_throw();
+ }
+ }
+
+ std::string get_names_csv(char separator = ',') const {
+ return std::accumulate(
+ m_names.begin(), m_names.end(), std::string{""},
+ [&](const std::string &result, const std::string &name) {
+ return result.empty() ? name : result + separator + name;
+ });
+ }
+
+ std::string get_usage_full() const {
+ std::stringstream usage;
+
+ usage << get_names_csv('/');
+ const std::string metavar = !m_metavar.empty() ? m_metavar : "VAR";
+ if (m_num_args_range.get_max() > 0) {
+ usage << " " << metavar;
+ if (m_num_args_range.get_max() > 1) {
+ usage << "...";
+ }
+ }
+ return usage.str();
}
std::string get_inline_usage() const {
@@ -677,13 +1026,13 @@ public:
// align multiline help message
auto stream_width = stream.width();
auto name_padding = std::string(name_stream.str().size(), ' ');
- auto pos = 0;
- auto prev = 0;
+ auto pos = std::string::size_type{};
+ auto prev = std::string::size_type{};
auto first_line = true;
auto hspace = " "; // minimal space between name and help message
stream << name_stream.str();
std::string_view help_view(argument.m_help);
- while ((pos = argument.m_help.find('\n', prev)) != (int)std::string::npos) {
+ while ((pos = argument.m_help.find('\n', prev)) != std::string::npos) {
auto line = help_view.substr(prev, pos - prev + 1);
if (first_line) {
stream << hspace << line;
@@ -735,8 +1084,7 @@ public:
using ValueType = typename T::value_type;
auto lhs = get();
return std::equal(std::begin(lhs), std::end(lhs), std::begin(rhs),
- std::end(rhs),
- [](const auto &a, const auto &b) {
+ std::end(rhs), [](const auto &a, const auto &b) {
return std::any_cast(a) == b;
});
}
@@ -1060,7 +1408,10 @@ private:
std::string m_metavar;
std::any m_default_value;
std::string m_default_value_repr;
+ std::optional
+ m_default_value_str; // used for checking default_value against choices
std::any m_implicit_value;
+ std::optional> m_choices{std::nullopt};
using valued_action = std::function;
using void_action = std::function;
std::variant m_action{
@@ -1082,14 +1433,15 @@ public:
explicit ArgumentParser(std::string program_name = {},
std::string version = "1.0",
default_arguments add_args = default_arguments::all,
- bool exit_on_default_arguments = true)
+ bool exit_on_default_arguments = true,
+ std::ostream &os = std::cout)
: m_program_name(std::move(program_name)), m_version(std::move(version)),
m_exit_on_default_arguments(exit_on_default_arguments),
m_parser_path(m_program_name) {
if ((add_args & default_arguments::help) == default_arguments::help) {
add_argument("-h", "--help")
.action([&](const auto & /*unused*/) {
- std::cout << help().str();
+ os << help().str();
if (m_exit_on_default_arguments) {
std::exit(0);
}
@@ -1102,7 +1454,7 @@ public:
if ((add_args & default_arguments::version) == default_arguments::version) {
add_argument("-v", "--version")
.action([&](const auto & /*unused*/) {
- std::cout << m_version << std::endl;
+ os << m_version << std::endl;
if (m_exit_on_default_arguments) {
std::exit(0);
}
@@ -1114,51 +1466,25 @@ public:
}
}
- ArgumentParser(ArgumentParser &&) noexcept = default;
- ArgumentParser &operator=(ArgumentParser &&) = default;
-
- ArgumentParser(const ArgumentParser &other)
- : m_program_name(other.m_program_name), m_version(other.m_version),
- m_description(other.m_description), m_epilog(other.m_epilog),
- m_prefix_chars(other.m_prefix_chars),
- m_assign_chars(other.m_assign_chars), m_is_parsed(other.m_is_parsed),
- m_positional_arguments(other.m_positional_arguments),
- m_optional_arguments(other.m_optional_arguments),
- m_parser_path(other.m_parser_path), m_subparsers(other.m_subparsers) {
- for (auto it = std::begin(m_positional_arguments);
- it != std::end(m_positional_arguments); ++it) {
- index_argument(it);
- }
- for (auto it = std::begin(m_optional_arguments);
- it != std::end(m_optional_arguments); ++it) {
- index_argument(it);
- }
- for (auto it = std::begin(m_subparsers); it != std::end(m_subparsers);
- ++it) {
- m_subparser_map.insert_or_assign(it->get().m_program_name, it);
- m_subparser_used.insert_or_assign(it->get().m_program_name, false);
- }
- }
-
~ArgumentParser() = default;
- ArgumentParser &operator=(const ArgumentParser &other) {
- auto tmp = other;
- std::swap(*this, tmp);
- return *this;
- }
+ // ArgumentParser is meant to be used in a single function.
+ // Setup everything and parse arguments in one place.
+ //
+ // ArgumentParser internally uses std::string_views,
+ // references, iterators, etc.
+ // Many of these elements become invalidated after a copy or move.
+ ArgumentParser(const ArgumentParser &other) = delete;
+ ArgumentParser &operator=(const ArgumentParser &other) = delete;
+ ArgumentParser(ArgumentParser &&) noexcept = delete;
+ ArgumentParser &operator=(ArgumentParser &&) = delete;
explicit operator bool() const {
- auto arg_used = std::any_of(m_argument_map.cbegin(),
- m_argument_map.cend(),
- [](auto &it) {
- return it.second->m_is_used;
- });
- auto subparser_used = std::any_of(m_subparser_used.cbegin(),
- m_subparser_used.cend(),
- [](auto &it) {
- return it.second;
- });
+ auto arg_used = std::any_of(m_argument_map.cbegin(), m_argument_map.cend(),
+ [](auto &it) { return it.second->m_is_used; });
+ auto subparser_used =
+ std::any_of(m_subparser_used.cbegin(), m_subparser_used.cend(),
+ [](auto &it) { return it.second; });
return m_is_parsed && (arg_used || subparser_used);
}
@@ -1180,10 +1506,47 @@ public:
return *argument;
}
+ class MutuallyExclusiveGroup {
+ friend class ArgumentParser;
+
+ public:
+ MutuallyExclusiveGroup() = delete;
+
+ explicit MutuallyExclusiveGroup(ArgumentParser &parent,
+ bool required = false)
+ : m_parent(parent), m_required(required), m_elements({}) {}
+
+ MutuallyExclusiveGroup(const MutuallyExclusiveGroup &other) = delete;
+ MutuallyExclusiveGroup &
+ operator=(const MutuallyExclusiveGroup &other) = delete;
+
+ MutuallyExclusiveGroup(MutuallyExclusiveGroup &&other) noexcept
+ : m_parent(other.m_parent), m_required(other.m_required),
+ m_elements(std::move(other.m_elements)) {
+ other.m_elements.clear();
+ }
+
+ template Argument &add_argument(Targs... f_args) {
+ auto &argument = m_parent.add_argument(std::forward(f_args)...);
+ m_elements.push_back(&argument);
+ return argument;
+ }
+
+ private:
+ ArgumentParser &m_parent;
+ bool m_required{false};
+ std::vector m_elements{};
+ };
+
+ MutuallyExclusiveGroup &add_mutually_exclusive_group(bool required = false) {
+ m_mutually_exclusive_groups.emplace_back(*this, required);
+ return m_mutually_exclusive_groups.back();
+ }
+
// Parameter packed add_parents method
// Accepts a variadic number of ArgumentParser objects
template
- ArgumentParser &add_parents(const Targs &... f_args) {
+ ArgumentParser &add_parents(const Targs &...f_args) {
for (const ArgumentParser &parent_parser : {std::ref(f_args)...}) {
for (const auto &argument : parent_parser.m_positional_arguments) {
auto it = m_positional_arguments.insert(
@@ -1212,8 +1575,7 @@ public:
/* Getter for arguments and subparsers.
* @throws std::logic_error in case of an invalid argument or subparser name
*/
- template
- T& at(std::string_view name) {
+ template T &at(std::string_view name) {
if constexpr (std::is_same_v) {
return (*this)[name];
} else {
@@ -1246,6 +1608,43 @@ public:
for ([[maybe_unused]] const auto &[unused, argument] : m_argument_map) {
argument->validate();
}
+
+ // Check each mutually exclusive group and make sure
+ // there are no constraint violations
+ for (const auto &group : m_mutually_exclusive_groups) {
+ auto mutex_argument_used{false};
+ Argument *mutex_argument_it{nullptr};
+ for (Argument *arg : group.m_elements) {
+ if (!mutex_argument_used && arg->m_is_used) {
+ mutex_argument_used = true;
+ mutex_argument_it = arg;
+ } else if (mutex_argument_used && arg->m_is_used) {
+ // Violation
+ throw std::runtime_error("Argument '" + arg->get_usage_full() +
+ "' not allowed with '" +
+ mutex_argument_it->get_usage_full() + "'");
+ }
+ }
+
+ if (!mutex_argument_used && group.m_required) {
+ // at least one argument from the group is
+ // required
+ std::string argument_names{};
+ std::size_t i = 0;
+ std::size_t size = group.m_elements.size();
+ for (Argument *arg : group.m_elements) {
+ if (i + 1 == size) {
+ // last
+ argument_names += "'" + arg->get_usage_full() + "' ";
+ } else {
+ argument_names += "'" + arg->get_usage_full() + "' or ";
+ }
+ i += 1;
+ }
+ throw std::runtime_error("One of the arguments " + argument_names +
+ "is required");
+ }
+ }
}
/* Call parse_known_args_internal - which does all the work
@@ -1324,7 +1723,7 @@ public:
}
/* Indexing operator. Return a reference to an Argument object
- * Used in conjuction with Argument.operator== e.g., parser["foo"] == true
+ * Used in conjunction with Argument.operator== e.g., parser["foo"] == true
* @throws std::logic_error in case of an invalid argument name
*/
Argument &operator[](std::string_view arg_name) const {
@@ -1385,12 +1784,20 @@ public:
stream << argument;
}
- if (!parser.m_subparser_map.empty()) {
+ bool has_visible_subcommands = std::any_of(
+ parser.m_subparser_map.begin(), parser.m_subparser_map.end(),
+ [](auto &p) { return !p.second->get().m_suppress; });
+
+ if (has_visible_subcommands) {
stream << (parser.m_positional_arguments.empty()
? (parser.m_optional_arguments.empty() ? "" : "\n")
: "\n")
<< "Subcommands:\n";
for (const auto &[command, subparser] : parser.m_subparser_map) {
+ if (subparser->get().m_suppress) {
+ continue;
+ }
+
stream << std::setw(2) << " ";
stream << std::setw(static_cast(longest_arg_length - 2))
<< command;
@@ -1435,7 +1842,11 @@ public:
if (!m_subparser_map.empty()) {
stream << " {";
std::size_t i{0};
- for (const auto &[command, unused] : m_subparser_map) {
+ for (const auto &[command, subparser] : m_subparser_map) {
+ if (subparser->get().m_suppress) {
+ continue;
+ }
+
if (i == 0) {
stream << command;
} else {
@@ -1465,6 +1876,8 @@ public:
m_subparser_used.insert_or_assign(parser.m_program_name, false);
}
+ void set_suppress(bool suppress) { m_suppress = suppress; }
+
private:
bool is_valid_prefix_char(char c) const {
return m_prefix_chars.find(c) != std::string::npos;
@@ -1569,8 +1982,41 @@ private:
unprocessed_arguments);
}
- throw std::runtime_error(
- "Maximum number of positional arguments exceeded");
+ if (m_positional_arguments.empty()) {
+
+ // Ask the user if they argument they provided was a typo
+ // for some sub-parser,
+ // e.g., user provided `git totes` instead of `git notes`
+ if (!m_subparser_map.empty()) {
+ throw std::runtime_error(
+ "Failed to parse '" + current_argument + "', did you mean '" +
+ std::string{details::get_most_similar_string(
+ m_subparser_map, current_argument)} +
+ "'");
+ }
+
+ // Ask the user if they meant to use a specific optional argument
+ if (!m_optional_arguments.empty()) {
+ for (const auto &opt : m_optional_arguments) {
+ if (!opt.m_implicit_value.has_value()) {
+ // not a flag, requires a value
+ if (!opt.m_is_used) {
+ throw std::runtime_error(
+ "Zero positional arguments expected, did you mean " +
+ opt.get_usage_full());
+ }
+ }
+ }
+
+ throw std::runtime_error("Zero positional arguments expected");
+ } else {
+ throw std::runtime_error("Zero positional arguments expected");
+ }
+ } else {
+ throw std::runtime_error("Maximum number of positional arguments "
+ "exceeded, failed to parse '" +
+ current_argument + "'");
+ }
}
auto argument = positional_argument_it++;
it = argument->consume(it, end);
@@ -1689,7 +2135,8 @@ private:
}
std::size_t max_size = 0;
for ([[maybe_unused]] const auto &[unused, argument] : m_argument_map) {
- max_size = std::max(max_size, argument->get_arguments_length());
+ max_size =
+ std::max(max_size, argument->get_arguments_length());
}
for ([[maybe_unused]] const auto &[command, unused] : m_subparser_map) {
max_size = std::max(max_size, command.size());
@@ -1698,6 +2145,7 @@ private:
}
using argument_it = std::list::iterator;
+ using mutex_group_it = std::vector::iterator;
using argument_parser_it =
std::list>::iterator;
@@ -1722,6 +2170,8 @@ private:
std::list> m_subparsers;
std::map m_subparser_map;
std::map m_subparser_used;
+ std::vector m_mutually_exclusive_groups;
+ bool m_suppress = false;
};
} // namespace argparse
diff --git a/thirdparty/argparse/module/argparse.cppm b/thirdparty/argparse/module/argparse.cppm
new file mode 100644
index 0000000000..625283e1ca
--- /dev/null
+++ b/thirdparty/argparse/module/argparse.cppm
@@ -0,0 +1,56 @@
+/*
+ __ _ _ __ __ _ _ __ __ _ _ __ ___ ___
+ / _` | '__/ _` | '_ \ / _` | '__/ __|/ _ \ Argument Parser for Modern C++
+| (_| | | | (_| | |_) | (_| | | \__ \ __/ http://github.com/p-ranav/argparse
+ \__,_|_| \__, | .__/ \__,_|_| |___/\___|
+ |___/|_|
+
+Licensed under the MIT License .
+SPDX-License-Identifier: MIT
+Copyright (c) 2019-2022 Pranav Srinivas Kumar
+and other contributors.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+*/
+
+module;
+
+#ifndef ARGPARSE_MODULE_USE_STD_MODULE
+#include
+#endif
+
+export module argparse;
+
+#ifdef ARGPARSE_MODULE_USE_STD_MODULE
+import std;
+
+extern "C++" {
+#include
+}
+#endif
+
+
+export namespace argparse {
+ using argparse::nargs_pattern;
+ using argparse::default_arguments;
+ using argparse::operator&;
+ using argparse::Argument;
+ using argparse::ArgumentParser;
+}
+
diff --git a/thirdparty/argparse/samples/CMakeLists.txt b/thirdparty/argparse/samples/CMakeLists.txt
index 94ba3b5ff9..dfcc92726b 100644
--- a/thirdparty/argparse/samples/CMakeLists.txt
+++ b/thirdparty/argparse/samples/CMakeLists.txt
@@ -26,6 +26,7 @@ function(add_sample NAME)
ADD_EXECUTABLE(ARGPARSE_SAMPLE_${NAME} ${NAME}.cpp)
INCLUDE_DIRECTORIES("../include" ".")
set_target_properties(ARGPARSE_SAMPLE_${NAME} PROPERTIES OUTPUT_NAME ${NAME})
+ set_property(TARGET ARGPARSE_SAMPLE_${NAME} PROPERTY CXX_STANDARD 17)
endfunction()
add_sample(positional_argument)
diff --git a/thirdparty/argparse/samples/compound_arguments.cpp b/thirdparty/argparse/samples/compound_arguments.cpp
index 4cfc037fcd..721fffa6e7 100644
--- a/thirdparty/argparse/samples/compound_arguments.cpp
+++ b/thirdparty/argparse/samples/compound_arguments.cpp
@@ -5,9 +5,9 @@
int main(int argc, char *argv[]) {
argparse::ArgumentParser program("test");
- program.add_argument("-a").default_value(false).implicit_value(true);
+ program.add_argument("-a").flag();
- program.add_argument("-b").default_value(false).implicit_value(true);
+ program.add_argument("-b").flag();
program.add_argument("-c")
.nargs(2)
@@ -16,7 +16,7 @@ int main(int argc, char *argv[]) {
try {
program.parse_args(argc, argv); // Example: ./main -abc 1.95 2.47
- } catch (const std::runtime_error &err) {
+ } catch (const std::exception &err) {
std::cerr << err.what() << std::endl;
std::cerr << program;
return 1;
diff --git a/thirdparty/argparse/samples/custom_assignment_characters.cpp b/thirdparty/argparse/samples/custom_assignment_characters.cpp
index 7e35ae0b19..d9e4ca38e9 100644
--- a/thirdparty/argparse/samples/custom_assignment_characters.cpp
+++ b/thirdparty/argparse/samples/custom_assignment_characters.cpp
@@ -13,7 +13,7 @@ int main(int argc, char *argv[]) {
try {
program.parse_args(argc, argv);
- } catch (const std::runtime_error &err) {
+ } catch (const std::exception &err) {
std::cerr << err.what() << std::endl;
std::cerr << program;
return 1;
diff --git a/thirdparty/argparse/samples/custom_prefix_characters.cpp b/thirdparty/argparse/samples/custom_prefix_characters.cpp
index 9f8917a366..058ba9f8ca 100644
--- a/thirdparty/argparse/samples/custom_prefix_characters.cpp
+++ b/thirdparty/argparse/samples/custom_prefix_characters.cpp
@@ -13,7 +13,7 @@ int main(int argc, char *argv[]) {
try {
program.parse_args(argc, argv);
- } catch (const std::runtime_error &err) {
+ } catch (const std::exception &err) {
std::cerr << err.what() << std::endl;
std::cerr << program;
return 1;
diff --git a/thirdparty/argparse/samples/description_epilog_metavar.cpp b/thirdparty/argparse/samples/description_epilog_metavar.cpp
index 02f12dd1c8..820e03797f 100644
--- a/thirdparty/argparse/samples/description_epilog_metavar.cpp
+++ b/thirdparty/argparse/samples/description_epilog_metavar.cpp
@@ -8,7 +8,7 @@ int main(int argc, char *argv[]) {
program.add_argument("--member")
.help("The alias for the member to pass to.")
.metavar("ALIAS");
- program.add_argument("--verbose").default_value(false).implicit_value(true);
+ program.add_argument("--verbose").flag();
program.add_description("Forward a thing to the next member.");
program.add_epilog("Possible things include betingalw, chiz, and res.");
diff --git a/thirdparty/argparse/samples/gathering_remaining_arguments.cpp b/thirdparty/argparse/samples/gathering_remaining_arguments.cpp
index be13f10f9b..099b07f80a 100644
--- a/thirdparty/argparse/samples/gathering_remaining_arguments.cpp
+++ b/thirdparty/argparse/samples/gathering_remaining_arguments.cpp
@@ -9,7 +9,7 @@ int main(int argc, char *argv[]) {
try {
program.parse_args(argc, argv);
- } catch (const std::runtime_error &err) {
+ } catch (const std::exception &err) {
std::cerr << err.what() << std::endl;
std::cerr << program;
return 1;
diff --git a/thirdparty/argparse/samples/is_used.cpp b/thirdparty/argparse/samples/is_used.cpp
index ffc05f880a..91134eef7d 100644
--- a/thirdparty/argparse/samples/is_used.cpp
+++ b/thirdparty/argparse/samples/is_used.cpp
@@ -13,7 +13,7 @@ int main(int argc, char *argv[]) {
try {
program.parse_args(argc, argv); // Example: ./main --color orange
- } catch (const std::runtime_error &err) {
+ } catch (const std::exception &err) {
std::cerr << err.what() << std::endl;
std::cerr << program;
return 1;
diff --git a/thirdparty/argparse/samples/joining_repeated_optional_arguments.cpp b/thirdparty/argparse/samples/joining_repeated_optional_arguments.cpp
index 0f6ab8159f..99f1f6e9e3 100644
--- a/thirdparty/argparse/samples/joining_repeated_optional_arguments.cpp
+++ b/thirdparty/argparse/samples/joining_repeated_optional_arguments.cpp
@@ -13,7 +13,7 @@ int main(int argc, char *argv[]) {
try {
program.parse_args(
argc, argv); // Example: ./main --color red --color green --color blue
- } catch (const std::runtime_error &err) {
+ } catch (const std::exception &err) {
std::cerr << err.what() << std::endl;
std::cerr << program;
return 1;
diff --git a/thirdparty/argparse/samples/list_of_arguments.cpp b/thirdparty/argparse/samples/list_of_arguments.cpp
index 996e7480e7..b808725d30 100644
--- a/thirdparty/argparse/samples/list_of_arguments.cpp
+++ b/thirdparty/argparse/samples/list_of_arguments.cpp
@@ -13,7 +13,7 @@ int main(int argc, char *argv[]) {
try {
program.parse_args(
argc, argv); // Example: ./main --input_files config.yml System.xml
- } catch (const std::runtime_error &err) {
+ } catch (const std::exception &err) {
std::cerr << err.what() << std::endl;
std::cerr << program;
return 1;
diff --git a/thirdparty/argparse/samples/negative_numbers.cpp b/thirdparty/argparse/samples/negative_numbers.cpp
index 319245fe4b..80fad6a6bb 100644
--- a/thirdparty/argparse/samples/negative_numbers.cpp
+++ b/thirdparty/argparse/samples/negative_numbers.cpp
@@ -14,7 +14,7 @@ int main(int argc, char *argv[]) {
try {
program.parse_args(argc, argv);
- } catch (const std::runtime_error &err) {
+ } catch (const std::exception &err) {
std::cerr << err.what() << std::endl;
std::cerr << program;
return 1;
diff --git a/thirdparty/argparse/samples/optional_flag_argument.cpp b/thirdparty/argparse/samples/optional_flag_argument.cpp
index f935ecf0be..b9bf497602 100644
--- a/thirdparty/argparse/samples/optional_flag_argument.cpp
+++ b/thirdparty/argparse/samples/optional_flag_argument.cpp
@@ -12,7 +12,7 @@ int main(int argc, char *argv[]) {
try {
program.parse_args(argc, argv);
- } catch (const std::runtime_error &err) {
+ } catch (const std::exception &err) {
std::cerr << err.what() << std::endl;
std::cerr << program;
return 1;
diff --git a/thirdparty/argparse/samples/positional_argument.cpp b/thirdparty/argparse/samples/positional_argument.cpp
index 4343863ce3..68959f1aaf 100644
--- a/thirdparty/argparse/samples/positional_argument.cpp
+++ b/thirdparty/argparse/samples/positional_argument.cpp
@@ -9,11 +9,11 @@ int main(int argc, char *argv[]) {
.help("display the square of a given number")
.scan<'i', int>();
- program.add_argument("--verbose").default_value(false).implicit_value(true);
+ program.add_argument("--verbose").flag();
try {
program.parse_args(argc, argv);
- } catch (const std::runtime_error &err) {
+ } catch (const std::exception &err) {
std::cerr << err.what() << std::endl;
std::cerr << program;
return 1;
diff --git a/thirdparty/argparse/samples/required_optional_argument.cpp b/thirdparty/argparse/samples/required_optional_argument.cpp
index 0271f500b3..4a93511726 100644
--- a/thirdparty/argparse/samples/required_optional_argument.cpp
+++ b/thirdparty/argparse/samples/required_optional_argument.cpp
@@ -11,7 +11,7 @@ int main(int argc, char *argv[]) {
try {
program.parse_args(argc, argv);
- } catch (const std::runtime_error &err) {
+ } catch (const std::exception &err) {
std::cerr << err.what() << std::endl;
std::cerr << program;
return 1;
diff --git a/thirdparty/argparse/samples/subcommands.cpp b/thirdparty/argparse/samples/subcommands.cpp
index ba1059dfc7..97b3bff866 100644
--- a/thirdparty/argparse/samples/subcommands.cpp
+++ b/thirdparty/argparse/samples/subcommands.cpp
@@ -57,7 +57,7 @@ int main(int argc, char *argv[]) {
try {
program.parse_args(argc, argv);
- } catch (const std::runtime_error &err) {
+ } catch (const std::exception &err) {
std::cerr << err.what() << std::endl;
std::cerr << program;
return 1;
diff --git a/thirdparty/argparse/test/CMakeLists.txt b/thirdparty/argparse/test/CMakeLists.txt
index a903f09f90..587eb824e3 100644
--- a/thirdparty/argparse/test/CMakeLists.txt
+++ b/thirdparty/argparse/test/CMakeLists.txt
@@ -10,7 +10,7 @@ if(MSVC)
endif()
elseif(CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX)
# Update if necessary
- set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wno-long-long -pedantic -Wsign-conversion -Wshadow -Wconversion")
+ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wno-long-long -Wpedantic -Wsign-conversion -Wshadow -Wconversion -Werror -Wextra")
endif()
if(NOT CMAKE_BUILD_TYPE)
@@ -29,16 +29,18 @@ file(GLOB ARGPARSE_TEST_SOURCES
test_append.cpp
test_as_container.cpp
test_bool_operator.cpp
+ test_choices.cpp
test_compound_arguments.cpp
test_container_arguments.cpp
- test_const_correct.cpp
test_default_args.cpp
test_default_value.cpp
+ test_error_reporting.cpp
test_get.cpp
test_help.cpp
test_invalid_arguments.cpp
test_is_used.cpp
test_issue_37.cpp
+ test_mutually_exclusive_group.cpp
test_negative_numbers.cpp
test_optional_arguments.cpp
test_parent_parsers.cpp
@@ -47,7 +49,7 @@ file(GLOB ARGPARSE_TEST_SOURCES
test_repr.cpp
test_required_arguments.cpp
test_scan.cpp
- test_value_semantics.cpp
+ test_stringstream.cpp
test_version.cpp
test_subparsers.cpp
test_parse_known_args.cpp
diff --git a/thirdparty/argparse/test/argparse_details.cppm b/thirdparty/argparse/test/argparse_details.cppm
new file mode 100644
index 0000000000..1c1668de5f
--- /dev/null
+++ b/thirdparty/argparse/test/argparse_details.cppm
@@ -0,0 +1,10 @@
+module;
+
+#include
+
+export module argparse.details;
+
+export namespace argparse::details {
+ using argparse::details::repr;
+}
+
diff --git a/thirdparty/argparse/test/doctest.hpp b/thirdparty/argparse/test/doctest.hpp
index d25f526827..5c754cde08 100644
--- a/thirdparty/argparse/test/doctest.hpp
+++ b/thirdparty/argparse/test/doctest.hpp
@@ -4,7 +4,7 @@
//
// doctest.h - the lightest feature-rich C++ single-header testing framework for unit tests and TDD
//
-// Copyright (c) 2016-2021 Viktor Kirilov
+// Copyright (c) 2016-2023 Viktor Kirilov
//
// Distributed under the MIT Software License
// See accompanying file LICENSE.txt or copy at
@@ -48,7 +48,7 @@
#define DOCTEST_VERSION_MAJOR 2
#define DOCTEST_VERSION_MINOR 4
-#define DOCTEST_VERSION_PATCH 8
+#define DOCTEST_VERSION_PATCH 11
// util we need here
#define DOCTEST_TOSTR_IMPL(x) #x
@@ -68,6 +68,12 @@
// ideas for the version stuff are taken from here: https://github.com/cxxstuff/cxx_detect
+#ifdef _MSC_VER
+#define DOCTEST_CPLUSPLUS _MSVC_LANG
+#else
+#define DOCTEST_CPLUSPLUS __cplusplus
+#endif
+
#define DOCTEST_COMPILER(MAJOR, MINOR, PATCH) ((MAJOR)*10000000 + (MINOR)*100000 + (PATCH))
// GCC/Clang and GCC/MSVC are mutually exclusive, but Clang/MSVC are not because of clang-cl...
@@ -79,12 +85,15 @@
DOCTEST_COMPILER(_MSC_VER / 100, (_MSC_FULL_VER / 100000) % 100, _MSC_FULL_VER % 100000)
#endif // MSVC
#endif // MSVC
-#if defined(__clang__) && defined(__clang_minor__)
+#if defined(__clang__) && defined(__clang_minor__) && defined(__clang_patchlevel__)
#define DOCTEST_CLANG DOCTEST_COMPILER(__clang_major__, __clang_minor__, __clang_patchlevel__)
#elif defined(__GNUC__) && defined(__GNUC_MINOR__) && defined(__GNUC_PATCHLEVEL__) && \
!defined(__INTEL_COMPILER)
#define DOCTEST_GCC DOCTEST_COMPILER(__GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__)
#endif // GCC
+#if defined(__INTEL_COMPILER)
+#define DOCTEST_ICC DOCTEST_COMPILER(__INTEL_COMPILER / 100, __INTEL_COMPILER % 100, 0)
+#endif // ICC
#ifndef DOCTEST_MSVC
#define DOCTEST_MSVC 0
@@ -95,12 +104,15 @@
#ifndef DOCTEST_GCC
#define DOCTEST_GCC 0
#endif // DOCTEST_GCC
+#ifndef DOCTEST_ICC
+#define DOCTEST_ICC 0
+#endif // DOCTEST_ICC
// =================================================================================================
// == COMPILER WARNINGS HELPERS ====================================================================
// =================================================================================================
-#if DOCTEST_CLANG
+#if DOCTEST_CLANG && !DOCTEST_ICC
#define DOCTEST_PRAGMA_TO_STR(x) _Pragma(#x)
#define DOCTEST_CLANG_SUPPRESS_WARNING_PUSH _Pragma("clang diagnostic push")
#define DOCTEST_CLANG_SUPPRESS_WARNING(w) DOCTEST_PRAGMA_TO_STR(clang diagnostic ignored w)
@@ -146,14 +158,13 @@
// =================================================================================================
// both the header and the implementation suppress all of these,
-// so it only makes sense to aggregrate them like so
+// so it only makes sense to aggregate them like so
#define DOCTEST_SUPPRESS_COMMON_WARNINGS_PUSH \
DOCTEST_CLANG_SUPPRESS_WARNING_PUSH \
DOCTEST_CLANG_SUPPRESS_WARNING("-Wunknown-pragmas") \
DOCTEST_CLANG_SUPPRESS_WARNING("-Wweak-vtables") \
DOCTEST_CLANG_SUPPRESS_WARNING("-Wpadded") \
DOCTEST_CLANG_SUPPRESS_WARNING("-Wmissing-prototypes") \
- DOCTEST_CLANG_SUPPRESS_WARNING("-Wunused-local-typedef") \
DOCTEST_CLANG_SUPPRESS_WARNING("-Wc++98-compat") \
DOCTEST_CLANG_SUPPRESS_WARNING("-Wc++98-compat-pedantic") \
\
@@ -164,7 +175,6 @@
DOCTEST_GCC_SUPPRESS_WARNING("-Wstrict-overflow") \
DOCTEST_GCC_SUPPRESS_WARNING("-Wstrict-aliasing") \
DOCTEST_GCC_SUPPRESS_WARNING("-Wmissing-declarations") \
- DOCTEST_GCC_SUPPRESS_WARNING("-Wunused-local-typedefs") \
DOCTEST_GCC_SUPPRESS_WARNING("-Wuseless-cast") \
DOCTEST_GCC_SUPPRESS_WARNING("-Wnoexcept") \
\
@@ -174,7 +184,7 @@
DOCTEST_MSVC_SUPPRESS_WARNING(4571) /* SEH related */ \
DOCTEST_MSVC_SUPPRESS_WARNING(4710) /* function not inlined */ \
DOCTEST_MSVC_SUPPRESS_WARNING(4711) /* function selected for inline expansion*/ \
- /* */ \
+ /* common ones */ \
DOCTEST_MSVC_SUPPRESS_WARNING(4616) /* invalid compiler warning */ \
DOCTEST_MSVC_SUPPRESS_WARNING(4619) /* invalid compiler warning */ \
DOCTEST_MSVC_SUPPRESS_WARNING(4996) /* The compiler encountered a deprecated declaration */ \
@@ -188,6 +198,7 @@
DOCTEST_MSVC_SUPPRESS_WARNING(5026) /* move constructor was implicitly deleted */ \
DOCTEST_MSVC_SUPPRESS_WARNING(4640) /* construction of local static object not thread-safe */ \
DOCTEST_MSVC_SUPPRESS_WARNING(5045) /* Spectre mitigation for memory load */ \
+ DOCTEST_MSVC_SUPPRESS_WARNING(5264) /* 'variable-name': 'const' variable is not used */ \
/* static analysis */ \
DOCTEST_MSVC_SUPPRESS_WARNING(26439) /* Function may not throw. Declare it 'noexcept' */ \
DOCTEST_MSVC_SUPPRESS_WARNING(26495) /* Always initialize a member variable */ \
@@ -231,7 +242,9 @@ DOCTEST_MSVC_SUPPRESS_WARNING(4623) // default constructor was implicitly define
DOCTEST_MSVC_SUPPRESS_WARNING(4623) /* default constructor was implicitly deleted */ \
DOCTEST_MSVC_SUPPRESS_WARNING(5039) /* pointer to pot. throwing function passed to extern C */ \
DOCTEST_MSVC_SUPPRESS_WARNING(5045) /* Spectre mitigation for memory load */ \
- DOCTEST_MSVC_SUPPRESS_WARNING(5105) /* macro producing 'defined' has undefined behavior */
+ DOCTEST_MSVC_SUPPRESS_WARNING(5105) /* macro producing 'defined' has undefined behavior */ \
+ DOCTEST_MSVC_SUPPRESS_WARNING(4738) /* storing float result in memory, loss of performance */ \
+ DOCTEST_MSVC_SUPPRESS_WARNING(5262) /* implicit fall-through */
#define DOCTEST_MAKE_STD_HEADERS_CLEAN_FROM_WARNINGS_ON_WALL_END DOCTEST_MSVC_SUPPRESS_WARNING_POP
@@ -266,7 +279,7 @@ DOCTEST_MSVC_SUPPRESS_WARNING(4623) // default constructor was implicitly define
#endif // DOCTEST_CONFIG_NO_WINDOWS_SEH
#if !defined(_WIN32) && !defined(__QNX__) && !defined(DOCTEST_CONFIG_POSIX_SIGNALS) && \
- !defined(__EMSCRIPTEN__)
+ !defined(__EMSCRIPTEN__) && !defined(__wasi__)
#define DOCTEST_CONFIG_POSIX_SIGNALS
#endif // _WIN32
#if defined(DOCTEST_CONFIG_NO_POSIX_SIGNALS) && defined(DOCTEST_CONFIG_POSIX_SIGNALS)
@@ -274,7 +287,8 @@ DOCTEST_MSVC_SUPPRESS_WARNING(4623) // default constructor was implicitly define
#endif // DOCTEST_CONFIG_NO_POSIX_SIGNALS
#ifndef DOCTEST_CONFIG_NO_EXCEPTIONS
-#if !defined(__cpp_exceptions) && !defined(__EXCEPTIONS) && !defined(_CPPUNWIND)
+#if !defined(__cpp_exceptions) && !defined(__EXCEPTIONS) && !defined(_CPPUNWIND) \
+ || defined(__wasi__)
#define DOCTEST_CONFIG_NO_EXCEPTIONS
#endif // no exceptions
#endif // DOCTEST_CONFIG_NO_EXCEPTIONS
@@ -289,6 +303,10 @@ DOCTEST_MSVC_SUPPRESS_WARNING(4623) // default constructor was implicitly define
#define DOCTEST_CONFIG_NO_TRY_CATCH_IN_ASSERTS
#endif // DOCTEST_CONFIG_NO_EXCEPTIONS && !DOCTEST_CONFIG_NO_TRY_CATCH_IN_ASSERTS
+#ifdef __wasi__
+#define DOCTEST_CONFIG_NO_MULTITHREADING
+#endif
+
#if defined(DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN) && !defined(DOCTEST_CONFIG_IMPLEMENT)
#define DOCTEST_CONFIG_IMPLEMENT
#endif // DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN
@@ -316,6 +334,16 @@ DOCTEST_MSVC_SUPPRESS_WARNING(4623) // default constructor was implicitly define
#define DOCTEST_INTERFACE
#endif // DOCTEST_CONFIG_IMPLEMENTATION_IN_DLL
+// needed for extern template instantiations
+// see https://github.com/fmtlib/fmt/issues/2228
+#if DOCTEST_MSVC
+#define DOCTEST_INTERFACE_DECL
+#define DOCTEST_INTERFACE_DEF DOCTEST_INTERFACE
+#else // DOCTEST_MSVC
+#define DOCTEST_INTERFACE_DECL DOCTEST_INTERFACE
+#define DOCTEST_INTERFACE_DEF
+#endif // DOCTEST_MSVC
+
#define DOCTEST_EMPTY
#if DOCTEST_MSVC
@@ -332,6 +360,12 @@ DOCTEST_MSVC_SUPPRESS_WARNING(4623) // default constructor was implicitly define
#define DOCTEST_ALIGNMENT(x) __attribute__((aligned(x)))
#endif
+#ifdef DOCTEST_CONFIG_NO_CONTRADICTING_INLINE
+#define DOCTEST_INLINE_NOINLINE inline
+#else
+#define DOCTEST_INLINE_NOINLINE inline DOCTEST_NOINLINE
+#endif
+
#ifndef DOCTEST_NORETURN
#if DOCTEST_MSVC && (DOCTEST_MSVC < DOCTEST_COMPILER(19, 0, 0))
#define DOCTEST_NORETURN
@@ -351,15 +385,36 @@ DOCTEST_MSVC_SUPPRESS_WARNING(4623) // default constructor was implicitly define
#ifndef DOCTEST_CONSTEXPR
#if DOCTEST_MSVC && (DOCTEST_MSVC < DOCTEST_COMPILER(19, 0, 0))
#define DOCTEST_CONSTEXPR const
+#define DOCTEST_CONSTEXPR_FUNC inline
#else // DOCTEST_MSVC
#define DOCTEST_CONSTEXPR constexpr
+#define DOCTEST_CONSTEXPR_FUNC constexpr
#endif // DOCTEST_MSVC
#endif // DOCTEST_CONSTEXPR
+#ifndef DOCTEST_NO_SANITIZE_INTEGER
+#if DOCTEST_CLANG >= DOCTEST_COMPILER(3, 7, 0)
+#define DOCTEST_NO_SANITIZE_INTEGER __attribute__((no_sanitize("integer")))
+#else
+#define DOCTEST_NO_SANITIZE_INTEGER
+#endif
+#endif // DOCTEST_NO_SANITIZE_INTEGER
+
// =================================================================================================
// == FEATURE DETECTION END ========================================================================
// =================================================================================================
+#define DOCTEST_DECLARE_INTERFACE(name) \
+ virtual ~name(); \
+ name() = default; \
+ name(const name&) = delete; \
+ name(name&&) = delete; \
+ name& operator=(const name&) = delete; \
+ name& operator=(name&&) = delete;
+
+#define DOCTEST_DEFINE_INTERFACE(name) \
+ name::~name() = default;
+
// internal macros for string concatenation and anonymous variable name generation
#define DOCTEST_CAT_IMPL(s1, s2) s1##s2
#define DOCTEST_CAT(s1, s2) DOCTEST_CAT_IMPL(s1, s2)
@@ -382,17 +437,19 @@ DOCTEST_MSVC_SUPPRESS_WARNING(4623) // default constructor was implicitly define
#define DOCTEST_PLATFORM_IPHONE
#elif defined(_WIN32)
#define DOCTEST_PLATFORM_WINDOWS
+#elif defined(__wasi__)
+#define DOCTEST_PLATFORM_WASI
#else // DOCTEST_PLATFORM
#define DOCTEST_PLATFORM_LINUX
#endif // DOCTEST_PLATFORM
namespace doctest { namespace detail {
- static DOCTEST_CONSTEXPR int consume(const int*, int) { return 0; }
+ static DOCTEST_CONSTEXPR int consume(const int*, int) noexcept { return 0; }
}}
-#define DOCTEST_GLOBAL_NO_WARNINGS(var, ...) \
- DOCTEST_CLANG_SUPPRESS_WARNING_WITH_PUSH("-Wglobal-constructors") \
- static const int var = doctest::detail::consume(&var, __VA_ARGS__); \
+#define DOCTEST_GLOBAL_NO_WARNINGS(var, ...) \
+ DOCTEST_CLANG_SUPPRESS_WARNING_WITH_PUSH("-Wglobal-constructors") \
+ static const int var = doctest::detail::consume(&var, __VA_ARGS__); \
DOCTEST_CLANG_SUPPRESS_WARNING_POP
#ifndef DOCTEST_BREAK_INTO_DEBUGGER
@@ -400,16 +457,19 @@ namespace doctest { namespace detail {
#ifdef DOCTEST_PLATFORM_LINUX
#if defined(__GNUC__) && (defined(__i386) || defined(__x86_64))
// Break at the location of the failing check if possible
-#define DOCTEST_BREAK_INTO_DEBUGGER() __asm__("int $3\n" : :) // NOLINT (hicpp-no-assembler)
+#define DOCTEST_BREAK_INTO_DEBUGGER() __asm__("int $3\n" : :) // NOLINT(hicpp-no-assembler)
#else
#include
#define DOCTEST_BREAK_INTO_DEBUGGER() raise(SIGTRAP)
#endif
#elif defined(DOCTEST_PLATFORM_MAC)
#if defined(__x86_64) || defined(__x86_64__) || defined(__amd64__) || defined(__i386)
-#define DOCTEST_BREAK_INTO_DEBUGGER() __asm__("int $3\n" : :) // NOLINT (hicpp-no-assembler)
+#define DOCTEST_BREAK_INTO_DEBUGGER() __asm__("int $3\n" : :) // NOLINT(hicpp-no-assembler)
+#elif defined(__ppc__) || defined(__ppc64__)
+// https://www.cocoawithlove.com/2008/03/break-into-debugger.html
+#define DOCTEST_BREAK_INTO_DEBUGGER() __asm__("li r0, 20\nsc\nnop\nli r0, 37\nli r4, 2\nsc\nnop\n": : : "memory","r0","r3","r4") // NOLINT(hicpp-no-assembler)
#else
-#define DOCTEST_BREAK_INTO_DEBUGGER() __asm__("brk #0"); // NOLINT (hicpp-no-assembler)
+#define DOCTEST_BREAK_INTO_DEBUGGER() __asm__("brk #0"); // NOLINT(hicpp-no-assembler)
#endif
#elif DOCTEST_MSVC
#define DOCTEST_BREAK_INTO_DEBUGGER() __debugbreak()
@@ -425,7 +485,9 @@ DOCTEST_GCC_SUPPRESS_WARNING_POP
// this is kept here for backwards compatibility since the config option was changed
#ifdef DOCTEST_CONFIG_USE_IOSFWD
+#ifndef DOCTEST_CONFIG_USE_STD_HEADERS
#define DOCTEST_CONFIG_USE_STD_HEADERS
+#endif
#endif // DOCTEST_CONFIG_USE_IOSFWD
// for clang - always include ciso646 (which drags some std stuff) because
@@ -435,35 +497,44 @@ DOCTEST_GCC_SUPPRESS_WARNING_POP
// https://github.com/doctest/doctest/issues/356
#if DOCTEST_CLANG
#include
-#ifdef _LIBCPP_VERSION
-#define DOCTEST_CONFIG_USE_STD_HEADERS
-#endif // _LIBCPP_VERSION
#endif // clang
+#ifdef _LIBCPP_VERSION
+#ifndef DOCTEST_CONFIG_USE_STD_HEADERS
+#define DOCTEST_CONFIG_USE_STD_HEADERS
+#endif
+#endif // _LIBCPP_VERSION
+
#ifdef DOCTEST_CONFIG_USE_STD_HEADERS
#ifndef DOCTEST_CONFIG_INCLUDE_TYPE_TRAITS
#define DOCTEST_CONFIG_INCLUDE_TYPE_TRAITS
#endif // DOCTEST_CONFIG_INCLUDE_TYPE_TRAITS
+DOCTEST_MAKE_STD_HEADERS_CLEAN_FROM_WARNINGS_ON_WALL_BEGIN
#include
#include
#include
+DOCTEST_MAKE_STD_HEADERS_CLEAN_FROM_WARNINGS_ON_WALL_END
#else // DOCTEST_CONFIG_USE_STD_HEADERS
// Forward declaring 'X' in namespace std is not permitted by the C++ Standard.
DOCTEST_MSVC_SUPPRESS_WARNING_WITH_PUSH(4643)
-namespace std { // NOLINT (cert-dcl58-cpp)
-typedef decltype(nullptr) nullptr_t;
+namespace std { // NOLINT(cert-dcl58-cpp)
+typedef decltype(nullptr) nullptr_t; // NOLINT(modernize-use-using)
+typedef decltype(sizeof(void*)) size_t; // NOLINT(modernize-use-using)
template
struct char_traits;
template <>
struct char_traits;
template
-class basic_ostream;
-typedef basic_ostream> ostream;
+class basic_ostream; // NOLINT(fuchsia-virtual-inheritance)
+typedef basic_ostream> ostream; // NOLINT(modernize-use-using)
+template
+// NOLINTNEXTLINE
+basic_ostream& operator<<(basic_ostream&, const char*);
template
class basic_istream;
-typedef basic_istream> istream;
+typedef basic_istream> istream; // NOLINT(modernize-use-using)
template
class tuple;
#if DOCTEST_MSVC >= DOCTEST_COMPILER(19, 20, 0)
@@ -486,8 +557,14 @@ DOCTEST_MSVC_SUPPRESS_WARNING_POP
namespace doctest {
+using std::size_t;
+
DOCTEST_INTERFACE extern bool is_running_in_test;
+#ifndef DOCTEST_CONFIG_STRING_SIZE_TYPE
+#define DOCTEST_CONFIG_STRING_SIZE_TYPE unsigned
+#endif
+
// A 24 byte string class (can be as small as 17 for x64 and 13 for x86) that can hold strings with length
// of up to 23 chars on the stack before going on the heap - the last byte of the buffer is used for:
// - "is small" bit - the highest bit - if "0" then it is small - otherwise its "1" (128)
@@ -500,7 +577,6 @@ DOCTEST_INTERFACE extern bool is_running_in_test;
// TODO:
// - optimizations - like not deleting memory unnecessarily in operator= and etc.
// - resize/reserve/clear
-// - substr
// - replace
// - back/front
// - iterator stuff
@@ -510,64 +586,80 @@ DOCTEST_INTERFACE extern bool is_running_in_test;
// - relational operators as free functions - taking const char* as one of the params
class DOCTEST_INTERFACE String
{
- static const unsigned len = 24; //!OCLINT avoid private static members
- static const unsigned last = len - 1; //!OCLINT avoid private static members
+public:
+ using size_type = DOCTEST_CONFIG_STRING_SIZE_TYPE;
+
+private:
+ static DOCTEST_CONSTEXPR size_type len = 24; //!OCLINT avoid private static members
+ static DOCTEST_CONSTEXPR size_type last = len - 1; //!OCLINT avoid private static members
struct view // len should be more than sizeof(view) - because of the final byte for flags
{
char* ptr;
- unsigned size;
- unsigned capacity;
+ size_type size;
+ size_type capacity;
};
union
{
- char buf[len];
+ char buf[len]; // NOLINT(*-avoid-c-arrays)
view data;
};
- char* allocate(unsigned sz);
+ char* allocate(size_type sz);
- bool isOnStack() const { return (buf[last] & 128) == 0; }
- void setOnHeap();
- void setLast(unsigned in = last);
+ bool isOnStack() const noexcept { return (buf[last] & 128) == 0; }
+ void setOnHeap() noexcept;
+ void setLast(size_type in = last) noexcept;
+ void setSize(size_type sz) noexcept;
void copy(const String& other);
public:
- String();
+ static DOCTEST_CONSTEXPR size_type npos = static_cast(-1);
+
+ String() noexcept;
~String();
// cppcheck-suppress noExplicitConstructor
String(const char* in);
- String(const char* in, unsigned in_size);
+ String(const char* in, size_type in_size);
- String(std::istream& in, unsigned in_size);
+ String(std::istream& in, size_type in_size);
String(const String& other);
String& operator=(const String& other);
String& operator+=(const String& other);
- String(String&& other);
- String& operator=(String&& other);
+ String(String&& other) noexcept;
+ String& operator=(String&& other) noexcept;
- char operator[](unsigned i) const;
- char& operator[](unsigned i);
+ char operator[](size_type i) const;
+ char& operator[](size_type i);
// the only functions I'm willing to leave in the interface - available for inlining
const char* c_str() const { return const_cast(this)->c_str(); } // NOLINT
char* c_str() {
- if(isOnStack())
+ if (isOnStack()) {
return reinterpret_cast(buf);
+ }
return data.ptr;
}
- unsigned size() const;
- unsigned capacity() const;
+ size_type size() const;
+ size_type capacity() const;
+
+ String substr(size_type pos, size_type cnt = npos) &&;
+ String substr(size_type pos, size_type cnt = npos) const &;
+
+ size_type find(char ch, size_type pos = 0) const;
+ size_type rfind(char ch, size_type pos = npos) const;
int compare(const char* other, bool no_case = false) const;
int compare(const String& other, bool no_case = false) const;
+
+friend DOCTEST_INTERFACE std::ostream& operator<<(std::ostream& s, const String& in);
};
DOCTEST_INTERFACE String operator+(const String& lhs, const String& rhs);
@@ -579,7 +671,21 @@ DOCTEST_INTERFACE bool operator>(const String& lhs, const String& rhs);
DOCTEST_INTERFACE bool operator<=(const String& lhs, const String& rhs);
DOCTEST_INTERFACE bool operator>=(const String& lhs, const String& rhs);
-DOCTEST_INTERFACE std::ostream& operator<<(std::ostream& s, const String& in);
+class DOCTEST_INTERFACE Contains {
+public:
+ explicit Contains(const String& string);
+
+ bool checkWith(const String& other) const;
+
+ String string;
+};
+
+DOCTEST_INTERFACE String toString(const Contains& in);
+
+DOCTEST_INTERFACE bool operator==(const String& lhs, const Contains& rhs);
+DOCTEST_INTERFACE bool operator==(const Contains& lhs, const String& rhs);
+DOCTEST_INTERFACE bool operator!=(const String& lhs, const Contains& rhs);
+DOCTEST_INTERFACE bool operator!=(const Contains& lhs, const String& rhs);
namespace Color {
enum Enum
@@ -652,7 +758,7 @@ namespace assertType {
DT_WARN_THROWS_WITH = is_throws_with | is_warn,
DT_CHECK_THROWS_WITH = is_throws_with | is_check,
DT_REQUIRE_THROWS_WITH = is_throws_with | is_require,
-
+
DT_WARN_THROWS_WITH_AS = is_throws_with | is_throws_as | is_warn,
DT_CHECK_THROWS_WITH_AS = is_throws_with | is_throws_as | is_check,
DT_REQUIRE_THROWS_WITH_AS = is_throws_with | is_throws_as | is_require,
@@ -733,9 +839,27 @@ struct DOCTEST_INTERFACE AssertData
String m_decomp;
// for specific exception-related asserts
- bool m_threw_as;
- const char* m_exception_type;
- const char* m_exception_string;
+ bool m_threw_as;
+ const char* m_exception_type;
+
+ class DOCTEST_INTERFACE StringContains {
+ private:
+ Contains content;
+ bool isContains;
+
+ public:
+ StringContains(const String& str) : content(str), isContains(false) { }
+ StringContains(Contains cntn) : content(static_cast(cntn)), isContains(true) { }
+
+ bool check(const String& str) { return isContains ? (content == str) : (content.string == str); }
+
+ operator const String&() const { return content.string; }
+
+ const char* c_str() const { return content.string.c_str(); }
+ } m_exception_string;
+
+ AssertData(assertType::Enum at, const char* file, int line, const char* expr,
+ const char* exception_type, const StringContains& exception_string);
};
struct DOCTEST_INTERFACE MessageData
@@ -752,13 +876,13 @@ struct DOCTEST_INTERFACE SubcaseSignature
const char* m_file;
int m_line;
+ bool operator==(const SubcaseSignature& other) const;
bool operator<(const SubcaseSignature& other) const;
};
struct DOCTEST_INTERFACE IContextScope
{
- IContextScope();
- virtual ~IContextScope();
+ DOCTEST_DECLARE_INTERFACE(IContextScope)
virtual void stringify(std::ostream*) const = 0;
};
@@ -815,200 +939,189 @@ struct ContextOptions //!OCLINT too many fields
};
namespace detail {
- template
- struct enable_if
- {};
-
- template
- struct enable_if
- { typedef TYPE type; };
-
- // clang-format off
- template struct remove_reference { typedef T type; };
- template struct remove_reference { typedef T type; };
- template struct remove_reference { typedef T type; };
-
- template U declval(int);
-
- template T declval(long);
-
- template auto declval() DOCTEST_NOEXCEPT -> decltype(declval(0)) ;
-
- template struct is_lvalue_reference { const static bool value=false; };
- template struct is_lvalue_reference { const static bool value=true; };
-
- template struct is_rvalue_reference { const static bool value=false; };
- template struct is_rvalue_reference { const static bool value=true; };
-
- template
- inline T&& forward(typename remove_reference::type& t) DOCTEST_NOEXCEPT
- {
- return static_cast(t);
- }
-
- template
- inline T&& forward(typename remove_reference::type&& t) DOCTEST_NOEXCEPT
- {
- static_assert(!is_lvalue_reference::value,
- "Can not forward an rvalue as an lvalue.");
- return static_cast(t);
- }
-
- template struct remove_const { typedef T type; };
- template struct remove_const { typedef T type; };
+ namespace types {
#ifdef DOCTEST_CONFIG_INCLUDE_TYPE_TRAITS
- template struct is_enum : public std::is_enum {};
- template struct underlying_type : public std::underlying_type {};
+ using namespace std;
#else
- // Use compiler intrinsics
- template struct is_enum { DOCTEST_CONSTEXPR static bool value = __is_enum(T); };
- template struct underlying_type { typedef __underlying_type(T) type; };
+ template
+ struct enable_if { };
+
+ template
+ struct enable_if { using type = T; };
+
+ struct true_type { static DOCTEST_CONSTEXPR bool value = true; };
+ struct false_type { static DOCTEST_CONSTEXPR bool value = false; };
+
+ template struct remove_reference { using type = T; };
+ template struct remove_reference { using type = T; };
+ template struct remove_reference { using type = T; };
+
+ template struct is_rvalue_reference : false_type { };
+ template struct is_rvalue_reference : true_type { };
+
+ template struct remove_const { using type = T; };
+ template struct remove_const { using type = T; };
+
+ // Compiler intrinsics
+ template struct is_enum { static DOCTEST_CONSTEXPR bool value = __is_enum(T); };
+ template struct underlying_type { using type = __underlying_type(T); };
+
+ template struct is_pointer : false_type { };
+ template struct is_pointer : true_type { };
+
+ template struct is_array : false_type { };
+ // NOLINTNEXTLINE(*-avoid-c-arrays)
+ template struct is_array : true_type { };
#endif
- // clang-format on
+ }
+
+ //
+ template
+ T&& declval();
+
+ template
+ DOCTEST_CONSTEXPR_FUNC T&& forward(typename types::remove_reference::type& t) DOCTEST_NOEXCEPT {
+ return static_cast(t);
+ }
+
+ template
+ DOCTEST_CONSTEXPR_FUNC T&& forward(typename types::remove_reference::type&& t) DOCTEST_NOEXCEPT {
+ return static_cast(t);
+ }
template
- struct deferred_false
- // cppcheck-suppress unusedStructMember
- { static const bool value = false; };
+ struct deferred_false : types::false_type { };
- namespace has_insertion_operator_impl {
- std::ostream &os();
- template
- DOCTEST_REF_WRAP(T) val();
+// MSVS 2015 :(
+#if !DOCTEST_CLANG && defined(_MSC_VER) && _MSC_VER <= 1900
+ template
+ struct has_global_insertion_operator : types::false_type { };
- template
- struct check {
- static DOCTEST_CONSTEXPR bool value = false;
- };
+ template
+ struct has_global_insertion_operator(), declval()), void())> : types::true_type { };
- template
- struct check(), void())> {
- static DOCTEST_CONSTEXPR bool value = true;
- };
- } // namespace has_insertion_operator_impl
+ template
+ struct has_insertion_operator { static DOCTEST_CONSTEXPR bool value = has_global_insertion_operator::value; };
- template
- using has_insertion_operator = has_insertion_operator_impl::check;
+ template
+ struct insert_hack;
+
+ template
+ struct insert_hack {
+ static void insert(std::ostream& os, const T& t) { ::operator<<(os, t); }
+ };
+
+ template
+ struct insert_hack {
+ static void insert(std::ostream& os, const T& t) { operator<<(os, t); }
+ };
+
+ template
+ using insert_hack_t = insert_hack::value>;
+#else
+ template
+ struct has_insertion_operator : types::false_type { };
+#endif
+
+ template
+ struct has_insertion_operator(), declval()), void())> : types::true_type { };
+
+ template
+ struct should_stringify_as_underlying_type {
+ static DOCTEST_CONSTEXPR bool value = detail::types::is_enum::value && !doctest::detail::has_insertion_operator::value;
+ };
DOCTEST_INTERFACE std::ostream* tlssPush();
DOCTEST_INTERFACE String tlssPop();
-
template
- struct StringMakerBase
- {
+ struct StringMakerBase {
template
static String convert(const DOCTEST_REF_WRAP(T)) {
+#ifdef DOCTEST_CONFIG_REQUIRE_STRINGIFICATION_FOR_ALL_USED_TYPES
+ static_assert(deferred_false::value, "No stringification detected for type T. See string conversion manual");
+#endif
return "{?}";
}
};
- // Vector