From f75fd22ba323bbb3b6621ed9beaf8f52f24796fd Mon Sep 17 00:00:00 2001 From: Philip Top Date: Fri, 7 Mar 2025 07:47:56 -0800 Subject: [PATCH] add tests and fixes for array options (#1136) Fixes #1135. Adds enable check to certain to_string functions as some std::array operations were ambiguous. The addition of the capability to convert tuples to strings created an ambiguity in the case std::array, which acts like a tuple and a container. So it worked with the container conversion before, but could also work with the new tuple conversion. And we didn't have any tests to catch this. This PR resolves the ambiguity, and adds some tests to check that array is handled well. --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- azure-pipelines.yml | 3 ++- examples/CMakeLists.txt | 4 ++++ examples/array_option.cpp | 21 ++++++++++++++++++ include/CLI/TypeTools.hpp | 4 ++-- tests/OptionTypeTest.cpp | 45 +++++++++++++++++++++++++++++++++++++++ 5 files changed, 74 insertions(+), 3 deletions(-) create mode 100644 examples/array_option.cpp diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 0f8a92c3..4bae11a8 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -99,7 +99,7 @@ jobs: steps: - task: UsePythonVersion@0 inputs: - versionSpec: "3.7" + versionSpec: "3.11" - script: python3 -m pip install meson ninja displayName: install meson - script: mkdir tests/mesonTest/subprojects @@ -140,6 +140,7 @@ jobs: clang3.4: containerImage: silkeh/clang:3.4 cli11.std: 11 + cli11.options: -DCLI11_WARNINGS_AS_ERRORS=OFF clang8: containerImage: silkeh/clang:8 cli11.std: 14 diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index ced9bd9c..07c8f64d 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -219,6 +219,10 @@ add_cli_exe(modhelp modhelp.cpp) add_test(NAME modhelp COMMAND modhelp -a test -h) set_property(TEST modhelp PROPERTY PASS_REGULAR_EXPRESSION "Option -a string in help: test") +add_cli_exe(array_option array_option.cpp) +add_test(NAME array_option COMMAND array_option --a 1 2) +set_property(TEST array_option PROPERTY PASS_REGULAR_EXPRESSION "pass") + add_subdirectory(subcom_in_files) add_test(NAME subcom_in_files COMMAND subcommand_main subcommand_a -f this.txt --with-foo) set_property(TEST subcom_in_files PROPERTY PASS_REGULAR_EXPRESSION "Working on file: this\.txt" diff --git a/examples/array_option.cpp b/examples/array_option.cpp new file mode 100644 index 00000000..875cad3d --- /dev/null +++ b/examples/array_option.cpp @@ -0,0 +1,21 @@ +// Copyright (c) 2017-2025, University of Cincinnati, developed by Henry Schreiner +// under NSF AWARD 1414736 and by the respective contributors. +// All rights reserved. +// +// SPDX-License-Identifier: BSD-3-Clause + +// Code modified from Loic Gouarin(https://github.com/gouarin) https://github.com/CLIUtils/CLI11/issues/1135 + +#include +#include +#include + +int main(int argc, char **argv) { + std::array a{0, 1}; + CLI::App app{"My app"}; + app.add_option("--a", a, "an array")->capture_default_str(); + app.parse(argc, argv); + + std::cout << "pass\n"; + return 0; +} diff --git a/include/CLI/TypeTools.hpp b/include/CLI/TypeTools.hpp index 4b56f10c..8bc3ba71 100644 --- a/include/CLI/TypeTools.hpp +++ b/include/CLI/TypeTools.hpp @@ -387,7 +387,7 @@ inline std::string to_string(T &&) { /// convert a readable container to a string template ::value && !std::is_constructible::value && - !is_ostreamable::value && is_readable_container::value, + !is_ostreamable::value && is_readable_container::value && !is_tuple_like::value, detail::enabler> = detail::dummy> inline std::string to_string(T &&variable) { auto cval = variable.begin(); @@ -1497,7 +1497,7 @@ bool lexical_conversion(const std::vector &strings, AssignTo &outp using FirstType = typename std::remove_const::type>::type; using SecondType = typename std::tuple_element<1, ConvertTo>::type; FirstType v1; - SecondType v2; + SecondType v2{}; bool retval = lexical_assign(strings[0], v1); retval = retval && lexical_assign((strings.size() > 1) ? strings[1] : std::string{}, v2); if(retval) { diff --git a/tests/OptionTypeTest.cpp b/tests/OptionTypeTest.cpp index 21262649..c1193bdf 100644 --- a/tests/OptionTypeTest.cpp +++ b/tests/OptionTypeTest.cpp @@ -9,6 +9,7 @@ #include "catch.hpp" #include +#include #include #include #include @@ -912,6 +913,50 @@ TEST_CASE_METHOD(TApp, "vectorPairTypeRange", "[optiontype]") { CHECK("str4" == custom_opt[2].second); } +TEST_CASE_METHOD(TApp, "ArrayTriple", "[optiontype]") { + + using TY = std::array; + TY custom_opt; + + app.add_option("posit", custom_opt); + + args = {"12", "1", "5"}; + + run(); + CHECK(12 == custom_opt[0]); + CHECK(1 == custom_opt[1]); + CHECK(5 == custom_opt[2]); + + // enable_if_t::value && !std::is_constructible::value && + // !is_ostreamable::value && is_tuple_like::value && type_count_base::value >= 2, + // detail::enabler>> + + CHECK(!std::is_convertible::value); + CHECK(!std::is_constructible::value); + CHECK(!CLI::detail::is_ostreamable::value); + auto ts = std::tuple_size::type>::value; + CHECK(ts == 3); + + auto vb = CLI::detail::type_count_base::value; + CHECK(vb >= 2); + CHECK(!CLI::detail::is_complex::value); + CHECK(CLI::detail::is_tuple_like::value); +} + +TEST_CASE_METHOD(TApp, "ArrayPair", "[optiontype]") { + + using TY = std::array; + TY custom_opt; + + app.add_option("posit", custom_opt); + + args = {"12", "1"}; + + run(); + CHECK(12 == custom_opt[0]); + CHECK(1 == custom_opt[1]); +} + // now with independent type sizes and expected this is possible TEST_CASE_METHOD(TApp, "vectorTuple", "[optiontype]") {