fix: do not run checks against default values (#1375) - #1376
Conversation
default_val() validated the default against all registered validators at configuration time, so a default that failed a check() (e.g. 0 with CLI::PositiveNumber) threw immediately during app setup — outside the CLI11_PARSE try/catch. The throw was also order-dependent, only firing when the check was registered before default_val(). Thread a transform_only flag through run_callback/_validate_results/_validate so that validating a default applies only modifying validators (transforms), skipping non-modifying checks. Transforms and conversion still run, so enum defaults still convert and un-convertible defaults still raise ConversionError; checks apply to parsed input and are caught by CLI11_PARSE as expected. Assisted-by: ClaudeCode:claude-opus-4.8
|
I think the |
|
I was experimenting with this - actually registering deferred evaluations and running them during parsing would be a much bigger change. You control the default, though (and can manually call the validator), so I'm not sure if we should require the default be able to validate. It would mean you could set a default that can't be passed, but sometimes you might want that. (I'm a little surprised 0 is not a positive number, I'd have said it was both). |
|
In my case, the default value is a subdirectory of the working directory. That's a runtime value, so it requires validation. Passing the option allows changing the subdirectory's name, so I do require all cases. (Including zero is typically called non-negative, which also has a builtin validator) |
🤖 AI text below 🤖
Fixes #1375.
Problem
add_option(...)->check(...)->default_val(...)throws immediately during app setup, not during parsing:default_val()validated the default against every registered validator, so a default that fails acheck()threw right there — outside theCLI11_PARSEtry/catch, terminating the program. The behavior was also order-dependent: it only threw when thecheck()was registered beforedefault_val().Fix
Thread a
transform_onlyflag throughrun_callback→_validate_results→_validate. When validating a default value, only modifying validators (transforms) are applied; non-modifying checks are skipped. This leans on the existing distinction thatcheck()marks its validatornon_modifying()whiletransform()does not.Consequences:
CheckedTransformerdefaults still convert correctly (preserves the default_val validate fails if ostream operator differs fromCheckedTransformer'CLI::ConversionError' (regression?) #1258 fix).ConversionErrorat config time.CLI11_PARSE.check()/default_val()ordering.Behavior change
default_val()no longer validates a default againstcheck()s. One existing assertion inNumericTypeTest(a bad default caught bycheck(CLI::Number)at config time) was updated to reflect this, with added coverage that the check still fires on parsed input.Tests
defaultValFailingCheckregression test (mirrors the issue MWE); confirmed it failed before the fix.doubleVectorFunctionRunCallbackOnDefault.README.mdandbook/chapters/options.md.prekclean.