1
0
mirror of https://github.com/CLIUtils/CLI11.git synced 2025-04-29 12:13:52 +00:00

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>
This commit is contained in:
Philip Top 2025-03-07 07:47:56 -08:00 committed by GitHub
parent 8fa8a0fa0c
commit f75fd22ba3
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 74 additions and 3 deletions

View File

@ -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

View File

@ -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"

21
examples/array_option.cpp Normal file
View File

@ -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 <CLI/CLI.hpp>
#include <array>
#include <iostream>
int main(int argc, char **argv) {
std::array<int, 2> 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;
}

View File

@ -387,7 +387,7 @@ inline std::string to_string(T &&) {
/// convert a readable container to a string
template <typename T,
enable_if_t<!std::is_convertible<T, std::string>::value && !std::is_constructible<std::string, T>::value &&
!is_ostreamable<T>::value && is_readable_container<T>::value,
!is_ostreamable<T>::value && is_readable_container<T>::value && !is_tuple_like<T>::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<std ::string> &strings, AssignTo &outp
using FirstType = typename std::remove_const<typename std::tuple_element<0, ConvertTo>::type>::type;
using SecondType = typename std::tuple_element<1, ConvertTo>::type;
FirstType v1;
SecondType v2;
SecondType v2{};
bool retval = lexical_assign<FirstType, FirstType>(strings[0], v1);
retval = retval && lexical_assign<SecondType, SecondType>((strings.size() > 1) ? strings[1] : std::string{}, v2);
if(retval) {

View File

@ -9,6 +9,7 @@
#include "catch.hpp"
#include <algorithm>
#include <array>
#include <atomic>
#include <cmath>
#include <complex>
@ -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<int, 3>;
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<!std::is_convertible<T, std::string>::value && !std::is_constructible<std::string, T>::value &&
// !is_ostreamable<T>::value && is_tuple_like<T>::value && type_count_base<T>::value >= 2,
// detail::enabler>>
CHECK(!std::is_convertible<TY, std::string>::value);
CHECK(!std::is_constructible<std::string, TY>::value);
CHECK(!CLI::detail::is_ostreamable<TY>::value);
auto ts = std::tuple_size<typename std::decay<TY>::type>::value;
CHECK(ts == 3);
auto vb = CLI::detail::type_count_base<TY>::value;
CHECK(vb >= 2);
CHECK(!CLI::detail::is_complex<TY>::value);
CHECK(CLI::detail::is_tuple_like<TY>::value);
}
TEST_CASE_METHOD(TApp, "ArrayPair", "[optiontype]") {
using TY = std::array<int, 2>;
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]") {