Compare commits

21 Commits

Author SHA1 Message Date
1015b66d80 chore: update docs 2026-05-13 14:17:25 +04:00
3f63bf958f feat: parser settings. 2026-05-13 13:05:51 +04:00
8a57dca32e docs: add docs. 2026-05-13 13:05:13 +04:00
566b9128db chore: refactor class names.
feat: introduce parser settings for controlling the exit behavior.
2026-05-13 13:04:57 +04:00
d1267c892f fix: incorrect starting point. 2026-05-13 10:43:38 +04:00
85e52a1dcd feat: accumulate to int. 2026-05-13 10:43:19 +04:00
b8e9f5c98c feat: introduce reference return, accumulators. chore: lint 2026-05-12 11:38:46 +04:00
21ca1f638b chore: format 2026-05-07 17:49:47 +04:00
Abdüssamet ERSOYLU
2b2a0df933 Merge pull request #9 from sametersoylu/refaction
feat: add reference capabilities to positional arguments. use existing
2026-05-05 12:01:17 +04:00
c067bbca38 update: example to display capturing capability via positional args. 2026-05-05 11:58:50 +04:00
54415f9527 feat: add reference capabilities to positional arguments. use existing
v2 api to capture reference arguments.
2026-05-05 11:57:36 +04:00
96fdbb5f00 chore: remove idea 2026-05-05 11:48:12 +04:00
Abdüssamet ERSOYLU
7fa218b7f3 Merge pull request #8 from sametersoylu/v3
V3
2026-05-05 11:45:43 +04:00
1c63622fd8 chore: update readme.md 2026-05-05 11:44:18 +04:00
1479892e7b chore: update todo. 2026-05-05 11:27:48 +04:00
708f63a00d feat: implement reference capture mode. 2026-05-05 11:20:53 +04:00
faf1715ee3 chore: remove compile commands 2026-05-05 10:38:19 +04:00
05c2f782b1 chore: demontrate concat. 2026-05-05 10:36:24 +04:00
7caeb20dfa feat: add compile time concatting capabilities for string building in
parsing traits for hints.
2026-05-05 10:36:12 +04:00
e1d72aaea7 chore: initial examples folder. 2026-05-04 13:52:08 +04:00
81a85149b4 feat: new argument builder. base for ref. 2026-05-04 13:51:54 +04:00
33 changed files with 2886 additions and 1781 deletions

1
.gitignore vendored
View File

@@ -3,3 +3,4 @@ build
.vscode
.vs
.cache
.idea

View File

@@ -1,7 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="CPP_MODULE" version="4">
<component name="NewModuleRootManager">
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

344
.idea/editor.xml generated
View File

@@ -1,344 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="BackendCodeEditorSettings">
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CDeclarationWithImplicitIntType/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CommentTypo/@EntryIndexedValue" value="DO_NOT_SHOW" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=ConstevalIfIsAlwaysConstant/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppAbstractClassWithoutSpecifier/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppAbstractFinalClass/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppAbstractVirtualFunctionCallInCtor/@EntryIndexedValue" value="ERROR" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppAccessSpecifierWithNoDeclarations/@EntryIndexedValue" value="SUGGESTION" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppAwaiterTypeIsNotClass/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppBooleanIncrementExpression/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppBoostFormatBadCode/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppBoostFormatLegacyCode/@EntryIndexedValue" value="SUGGESTION" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppBoostFormatMixedArgs/@EntryIndexedValue" value="ERROR" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppBoostFormatTooFewArgs/@EntryIndexedValue" value="ERROR" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppBoostFormatTooManyArgs/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppCStyleCast/@EntryIndexedValue" value="SUGGESTION" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppCVQualifierCanNotBeAppliedToReference/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppClassCanBeFinal/@EntryIndexedValue" value="HINT" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppClassIsIncomplete/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppClassNeedsConstructorBecauseOfUninitializedMember/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppClassNeverUsed/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppCompileTimeConstantCanBeReplacedWithBooleanConstant/@EntryIndexedValue" value="SUGGESTION" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppConceptNeverUsed/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppConditionalExpressionCanBeSimplified/@EntryIndexedValue" value="SUGGESTION" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppConstParameterInDeclaration/@EntryIndexedValue" value="SUGGESTION" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppConstValueFunctionReturnType/@EntryIndexedValue" value="SUGGESTION" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppCoroutineCallResolveError/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppDFAArrayIndexOutOfBounds/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppDFAConstantConditions/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppDFAConstantFunctionResult/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppDFAConstantParameter/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppDFADeletedPointer/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppDFAEndlessLoop/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppDFAInfiniteRecursion/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppDFAInvalidatedMemory/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppDFALocalValueEscapesFunction/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppDFALocalValueEscapesScope/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppDFALoopConditionNotUpdated/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppDFAMemoryLeak/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppDFANotInitializedField/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppDFANullDereference/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppDFATimeOver/@EntryIndexedValue" value="HINT" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppDFAUnreachableCode/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppDFAUnreachableFunctionCall/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppDFAUnreadVariable/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppDFAUnusedValue/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppDeclarationHidesLocal/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppDeclarationHidesUncapturedLocal/@EntryIndexedValue" value="HINT" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppDeclarationSpecifierWithoutDeclarators/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppDeclaratorDisambiguatedAsFunction/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppDeclaratorNeverUsed/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppDeclaratorUsedBeforeInitialization/@EntryIndexedValue" value="ERROR" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppDefaultCaseNotHandledInSwitchStatement/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppDefaultInitializationWithNoUserConstructor/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppDefaultIsUsedAsIdentifier/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppDefaultedSpecialMemberFunctionIsImplicitlyDeleted/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppDeletingVoidPointer/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppDependentTemplateWithoutTemplateKeyword/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppDependentTypeWithoutTypenameKeyword/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppDeprecatedEntity/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppDeprecatedOverridenMethod/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppDeprecatedRegisterStorageClassSpecifier/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppDereferenceOperatorLimitExceeded/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppDiscardedPostfixOperatorResult/@EntryIndexedValue" value="SUGGESTION" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppDoxygenSyntaxError/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppDoxygenUndocumentedParameter/@EntryIndexedValue" value="SUGGESTION" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppDoxygenUnresolvedReference/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppEmptyDeclaration/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppEnforceCVQualifiersOrder/@EntryIndexedValue" value="DO_NOT_SHOW" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppEnforceCVQualifiersPlacement/@EntryIndexedValue" value="DO_NOT_SHOW" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppEnforceDoStatementBraces/@EntryIndexedValue" value="DO_NOT_SHOW" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppEnforceForStatementBraces/@EntryIndexedValue" value="DO_NOT_SHOW" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppEnforceFunctionDeclarationStyle/@EntryIndexedValue" value="DO_NOT_SHOW" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppEnforceIfStatementBraces/@EntryIndexedValue" value="DO_NOT_SHOW" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppEnforceNestedNamespacesStyle/@EntryIndexedValue" value="HINT" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppEnforceOverridingDestructorStyle/@EntryIndexedValue" value="SUGGESTION" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppEnforceOverridingFunctionStyle/@EntryIndexedValue" value="SUGGESTION" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppEnforceTypeAliasCodeStyle/@EntryIndexedValue" value="DO_NOT_SHOW" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppEnforceWhileStatementBraces/@EntryIndexedValue" value="DO_NOT_SHOW" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppEntityAssignedButNoRead/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppEntityUsedOnlyInUnevaluatedContext/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppEnumeratorNeverUsed/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppEqualOperandsInBinaryExpression/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppEvaluationFailure/@EntryIndexedValue" value="ERROR" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppExplicitSpecializationInNonNamespaceScope/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppExpressionWithoutSideEffects/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppFinalFunctionInFinalClass/@EntryIndexedValue" value="SUGGESTION" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppFinalNonOverridingVirtualFunction/@EntryIndexedValue" value="SUGGESTION" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppForLoopCanBeReplacedWithWhile/@EntryIndexedValue" value="SUGGESTION" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppForwardEnumDeclarationWithoutUnderlyingType/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppFunctionDoesntReturnValue/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppFunctionIsNotImplemented/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppFunctionResultShouldBeUsed/@EntryIndexedValue" value="HINT" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppFunctionalStyleCast/@EntryIndexedValue" value="SUGGESTION" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppHeaderHasBeenAlreadyIncluded/@EntryIndexedValue" value="HINT" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppHiddenFunction/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppHidingFunction/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppIdenticalOperandsInBinaryExpression/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppIfCanBeReplacedByConstexprIf/@EntryIndexedValue" value="SUGGESTION" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppImplicitDefaultConstructorNotAvailable/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppIncompatiblePointerConversion/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppIncompleteSwitchStatement/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppInconsistentNaming/@EntryIndexedValue" value="DO_NOT_SHOW" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppIntegralToPointerConversion/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppInvalidLineContinuation/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppJoinDeclarationAndAssignment/@EntryIndexedValue" value="SUGGESTION" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppLambdaCaptureNeverUsed/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppLocalVariableMayBeConst/@EntryIndexedValue" value="HINT" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppLocalVariableMightNotBeInitialized/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppLocalVariableWithNonTrivialDtorIsNeverUsed/@EntryIndexedValue" value="DO_NOT_SHOW" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppLongFloat/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppMemberFunctionMayBeConst/@EntryIndexedValue" value="SUGGESTION" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppMemberFunctionMayBeStatic/@EntryIndexedValue" value="SUGGESTION" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppMemberInitializersOrder/@EntryIndexedValue" value="SUGGESTION" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppMismatchedClassTags/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppMissingIncludeGuard/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppMissingKeywordThrow/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppModulePartitionWithSeveralPartitionUnits/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppMsExtAddressOfClassRValue/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppMsExtBindingRValueToLvalueReference/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppMsExtCopyElisionInCopyInitDeclarator/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppMsExtDoubleUserConversionInCopyInit/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppMsExtNotInitializedStaticConstLocalVar/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppMsExtReinterpretCastFromNullptr/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppMultiCharacterLiteral/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppMultiCharacterWideLiteral/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppMustBePublicVirtualToImplementInterface/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppMutableSpecifierOnReferenceMember/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppNoDiscardExpression/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppNodiscardFunctionWithoutReturnValue/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppNonExceptionSafeResourceAcquisition/@EntryIndexedValue" value="HINT" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppNonExplicitConversionOperator/@EntryIndexedValue" value="HINT" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppNonExplicitConvertingConstructor/@EntryIndexedValue" value="HINT" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppNonInlineFunctionDefinitionInHeaderFile/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppNonInlineVariableDefinitionInHeaderFile/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppNotAllPathsReturnValue/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppObjectMemberMightNotBeInitialized/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppOutParameterMustBeWritten/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppParameterMayBeConst/@EntryIndexedValue" value="HINT" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppParameterMayBeConstPtrOrRef/@EntryIndexedValue" value="SUGGESTION" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppParameterNamesMismatch/@EntryIndexedValue" value="HINT" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppParameterNeverUsed/@EntryIndexedValue" value="HINT" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppPassValueParameterByConstReference/@EntryIndexedValue" value="SUGGESTION" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppPointerConversionDropsQualifiers/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppPointerToIntegralConversion/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppPolymorphicClassWithNonVirtualPublicDestructor/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppPossiblyErroneousEmptyStatements/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppPossiblyUninitializedMember/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppPossiblyUnintendedObjectSlicing/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppPrecompiledHeaderIsNotIncluded/@EntryIndexedValue" value="ERROR" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppPrecompiledHeaderNotFound/@EntryIndexedValue" value="ERROR" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppPrintfBadFormat/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppPrintfExtraArg/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppPrintfMissedArg/@EntryIndexedValue" value="ERROR" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppPrintfRiskyFormat/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppPrivateSpecialMemberFunctionIsNotImplemented/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppRangeBasedForIncompatibleReference/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppRedefinitionOfDefaultArgumentInOverrideFunction/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppRedundantAccessSpecifier/@EntryIndexedValue" value="HINT" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppRedundantBaseClassAccessSpecifier/@EntryIndexedValue" value="HINT" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppRedundantBaseClassInitializer/@EntryIndexedValue" value="SUGGESTION" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppRedundantBooleanExpressionArgument/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppRedundantCastExpression/@EntryIndexedValue" value="HINT" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppRedundantComplexityInComparison/@EntryIndexedValue" value="SUGGESTION" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppRedundantConditionalExpression/@EntryIndexedValue" value="SUGGESTION" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppRedundantConstSpecifier/@EntryIndexedValue" value="HINT" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppRedundantControlFlowJump/@EntryIndexedValue" value="HINT" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppRedundantDereferencingAndTakingAddress/@EntryIndexedValue" value="SUGGESTION" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppRedundantElaboratedTypeSpecifier/@EntryIndexedValue" value="HINT" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppRedundantElseKeyword/@EntryIndexedValue" value="HINT" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppRedundantElseKeywordInsideCompoundStatement/@EntryIndexedValue" value="HINT" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppRedundantEmptyDeclaration/@EntryIndexedValue" value="HINT" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppRedundantEmptyStatement/@EntryIndexedValue" value="HINT" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppRedundantExportKeyword/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppRedundantFwdClassOrEnumSpecifier/@EntryIndexedValue" value="SUGGESTION" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppRedundantInlineSpecifier/@EntryIndexedValue" value="HINT" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppRedundantLambdaParameterList/@EntryIndexedValue" value="HINT" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppRedundantMemberInitializer/@EntryIndexedValue" value="SUGGESTION" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppRedundantNamespaceDefinition/@EntryIndexedValue" value="SUGGESTION" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppRedundantParentheses/@EntryIndexedValue" value="HINT" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppRedundantQualifier/@EntryIndexedValue" value="HINT" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppRedundantQualifierADL/@EntryIndexedValue" value="DO_NOT_SHOW" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppRedundantStaticSpecifierOnMemberAllocationFunction/@EntryIndexedValue" value="HINT" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppRedundantStaticSpecifierOnThreadLocalLocalVariable/@EntryIndexedValue" value="HINT" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppRedundantTemplateArguments/@EntryIndexedValue" value="HINT" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppRedundantTemplateKeyword/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppRedundantTypenameKeyword/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppRedundantVoidArgumentList/@EntryIndexedValue" value="SUGGESTION" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppRedundantZeroInitializerInAggregateInitialization/@EntryIndexedValue" value="SUGGESTION" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppReinterpretCastFromVoidPtr/@EntryIndexedValue" value="SUGGESTION" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppRemoveRedundantBraces/@EntryIndexedValue" value="DO_NOT_SHOW" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppReplaceMemsetWithZeroInitialization/@EntryIndexedValue" value="SUGGESTION" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppReplaceTieWithStructuredBinding/@EntryIndexedValue" value="SUGGESTION" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppReturnNoValueInNonVoidFunction/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppSmartPointerVsMakeFunction/@EntryIndexedValue" value="SUGGESTION" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppSomeObjectMembersMightNotBeInitialized/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppSpecialFunctionWithoutNoexceptSpecification/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppStaticAssertFailure/@EntryIndexedValue" value="ERROR" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppStaticDataMemberInUnnamedStruct/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppStaticSpecifierOnAnonymousNamespaceMember/@EntryIndexedValue" value="SUGGESTION" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppStringLiteralToCharPointerConversion/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppTabsAreDisallowed/@EntryIndexedValue" value="DO_NOT_SHOW" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppTemplateArgumentsCanBeDeduced/@EntryIndexedValue" value="HINT" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppTemplateParameterNeverUsed/@EntryIndexedValue" value="HINT" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppTemplateParameterShadowing/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppThrowExpressionCanBeReplacedWithRethrow/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppTooWideScope/@EntryIndexedValue" value="SUGGESTION" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppTooWideScopeInitStatement/@EntryIndexedValue" value="HINT" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppTypeAliasNeverUsed/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppUninitializedDependentBaseClass/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppUninitializedNonStaticDataMember/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppUnionMemberOfReferenceType/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppUnmatchedPragmaEndRegionDirective/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppUnmatchedPragmaRegionDirective/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppUnnamedNamespaceInHeaderFile/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppUnnecessaryWhitespace/@EntryIndexedValue" value="DO_NOT_SHOW" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppUnsignedZeroComparison/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppUnusedIncludeDirective/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppUseAlgorithmWithCount/@EntryIndexedValue" value="SUGGESTION" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppUseAssociativeContains/@EntryIndexedValue" value="SUGGESTION" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppUseAuto/@EntryIndexedValue" value="HINT" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppUseAutoForNumeric/@EntryIndexedValue" value="HINT" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppUseElementsView/@EntryIndexedValue" value="SUGGESTION" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppUseEraseAlgorithm/@EntryIndexedValue" value="SUGGESTION" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppUseFamiliarTemplateSyntaxForGenericLambdas/@EntryIndexedValue" value="SUGGESTION" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppUseRangeAlgorithm/@EntryIndexedValue" value="SUGGESTION" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppUseStdSize/@EntryIndexedValue" value="SUGGESTION" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppUseStructuredBinding/@EntryIndexedValue" value="HINT" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppUseTypeTraitAlias/@EntryIndexedValue" value="SUGGESTION" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppUserDefinedLiteralSuffixDoesNotStartWithUnderscore/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppUsingResultOfAssignmentAsCondition/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppVariableCanBeMadeConstexpr/@EntryIndexedValue" value="SUGGESTION" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppVirtualFunctionCallInsideCtor/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppVirtualFunctionInFinalClass/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppVolatileParameterInDeclaration/@EntryIndexedValue" value="SUGGESTION" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppWarningDirective/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppWrongIncludesOrder/@EntryIndexedValue" value="HINT" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppWrongSlashesInIncludeDirective/@EntryIndexedValue" value="HINT" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppZeroConstantCanBeReplacedWithNullptr/@EntryIndexedValue" value="SUGGESTION" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=CppZeroValuedExpressionUsedAsNullPointer/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=IdentifierTypo/@EntryIndexedValue" value="DO_NOT_SHOW" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=IfStdIsConstantEvaluatedCanBeReplaced/@EntryIndexedValue" value="SUGGESTION" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=StdIsConstantEvaluatedWillAlwaysEvaluateToConstant/@EntryIndexedValue" value="WARNING" type="string" />
<option name="/Default/CodeInspection/Highlighting/InspectionSeverities/=StringLiteralTypo/@EntryIndexedValue" value="DO_NOT_SHOW" type="string" />
<option name="/Default/CodeStyle/CodeFormatting/CppClangFormat/EnableClangFormatSupport/@EntryValue" value="false" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/ALIGN_MULTILINE_ARGUMENT/@EntryValue" value="true" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/ALIGN_MULTILINE_BINARY_EXPRESSIONS_CHAIN/@EntryValue" value="true" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/ALIGN_MULTILINE_CALLS_CHAIN/@EntryValue" value="false" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/ALIGN_MULTILINE_EXPRESSION/@EntryValue" value="false" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/ALIGN_MULTILINE_EXTENDS_LIST/@EntryValue" value="true" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/ALIGN_MULTILINE_FOR_STMT/@EntryValue" value="true" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/ALIGN_MULTILINE_PARAMETER/@EntryValue" value="true" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/ALIGN_MULTILINE_TYPE_ARGUMENT/@EntryValue" value="false" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/ALIGN_MULTILINE_TYPE_PARAMETER/@EntryValue" value="false" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/ALIGN_MULTIPLE_DECLARATION/@EntryValue" value="false" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/ALIGN_TERNARY/@EntryValue" value="ALIGN_ALL" type="string" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/ANONYMOUS_METHOD_DECLARATION_BRACES/@EntryValue" value="END_OF_LINE" type="string" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/BLANK_LINES_AROUND_CLASS_DEFINITION/@EntryValue" value="1" type="int" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/BLANK_LINES_AROUND_DECLARATIONS/@EntryValue" value="0" type="int" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/BLANK_LINES_AROUND_FUNCTION_DECLARATION/@EntryValue" value="1" type="int" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/BLANK_LINES_AROUND_FUNCTION_DEFINITION/@EntryValue" value="1" type="int" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/BREAK_TEMPLATE_DECLARATION/@EntryValue" value="LINE_BREAK" type="string" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/CASE_BLOCK_BRACES/@EntryValue" value="END_OF_LINE" type="string" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/CONTINUOUS_LINE_INDENT/@EntryValue" value="Double" type="string" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/INDENT_ACCESS_SPECIFIERS_FROM_CLASS/@EntryValue" value="false" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/INDENT_CASE_FROM_SWITCH/@EntryValue" value="true" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/INDENT_CLASS_MEMBERS_FROM_ACCESS_SPECIFIERS/@EntryValue" value="true" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/INDENT_COMMENT/@EntryValue" value="true" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/INDENT_SIZE/@EntryValue" value="4" type="int" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/INDENT_STYLE/@EntryValue" value="Space" type="string" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/INITIALIZER_BRACES/@EntryValue" value="END_OF_LINE_NO_SPACE" type="string" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/INT_ALIGN_EQ/@EntryValue" value="false" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/INVOCABLE_DECLARATION_BRACES/@EntryValue" value="END_OF_LINE" type="string" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/KEEP_BLANK_LINES_IN_CODE/@EntryValue" value="2" type="int" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/KEEP_BLANK_LINES_IN_DECLARATIONS/@EntryValue" value="2" type="int" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/KEEP_USER_LINEBREAKS/@EntryValue" value="true" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/LINE_BREAK_AFTER_COLON_IN_MEMBER_INITIALIZER_LISTS/@EntryValue" value="ON_SINGLE_LINE" type="string" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/MEMBER_INITIALIZER_LIST_STYLE/@EntryValue" value="DO_NOT_CHANGE" type="string" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/NAMESPACE_DECLARATION_BRACES/@EntryValue" value="END_OF_LINE" type="string" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/NAMESPACE_INDENTATION/@EntryValue" value="All" type="string" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/OTHER_BRACES/@EntryValue" value="END_OF_LINE" type="string" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/PLACE_CATCH_ON_NEW_LINE/@EntryValue" value="false" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/PLACE_ELSE_ON_NEW_LINE/@EntryValue" value="false" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/PLACE_NAMESPACE_DEFINITIONS_ON_SAME_LINE/@EntryValue" value="false" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/PLACE_WHILE_ON_NEW_LINE/@EntryValue" value="false" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SIMPLE_BLOCK_STYLE/@EntryValue" value="DO_NOT_CHANGE" type="string" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_AFTER_CAST_EXPRESSION_PARENTHESES/@EntryValue" value="true" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_AFTER_COLON_IN_BITFIELD_DECLARATOR/@EntryValue" value="true" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_AFTER_COMMA_IN_TEMPLATE_ARGS/@EntryValue" value="true" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_AFTER_COMMA_IN_TEMPLATE_PARAMS/@EntryValue" value="true" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_AFTER_EXTENDS_COLON/@EntryValue" value="true" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_AFTER_FOR_COLON/@EntryValue" value="true" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_AFTER_FOR_SEMICOLON/@EntryValue" value="true" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_AFTER_PTR_IN_DATA_MEMBER/@EntryValue" value="false" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_AFTER_PTR_IN_DATA_MEMBERS/@EntryValue" value="false" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_AFTER_PTR_IN_METHOD/@EntryValue" value="false" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_AFTER_PTR_IN_NESTED_DECLARATOR/@EntryValue" value="false" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_AFTER_REF_IN_DATA_MEMBER/@EntryValue" value="false" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_AFTER_REF_IN_DATA_MEMBERS/@EntryValue" value="false" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_AFTER_REF_IN_METHOD/@EntryValue" value="false" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_AFTER_UNARY_OPERATOR/@EntryValue" value="false" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_BEFORE_COLON_IN_BITFIELD_DECLARATOR/@EntryValue" value="false" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_BEFORE_EXTENDS_COLON/@EntryValue" value="true" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_BEFORE_FOR_COLON/@EntryValue" value="false" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_BEFORE_FOR_SEMICOLON/@EntryValue" value="false" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_BEFORE_PTR_IN_ABSTRACT_DECL/@EntryValue" value="true" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_BEFORE_PTR_IN_DATA_MEMBER/@EntryValue" value="true" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_BEFORE_PTR_IN_DATA_MEMBERS/@EntryValue" value="true" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_BEFORE_PTR_IN_METHOD/@EntryValue" value="true" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_BEFORE_REF_IN_ABSTRACT_DECL/@EntryValue" value="true" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_BEFORE_REF_IN_DATA_MEMBER/@EntryValue" value="true" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_BEFORE_REF_IN_DATA_MEMBERS/@EntryValue" value="true" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_BEFORE_REF_IN_METHOD/@EntryValue" value="true" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_BEFORE_TEMPLATE_ARGS/@EntryValue" value="false" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_BEFORE_TEMPLATE_PARAMS/@EntryValue" value="false" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_BETWEEN_CLOSING_ANGLE_BRACKETS_IN_TEMPLATE_ARGS/@EntryValue" value="true" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_WITHIN_ARRAY_ACCESS_BRACKETS/@EntryValue" value="false" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_WITHIN_CAST_EXPRESSION_PARENTHESES/@EntryValue" value="false" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_WITHIN_DECLARATION_PARENTHESES/@EntryValue" value="false" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_WITHIN_EMPTY_BLOCKS/@EntryValue" value="false" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_WITHIN_EMPTY_INITIALIZER_BRACES/@EntryValue" value="false" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_WITHIN_EMPTY_METHOD_PARENTHESES/@EntryValue" value="false" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_WITHIN_EMPTY_TEMPLATE_PARAMS/@EntryValue" value="false" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_WITHIN_INITIALIZER_BRACES/@EntryValue" value="false" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_WITHIN_TEMPLATE_ARGS/@EntryValue" value="false" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPACE_WITHIN_TEMPLATE_PARAMS/@EntryValue" value="false" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/SPECIAL_ELSE_IF_TREATMENT/@EntryValue" value="true" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/TAB_WIDTH/@EntryValue" value="4" type="int" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/TYPE_DECLARATION_BRACES/@EntryValue" value="END_OF_LINE" type="string" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/WRAP_AFTER_BINARY_OPSIGN/@EntryValue" value="true" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/WRAP_AFTER_DECLARATION_LPAR/@EntryValue" value="false" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/WRAP_AFTER_INVOCATION_LPAR/@EntryValue" value="false" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/WRAP_ARGUMENTS_STYLE/@EntryValue" value="WRAP_IF_LONG" type="string" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/WRAP_BEFORE_DECLARATION_LPAR/@EntryValue" value="false" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/WRAP_BEFORE_DECLARATION_RPAR/@EntryValue" value="false" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/WRAP_BEFORE_INVOCATION_LPAR/@EntryValue" value="false" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/WRAP_BEFORE_INVOCATION_RPAR/@EntryValue" value="false" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/WRAP_BEFORE_TERNARY_OPSIGNS/@EntryValue" value="true" type="bool" />
<option name="/Default/CodeStyle/CodeFormatting/CppFormatting/WRAP_PARAMETERS_STYLE/@EntryValue" value="WRAP_IF_LONG" type="string" />
<option name="/Default/CodeStyle/EditorConfig/EnableClangFormatSupport/@EntryValue" value="false" type="bool" />
</component>
</project>

365
.idea/workspace.xml generated
View File

@@ -1,365 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="AutoImportSettings">
<option name="autoReloadType" value="SELECTIVE" />
</component>
<component name="BackendCodeEditorMiscSettings">
<option name="/Default/Housekeeping/FeatureSuggestion/FeatureSuggestionManager/DisabledSuggesters/=SwitchToGoToActionSuggester/@EntryIndexedValue" value="true" type="bool" />
</component>
<component name="CMakePresetLoader">{
&quot;useNewFormat&quot;: true
}</component>
<component name="CMakeProjectFlavorService">
<option name="flavorId" value="CMakePlainProjectFlavor" />
</component>
<component name="CMakeReloadState">
<option name="reloaded" value="true" />
</component>
<component name="CMakeRunConfigurationManager">
<generated>
<config projectName="argument_parser" targetName="test" />
</generated>
</component>
<component name="CMakeSettings">
<configurations>
<configuration PROFILE_NAME="Debug" ENABLED="true" CONFIG_NAME="Debug" GENERATION_OPTIONS="-G Ninja -DCMAKE_BUILD_TYPE=Debug" />
<configuration PROFILE_NAME="Release" ENABLED="true" CONFIG_NAME="Release" GENERATION_OPTIONS="-G Ninja -DCMAKE_BUILD_TYPE=Release" />
</configurations>
</component>
<component name="ChangeListManager">
<list default="true" id="7ddada81-7b11-42b9-8fbe-5e1e6cce3b05" name="Changes" comment="">
<change beforePath="$PROJECT_DIR$/include/argparse" beforeDir="false" afterPath="$PROJECT_DIR$/include/argparse" afterDir="false" />
<change beforePath="$PROJECT_DIR$/include/parser/platform_headers/macos_parser.hpp" beforeDir="false" afterPath="$PROJECT_DIR$/include/parser/platform_headers/macos_parser.hpp" afterDir="false" />
</list>
<option name="SHOW_DIALOG" value="false" />
<option name="HIGHLIGHT_CONFLICTS" value="true" />
<option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
<option name="LAST_RESOLUTION" value="IGNORE" />
</component>
<component name="ClangdSettings">
<option name="formatViaClangd" value="false" />
</component>
<component name="ExecutionTargetManager" SELECTED_TARGET="CMakeBuildProfile:Debug" />
<component name="Git.Settings">
<option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$" />
<option name="RESET_MODE" value="HARD" />
</component>
<component name="HighlightingSettingsPerFile">
<setting file="mock:///Users/killua/Projects/argument-parser/include/argparse" root0="SKIP_HIGHLIGHTING" />
<setting file="mock:///Users/killua/Projects/argument-parser/include/parser/argument_parser.hpp" root0="SKIP_HIGHLIGHTING" />
<setting file="mock:///Users/killua/Projects/argument-parser/include/parser/platform_headers/macos_parser.hpp" root0="SKIP_HIGHLIGHTING" />
<setting file="mock:///Users/killua/Projects/argument-parser/src/main.cpp" root0="SKIP_HIGHLIGHTING" />
<setting file="mock:///dummy.cpp" root0="SKIP_HIGHLIGHTING" />
<setting file="mock:///dummy.cpp" root0="SKIP_HIGHLIGHTING" />
<setting file="mock:///dummy.cpp" root0="SKIP_HIGHLIGHTING" />
<setting file="mock:///dummy.cpp" root0="SKIP_HIGHLIGHTING" />
<setting file="mock:///dummy.cpp" root0="SKIP_HIGHLIGHTING" />
<setting file="mock:///dummy.cpp" root0="SKIP_HIGHLIGHTING" />
<setting file="mock:///dummy.cpp" root0="SKIP_HIGHLIGHTING" />
<setting file="mock:///dummy.cpp" root0="SKIP_HIGHLIGHTING" />
<setting file="mock:///dummy.cpp" root0="SKIP_HIGHLIGHTING" />
<setting file="mock:///dummy.cpp" root0="SKIP_HIGHLIGHTING" />
<setting file="mock:///dummy.cpp" root0="SKIP_HIGHLIGHTING" />
<setting file="mock:///dummy.cpp" root0="SKIP_HIGHLIGHTING" />
<setting file="mock:///dummy.cpp" root0="SKIP_HIGHLIGHTING" />
<setting file="mock:///dummy.cpp" root0="SKIP_HIGHLIGHTING" />
<setting file="mock:///dummy.cpp" root0="SKIP_HIGHLIGHTING" />
<setting file="mock:///dummy.cpp" root0="SKIP_HIGHLIGHTING" />
<setting file="mock:///dummy.cpp" root0="SKIP_HIGHLIGHTING" />
<setting file="mock:///dummy.cpp" root0="SKIP_HIGHLIGHTING" />
<setting file="mock:///dummy.cpp" root0="SKIP_HIGHLIGHTING" />
<setting file="mock:///dummy.cpp" root0="SKIP_HIGHLIGHTING" />
<setting file="mock:///dummy.cpp" root0="SKIP_HIGHLIGHTING" />
<setting file="mock:///dummy.cpp" root0="SKIP_HIGHLIGHTING" />
<setting file="mock:///dummy.cpp" root0="SKIP_HIGHLIGHTING" />
<setting file="mock:///dummy.cpp" root0="SKIP_HIGHLIGHTING" />
<setting file="mock:///dummy.cpp" root0="SKIP_HIGHLIGHTING" />
<setting file="mock:///dummy.cpp" root0="SKIP_HIGHLIGHTING" />
<setting file="mock:///dummy.cpp" root0="SKIP_HIGHLIGHTING" />
<setting file="mock:///dummy.cpp" root0="SKIP_HIGHLIGHTING" />
<setting file="mock:///dummy.cpp" root0="SKIP_HIGHLIGHTING" />
<setting file="mock:///dummy.cpp" root0="SKIP_HIGHLIGHTING" />
<setting file="mock:///dummy.cpp" root0="SKIP_HIGHLIGHTING" />
<setting file="mock:///dummy.cpp" root0="SKIP_HIGHLIGHTING" />
<setting file="mock:///dummy.cpp" root0="SKIP_HIGHLIGHTING" />
<setting file="mock:///dummy.cpp" root0="SKIP_HIGHLIGHTING" />
<setting file="mock:///dummy.cpp" root0="SKIP_HIGHLIGHTING" />
<setting file="mock:///dummy.cpp" root0="SKIP_HIGHLIGHTING" />
<setting file="mock:///dummy.cpp" root0="SKIP_HIGHLIGHTING" />
<setting file="mock:///dummy.cpp" root0="SKIP_HIGHLIGHTING" />
<setting file="mock:///dummy.cpp" root0="SKIP_HIGHLIGHTING" />
<setting file="mock:///dummy.cpp" root0="SKIP_HIGHLIGHTING" />
<setting file="mock:///dummy.cpp" root0="SKIP_HIGHLIGHTING" />
<setting file="mock:///dummy.cpp" root0="SKIP_HIGHLIGHTING" />
<setting file="mock:///dummy.cpp" root0="SKIP_HIGHLIGHTING" />
<setting file="mock:///dummy.cpp" root0="SKIP_HIGHLIGHTING" />
<setting file="mock:///dummy.cpp" root0="SKIP_HIGHLIGHTING" />
<setting file="mock:///dummy.cpp" root0="SKIP_HIGHLIGHTING" />
<setting file="mock:///dummy.cpp" root0="SKIP_HIGHLIGHTING" />
<setting file="mock:///dummy.cpp" root0="SKIP_HIGHLIGHTING" />
<setting file="mock:///dummy.cpp" root0="SKIP_HIGHLIGHTING" />
<setting file="mock:///dummy.cpp" root0="SKIP_HIGHLIGHTING" />
<setting file="mock:///dummy.cpp" root0="SKIP_HIGHLIGHTING" />
<setting file="mock:///dummy.cpp" root0="SKIP_HIGHLIGHTING" />
<setting file="mock:///dummy.cpp" root0="SKIP_HIGHLIGHTING" />
<setting file="mock:///dummy.cpp" root0="SKIP_HIGHLIGHTING" />
<setting file="mock:///dummy.cpp" root0="SKIP_HIGHLIGHTING" />
<setting file="mock:///dummy.cpp" root0="SKIP_HIGHLIGHTING" />
<setting file="mock:///dummy.cpp" root0="SKIP_HIGHLIGHTING" />
<setting file="mock:///dummy.cpp" root0="SKIP_HIGHLIGHTING" />
<setting file="mock:///dummy.cpp" root0="SKIP_HIGHLIGHTING" />
<setting file="mock:///dummy.cpp" root0="SKIP_HIGHLIGHTING" />
<setting file="mock:///dummy.cpp" root0="SKIP_HIGHLIGHTING" />
<setting file="mock:///dummy.cpp" root0="SKIP_HIGHLIGHTING" />
<setting file="mock:///dummy.cpp" root0="SKIP_HIGHLIGHTING" />
<setting file="mock:///dummy.cpp" root0="SKIP_HIGHLIGHTING" />
<setting file="mock:///dummy.cpp" root0="SKIP_HIGHLIGHTING" />
<setting file="mock:///dummy.cpp" root0="SKIP_HIGHLIGHTING" />
<setting file="mock:///dummy.cpp" root0="SKIP_HIGHLIGHTING" />
<setting file="mock:///dummy.cpp" root0="SKIP_HIGHLIGHTING" />
<setting file="mock:///dummy.cpp" root0="SKIP_HIGHLIGHTING" />
<setting file="mock:///dummy.cpp" root0="SKIP_HIGHLIGHTING" />
<setting file="mock:///dummy.cpp" root0="SKIP_HIGHLIGHTING" />
<setting file="mock:///dummy.cpp" root0="SKIP_HIGHLIGHTING" />
<setting file="mock:///dummy.cpp" root0="SKIP_HIGHLIGHTING" />
<setting file="mock:///dummy.cpp" root0="SKIP_HIGHLIGHTING" />
<setting file="mock:///dummy.cpp" root0="SKIP_HIGHLIGHTING" />
<setting file="mock:///dummy.cpp" root0="SKIP_HIGHLIGHTING" />
<setting file="mock:///dummy.cpp" root0="SKIP_HIGHLIGHTING" />
<setting file="mock:///dummy.cpp" root0="SKIP_HIGHLIGHTING" />
<setting file="mock:///dummy.cpp" root0="SKIP_HIGHLIGHTING" />
<setting file="mock:///dummy.cpp" root0="SKIP_HIGHLIGHTING" />
<setting file="mock:///dummy.cpp" root0="SKIP_HIGHLIGHTING" />
<setting file="mock:///dummy.cpp" root0="SKIP_HIGHLIGHTING" />
<setting file="mock:///dummy.cpp" root0="SKIP_HIGHLIGHTING" />
<setting file="mock:///dummy.cpp" root0="SKIP_HIGHLIGHTING" />
<setting file="mock:///dummy.cpp" root0="SKIP_HIGHLIGHTING" />
<setting file="mock:///dummy.cpp" root0="SKIP_HIGHLIGHTING" />
<setting file="mock:///dummy.cpp" root0="SKIP_HIGHLIGHTING" />
<setting file="mock:///dummy.cpp" root0="SKIP_HIGHLIGHTING" />
<setting file="mock:///dummy.cpp" root0="SKIP_HIGHLIGHTING" />
<setting file="mock:///dummy.cpp" root0="SKIP_HIGHLIGHTING" />
<setting file="mock:///dummy.cpp" root0="SKIP_HIGHLIGHTING" />
<setting file="mock:///dummy.cpp" root0="SKIP_HIGHLIGHTING" />
<setting file="mock:///dummy.cpp" root0="SKIP_HIGHLIGHTING" />
<setting file="mock:///dummy.cpp" root0="SKIP_HIGHLIGHTING" />
<setting file="mock:///dummy.cpp" root0="SKIP_HIGHLIGHTING" />
<setting file="mock:///dummy.cpp" root0="SKIP_HIGHLIGHTING" />
<setting file="mock:///dummy.cpp" root0="SKIP_HIGHLIGHTING" />
<setting file="mock:///dummy.cpp" root0="SKIP_HIGHLIGHTING" />
<setting file="mock:///dummy.cpp" root0="SKIP_HIGHLIGHTING" />
<setting file="mock:///dummy.cpp" root0="SKIP_HIGHLIGHTING" />
<setting file="mock:///dummy.cpp" root0="SKIP_HIGHLIGHTING" />
<setting file="mock:///dummy.cpp" root0="SKIP_HIGHLIGHTING" />
<setting file="mock:///dummy.cpp" root0="SKIP_HIGHLIGHTING" />
<setting file="mock:///dummy.cpp" root0="SKIP_HIGHLIGHTING" />
<setting file="mock:///dummy.cpp" root0="SKIP_HIGHLIGHTING" />
<setting file="mock:///dummy.cpp" root0="SKIP_HIGHLIGHTING" />
<setting file="mock:///dummy.cpp" root0="SKIP_HIGHLIGHTING" />
<setting file="mock:///dummy.cpp" root0="SKIP_HIGHLIGHTING" />
<setting file="mock:///dummy.cpp" root0="SKIP_HIGHLIGHTING" />
<setting file="mock:///dummy.cpp" root0="SKIP_HIGHLIGHTING" />
<setting file="mock:///dummy.cpp" root0="SKIP_HIGHLIGHTING" />
<setting file="mock:///dummy.cpp" root0="SKIP_HIGHLIGHTING" />
<setting file="mock:///dummy.cpp" root0="SKIP_HIGHLIGHTING" />
<setting file="mock:///dummy.cpp" root0="SKIP_HIGHLIGHTING" />
<setting file="mock:///dummy.cpp" root0="SKIP_HIGHLIGHTING" />
<setting file="mock:///dummy.cpp" root0="SKIP_HIGHLIGHTING" />
<setting file="mock:///dummy.cpp" root0="SKIP_HIGHLIGHTING" />
<setting file="mock:///dummy.cpp" root0="SKIP_HIGHLIGHTING" />
<setting file="mock:///dummy.cpp" root0="SKIP_HIGHLIGHTING" />
<setting file="mock:///dummy.cpp" root0="SKIP_HIGHLIGHTING" />
<setting file="mock:///dummy.cpp" root0="SKIP_HIGHLIGHTING" />
<setting file="mock:///dummy.cpp" root0="SKIP_HIGHLIGHTING" />
<setting file="mock:///dummy.cpp" root0="SKIP_HIGHLIGHTING" />
<setting file="mock:///dummy.cpp" root0="SKIP_HIGHLIGHTING" />
<setting file="mock:///dummy.cpp" root0="SKIP_HIGHLIGHTING" />
<setting file="mock:///dummy.cpp" root0="SKIP_HIGHLIGHTING" />
<setting file="mock:///dummy.cpp" root0="SKIP_HIGHLIGHTING" />
<setting file="mock:///dummy.cpp" root0="SKIP_HIGHLIGHTING" />
<setting file="mock:///dummy.cpp" root0="SKIP_HIGHLIGHTING" />
<setting file="mock:///dummy.cpp" root0="SKIP_HIGHLIGHTING" />
<setting file="mock:///dummy.cpp" root0="SKIP_HIGHLIGHTING" />
<setting file="mock:///dummy.cpp" root0="SKIP_HIGHLIGHTING" />
<setting file="mock:///dummy.cpp" root0="SKIP_HIGHLIGHTING" />
<setting file="mock:///dummy.cpp" root0="SKIP_HIGHLIGHTING" />
<setting file="mock:///dummy.cpp" root0="SKIP_HIGHLIGHTING" />
<setting file="mock:///dummy.cpp" root0="SKIP_HIGHLIGHTING" />
<setting file="mock:///dummy.cpp" root0="SKIP_HIGHLIGHTING" />
<setting file="mock:///dummy.cpp" root0="SKIP_HIGHLIGHTING" />
<setting file="mock:///dummy.cpp" root0="SKIP_HIGHLIGHTING" />
<setting file="mock:///dummy.cpp" root0="SKIP_HIGHLIGHTING" />
<setting file="mock:///dummy.cpp" root0="SKIP_HIGHLIGHTING" />
<setting file="mock:///dummy.cpp" root0="SKIP_HIGHLIGHTING" />
<setting file="mock:///dummy.cpp" root0="SKIP_HIGHLIGHTING" />
<setting file="mock:///dummy.cpp" root0="SKIP_HIGHLIGHTING" />
<setting file="mock:///dummy.cpp" root0="SKIP_HIGHLIGHTING" />
<setting file="mock:///dummy.cpp" root0="SKIP_HIGHLIGHTING" />
<setting file="mock:///dummy.cpp" root0="SKIP_HIGHLIGHTING" />
<setting file="mock:///dummy.cpp" root0="SKIP_HIGHLIGHTING" />
<setting file="mock:///dummy.cpp" root0="SKIP_HIGHLIGHTING" />
<setting file="mock:///dummy.cpp" root0="SKIP_HIGHLIGHTING" />
<setting file="mock:///dummy.cpp" root0="SKIP_HIGHLIGHTING" />
<setting file="mock:///dummy.cpp" root0="SKIP_HIGHLIGHTING" />
<setting file="mock:///dummy.cpp" root0="SKIP_HIGHLIGHTING" />
<setting file="mock:///dummy.cpp" root0="SKIP_HIGHLIGHTING" />
<setting file="mock:///dummy.cpp" root0="SKIP_HIGHLIGHTING" />
<setting file="mock:///dummy.cpp" root0="SKIP_HIGHLIGHTING" />
<setting file="mock:///dummy.cpp" root0="SKIP_HIGHLIGHTING" />
<setting file="mock:///dummy.cpp" root0="SKIP_HIGHLIGHTING" />
<setting file="mock:///dummy.cpp" root0="SKIP_HIGHLIGHTING" />
<setting file="mock:///dummy.cpp" root0="SKIP_HIGHLIGHTING" />
<setting file="mock:///dummy.cpp" root0="SKIP_HIGHLIGHTING" />
<setting file="mock:///dummy.cpp" root0="SKIP_HIGHLIGHTING" />
<setting file="mock:///dummy.cpp" root0="SKIP_HIGHLIGHTING" />
<setting file="mock:///dummy.cpp" root0="SKIP_HIGHLIGHTING" />
<setting file="mock:///dummy.cpp" root0="SKIP_HIGHLIGHTING" />
<setting file="mock:///dummy.cpp" root0="SKIP_HIGHLIGHTING" />
<setting file="mock:///dummy.cpp" root0="SKIP_HIGHLIGHTING" />
<setting file="mock:///dummy.cpp" root0="SKIP_HIGHLIGHTING" />
<setting file="mock:///dummy.cpp" root0="SKIP_HIGHLIGHTING" />
<setting file="mock:///dummy.cpp" root0="SKIP_HIGHLIGHTING" />
<setting file="mock:///dummy.cpp" root0="SKIP_HIGHLIGHTING" />
<setting file="mock:///dummy.cpp" root0="SKIP_HIGHLIGHTING" />
<setting file="mock:///dummy.cpp" root0="SKIP_HIGHLIGHTING" />
<setting file="mock:///dummy.cpp" root0="SKIP_HIGHLIGHTING" />
<setting file="mock:///dummy.cpp" root0="SKIP_HIGHLIGHTING" />
<setting file="mock:///dummy.cpp" root0="SKIP_HIGHLIGHTING" />
<setting file="mock:///dummy.cpp" root0="SKIP_HIGHLIGHTING" />
<setting file="mock:///dummy.cpp" root0="SKIP_HIGHLIGHTING" />
<setting file="mock:///dummy.cpp" root0="SKIP_HIGHLIGHTING" />
<setting file="mock:///dummy.cpp" root0="SKIP_HIGHLIGHTING" />
<setting file="mock:///dummy.cpp" root0="SKIP_HIGHLIGHTING" />
<setting file="mock:///dummy.cpp" root0="SKIP_HIGHLIGHTING" />
<setting file="mock:///dummy.cpp" root0="SKIP_HIGHLIGHTING" />
<setting file="mock:///dummy.cpp" root0="SKIP_HIGHLIGHTING" />
<setting file="mock:///dummy.cpp" root0="SKIP_HIGHLIGHTING" />
<setting file="mock:///dummy.cpp" root0="SKIP_HIGHLIGHTING" />
<setting file="mock:///dummy.cpp" root0="SKIP_HIGHLIGHTING" />
<setting file="mock:///dummy.cpp" root0="SKIP_HIGHLIGHTING" />
<setting file="mock:///dummy.cpp" root0="SKIP_HIGHLIGHTING" />
<setting file="mock:///dummy.cpp" root0="SKIP_HIGHLIGHTING" />
<setting file="mock:///dummy.cpp" root0="SKIP_HIGHLIGHTING" />
<setting file="mock:///dummy.cpp" root0="SKIP_HIGHLIGHTING" />
<setting file="mock:///dummy.cpp" root0="SKIP_HIGHLIGHTING" />
<setting file="mock:///dummy.cpp" root0="SKIP_HIGHLIGHTING" />
<setting file="mock:///dummy.cpp" root0="SKIP_HIGHLIGHTING" />
<setting file="mock:///dummy.cpp" root0="SKIP_HIGHLIGHTING" />
<setting file="mock:///dummy.cpp" root0="SKIP_HIGHLIGHTING" />
<setting file="mock:///dummy.cpp" root0="SKIP_HIGHLIGHTING" />
<setting file="mock:///dummy.cpp" root0="SKIP_HIGHLIGHTING" />
<setting file="mock:///dummy.cpp" root0="SKIP_HIGHLIGHTING" />
<setting file="mock:///dummy.cpp" root0="SKIP_HIGHLIGHTING" />
<setting file="mock:///dummy.cpp" root0="SKIP_HIGHLIGHTING" />
<setting file="mock:///dummy.cpp" root0="SKIP_HIGHLIGHTING" />
</component>
<component name="ProjectApplicationVersion">
<option name="ide" value="CLion" />
<option name="majorVersion" value="2025" />
<option name="minorVersion" value="2.3" />
<option name="productBranch" value="Classic" />
</component>
<component name="ProjectColorInfo">{
&quot;associatedIndex&quot;: 5
}</component>
<component name="ProjectId" id="33gk58BoSkVT3osKJJ81IgwdTvK" />
<component name="ProjectViewState">
<option name="hideEmptyMiddlePackages" value="true" />
<option name="showLibraryContents" value="true" />
</component>
<component name="PropertiesComponent"><![CDATA[{
"keyToString": {
"ASKED_SHARE_PROJECT_CONFIGURATION_FILES": "true",
"CMake Application.test.executor": "Debug",
"ModuleVcsDetector.initialDetectionPerformed": "true",
"RunOnceActivity.RadMigrateCodeStyle": "true",
"RunOnceActivity.ShowReadmeOnStart": "true",
"RunOnceActivity.TerminalTabsStorage.copyFrom.TerminalArrangementManager.252": "true",
"RunOnceActivity.cidr.known.project.marker": "true",
"RunOnceActivity.git.unshallow": "true",
"RunOnceActivity.readMode.enableVisualFormatting": "true",
"RunOnceActivity.west.config.association.type.startup.service": "true",
"SHARE_PROJECT_CONFIGURATION_FILES": "true",
"cf.first.check.clang-format": "false",
"cidr.known.project.marker": "true",
"git-widget-placeholder": "main",
"last_opened_file_path": "/Users/killua/Projects/argument-parser",
"node.js.detected.package.eslint": "true",
"node.js.detected.package.tslint": "true",
"node.js.selected.package.eslint": "(autodetect)",
"node.js.selected.package.tslint": "(autodetect)",
"nodejs_package_manager_path": "npm",
"settings.editor.selected.configurable": "CMakeSettings",
"vue.rearranger.settings.migration": "true"
}
}]]></component>
<component name="RunManager">
<configuration default="true" type="CLionExternalRunConfiguration" factoryName="Application" REDIRECT_INPUT="false" ELEVATE="false" USE_EXTERNAL_CONSOLE="false" EMULATE_TERMINAL="false" PASS_PARENT_ENVS_2="true">
<method v="2">
<option name="CLION.EXTERNAL.BUILD" enabled="true" />
</method>
</configuration>
<configuration name="test" type="CMakeRunConfiguration" factoryName="Application" PROGRAM_PARAMS="-h " REDIRECT_INPUT="false" ELEVATE="false" USE_EXTERNAL_CONSOLE="false" EMULATE_TERMINAL="false" PASS_PARENT_ENVS_2="true" PROJECT_NAME="argument_parser" TARGET_NAME="test" CONFIG_NAME="Debug" RUN_TARGET_PROJECT_NAME="argument_parser" RUN_TARGET_NAME="test">
<method v="2">
<option name="com.jetbrains.cidr.execution.CidrBuildBeforeRunTaskProvider$BuildBeforeRunTask" enabled="true" />
</method>
</configuration>
</component>
<component name="TaskManager">
<task active="true" id="Default" summary="Default task">
<changelist id="7ddada81-7b11-42b9-8fbe-5e1e6cce3b05" name="Changes" comment="" />
<created>1759745916574</created>
<option name="number" value="Default" />
<option name="presentableId" value="Default" />
<updated>1759745916574</updated>
<workItem from="1759745917880" duration="2252000" />
<workItem from="1759749176613" duration="2694000" />
</task>
<servers />
</component>
<component name="TypeScriptGeneratedFilesManager">
<option name="version" value="3" />
</component>
<component name="VCPKGProject">
<isAutomaticCheckingOnLaunch value="false" />
<isAutomaticFoundErrors value="true" />
<isAutomaticReloadCMake value="true" />
</component>
<component name="Vcs.Log.Tabs.Properties">
<option name="TAB_STATES">
<map>
<entry key="MAIN">
<value>
<State />
</value>
</entry>
</map>
</option>
</component>
<component name="XDebuggerManager">
<breakpoint-manager>
<breakpoints>
<line-breakpoint enabled="true" type="com.jetbrains.cidr.execution.debugger.OCBreakpointType">
<url>file://$PROJECT_DIR$/include/parser/argument_parser.hpp</url>
<line>145</line>
<option name="timeStamp" value="4" />
</line-breakpoint>
<line-breakpoint enabled="true" type="com.jetbrains.cidr.execution.debugger.OCBreakpointType">
<url>file://$PROJECT_DIR$/include/parser/argument_parser.hpp</url>
<line>277</line>
<option name="timeStamp" value="6" />
</line-breakpoint>
<line-breakpoint enabled="true" type="com.jetbrains.cidr.execution.debugger.OCBreakpointType">
<url>file://$PROJECT_DIR$/include/parser/argument_parser.hpp</url>
<line>276</line>
<option name="timeStamp" value="7" />
</line-breakpoint>
<line-breakpoint enabled="true" type="com.jetbrains.cidr.execution.debugger.OCBreakpointType">
<url>file://$PROJECT_DIR$/include/parser/argument_parser.hpp</url>
<line>219</line>
<option name="timeStamp" value="9" />
</line-breakpoint>
</breakpoints>
</breakpoint-manager>
</component>
</project>

936
DOCS.MD Normal file
View File

@@ -0,0 +1,936 @@
# Argument Parser API Documentation
This document covers the public API exposed by:
- `src/headers/parser/argument_parser.hpp`: the original v1 parser surface.
- `src/headers/parser/parser_v2.hpp`: the v2 map/initializer-list facade.
- `src/headers/parser/argument_builder.hpp`: the staged fluent builder for v2.
The lower-level v1 API still powers the platform parsers and v2. New code will
usually be clearest with `argument_parser::v2` and
`argument_parser::builder::new_argument()`, but the v1 types remain public and
useful when you need direct control over actions, stored values, or positional
registration.
## Common Concepts
Argument names are registered without command-line prefixes. Use `"v"` and
`"verbose"`, not `"-v"` or `"--verbose"`. The active convention objects decide
which concrete syntaxes are accepted during parsing and how help text is
rendered.
Typed values are parsed through
`argument_parser::parsing_traits::parser_trait<T>::parse(std::string const&)`.
The built-in traits cover common scalar types such as `std::string`, `bool`,
`int`, `float`, and `double`; custom value types need a trait specialization.
Trait `format_hint` and `purpose_hint` members are used in help text and parse
error messages when available.
Stored values are type-erased internally. Retrieve them with the same type used
when the argument was registered.
## v1 API: `argument_parser.hpp`
The public v1 types live in the `argument_parser` namespace unless otherwise
noted.
### `action_base`
```cpp
class action_base {
public:
virtual ~action_base() = default;
[[nodiscard]] virtual bool expects_parameter() const = 0;
virtual void invoke() const = 0;
virtual void invoke_with_parameter(std::string const& param) const = 0;
[[nodiscard]] virtual std::pair<std::string, std::string> get_trait_hints() const = 0;
[[nodiscard]] virtual std::unique_ptr<action_base> clone() const = 0;
};
```
`action_base` is the polymorphic interface stored by registered arguments.
Application code normally uses `action_with_param<T>`, `action_no_param`, or
the overloaded `helpers::make_action(...)` factories instead of implementing
this interface directly.
- `expects_parameter()` reports whether the action consumes a value token.
- `invoke()` runs a no-value action. On `action_with_param<T>`, it throws
`std::runtime_error`.
- `invoke_with_parameter(param)` runs a value action. On
`action_no_param`, the parameter is ignored.
- `get_trait_hints()` returns `{format_hint, purpose_hint}` for typed actions
when the parser trait exposes those members.
- `clone()` returns a heap-allocated copy used by `argument`.
### `action_with_param<T>`
```cpp
template <typename T>
class action_with_param : public action_base {
public:
using parameter_type = T;
explicit action_with_param(std::function<void(T const&)> const& handler);
void invoke(T const& arg) const;
[[nodiscard]] bool expects_parameter() const override;
void invoke() const override;
void invoke_with_parameter(std::string const& param) const override;
[[nodiscard]] std::pair<std::string, std::string> get_trait_hints() const override;
[[nodiscard]] std::unique_ptr<action_base> clone() const override;
};
```
Adapts a `std::function<void(T const&)>` into a parser action. During
`invoke_with_parameter`, the raw string is converted with
`parser_trait<T>::parse(param)` and the parsed value is passed to the handler.
Parse failures are reported as `std::runtime_error` with trait-derived hints
when available.
### `action_no_param`
```cpp
class action_no_param : public action_base {
public:
explicit action_no_param(std::function<void()> const& handler);
void invoke() const override;
[[nodiscard]] bool expects_parameter() const override;
void invoke_with_parameter(std::string const& param) const override;
[[nodiscard]] std::pair<std::string, std::string> get_trait_hints() const override;
[[nodiscard]] std::unique_ptr<action_base> clone() const override;
};
```
Adapts a `std::function<void()>` into a flag-style action.
`expects_parameter()` returns `false`; `invoke_with_parameter(...)` ignores the
parameter and runs the no-value handler.
### `argument_parser::helpers`
```cpp
namespace argument_parser::helpers {
template <typename T>
action_with_param<T>
make_action(std::function<void(T const&)> const& function);
action_no_param
make_action(std::function<void()> const& function);
}
```
Factory helpers for creating action objects used by `base_parser` and v2.
### `parser_settings`
```cpp
struct parser_settings {
bool should_exit_on_error = true;
bool should_exit_on_missing_required = true;
bool should_exit_on_unknown_argument = true;
bool should_exit_on_help = true;
bool ignore_unknown_arguments = false;
bool ignore_errors = false;
};
constexpr static inline parser_settings no_exit{
false,
false,
false,
false
};
```
`parser_settings` configures how the parser reacts to help, parse errors,
unknown arguments, and missing required arguments. The default settings preserve
CLI-style behavior: help exits after printing, unknown arguments and parse
errors exit with status `1`, and missing required arguments print diagnostics
and help before exiting.
Use `argument_parser::no_exit` when embedding the parser in tests, tools, or
larger applications that should receive exceptions instead of process exits:
```cpp
argument_parser::v2::parser parser(argument_parser::no_exit);
```
For targeted behavior, pass an explicit settings object to the v2 platform
parser constructor:
```cpp
argument_parser::parser_settings settings;
settings.should_exit_on_help = false;
settings.should_exit_on_error = false;
settings.should_exit_on_unknown_argument = false;
settings.should_exit_on_missing_required = false;
settings.ignore_unknown_arguments = true;
argument_parser::v2::parser parser(settings);
```
The fields have these effects:
- `should_exit_on_help`: controls whether the automatically registered v2
`-h` / `--help` action calls `std::exit(0)` after printing help.
- `should_exit_on_error`: when `true`, non-unknown parse errors caught by
`handle_arguments(...)` call `std::exit(1)`; when `false`, the exception is
rethrown.
- `should_exit_on_unknown_argument`: when `true`, unknown arguments caught by
`handle_arguments(...)` call `std::exit(1)`; when `false`, the unknown
argument exception is rethrown.
- `should_exit_on_missing_required`: when `true`, missing required arguments
print diagnostics/help and call `std::exit(1)`; when `false`, they throw
`std::runtime_error`.
- `ignore_unknown_arguments`: when `true`, tokens that match no convention and
cannot be consumed as positionals are skipped.
- `ignore_errors`: reserved in the public settings struct in the current
worktree; no runtime branch currently reads it.
### `argument`
```cpp
class argument {
public:
argument();
template <typename ActionType>
argument(int id, std::string name, ActionType const& action);
argument(argument const& other);
argument& operator=(argument const& other);
argument(argument&& other) noexcept = default;
argument& operator=(argument&& other) noexcept = default;
[[nodiscard]] bool is_required() const;
[[nodiscard]] std::string get_name() const;
[[nodiscard]] bool is_invoked() const;
[[nodiscard]] bool expects_parameter() const;
[[nodiscard]] std::string get_help_text() const;
[[nodiscard]] bool is_positional() const;
[[nodiscard]] bool is_positional_accumulator() const;
[[nodiscard]] std::optional<int> get_position_index() const;
};
```
`argument` is the public descriptor returned by parser lookup operations. It is
copyable; copies clone their stored action.
- `is_required()` reports whether parsing must see this argument.
- `get_name()` returns the registered display name. Named options are stored as
`"<short>|<long>"`; positional arguments use their positional name.
- `is_invoked()` reports whether the action ran during `handle_arguments(...)`.
- `expects_parameter()` mirrors the stored action.
- `get_help_text()` returns the registration help text.
- `is_positional()` identifies positional arguments.
- `is_positional_accumulator()` is true for positional accumulators.
- `get_position_index()` returns the explicit requested position, or
`std::nullopt`.
### `base_parser`
```cpp
class base_parser {
public:
template <typename T>
void add_argument(
std::string const& short_arg,
std::string const& long_arg,
std::string const& help_text,
action_with_param<T> const& action,
bool required);
template <typename T>
void add_argument(
std::string const& short_arg,
std::string const& long_arg,
std::string const& help_text,
bool required);
void add_argument(
std::string const& short_arg,
std::string const& long_arg,
std::string const& help_text,
action_no_param const& action,
bool required);
void add_argument(
std::string const& short_arg,
std::string const& long_arg,
std::string const& help_text,
bool required);
template <typename T>
void add_positional_argument(
std::string const& name,
std::string const& help_text,
action_with_param<T> const& action,
bool required,
std::optional<int> position = std::nullopt);
template <typename T>
void add_positional_argument(
std::string const& name,
std::string const& help_text,
bool required,
std::optional<int> position = std::nullopt);
template <typename T>
void add_positional_accumulator(
std::string const& name,
std::string const& help_text,
action_with_param<T> const& action,
bool required,
std::optional<int> position = std::nullopt);
void on_complete(std::function<void(base_parser const&)> const& action);
template <typename T>
std::optional<T> get_optional(std::string const& arg) const;
[[nodiscard]] std::string build_help_text(
std::initializer_list<conventions::convention const* const> convention_types) const;
argument& get_argument(conventions::parsed_argument const& arg);
[[nodiscard]] std::optional<int> find_argument_id(std::string const& arg) const;
void handle_arguments(
std::initializer_list<conventions::convention const* const> convention_types);
void display_help(
std::initializer_list<conventions::convention const* const> convention_types) const;
protected:
base_parser() = default;
};
```
`base_parser` owns registrations, parsed command-line tokens, stored values,
required-argument checks, and completion hooks. Its constructor is protected;
users normally instantiate a concrete parser from `<argparse>` or a platform
header. Platform parser constructors populate `program_name` and
`parsed_arguments` from the native process arguments.
`base_parser` is not thread-safe. `handle_arguments(...)` must be called on the
same thread that created the parser or it throws `std::runtime_error`.
#### Named Arguments
Use `add_argument(...)` for short and/or long options. The v1 API uses `"-"` as
the sentinel for an omitted short or long name.
```cpp
argument_parser::parser parser;
parser.add_argument<std::string>(
"o",
"output",
"Output file.",
argument_parser::helpers::make_action<std::string>(
[](std::string const& value) {
// use value
}),
true);
parser.add_argument(
"v",
"verbose",
"Enable verbose output.",
false);
```
Registration fails with `std::runtime_error("The key already exists!")` if the
short or long key is already registered. Because duplicate checks also see the
`"-"` sentinel, avoid registering multiple v1 arguments that pass `"-"` for the
same side unless you have verified that behavior.
The overloads behave as follows:
- `add_argument<T>(..., action_with_param<T> const& action, bool required)`
parses a value and invokes the supplied typed action.
- `add_argument<T>(..., bool required)` parses a value and stores it internally
for `get_optional<T>(...)`.
- `add_argument(..., action_no_param const& action, bool required)`
invokes a no-value action.
- `add_argument(..., bool required)` stores `true` internally when the option is
present.
#### Positional Arguments
```cpp
parser.add_positional_argument<std::string>(
"input",
"Input path.",
true,
0);
parser.add_positional_argument<int>(
"count",
"Number of iterations.",
argument_parser::helpers::make_action<int>(
[](int value) {
// use value
}),
false);
```
Positional arguments are matched against tokens that do not match any supplied
convention. You can provide an explicit zero-based `position`, or omit it to use
the next available positional slot.
`add_positional_argument<T>(..., bool required, ...)` stores the parsed value
internally under the positional name. The action overload invokes the supplied
typed action.
`add_positional_accumulator<T>(...)` registers a typed positional action that can
consume repeated positional tokens at its slot. Only one positional accumulator
is allowed, and it must be the last positional argument. Duplicate names,
negative explicit positions, or invalid accumulator placement throw
`std::runtime_error`.
#### Parsing
```cpp
parser.handle_arguments({
&argument_parser::conventions::gnu_argument_convention,
&argument_parser::conventions::gnu_equal_argument_convention,
&argument_parser::conventions::windows_argument_convention,
&argument_parser::conventions::windows_equal_argument_convention,
});
```
`handle_arguments(...)` invokes matching actions, records stored values, checks
required arguments, then runs completion hooks. For each token, conventions are
tried in order. If no convention matches, the token is consumed as the next
positional value when one is available. `--` forces all following tokens to be
treated as positional values.
Unknown options, missing values, parse failures, unexpected positional values,
and action errors are reported with `std::runtime_error`. Missing required
arguments print diagnostics and help text, then call `std::exit(1)`.
If a registered argument named `h` or `help` is found, the parser invokes that
help argument and returns from argument invocation early. Required checks and
completion hooks still run after invocation returns.
#### Lookup And Help
```cpp
if (auto output = parser.get_optional<std::string>("output")) {
// *output is the stored value
}
auto id = parser.find_argument_id("output");
std::string help = parser.build_help_text({
&argument_parser::conventions::gnu_argument_convention,
});
parser.display_help({
&argument_parser::conventions::gnu_argument_convention,
});
```
`get_optional<T>(arg)` returns a stored value for a registered short name, long
name, or positional name. It returns `std::nullopt` when no argument by that name
exists or when no value was stored. If a value exists but `T` does not match the
stored type, `std::any_cast<T>` throws `std::bad_any_cast`.
`find_argument_id(arg)` returns the internal integer id for a registered short
name, long name, or positional name. `get_argument(parsed_argument)` resolves a
parsed convention result to an `argument&` and throws when no match exists.
`build_help_text(...)` returns formatted usage text. `display_help(...)` writes
that text to `std::cout`.
#### Completion Hooks
```cpp
parser.on_complete([](argument_parser::base_parser const& state) {
// inspect state.get_optional<T>(...)
});
```
`on_complete(...)` appends a callback that runs after successful argument
invocation and required-argument checks.
## v2 API: `parser_v2.hpp`
The v2 API is declared under `argument_parser::v2`. It provides a
map/initializer-list facade over the v1 `base_parser`, while still accepting the
same convention objects and action helpers.
Typical use is through the platform alias from `<argparse>`:
```cpp
#include <argparse>
using namespace argument_parser::v2::flags;
int main() {
argument_parser::v2::parser parser(argument_parser::no_exit);
parser.add_argument<int>({
{LongArgument, "count"},
{HelpText, "Number of items to process."},
{Required, true},
});
parser.handle_arguments({
&argument_parser::conventions::gnu_argument_convention,
&argument_parser::conventions::windows_argument_convention,
});
auto count = parser.get_optional<int>("count");
}
```
### `enum class add_argument_flags`
These keys describe an argument passed to `v2::base_parser::add_argument`.
| Flag | Value type | Meaning |
| --- | --- | --- |
| `ShortArgument` | `std::string` | Short option name, such as `"v"`. |
| `LongArgument` | `std::string` | Long option name, such as `"verbose"`. |
| `Positional` | `std::string` | Registers a positional argument. When present, the positional registration path is used. |
| `Position` | `int` | Optional zero-based position for a positional argument. |
| `HelpText` | `std::string` | Help text shown in generated help output. |
| `Action` | `action_with_param<T>` or `action_no_param` | Callback invoked when the argument is found. |
| `Required` | `bool` | Marks the argument required when `true`. |
| `Reference` | `T*` | Stores a parsed typed value into an external object. Cannot be combined with `Action`. |
| `Accumulate` | `bool` or `T*` | Repeated-value mode. For `std::vector<U>`, each parsed `U` is appended. For `int`, each occurrence increments the count. |
The `argument_parser::v2::flags` namespace exposes inline constants with the
same names for concise initializer-list calls.
```cpp
using namespace argument_parser::v2::flags;
parser.add_argument({
{ShortArgument, "q"},
{HelpText, "Quiet mode."},
});
```
### `argument_parser::v2::deducers`
```cpp
template <typename, typename = void>
struct has_value_type;
template <typename T>
struct is_vector;
template <typename T>
constexpr bool is_vector_v = is_vector<T>::test();
```
These public type traits are used by v2 accumulator logic. `has_value_type`
detects `T::value_type`. `is_vector<T>::test()` and `is_vector_v<T>` are true
only when `T` is exactly a `std::vector` specialization.
### `argument_parser::v2::base_parser`
`v2::base_parser` is the v2 facade. Concrete platform parsers such as
`argument_parser::v2::linux_parser`, `macos_parser`, `windows_parser`, and the
portable `<argparse>` alias `argument_parser::v2::parser` derive from it. The
v2 platform parser constructors accept `parser_settings const& settings = {}`.
Use the default constructor for normal command-line tools, or pass
`argument_parser::no_exit` / a custom `parser_settings` object when the caller
should handle errors without automatic process exits.
The class privately inherits from the v1 `argument_parser::base_parser` and
re-exposes selected operations.
```cpp
template <typename T>
using typed_flag_value =
std::variant<std::string, action_with_param<T>, bool, int, T*>;
using non_typed_flag_value =
std::variant<std::string, action_no_param, bool, int>;
template <typename T>
using typed_argument_pair =
std::pair<add_argument_flags, typed_flag_value<T>>;
using non_typed_argument_pair =
std::pair<add_argument_flags, non_typed_flag_value>;
```
Use typed aliases for arguments that parse a value of `T`, run an
`action_with_param<T>`, store into a `T`, or accumulate values. Use non-typed
aliases for boolean flags and no-parameter actions.
#### Typed `add_argument`
```cpp
template <typename T>
void add_argument(
std::unordered_map<add_argument_flags, typed_flag_value<T>> const& argument_pairs);
template <typename T>
void add_argument(std::initializer_list<typed_argument_pair<T>> const& pairs);
```
Registers a typed option or positional argument. For non-positional options,
provide at least one of `ShortArgument` or `LongArgument`. If only one name is
supplied, the other is internally set to `"-"` and not registered.
Without `Action`, `Reference`, or `Accumulate`, the parsed `T` is stored
internally and can be read with `get_optional<T>()`. For positional arguments,
include `Positional`; `Position` can place the argument at a specific zero-based
slot.
```cpp
int threshold = 0;
parser.add_argument<int>({
{LongArgument, "threshold"},
{Reference, &threshold},
{HelpText, "Store the parsed threshold."},
});
```
#### Non-Typed `add_argument`
```cpp
void add_argument(
std::unordered_map<add_argument_flags, non_typed_flag_value> const& argument_pairs);
void add_argument(std::initializer_list<non_typed_argument_pair> const& pairs);
```
Registers a non-typed option or positional argument. For options, provide at
least one of `ShortArgument` or `LongArgument`. Without `Action`, the argument
behaves as a boolean flag: when present, it stores `true` for
`get_optional<bool>()`.
With `Action`, provide an `action_no_param`. Non-typed arguments cannot use
`Reference` or `Accumulate`. A non-typed positional argument is stored as
`std::string`.
```cpp
parser.add_argument({
{ShortArgument, "v"},
{LongArgument, "verbose"},
{HelpText, "Enable verbose output."},
});
```
#### Other Public Operations
```cpp
argument_parser::base_parser& to_v1();
void handle_arguments(
std::initializer_list<conventions::convention const* const> convention_types);
template <typename T>
std::optional<T> get_optional(std::string const& arg);
using argument_parser::base_parser::display_help;
using argument_parser::base_parser::on_complete;
```
`to_v1()` returns a mutable reference to the underlying v1 parser interface.
`handle_arguments(...)`, `get_optional<T>(...)`, `display_help(...)`, and
`on_complete(...)` follow the v1 behavior described above.
### Accumulation
`Accumulate` has special typed behavior:
- `T = std::vector<U>` registers an argument that accepts repeated `U` values.
- `T = int` registers a counter-style option; each occurrence increments the
count.
- `Accumulate` set to `true` stores the accumulated result internally and makes
it available through `get_optional<T>()`.
- `Accumulate` set to `T*`, or `Accumulate` combined with `Reference`, writes
directly to an external object.
For vector accumulators, the public argument type is `std::vector<U>`, but each
individual command-line occurrence is parsed as `U`.
```cpp
parser.add_argument<std::vector<int>>({
{LongArgument, "include-id"},
{Accumulate, true},
{HelpText, "May be repeated."},
});
auto ids = parser.get_optional<std::vector<int>>("include-id");
```
## Builder API: `argument_builder.hpp`
The public builder API lives under `argument_parser::builder`. It provides a
staged fluent interface for registering arguments with
`argument_parser::v2::base_parser`.
```cpp
#include <argument_builder.hpp>
// or
#include <argparse>
using argument_parser::builder::new_argument;
```
### Public Types
`non_type` is a marker type used before a value-producing mode has been
selected. Application code normally does not need to name it.
`builder_mask` is a public namespace containing the compile-time machinery used
by `argument<mask, store_type>`. It exposes `mask_type`, `value_mode`,
`extra_capability`, capability constants such as `short_argument`,
`long_argument`, `positional`, `help_text`, `required`, `reference`,
`accumulate`, `count`, `store`, and `flag`, plus constexpr helpers such as
`bit`, `has`, `remove`, `replace`, `is_buildable`, and
`is_build_and_gettable`. These are mainly useful for type-level tests or
advanced wrappers.
### `container<store_type>`
```cpp
template <typename store_type>
class container;
```
Returned by `build_and_get(parser)`. It is populated after
`parser.handle_arguments(...)` completes.
- `store_type get() const` returns the stored value. Call only after the
container is populated.
- `store_type& operator*()` dereferences the stored value.
- `store_type* operator->()` accesses members of the stored value.
- `operator bool()` reports whether a value is available.
For builder-managed storage, `build_and_get()` registers an `on_complete`
callback that reads the parsed value by lookup key. For
`accumulate(existing_vector)`, the container references the provided vector
directly.
### `argument<mask, store_type>`
```cpp
template <
argument_parser::builder::builder_mask::mask_type mask =
argument_parser::builder::builder_mask::initial,
typename store_type = argument_parser::builder::non_type>
class argument;
```
The staged fluent builder. `mask` enables only valid next methods at compile
time. `store_type` is the type produced by the selected value mode. Most users
should create builders with `new_argument()`.
### Entry Points
```cpp
static auto argument<>::start() -> argument<builder_mask::initial>;
static inline auto new_argument();
```
`new_argument()` is equivalent to `argument<>::start()`.
```cpp
new_argument()
.long_argument("output")
.store<std::string>()
.required()
.build(parser);
```
### Identifier Methods
At least one identifier method must be used before `build(parser)` or
`build_and_get(parser)`.
```cpp
auto short_argument(std::string short_name) const -> argument<..., store_type>;
auto long_argument(std::string long_name) const -> argument<..., store_type>;
auto positional(std::string positional_name) const -> argument<..., store_type>;
auto position(int index) const -> argument<..., store_type>;
```
`short_argument(...)` and `long_argument(...)` set named option identifiers and
can be combined with each other. `positional(...)` registers a positional
argument and is mutually exclusive with named identifiers. `position(...)` is
available only after `positional(...)` and only once. If no value mode is
selected for a positional argument, `build(parser)` registers a `std::string`
positional value.
### Metadata Methods
```cpp
auto help_text(std::string help) const -> argument<..., store_type>;
auto required(bool value = true) const -> argument<..., store_type>;
```
`help_text(...)` sets generated help text and is single-use. If omitted, v2
generates default text, often using parser trait hints for typed value modes.
`required(...)` marks the argument required when `value` is `true`. There is no
public `setRequired(...)` method in `argument_builder.hpp`; the public fluent API
is `required(...)`.
### Terminal Value Modes
Exactly one terminal value mode can be selected. Once selected, the other modes
are removed from the builder type.
```cpp
template <typename T = std::string>
auto store() const -> argument<..., T>;
template <typename T>
auto reference(T& value) const -> argument<..., T>;
auto flag() const -> argument<..., bool>;
template <typename T = std::string>
auto accumulate() const -> argument<..., std::vector<T>>;
template <typename T>
auto accumulate(T& value) const -> argument<..., T>;
auto count() const -> argument<..., int>;
auto count(int& value) const -> argument<..., int>;
template <typename Callable>
auto action(Callable&& handler) const -> argument<..., non_type>;
template <typename T = std::string, typename Callable>
auto action(Callable&& handler) const -> argument<..., T>;
```
`store<T>()` parses a value and stores it in the parser for later lookup.
`store<void>()` is rejected at compile time.
`reference(T&)` parses a value and assigns it directly to an object that must
outlive parsing.
`flag()` registers a boolean presence flag. It is not available for positional
arguments. Named arguments also default to flag behavior when no terminal mode
is selected.
`accumulate<T>()` accepts repeated values and stores them as `std::vector<T>`.
`accumulate(existing)` appends repeated values into an existing vector. The
existing object must be a `std::vector` and must outlive parsing.
`count()` counts how many times a named argument appears. `count(int&)` writes
the count into an existing integer. `count()` is not available for positional
arguments.
`action(handler)` registers a no-value action when `handler` is invocable as
`void()`. For positional arguments, the builder adapts it through a
`std::string` positional action and ignores the parsed positional text.
`action<T>(handler)` registers a typed action when `handler` is invocable as
`void(T const&)`. `action<void>(...)` is rejected at compile time.
### Build Methods
```cpp
auto build(argument_parser::v2::base_parser& parser) const -> void;
auto build_and_get(argument_parser::v2::base_parser& parser) const
-> container<store_type>;
```
`build(parser)` registers the configured argument with the parser. It requires
an identifier, dispatches to the selected terminal mode, and defaults to a named
boolean flag or `std::string` positional value when no terminal mode was
selected. It may propagate validation errors from `v2::base_parser`.
`build_and_get(parser)` registers the argument and returns a
`container<store_type>` for storable modes such as `store<T>()`, `flag()`,
default named flags, `accumulate<T>()`, `accumulate(vector&)`, `count()`, and
`count(int&)`. It throws `std::logic_error` for unsupported terminal modes such
as actions. The returned container is normally empty until
`parser.handle_arguments(...)` completes.
### Compile-Time Builder Rules
Invalid fluent chains fail to compile rather than failing at runtime.
- `build(...)` requires `short_argument(...)`, `long_argument(...)`, or
`positional(...)`.
- `short_argument(...)` and `long_argument(...)` can be combined.
- `positional(...)` is mutually exclusive with named identifiers.
- `position(...)` is available only after `positional(...)` and only once.
- `help_text(...)` and `required(...)` are single-use.
- Terminal value modes are mutually exclusive.
- `flag()` and `count()` are not available for positional arguments.
- `store<void>()` and `action<void>(...)` are rejected with `static_assert`.
- `accumulate(existing)` requires `existing` to be a `std::vector`.
The header also exposes `argument_parser::builder::assertions`, a namespace of
small compile-time detection helpers and `static_assert` checks used to verify
these staged-builder rules. They are not needed for normal parser
configuration.
### Complete Builder Example
```cpp
#include <argparse>
#include <iostream>
#include <string>
#include <vector>
using argument_parser::builder::new_argument;
int main() {
argument_parser::v2::parser parser(argument_parser::no_exit);
int threshold = 0;
std::vector<int> ids;
new_argument()
.long_argument("file")
.store<std::string>()
.required()
.help_text("Input file.")
.build(parser);
new_argument()
.long_argument("threshold")
.reference(threshold)
.build(parser);
new_argument()
.long_argument("id")
.accumulate(ids)
.help_text("Repeatable numeric id.")
.build(parser);
auto verbose = new_argument()
.short_argument("v")
.count()
.build_and_get(parser);
parser.handle_arguments({
&argument_parser::conventions::gnu_argument_convention,
&argument_parser::conventions::windows_argument_convention,
});
if (auto file = parser.get_optional<std::string>("file")) {
std::cout << "file: " << *file << '\n';
}
if (verbose) {
std::cout << "verbosity: " << *verbose << '\n';
}
std::cout << "threshold: " << threshold << '\n';
std::cout << "ids: " << ids.size() << '\n';
}
```
## Caveats
- The v1 API is retained as public compatibility surface, but the newer v2 and
builder APIs are generally more ergonomic.
- Parser registration and parsing are single-threaded; `handle_arguments(...)`
enforces the parser creation thread.
- `handle_arguments(...)` resets the current convention list after it returns
or throws.
- Namespaces named `argument_parser::internal::*` are implementation details
despite being present in public headers.

334
README.md
View File

@@ -1,17 +1,24 @@
# argument-parser
A lightweight, modern, expressively typed, and highly customizable C++17 argument parser library.
A lightweight, modern, and highly customizable C++17 argument parser with native platform argument collection, trait-driven typed parsing, repeatable argument accumulation, pluggable option conventions, and a fluent `v2` builder API.
> `v1` is deprecated and mainly kept as implementation history. For new projects, use `argument_parser::v2` together with `argument_parser::builder`.
## Features
- **Type-safe Argument Extraction**: Use type traits to automatically parse fundamental types and custom structures (e.g. `std::vector<int>`, `std::regex`, `Point`).
- **Support for Multiple Parsing Conventions**: Pluggable convention system out of the box, offering GNU-style (`-a`, `--arg`), GNU-equal-style (`--arg=value`), Windows-style (`/arg`), and Windows-equal-style (`/arg:value`).
- **Automated Help Text Formatting**: Call `parser.display_help(conventions)` to easily generate beautifully formatted usage instructions.
- **Cross-Platform Native Parsers**: Dedicated parsers that automatically fetch command-line arguments using OS-specific APIs (`windows_parser`, `linux_parser`, `macos_parser`), so you don't need to manually pass `argc` and `argv` on most platforms.
- **Fluid setup**: Enjoy fluid setup routines with maps and initializer lists.
### Important Note:
V1 is deprecated and is mainly kept as a base implementation for the V2. You should use V2 for your projects. If any features are missing compared to V1, please let me know so I can introduce them!
- Native platform parser alias: `argument_parser::v2::parser` resolves to the current platform parser and reads arguments directly from OS APIs.
- Fluent builder API with compile-time builder constraints that prevent invalid combinations after a terminal/mutually exclusive mode has been selected.
- Type-safe parsing and extraction. Extend `parser_trait<T>` for your own types and retrieve stored values with `get_optional<T>()`.
- Repeatable value accumulation with `accumulate<T>()`, `accumulate(vector&)`, `count()`, and `count(int&)`.
- `build_and_get(parser)` for storable builder modes, returning a small container that is populated after parsing completes.
- Positional arguments with optional explicit ordering and support for `--` as a positional separator.
- Trait-driven `format_hint` and `purpose_hint` metadata used in generated help text and parse errors.
- Automatic help flag on `argument_parser::v2::parser` (`-h`, `--help`) with configurable exit behavior through `parser_settings`.
- `parser_settings` for choosing whether help, unknown arguments, parse errors, and missing required arguments exit or throw.
- Auto-formatted help output.
- Completion hooks via `parser.on_complete(...)`.
- Pluggable conventions for GNU next-token, GNU equal-style, Windows next-token, and Windows inline `=` / `:` parsing, or bring your own!
- Testing helper + pseudo command handler `argument_parser::v2::fake_parser`.
## Requirements
@@ -20,104 +27,307 @@ V1 is deprecated and is mainly kept as a base implementation for the V2. You sho
## Quick Start
### 1. Create your Parser and Define Arguments
```cpp
#include <argparse>
#include <iostream>
#include <string>
#include <regex>
#include <argparse> // Provides the native parser for your compiling platform
#include <vector>
using argument_parser::builder::new_argument;
int main() {
using namespace argument_parser::v2::flags;
argument_parser::v2::parser parser(argument_parser::no_exit); // throw instead of exiting
// Automatically uses the platform-native parser!
// It will fetch arguments directly from OS APIs (e.g., GetCommandLineW on Windows)
argument_parser::v2::parser parser;
int threshold = 0;
std::vector<int> ids;
// A flag with an action
parser.add_argument<std::string>({
{ShortArgument, "e"},
{LongArgument, "echo"},
{Action, argument_parser::helpers::make_parametered_action<std::string>(
[](std::string const &text) { std::cout << text << std::endl; }
)},
{HelpText, "echoes given variable"}
});
new_argument()
.short_argument("e")
.long_argument("echo")
.action<std::string>([](std::string const& text) {
std::cout << text << '\n';
})
.build(parser);
// A flag that just stores the value to extract later
parser.add_argument<std::regex>({
{ShortArgument, "g"},
{LongArgument, "grep"},
{HelpText, "Grep pattern"}
});
new_argument()
.long_argument("file")
.store<std::string>()
.required()
.help_text("Input file to process.")
.build(parser);
// A required flag
parser.add_argument<std::string>({
{LongArgument, "file"},
{Required, true},
{HelpText, "File to grep"}
});
new_argument()
.long_argument("threshold")
.reference(threshold)
.help_text("Numeric threshold.")
.build(parser);
// Run action callback on complete
parser.on_complete([](argument_parser::base_parser const &p) {
auto filename = p.get_optional<std::string>("file");
auto pattern = p.get_optional<std::regex>("grep");
auto verbose = new_argument()
.short_argument("v")
.help_text("Increase verbosity. Repeat for a higher level.")
.count()
.build_and_get(parser);
if (filename && pattern) {
std::cout << "Grepping " << filename.value() << " with pattern." << std::endl;
new_argument()
.long_argument("id")
.help_text("Collect an id. May be repeated.")
.accumulate(ids)
.build(parser);
new_argument()
.positional("output")
.position(0)
.help_text("Output file.")
.build(parser);
parser.on_complete([](auto const& state) {
if (auto file = state.template get_optional<std::string>("file")) {
std::cout << "completed for: " << *file << '\n';
}
});
// Register Conventions
const std::initializer_list<argument_parser::conventions::convention const *const> conventions = {
&argument_parser::conventions::gnu_argument_convention,
&argument_parser::conventions::windows_argument_convention
&argument_parser::conventions::gnu_equal_argument_convention,
&argument_parser::conventions::windows_argument_convention,
&argument_parser::conventions::windows_equal_argument_convention,
};
// Execute logic!
parser.handle_arguments(conventions);
return 0;
if (auto file = parser.get_optional<std::string>("file")) {
std::cout << "file: " << *file << '\n';
}
std::cout << "threshold: " << threshold << '\n';
std::cout << "ids: " << ids.size() << '\n';
if (verbose) {
std::cout << "verbosity: " << *verbose << '\n';
}
}
```
### 2. Custom Type Parsing
## Trait-Driven Parsing and Hints
You can natively parse your custom structs, objects, or arrays by specializing `argument_parser::parsing_traits::parser_trait<T>`.
Specialize `argument_parser::parsing_traits::parser_trait<T>` to add support for your own types and to describe their expected format.
```cpp
#include <macros.h>
#include <traits.hpp>
#include <stdexcept>
#include <string>
struct Point {
int x, y;
int x;
int y;
};
template <> struct argument_parser::parsing_traits::parser_trait<Point> {
static Point parse(const std::string &input) {
auto comma_pos = input.find(',');
int x = std::stoi(input.substr(0, comma_pos));
int y = std::stoi(input.substr(comma_pos + 1));
return {x, y};
template <>
struct argument_parser::parsing_traits::parser_trait<Point> {
static Point parse(std::string const& input) {
auto comma = input.find(',');
if (comma == std::string::npos) {
throw std::runtime_error("Expected x,y");
}
return {
std::stoi(input.substr(0, comma)),
std::stoi(input.substr(comma + 1))
};
}
};
// Now you can directly use your type:
// parser.add_argument<Point>({ {LongArgument, "point"} });
// auto point = parser.get_optional<Point>("point");
ARGPARSE_TRAIT_FORMAT_HINT = "x,y";
ARGPARSE_TRAIT_PURPOSE_HINT = "point coordinates";
};
```
Then use the type directly from the builder:
```cpp
new_argument()
.long_argument("point")
.store<Point>()
.build(parser);
```
If you omit `help_text()`, `v2` uses the trait hints to generate help such as `Accepts point coordinates in x,y format.` The same hints are also included in type conversion errors.
## Help Behavior
`argument_parser::v2::parser` automatically registers `-h` and `--help`.
```cpp
argument_parser::v2::parser parser; // help prints and exits
argument_parser::v2::parser parser(argument_parser::no_exit); // help prints without exiting
```
You can also display help manually:
```cpp
parser.display_help(conventions);
```
## Parser Settings
Use `argument_parser::parser_settings` when the parser is embedded in a larger
application, test, or REPL-like tool where parse failures should be handled by
the caller instead of ending the process.
```cpp
argument_parser::parser_settings settings;
settings.should_exit_on_help = false;
settings.should_exit_on_error = false;
settings.should_exit_on_unknown_argument = false;
settings.should_exit_on_missing_required = false;
settings.ignore_unknown_arguments = true;
argument_parser::v2::parser parser(settings);
```
The convenience constant `argument_parser::no_exit` disables the four automatic
exit paths while leaving unknown-argument handling enabled:
```cpp
argument_parser::v2::parser parser(argument_parser::no_exit);
```
Settings fields:
- `should_exit_on_help`: exit with status `0` after automatic `-h` / `--help`.
- `should_exit_on_error`: exit with status `1` for non-unknown parse errors; otherwise rethrow.
- `should_exit_on_unknown_argument`: exit with status `1` for unknown arguments; otherwise rethrow.
- `should_exit_on_missing_required`: exit with status `1` after reporting missing required arguments; otherwise throw.
- `ignore_unknown_arguments`: skip unknown arguments that cannot be consumed as positionals.
- `ignore_errors`: present in the public settings struct, but not currently read by the parser runtime.
## Supported Conventions
- GNU next-token: `-o value`, `--output value`
- GNU equal-style: `-o=value`, `--output=value`
- Windows next-token: `/output value`
- Windows inline value: `/output=value`, `/output:value`
Mix any of them in the same parser by passing the conventions you want to `handle_arguments()`.
## Builder API
`argument_parser::builder::argument<>` is a staged builder. Prefer `argument_parser::builder::new_argument()` as the entry point:
```cpp
using argument_parser::builder::new_argument;
```
`build(parser)` registers the argument and returns `void`. `build_and_get(parser)` is available for storable modes and returns a lightweight `builder::container<T>`. The container is filled by an internal completion hook after `handle_arguments(...)` runs.
Before `build(...)`, you compose an argument from three kinds of steps:
- Identifier selection: `short_argument(...)`, `long_argument(...)`, or `positional(...)`
- Optional metadata: `position(...)` for positional arguments, `help_text(...)`, and `required(...)`
- One mutually exclusive value behavior:
- `store<T>()` to parse and retain a value for later `get_optional<T>()`
- `flag()` to store a boolean presence flag
- `reference(value)` to write the parsed result directly into an existing variable
- `accumulate<T>()` to collect repeated values into a stored `std::vector<T>`
- `accumulate(vector)` to collect repeated values into an existing `std::vector<T>`
- `count()` to store how many times an option appears
- `count(value)` to write the occurrence count into an existing `int`
- `action([] { ... })` for no-value callbacks
- `action<T>([](T const&) { ... })` for typed value callbacks
Once you select one value behavior, the other value behavior methods are disabled at compile time, so combinations like `store<T>().action(...)` or `flag().reference(value)` are rejected by the type system. The same staged typing also prevents repeating one-shot methods such as `help_text(...)`, `position(...)`, or `store<T>()` on the same builder chain.
If you do not select a value behavior explicitly, `build(parser)` uses the default for the argument kind: named arguments become boolean flags, while positional arguments store a `std::string`.
## Accumulators and Counts
Use `accumulate<T>()` when the parser should accept the same value-bearing argument multiple times and store all parsed values:
```cpp
auto values = new_argument()
.short_argument("n")
.long_argument("number")
.accumulate<int>()
.build_and_get(parser);
parser.handle_arguments(conventions);
if (values) {
for (int value : *values) {
std::cout << value << '\n';
}
}
```
Use `accumulate(target)` to append into a vector you own:
```cpp
std::vector<int> ids;
new_argument()
.long_argument("id")
.accumulate(ids)
.build(parser);
```
Use `count()` for repeatable flags such as `-v -v -v`:
```cpp
auto verbosity = new_argument()
.short_argument("v")
.count()
.build_and_get(parser);
```
The lower-level `v2::base_parser::add_argument` API exposes the same accumulator behavior through the `Accumulate` flag:
```cpp
using namespace argument_parser::v2::flags;
parser.add_argument<std::vector<int>>({
{LongArgument, "id"},
{HelpText, "Collect an id. May be repeated."},
{Accumulate, true},
});
std::vector<int> captured_ids;
parser.add_argument<std::vector<int>>({
{LongArgument, "captured-id"},
{Accumulate, &captured_ids},
});
```
## Testing
For unit tests or synthetic argument lists, use `argument_parser::v2::fake_parser` instead of the native platform parser:
```cpp
#include <fake_parser.hpp>
argument_parser::v2::fake_parser parser("tool", {"--count", "3", "input.txt"});
```
## CMake Integration
The library can be installed globally via CMake or incorporated into your project.
Use the project directly:
```cmake
add_subdirectory(argument-parser)
target_link_libraries(your_target PRIVATE argument_parser)
```
Or install and consume it as a package:
```cmake
find_package(argument_parser CONFIG REQUIRED)
target_link_libraries(your_target PRIVATE argument_parser::argument_parser)
```
## Building & Installing
```bash
mkdir build && cd build
cmake ..
cmake --build .
cmake --install .
```

39
TODO.md
View File

@@ -19,8 +19,6 @@ After handle_parse(...) ends, we can call the on_parse_done callbacks. But this
1. Storable arguments. Those would be not actions, but stored values. So you could get them later on in the callback or somewhere else.
2. The on_parse_done callbacks should be called after all actions are invoked. But we should make this a toggle, so the callback only gets executed if the user explicitly wants it to be called. By default we would enable it.
## Self reminder, implement this feature. It'd be helpful.
### Update: 29 March. 2026
Help text is clear, supports multi convention print, also provides hints for types that provides the hints in the traits.
Positional argument support is here. Also we now have some tests to ensure the library works as intended.
@@ -39,19 +37,44 @@ Configure CMAKE to ensure the library can be built as a library and can be insta
# TODO 5: Display help | DONE
Display help doesn't reflect the conventions right now. Also it should come automatically, and should be allowed to overriden by user.
# TODO 6: Accumulate repeated calls
Add support to letting users accumulate repeated calls to a flag. If the flag is called x times, the result should be x items stored in a vector,
instead of an action doing it.
# TODO 6: Accumulate repeated calls | DONE
Add support to let users accumulate repeated calls to a flag. If the flag is called x times, the result should be x items stored in a vector, instead of an action doing it.
# TODO 7: Defaults/Implicits
If given, an arguments default store value could be changed. If nothing was given use that value instead.
# TODO 8: Validators
# TODO 8: Validators | DONE
If given, validate the argument before passing to the storage or action. If fail, let user decide fail loud or fail skip.
# TODO 9: Subcommand/Subactions
Implement subcommand support. Users should be able to define subactions to the higher level action. For example,
```cpp
parser.add_argument<std::string>(
{{ShortArgument, "l"}, {LongArgument, "list"}, {Action, list_files}, {HelpText, "Lists files in the directory"}});
parser.add_argument(
{{ShortArgument, "g"}, {LongArgument, "get"}, {Action, get_}, {HelpText, "Gets <files, system_info, status>"}}
);
parser.add_argument(
{{BaseArgument, "g"}, {ShortArgument, "f"}, {LongArgument, "files"}, {Action, get_files}, {HelpText, "Gets files"}}
);
...
```
# TODO 10: Reference capture | DONE
Reference capturing.
```cpp
parser.add_argument<int>({
{ShortArgument, "c"},
{HelpText, "capture value"},
{Reference, &captured_value},
});
```
# TODO 11: Builder | DONE
Implement type safe logic enforcing argument builder;
```cpp
argument::start()
.short_argument("e")
.long_argument("echo")
.help_text("Echo the parsed value.")
.action(echo)
.build(parser);
```

View File

@@ -0,0 +1,11 @@
cmake_minimum_required(VERSION 3.10)
project(test)
set(CMAKE_BUILD_TYPE Debug)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
find_package(argument_parser REQUIRED)
add_executable(main main.cpp)
target_link_libraries(main argument_parser)

167
examples/test/main.cpp Normal file
View File

@@ -0,0 +1,167 @@
#include <argparse>
#include <argument_parser.hpp>
#include <gnu_argument_convention.hpp>
#include <iostream>
#include <macros.h>
#include <parser_v2.hpp>
#include <string>
#include <traits.hpp>
#include <vector>
#include <windows_argument_convention.hpp>
using argument_parser::builder::new_argument;
using argument_parser::parsing_traits::hint_type;
auto echo(std::string const &s) -> void {
std::cout << s << '\n';
}
using namespace argument_parser::parsing_traits;
constexpr hint_type vector_purpose_hint = "vector of ";
template <typename T> struct parser_trait<std::vector<T>> {
static std::vector<T> parse(std::string const &s) {
std::vector<T> result;
std::stringstream ss(s);
for (std::string line; std::getline(ss, line, ',');) {
T item = parser_trait<T>::parse(line);
result.push_back(item);
}
return result;
}
ARGPARSE_TRAIT_FORMAT_HINT = concat<hint_provider<&parser_trait<T>::format_hint>, hint_provider<&comma>,
hint_provider<&parser_trait<T>::format_hint>>;
ARGPARSE_TRAIT_PURPOSE_HINT =
concat<hint_provider<&vector_purpose_hint>, hint_provider<&parser_trait<T>::purpose_hint>>;
};
using namespace argument_parser::v2::flags;
auto main() -> int {
argument_parser::v2::parser parser(argument_parser::no_exit);
new_argument()
.positional("count")
.position(1)
.help_text("How many times to repeat the action.")
.action<int>([](int const &count) { std::cout << "count action configured for " << count << '\n'; })
.build(parser);
int captured_value = 0;
new_argument()
.long_argument("threshold")
.help_text("Store the parsed value through a reference.")
.reference(captured_value)
.build(parser);
new_argument()
.positional("captured")
.position(0)
.help_text("Store the parsed value through a reference.")
.reference(captured_value)
.build(parser);
// parser.add_argument<int>({
// {ShortArgument, "c"},
// {HelpText, "capture count"},
// {Reference, &captured_value},
// });
new_argument().short_argument("q").help_text("Store a boolean flag.").build(parser);
// argument::start()
// .long_argument("regex")
// .help_text("Store a regex value.")
// .store<std::optional<std::regex>>()
// .build(parser);
new_argument()
.short_argument("e")
.long_argument("echo")
.help_text("Echo the parsed value.")
.action(echo)
.build(parser);
new_argument()
.long_argument("vecstr")
.short_argument("vs")
.action<std::vector<int>>([](std::vector<int> const &vecstr) {
for (auto const &str : vecstr) {
std::cout << str << '\n';
}
})
.build(parser);
auto accumulated_pos = new_argument()
.short_argument("v")
.help_text("turns on verbose execution. up to three levels of verbosity.")
.count()
.build_and_get(parser);
auto accumulate_vec =
new_argument().long_argument("vecstr1").short_argument("vs1").accumulate<int>().build_and_get(parser);
parser.add_argument<std::vector<int>>({
{LongArgument, "accumulate"},
{HelpText, "accumulates given ints into the vector (flag ver)"},
{Accumulate, true},
});
std::vector<int> captured_vec;
parser.add_argument<std::vector<int>>({
{LongArgument, "accumulate2"},
{HelpText, "accumulates given ints into the vector (ref ver)"},
{Accumulate, &captured_vec},
});
std::vector<int> captured_vec2;
parser.add_argument<std::vector<int>>({
{LongArgument, "accumulate3"},
{HelpText, "accumulates given ints into the vector (ref ver)"},
{Accumulate, true},
{Reference, &captured_vec2},
});
parser.on_complete([](argument_parser::base_parser const &p) {
if (const auto value = p.get_optional<std::vector<int>>("accumulate"); value.has_value()) {
std::cout << "accumulate: ";
for (auto const &str : *value) {
std::cout << str << '\n';
}
}
});
parser.handle_arguments({&argument_parser::conventions::gnu_argument_convention,
&argument_parser::conventions::windows_argument_convention});
if (!captured_vec.empty()) {
std::cout << "accumulate2: ";
for (auto const &str : captured_vec) {
std::cout << str << '\n';
}
}
if (!captured_vec2.empty()) {
std::cout << "accumulate3: ";
for (auto const &str : captured_vec2) {
std::cout << str << '\n';
}
}
if (accumulate_vec) {
std::cout << "accumulate_vec: ";
for (auto const &str : *accumulate_vec) {
std::cout << str << '\n';
}
}
if (accumulated_pos) {
std::cout << "accumulated_pos: " << *accumulated_pos << '\n';
}
return 0;
}

View File

@@ -1,8 +1,10 @@
#pragma once
#ifndef ARGPARSE_HPP
#define ARGPARSE_HPP
#include <argument_parser.hpp>
#include "macros.h"
#include <argument_builder.hpp>
#include <argument_parser.hpp>
#include <parser_v2.hpp>
#ifdef __linux__
#include <linux_parser.hpp>

View File

@@ -14,15 +14,15 @@ namespace argument_parser::conventions {
class base_convention {
public:
virtual std::string extract_value(std::string const &) const = 0;
virtual parsed_argument get_argument(std::string const &) const = 0;
virtual bool requires_next_token() const = 0;
virtual std::string name() const = 0;
virtual std::string short_prec() const = 0;
virtual std::string long_prec() const = 0;
virtual std::pair<std::string, std::string> make_help_text(std::string const &short_arg, std::string const &long_arg,
bool requires_value) const = 0;
virtual std::vector<convention_features> get_features() const = 0;
[[nodiscard]] virtual std::string extract_value(std::string const &) const = 0;
[[nodiscard]] virtual parsed_argument get_argument(std::string const &) const = 0;
[[nodiscard]] virtual bool requires_next_token() const = 0;
[[nodiscard]] virtual std::string name() const = 0;
[[nodiscard]] virtual std::string short_prec() const = 0;
[[nodiscard]] virtual std::string long_prec() const = 0;
[[nodiscard]] virtual std::pair<std::string, std::string>
make_help_text(std::string const &short_arg, std::string const &long_arg, bool requires_value) const = 0;
[[nodiscard]] virtual std::vector<convention_features> get_features() const = 0;
protected:
base_convention() = default;

View File

@@ -8,15 +8,16 @@ namespace argument_parser::conventions::implementations {
class gnu_argument_convention : public base_convention {
public:
parsed_argument get_argument(std::string const &raw) const override;
std::string extract_value(std::string const & /*raw*/) const override;
bool requires_next_token() const override;
std::string name() const override;
std::string short_prec() const override;
std::string long_prec() const override;
std::pair<std::string, std::string> make_help_text(std::string const &short_arg, std::string const &long_arg,
bool requires_value) const override;
std::vector<convention_features> get_features() const override;
virtual ~gnu_argument_convention() = default;
[[nodiscard]] parsed_argument get_argument(std::string const &raw) const override;
[[nodiscard]] std::string extract_value(std::string const & /*raw*/) const override;
[[nodiscard]] bool requires_next_token() const override;
[[nodiscard]] std::string name() const override;
[[nodiscard]] std::string short_prec() const override;
[[nodiscard]] std::string long_prec() const override;
[[nodiscard]] std::pair<std::string, std::string> make_help_text(std::string const &short_arg, std::string const &long_arg,
bool requires_value) const override;
[[nodiscard]] std::vector<convention_features> get_features() const override;
static gnu_argument_convention instance;
@@ -26,15 +27,16 @@ namespace argument_parser::conventions::implementations {
class gnu_equal_argument_convention : public base_convention {
public:
parsed_argument get_argument(std::string const &raw) const override;
std::string extract_value(std::string const &raw) const override;
bool requires_next_token() const override;
std::string name() const override;
std::string short_prec() const override;
std::string long_prec() const override;
std::pair<std::string, std::string> make_help_text(std::string const &short_arg, std::string const &long_arg,
bool requires_value) const override;
std::vector<convention_features> get_features() const override;
virtual ~gnu_equal_argument_convention() = default;
[[nodiscard]] parsed_argument get_argument(std::string const &raw) const override;
[[nodiscard]] std::string extract_value(std::string const &raw) const override;
[[nodiscard]] bool requires_next_token() const override;
[[nodiscard]] std::string name() const override;
[[nodiscard]] std::string short_prec() const override;
[[nodiscard]] std::string long_prec() const override;
[[nodiscard]] std::pair<std::string, std::string> make_help_text(std::string const &short_arg, std::string const &long_arg,
bool requires_value) const override;
[[nodiscard]] std::vector<convention_features> get_features() const override;
static gnu_equal_argument_convention instance;

View File

@@ -5,22 +5,23 @@
#define WINDOWS_ARGUMENT_CONVENTION_HPP
#ifndef ALLOW_DASH_FOR_WINDOWS
#define ALLOW_DASH_FOR_WINDOWS 1
#define ALLOW_DASH_FOR_WINDOWS true
#endif
namespace argument_parser::conventions::implementations {
class windows_argument_convention : public base_convention {
public:
virtual ~windows_argument_convention() = default;
explicit windows_argument_convention(bool accept_dash = true);
parsed_argument get_argument(std::string const &raw) const override;
std::string extract_value(std::string const & /*raw*/) const override;
bool requires_next_token() const override;
std::string name() const override;
std::string short_prec() const override;
std::string long_prec() const override;
std::pair<std::string, std::string> make_help_text(std::string const &short_arg, std::string const &long_arg,
bool requires_value) const override;
std::vector<convention_features> get_features() const override;
[[nodiscard]] parsed_argument get_argument(std::string const &raw) const override;
[[nodiscard]] std::string extract_value(std::string const & /*raw*/) const override;
[[nodiscard]] bool requires_next_token() const override;
[[nodiscard]] std::string name() const override;
[[nodiscard]] std::string short_prec() const override;
[[nodiscard]] std::string long_prec() const override;
[[nodiscard]] std::pair<std::string, std::string> make_help_text(std::string const &short_arg, std::string const &long_arg,
bool requires_value) const override;
[[nodiscard]] std::vector<convention_features> get_features() const override;
static windows_argument_convention instance;
@@ -30,16 +31,17 @@ namespace argument_parser::conventions::implementations {
class windows_kv_argument_convention : public base_convention {
public:
virtual ~windows_kv_argument_convention() = default;
explicit windows_kv_argument_convention(bool accept_dash = true);
parsed_argument get_argument(std::string const &raw) const override;
std::string extract_value(std::string const &raw) const override;
bool requires_next_token() const override;
std::string name() const override;
std::string short_prec() const override;
std::string long_prec() const override;
std::pair<std::string, std::string> make_help_text(std::string const &short_arg, std::string const &long_arg,
bool requires_value) const override;
std::vector<convention_features> get_features() const override;
[[nodiscard]] parsed_argument get_argument(std::string const &raw) const override;
[[nodiscard]] std::string extract_value(std::string const &raw) const override;
[[nodiscard]] bool requires_next_token() const override;
[[nodiscard]] std::string name() const override;
[[nodiscard]] std::string short_prec() const override;
[[nodiscard]] std::string long_prec() const override;
[[nodiscard]] std::pair<std::string, std::string> make_help_text(std::string const &short_arg, std::string const &long_arg,
bool requires_value) const override;
[[nodiscard]] std::vector<convention_features> get_features() const override;
static windows_kv_argument_convention instance;
@@ -48,9 +50,9 @@ namespace argument_parser::conventions::implementations {
};
inline windows_argument_convention windows_argument_convention::instance =
windows_argument_convention(bool(ALLOW_DASH_FOR_WINDOWS));
windows_argument_convention(ALLOW_DASH_FOR_WINDOWS);
inline windows_kv_argument_convention windows_kv_argument_convention::instance =
windows_kv_argument_convention(bool(ALLOW_DASH_FOR_WINDOWS));
windows_kv_argument_convention(ALLOW_DASH_FOR_WINDOWS);
} // namespace argument_parser::conventions::implementations
namespace argument_parser::conventions {

View File

@@ -0,0 +1,679 @@
#pragma once
#include "argument_parser.hpp"
#include <functional>
#include <memory>
#include <parser_v2.hpp>
#include <type_traits>
#ifndef ARGUMENT_PARSER_PARSER_BUILDER_HPP
#define ARGUMENT_PARSER_PARSER_BUILDER_HPP
namespace argument_parser::builder {
class non_type {};
namespace builder_mask {
using v2_flag = argument_parser::v2::add_argument_flags;
using mask_type = std::uint64_t;
enum class value_mode {
unresolved,
store,
flag,
reference,
accumulate,
count,
action_no_param,
action_with_param
};
enum class extra_capability : unsigned { Store = static_cast<unsigned>(v2_flag::Reference) + 1, Flag };
constexpr auto bit(v2_flag flag) -> mask_type {
return mask_type{1} << static_cast<unsigned>(flag);
}
constexpr auto bit(extra_capability capability) -> mask_type {
return mask_type{1} << static_cast<unsigned>(capability);
}
constexpr mask_type short_argument = bit(v2_flag::ShortArgument);
constexpr mask_type long_argument = bit(v2_flag::LongArgument);
constexpr mask_type positional = bit(v2_flag::Positional);
constexpr mask_type position = bit(v2_flag::Position);
constexpr mask_type help_text = bit(v2_flag::HelpText);
constexpr mask_type action = bit(v2_flag::Action);
constexpr mask_type required = bit(v2_flag::Required);
constexpr mask_type reference = bit(v2_flag::Reference);
constexpr mask_type accumulate = bit(v2_flag::Accumulate);
constexpr mask_type count = bit(
static_cast<v2_flag>(static_cast<int>(v2_flag::Accumulate) + (static_cast<int>(extra_capability::Store) |
static_cast<int>(extra_capability::Flag))));
constexpr mask_type store = bit(extra_capability::Store);
constexpr mask_type flag = bit(extra_capability::Flag);
constexpr mask_type value_mode_group = action | reference | accumulate | count | store | flag;
constexpr mask_type initial = short_argument | long_argument | positional | help_text | action | required |
reference | accumulate | count | store | flag;
constexpr mask_type storable_mode_group = (count | store | flag | accumulate | reference) >> 1;
constexpr auto has(mask_type mask, mask_type capability) -> bool {
return (mask & capability) == capability;
}
constexpr auto remove(mask_type mask, mask_type capability) -> mask_type {
return mask & ~capability;
}
constexpr auto replace(mask_type mask, mask_type remove_bits, mask_type add_bits = 0) -> mask_type {
return (mask & ~remove_bits) | add_bits;
}
constexpr auto has_selected_identifier(mask_type mask) -> bool {
return !has(mask, short_argument) || !has(mask, long_argument) || !has(mask, positional);
}
constexpr auto is_buildable(mask_type mask) -> bool {
return has_selected_identifier(mask);
}
constexpr auto is_build_and_gettable(mask_type mask) -> bool {
return has_selected_identifier(mask) && has(mask, storable_mode_group);
}
} // namespace builder_mask
template <typename store_type> class container {
public:
store_type get() const {
return m_container.get();
}
store_type &operator*() {
return m_container.operator*();
}
store_type *operator->() {
return m_container.operator->();
}
operator bool() {
return m_container.operator bool();
}
private:
container() = default;
explicit container(store_type *ptr) {
m_container = std::shared_ptr<store_type>(ptr, [](store_type *) {
/* noop. we don't own this reference, so it is not ours to free, but still use std::shared_ptr to manage
* it. */
});
}
void set_container(store_type const &container) {
m_container = std::make_shared<store_type>(container);
}
std::shared_ptr<store_type> m_container;
template <builder_mask::mask_type mask, typename __store_type> friend class argument;
};
template <builder_mask::mask_type mask = builder_mask::initial, typename store_type = non_type> class argument {
public:
using mask_type = builder_mask::mask_type;
using v2_flag = argument_parser::v2::add_argument_flags;
using value_mode = builder_mask::value_mode;
static auto start() -> argument<builder_mask::initial> {
return {};
}
template <mask_type current_mask = mask,
std::enable_if_t<builder_mask::has(current_mask, builder_mask::short_argument), int> = 0>
auto short_argument(std::string short_name) const
-> argument<builder_mask::replace(current_mask, builder_mask::short_argument | builder_mask::positional |
builder_mask::position),
store_type> {
using next_argument =
argument<builder_mask::replace(current_mask, builder_mask::short_argument | builder_mask::positional |
builder_mask::position),
store_type>;
next_argument next{*this};
next.m_short_argument = std::move(short_name);
return next;
}
template <mask_type current_mask = mask,
std::enable_if_t<builder_mask::has(current_mask, builder_mask::long_argument), int> = 0>
auto long_argument(std::string long_name) const
-> argument<builder_mask::replace(current_mask, builder_mask::long_argument | builder_mask::positional |
builder_mask::position),
store_type> {
using next_argument =
argument<builder_mask::replace(current_mask, builder_mask::long_argument | builder_mask::positional |
builder_mask::position),
store_type>;
next_argument next{*this};
next.m_long_argument = std::move(long_name);
return next;
}
template <mask_type current_mask = mask,
std::enable_if_t<builder_mask::has(current_mask, builder_mask::positional), int> = 0>
auto positional(std::string positional_name) const
-> argument<builder_mask::replace(current_mask,
builder_mask::short_argument | builder_mask::long_argument |
builder_mask::positional | builder_mask::flag,
builder_mask::position),
store_type> {
using next_argument =
argument<builder_mask::replace(current_mask,
builder_mask::short_argument | builder_mask::long_argument |
builder_mask::positional | builder_mask::flag | builder_mask::count,
builder_mask::position),
store_type>;
next_argument next{*this};
next.m_positional_name = std::move(positional_name);
return next;
}
template <mask_type current_mask = mask,
std::enable_if_t<builder_mask::has(current_mask, builder_mask::position), int> = 0>
auto position(int index) const
-> argument<builder_mask::remove(current_mask, builder_mask::position), store_type> {
using next_argument = argument<builder_mask::remove(current_mask, builder_mask::position), store_type>;
next_argument next{*this};
next.m_position = index;
return next;
}
template <mask_type current_mask = mask,
std::enable_if_t<builder_mask::has(current_mask, builder_mask::help_text), int> = 0>
auto help_text(std::string help) const
-> argument<builder_mask::remove(current_mask, builder_mask::help_text), store_type> {
using next_argument = argument<builder_mask::remove(current_mask, builder_mask::help_text), store_type>;
next_argument next{*this};
next.m_help_text = std::move(help);
return next;
}
template <mask_type current_mask = mask,
std::enable_if_t<builder_mask::has(current_mask, builder_mask::required), int> = 0>
auto required(bool value = true) const
-> argument<builder_mask::remove(current_mask, builder_mask::required), store_type> {
using next_argument = argument<builder_mask::remove(current_mask, builder_mask::required), store_type>;
next_argument next{*this};
next.m_required = value;
return next;
}
template <typename T = std::string, mask_type current_mask = mask,
std::enable_if_t<builder_mask::has(current_mask, builder_mask::store), int> = 0>
auto store() const -> argument<builder_mask::remove(current_mask, builder_mask::value_mode_group), T> {
static_assert(!std::is_same_v<T, void>,
"store<void>() is not supported. Use flag() for boolean-style arguments.");
using next_argument = argument<builder_mask::remove(current_mask, builder_mask::value_mode_group), T>;
next_argument next{*this};
next.m_value_mode = value_mode::store;
return next;
}
template <typename T = std::string, mask_type current_mask = mask,
std::enable_if_t<builder_mask::has(current_mask, builder_mask::accumulate), int> = 0>
auto accumulate() const -> argument<builder_mask::replace(current_mask, builder_mask::value_mode_group,
builder_mask::storable_mode_group),
std::vector<T>> {
using vector_type = std::vector<T>;
using next_argument = argument<builder_mask::replace(current_mask, builder_mask::value_mode_group,
builder_mask::storable_mode_group),
vector_type>;
next_argument next{*this};
next.m_value_mode = value_mode::accumulate;
return next;
}
template <mask_type current_mask = mask,
std::enable_if_t<builder_mask::has(current_mask, builder_mask::accumulate), int> = 0, typename T>
auto accumulate(T &value) const -> argument<
builder_mask::replace(current_mask, builder_mask::value_mode_group, builder_mask::storable_mode_group), T> {
static_assert(argument_parser::v2::deducers::is_vector_v<T>,
"accumulate(target) requires a std::vector target.");
using next_argument = argument<builder_mask::replace(current_mask, builder_mask::value_mode_group,
builder_mask::storable_mode_group),
T>;
next_argument next{*this};
next.m_reference = std::addressof(value);
next.m_value_mode = value_mode::accumulate;
return next;
}
template <mask_type current_mask = mask,
std::enable_if_t<builder_mask::has(current_mask, builder_mask::count), int> = 0>
auto count() const -> argument<builder_mask::replace(current_mask, builder_mask::value_mode_group,
builder_mask::storable_mode_group),
int> {
using next_argument = argument<builder_mask::replace(current_mask, builder_mask::value_mode_group,
builder_mask::storable_mode_group),
int>;
next_argument next{*this};
next.m_value_mode = value_mode::count;
return next;
}
template <mask_type current_mask = mask,
std::enable_if_t<builder_mask::has(current_mask, builder_mask::count), int> = 0>
auto count(int &value) const -> argument<builder_mask::replace(current_mask, builder_mask::value_mode_group,
builder_mask::storable_mode_group),
int> {
using next_argument = argument<builder_mask::replace(current_mask, builder_mask::value_mode_group,
builder_mask::storable_mode_group),
int>;
next_argument next{*this};
next.m_reference = std::addressof(value);
next.m_value_mode = value_mode::count;
return next;
}
template <mask_type current_mask = mask,
std::enable_if_t<builder_mask::has(current_mask, builder_mask::flag), int> = 0>
auto flag() const -> argument<builder_mask::replace(current_mask, builder_mask::value_mode_group,
builder_mask::storable_mode_group),
bool> {
using next_argument = argument<builder_mask::replace(current_mask, builder_mask::value_mode_group,
builder_mask::storable_mode_group),
bool>;
next_argument next{*this};
next.m_value_mode = value_mode::flag;
return next;
}
template <mask_type current_mask = mask,
std::enable_if_t<builder_mask::has(current_mask, builder_mask::reference), int> = 0, typename T>
auto reference(T &value) const -> argument<
builder_mask::replace(current_mask, builder_mask::value_mode_group, builder_mask::storable_mode_group), T> {
using next_argument = argument<builder_mask::replace(current_mask, builder_mask::value_mode_group,
builder_mask::storable_mode_group),
T>;
next_argument next{*this};
next.m_reference = std::addressof(value);
next.m_value_mode = value_mode::reference;
return next;
}
template <mask_type current_mask = mask,
std::enable_if_t<builder_mask::has(current_mask, builder_mask::action), int> = 0, typename Callable>
auto action(Callable &&handler) const -> std::enable_if_t<
std::is_invocable_r_v<void, Callable>,
argument<builder_mask::remove(current_mask, builder_mask::value_mode_group), non_type>> {
using next_argument =
argument<builder_mask::remove(current_mask, builder_mask::value_mode_group), non_type>;
next_argument next{*this};
next.m_action = std::make_shared<argument_parser::action_no_param>(
std::function<void()>(std::forward<Callable>(handler)));
next.m_value_mode = value_mode::action_no_param;
return next;
}
template <typename T = std::string, mask_type current_mask = mask,
std::enable_if_t<builder_mask::has(current_mask, builder_mask::action), int> = 0, typename Callable>
auto action(Callable &&handler) const
-> std::enable_if_t<std::is_invocable_r_v<void, Callable, const T &>,
argument<builder_mask::remove(current_mask, builder_mask::value_mode_group), T>> {
static_assert(!std::is_same_v<T, void>,
"action<void>(...) is not supported. Use action([] { ... }) instead.");
using next_argument = argument<builder_mask::remove(current_mask, builder_mask::value_mode_group), T>;
next_argument next{*this};
next.m_action = std::make_shared<argument_parser::action_with_param<T>>(
std::function<void(const T &)>(std::forward<Callable>(handler)));
next.m_value_mode = value_mode::action_with_param;
return next;
}
template <mask_type current_mask = mask, std::enable_if_t<builder_mask::is_buildable(current_mask), int> = 0>
auto build(argument_parser::v2::base_parser &parser) const -> void {
assert_has_identifier();
switch (m_value_mode) {
case value_mode::flag:
build_flag(parser);
return;
case value_mode::action_no_param:
build_action_with_no_param(parser);
return;
case value_mode::store:
if constexpr (!std::is_same_v<store_type, non_type>) {
build_store(parser);
return;
}
break;
case value_mode::reference:
if constexpr (!std::is_same_v<store_type, non_type>) {
build_reference(parser);
return;
}
break;
case value_mode::accumulate:
case value_mode::count:
if constexpr (!std::is_same_v<store_type, non_type>) {
build_accumulate(parser);
return;
}
break;
case value_mode::action_with_param:
if constexpr (!std::is_same_v<store_type, non_type>) {
build_action_with_param(parser);
return;
}
break;
case value_mode::unresolved:
if (is_positional()) {
build_default_positional(parser);
} else {
build_flag(parser);
}
return;
}
throw std::logic_error("The builder reached build() without a supported terminal value mode.");
}
template <mask_type current_mask = mask,
std::enable_if_t<builder_mask::is_build_and_gettable(current_mask), int> = 0>
auto build_and_get(argument_parser::v2::base_parser &parser) const -> container<store_type> {
assert_has_identifier();
switch (m_value_mode) {
case value_mode::store:
case value_mode::flag:
case value_mode::unresolved:
case value_mode::accumulate:
case value_mode::count:
build(parser);
break;
default:
throw std::logic_error("The builder reached build() without a supported terminal value mode.");
}
if (m_value_mode == value_mode::accumulate && m_reference != nullptr) {
return container(m_reference);
}
std::string lk = lookup_key();
container<store_type> container;
parser.on_complete([lk, &container](base_parser const &p) {
auto value = p.get_optional<store_type>(lk);
if (value.has_value()) {
container.set_container(*value);
}
});
return container;
}
private:
argument() = default;
template <mask_type other_mask, typename other_store_type>
argument(argument<other_mask, other_store_type> const &other)
: m_short_argument(other.m_short_argument), m_long_argument(other.m_long_argument),
m_positional_name(other.m_positional_name), m_position(other.m_position), m_help_text(other.m_help_text),
m_required(other.m_required), m_action(other.m_action), m_reference(copy_reference(other.m_reference)),
m_value_mode(other.m_value_mode) {}
template <typename T> using typed_map = std::unordered_map<v2_flag, v2::base_parser::typed_flag_value<T>>;
using non_typed_map = std::unordered_map<v2_flag, v2::base_parser::non_typed_flag_value>;
auto is_positional() const -> bool {
return !m_positional_name.empty();
}
auto lookup_key() const -> std::string {
if (is_positional()) {
return m_positional_name;
}
if (!m_long_argument.empty()) {
return m_long_argument;
}
if (!m_short_argument.empty()) {
return m_short_argument;
}
throw std::logic_error("No argument identifier is available for lookup.");
}
auto assert_has_identifier() const -> void {
if (!is_positional() && m_short_argument.empty() && m_long_argument.empty()) {
throw std::logic_error("build() requires short_argument(), long_argument(), or positional().");
}
}
template <typename Map> auto add_common_pairs(Map &pairs) const -> void {
using namespace argument_parser::v2::flags;
if (is_positional()) {
pairs[Positional] = m_positional_name;
if (m_position.has_value()) {
pairs[Position] = m_position.value();
}
} else {
if (!m_short_argument.empty()) {
pairs[ShortArgument] = m_short_argument;
}
if (!m_long_argument.empty()) {
pairs[LongArgument] = m_long_argument;
}
}
if (!m_help_text.empty()) {
pairs[HelpText] = m_help_text;
}
if (m_required) {
pairs[Required] = true;
}
}
template <typename T> auto make_typed_pairs() const -> typed_map<T> {
typed_map<T> pairs;
add_common_pairs(pairs);
return pairs;
}
auto make_non_typed_pairs() const -> non_typed_map {
non_typed_map pairs;
add_common_pairs(pairs);
return pairs;
}
auto build_flag(argument_parser::v2::base_parser &parser) const -> void {
auto pairs = make_non_typed_pairs();
parser.add_argument(pairs);
}
auto build_default_positional(argument_parser::v2::base_parser &parser) const -> void {
auto pairs = make_non_typed_pairs();
parser.add_argument(pairs);
}
auto build_store(argument_parser::v2::base_parser &parser) const -> void {
auto pairs = make_typed_pairs<store_type>();
parser.template add_argument<store_type>(pairs);
}
auto build_reference(argument_parser::v2::base_parser &parser) const -> void {
auto pairs = make_typed_pairs<store_type>();
auto *target = m_reference;
if (target == nullptr) {
throw std::logic_error("reference() was selected without a target.");
}
pairs[argument_parser::v2::flags::Reference] = target;
parser.template add_argument<store_type>(pairs);
}
auto build_accumulate(argument_parser::v2::base_parser &parser) const -> void {
auto pairs = make_typed_pairs<store_type>();
if (m_reference != nullptr) {
pairs[argument_parser::v2::flags::Accumulate] = m_reference;
} else {
pairs[argument_parser::v2::flags::Accumulate] = true;
}
parser.template add_argument<store_type>(pairs);
}
auto build_action_with_param(argument_parser::v2::base_parser &parser) const -> void {
auto const *typed_action =
dynamic_cast<argument_parser::action_with_param<store_type> const *>(m_action.get());
if (typed_action == nullptr) {
throw std::logic_error("Stored action is not compatible with the requested parameter type.");
}
auto pairs = make_typed_pairs<store_type>();
pairs[argument_parser::v2::flags::Action] = *typed_action;
parser.template add_argument<store_type>(pairs);
}
auto build_action_with_no_param(argument_parser::v2::base_parser &parser) const -> void {
auto const *nonparametered_action = dynamic_cast<argument_parser::action_no_param const *>(m_action.get());
if (nonparametered_action == nullptr) {
throw std::logic_error("Stored action is not a non-parametered action.");
}
if (is_positional()) {
auto wrapped_action = argument_parser::helpers::make_action<std::string>(
[action = *nonparametered_action](std::string const &) { action.invoke(); });
auto pairs = make_typed_pairs<std::string>();
pairs[argument_parser::v2::flags::Action] = wrapped_action;
parser.template add_argument<std::string>(pairs);
return;
}
auto pairs = make_non_typed_pairs();
pairs[argument_parser::v2::flags::Action] = *nonparametered_action;
parser.add_argument(pairs);
}
std::string m_short_argument{};
std::string m_long_argument{};
std::string m_positional_name{};
std::optional<int> m_position{};
std::string m_help_text{};
bool m_required = false;
std::shared_ptr<argument_parser::action_base const> m_action{};
store_type *m_reference = nullptr;
value_mode m_value_mode = value_mode::unresolved;
template <typename other_store_type> static auto copy_reference(other_store_type *reference) -> store_type * {
if constexpr (std::is_same_v<store_type, other_store_type>) {
return reference;
} else {
return nullptr;
}
}
template <mask_type other_mask, typename other_store_type> friend class argument;
};
static inline auto new_argument() {
return argument<>::start();
}
namespace assertions {
struct noop_handler {
void operator()() const {}
};
template <typename T> struct parameter_sink {
void operator()(T const &) const {}
};
template <typename T, typename = void> struct can_use_help_text : std::false_type {};
template <typename T>
struct can_use_help_text<T, std::void_t<decltype(std::declval<T>().help_text(std::declval<std::string>()))>>
: std::true_type {};
template <typename T, typename = void> struct can_use_position : std::false_type {};
template <typename T>
struct can_use_position<T, std::void_t<decltype(std::declval<T>().position(0))>> : std::true_type {};
template <typename T, typename = void> struct can_use_nonparametered_action : std::false_type {};
template <typename T>
struct can_use_nonparametered_action<T, std::void_t<decltype(std::declval<T>().action(noop_handler{}))>>
: std::true_type {};
template <typename T, typename U, typename = void> struct can_use_parametered_action : std::false_type {};
template <typename T, typename U>
struct can_use_parametered_action<
T, U, std::void_t<decltype(std::declval<T>().template action<U>(parameter_sink<U>{}))>> : std::true_type {};
template <typename T, typename U, typename = void> struct can_use_store : std::false_type {};
template <typename T, typename U>
struct can_use_store<T, U, std::void_t<decltype(std::declval<T>().template store<U>())>> : std::true_type {};
template <typename T, typename = void> struct can_use_flag : std::false_type {};
template <typename T>
struct can_use_flag<T, std::void_t<decltype(std::declval<T>().flag())>> : std::true_type {};
template <typename T, typename U, typename = void> struct can_use_reference : std::false_type {};
template <typename T, typename U>
struct can_use_reference<T, U, std::void_t<decltype(std::declval<T>().reference(std::declval<U &>()))>>
: std::true_type {};
using after_help_text = decltype(argument<>::start().help_text("help"));
static_assert(!can_use_help_text<after_help_text>::value, "help_text() should be single-use.");
using after_positional = decltype(argument<>::start().positional("path"));
static_assert(can_use_position<after_positional>::value, "positional() should unlock position().");
using after_position = decltype(argument<>::start().positional("path").position(0));
static_assert(!can_use_position<after_position>::value, "position() should be single-use.");
using after_positional_mode_selection = decltype(argument<>::start().positional("path").store<>());
static_assert(!can_use_flag<after_positional_mode_selection>::value,
"flag() should not be available for positional arguments.");
using after_nonparametered_action =
decltype(argument<>::start().short_argument("v").help_text("verbose").action(noop_handler{}));
static_assert(!can_use_nonparametered_action<after_nonparametered_action>::value,
"action() should not remain callable after selecting a non-parametered action.");
static_assert(!can_use_parametered_action<after_nonparametered_action, int>::value,
"typed action() should also be disabled after selecting a non-parametered action.");
static_assert(!can_use_store<after_nonparametered_action, int>::value,
"store() should be mutually exclusive with action().");
static_assert(!can_use_flag<after_nonparametered_action>::value,
"flag() should be mutually exclusive with action().");
static_assert(!can_use_reference<after_nonparametered_action, int>::value,
"reference() should be mutually exclusive with action().");
using after_parametered_action =
decltype(argument<>::start().positional("count").position(0).action<int>(parameter_sink<int>{}));
static_assert(!can_use_nonparametered_action<after_parametered_action>::value,
"non-parametered action() should be disabled after selecting a typed action.");
static_assert(!can_use_parametered_action<after_parametered_action, std::string>::value,
"typed action() should be single-use regardless of the parameter type.");
} // namespace assertions
} // namespace argument_parser::builder
#endif

View File

@@ -1,3 +1,5 @@
// ReSharper disable CppFunctionIsNotImplemented
// ReSharper disable All
#pragma once
#ifndef ARGUMENT_PARSER_HPP
#define ARGUMENT_PARSER_HPP
@@ -64,9 +66,9 @@ namespace argument_parser {
copyable_atomic &operator=(copyable_atomic &&other) noexcept = default;
~copyable_atomic() = default;
T operator=(T desired) noexcept {
copyable_atomic &operator=(T desired) noexcept {
store(desired);
return desired;
return *this;
}
operator T() const noexcept {
@@ -79,7 +81,7 @@ namespace argument_parser {
}
}
T load(std::memory_order order = std::memory_order_seq_cst) const noexcept {
[[nodiscard]] T load(std::memory_order order = std::memory_order_seq_cst) const noexcept {
return value ? value->load(order) : T{};
}
@@ -102,9 +104,9 @@ namespace argument_parser {
[[nodiscard]] virtual std::unique_ptr<action_base> clone() const = 0;
};
template <typename T> class parametered_action : public action_base {
template <typename T> class action_with_param : public action_base {
public:
explicit parametered_action(std::function<void(const T &)> const &handler) : handler(handler) {}
explicit action_with_param(std::function<void(const T &)> const &handler) : handler(handler) {}
using parameter_type = T;
void invoke(const T &arg) const {
handler(arg);
@@ -124,7 +126,7 @@ namespace argument_parser {
T parsed_value = parsing_traits::parser_trait<T>::parse(param);
parse_success = true;
invoke(parsed_value);
} catch (const std::runtime_error &e) {
} catch (const std::runtime_error &_) {
if (!parse_success) {
auto [format_hint, purpose_hint] = get_trait_hints();
if (purpose_hint.empty())
@@ -147,16 +149,16 @@ namespace argument_parser {
}
[[nodiscard]] std::unique_ptr<action_base> clone() const override {
return std::make_unique<parametered_action<T>>(handler);
return std::make_unique<action_with_param<T>>(handler);
}
private:
std::function<void(const T &)> handler;
};
class non_parametered_action : public action_base {
class action_no_param : public action_base {
public:
explicit non_parametered_action(std::function<void()> const &handler) : handler(handler) {}
explicit action_no_param(std::function<void()> const &handler) : handler(handler) {}
void invoke() const override {
handler();
@@ -175,7 +177,7 @@ namespace argument_parser {
}
[[nodiscard]] std::unique_ptr<action_base> clone() const override {
return std::make_unique<non_parametered_action>(handler);
return std::make_unique<action_no_param>(handler);
}
private:
@@ -203,6 +205,7 @@ namespace argument_parser {
[[nodiscard]] bool expects_parameter() const;
[[nodiscard]] std::string get_help_text() const;
[[nodiscard]] bool is_positional() const;
[[nodiscard]] bool is_positional_accumulator() const;
[[nodiscard]] std::optional<int> get_position_index() const;
private:
@@ -210,6 +213,7 @@ namespace argument_parser {
void set_invoked(bool val);
void set_help_text(std::string const &text);
void set_positional(bool val);
void set_positional_accumulator(bool val);
void set_position_index(std::optional<int> idx);
friend class base_parser;
@@ -221,20 +225,40 @@ namespace argument_parser {
bool invoked;
std::string help_text;
bool positional = false;
bool positional_accumulator = false;
std::optional<int> position_index = std::nullopt;
};
namespace helpers {
template <typename T>
static parametered_action<T> make_parametered_action(std::function<void(const T &)> const &function) {
return parametered_action<T>(function);
template <typename T> static action_with_param<T> make_action(std::function<void(const T &)> const &function) {
return action_with_param<T>(function);
}
static non_parametered_action make_non_parametered_action(std::function<void()> const &function) {
return non_parametered_action(function);
static action_no_param make_action(std::function<void()> const &function) {
return action_no_param(function);
}
} // namespace helpers
struct parser_settings {
bool should_exit_on_error = true;
bool should_exit_on_missing_required = true;
bool should_exit_on_unknown_argument = true;
bool should_exit_on_help = true;
bool ignore_unknown_arguments = false;
bool ignore_errors = false;
};
constexpr static inline parser_settings no_exit{
.should_exit_on_error = false,
.should_exit_on_missing_required = false,
.should_exit_on_unknown_argument = false,
.should_exit_on_help = false,
.ignore_unknown_arguments = false,
.ignore_errors = false,
};
/**
* @brief Base class for parsing arguments from the command line.
*
@@ -246,7 +270,7 @@ namespace argument_parser {
public:
template <typename T>
void add_argument(std::string const &short_arg, std::string const &long_arg, std::string const &help_text,
parametered_action<T> const &action, bool required) {
action_with_param<T> const &action, bool required) {
base_add_argument(short_arg, long_arg, help_text, action, required);
}
@@ -257,7 +281,7 @@ namespace argument_parser {
}
void add_argument(std::string const &short_arg, std::string const &long_arg, std::string const &help_text,
non_parametered_action const &action, bool required) {
action_no_param const &action, bool required) {
base_add_argument(short_arg, long_arg, help_text, action, required);
}
@@ -268,7 +292,7 @@ namespace argument_parser {
template <typename T>
void add_positional_argument(std::string const &name, std::string const &help_text,
parametered_action<T> const &action, bool required,
action_with_param<T> const &action, bool required,
std::optional<int> position = std::nullopt) {
base_add_positional_argument(name, help_text, action, required, position);
}
@@ -279,6 +303,13 @@ namespace argument_parser {
base_add_positional_argument<T>(name, help_text, required, position);
}
template <typename T>
void add_positional_accumulator(std::string const &name, std::string const &help_text,
action_with_param<T> const &action, bool required,
std::optional<int> position = std::nullopt) {
base_add_positional_argument(name, help_text, action, required, position, true);
}
void on_complete(std::function<void(base_parser const &)> const &action);
template <typename T> std::optional<T> get_optional(std::string const &arg) const {
@@ -317,28 +348,41 @@ namespace argument_parser {
return _current_conventions;
}
private:
bool test_conventions(std::initializer_list<conventions::convention const *const> convention_types,
std::unordered_map<std::string, std::string> &values_for_arguments,
std::vector<std::pair<std::string, argument>> &found_arguments,
std::optional<argument> &found_help, std::vector<std::string>::iterator &it,
std::stringstream &error_stream);
void extract_arguments(std::initializer_list<conventions::convention const *const> convention_types,
std::unordered_map<std::string, std::string> &values_for_arguments,
std::vector<std::pair<std::string, argument>> &found_arguments,
std::optional<argument> &found_help);
std::unordered_map<int, std::any> &ref_stored_arguments() {
return stored_arguments;
}
void invoke_arguments(std::unordered_map<std::string, std::string> const &values_for_arguments,
std::vector<std::pair<std::string, argument>> &found_arguments,
std::optional<argument> const &found_help);
void enforce_creation_thread();
void on_complete(std::function<void(base_parser const &)> const &handler, bool to_start);
void set_settings(parser_settings const &settings) {
m_settings = settings;
}
private:
parser_settings m_settings;
struct found_argument {
std::string key;
argument arg;
std::optional<std::string> value = std::nullopt;
};
bool test_conventions(std::initializer_list<conventions::convention const *const> convention_types,
std::vector<found_argument> &found_arguments, std::optional<argument> &found_help,
std::vector<std::string>::iterator &it, std::stringstream &error_stream);
void extract_arguments(std::initializer_list<conventions::convention const *const> convention_types,
std::vector<found_argument> &found_arguments, std::optional<argument> &found_help);
void invoke_arguments(std::vector<found_argument> &found_arguments, std::optional<argument> const &found_help);
void enforce_creation_thread() const;
void assert_argument_not_exist(std::string const &short_arg, std::string const &long_arg) const;
void assert_positional_not_exist(std::string const &name) const;
void assert_can_place_positional(int id, std::optional<int> position, bool accumulator) const;
static void set_argument_status(bool is_required, std::string const &help_text, argument &arg);
void place_argument(int id, argument const &arg, std::string const &short_arg, std::string const &long_arg);
void place_positional_argument(int id, argument const &arg, std::string const &name,
std::optional<int> position);
std::optional<int> position, bool accumulator = false);
[[nodiscard]] std::optional<size_t> next_positional_slot(size_t start) const;
template <typename ActionType>
void base_add_argument(std::string const &short_arg, std::string const &long_arg, std::string const &help_text,
@@ -356,13 +400,12 @@ namespace argument_parser {
assert_argument_not_exist(short_arg, long_arg);
int id = id_counter.fetch_add(1);
if constexpr (std::is_same_v<StoreType, void>) {
auto action =
helpers::make_non_parametered_action([id, this] { stored_arguments[id] = std::any{true}; });
auto action = helpers::make_action([id, this] { stored_arguments[id] = std::any{true}; });
argument arg(id, short_arg + "|" + long_arg, action);
set_argument_status(required, help_text, arg);
place_argument(id, arg, short_arg, long_arg);
} else {
auto action = helpers::make_parametered_action<StoreType>(
auto action = helpers::make_action<StoreType>(
[id, this](StoreType const &value) { stored_arguments[id] = std::any{value}; });
argument arg(id, short_arg + "|" + long_arg, action);
set_argument_status(required, help_text, arg);
@@ -373,14 +416,15 @@ namespace argument_parser {
template <typename ActionType>
void base_add_positional_argument(std::string const &name, std::string const &help_text,
ActionType const &action, bool required,
std::optional<int> position = std::nullopt) {
std::optional<int> position = std::nullopt, bool accumulator = false) {
assert_positional_not_exist(name);
int id = id_counter.fetch_add(1);
argument arg(id, name, action);
set_argument_status(required, help_text, arg);
arg.set_positional(true);
arg.set_positional_accumulator(accumulator);
arg.set_position_index(position);
place_positional_argument(id, arg, name, position);
place_positional_argument(id, arg, name, position, accumulator);
}
template <typename StoreType>
@@ -388,7 +432,7 @@ namespace argument_parser {
std::optional<int> position = std::nullopt) {
assert_positional_not_exist(name);
int id = id_counter.fetch_add(1);
auto action = helpers::make_parametered_action<StoreType>(
auto action = helpers::make_action<StoreType>(
[id, this](StoreType const &value) { stored_arguments[id] = std::any{value}; });
argument arg(id, name, action);
set_argument_status(required, help_text, arg);

View File

@@ -0,0 +1,18 @@
#pragma once
#ifndef ARGUMENT_PARSER_EXCEPTIONS_HPP
#define ARGUMENT_PARSER_EXCEPTIONS_HPP
#include <stdexcept>
namespace argument_parser {
namespace parser {
class unknown_argument_exception : public std::runtime_error {
public:
unknown_argument_exception(const std::string &message) : std::runtime_error(message) {}
};
} // namespace parser
} // namespace argument_parser
#endif // ARGUMENT_PARSER_EXCEPTIONS_HPP

View File

@@ -25,13 +25,13 @@ namespace argument_parser {
public:
fake_parser() = default;
fake_parser(std::string program_name, std::vector<std::string> const &arguments);
fake_parser(std::string const &program_name, std::vector<std::string> &&arguments);
fake_parser(std::string const &program_name, std::initializer_list<std::string> const &arguments);
fake_parser(std::string program_name, std::vector<std::string> &&arguments);
fake_parser(std::string program_name, std::initializer_list<std::string> const &arguments);
void set_program_name(std::string const &program_name);
void set_parsed_arguments(std::vector<std::string> const &parsed_arguments);
};
}
} // namespace v2
} // namespace argument_parser
#endif

View File

@@ -2,19 +2,29 @@
#include "traits.hpp"
#include <argument_parser.hpp>
#include <array>
#include <cstdlib>
#include <initializer_list>
#include <memory>
#include <optional>
#include <stdexcept>
#include <string>
#include <string_view>
#include <type_traits>
#include <unordered_map>
#include <variant>
#include <vector>
namespace argument_parser::v2 {
enum class add_argument_flags { ShortArgument, LongArgument, Positional, Position, HelpText, Action, Required };
enum class add_argument_flags {
ShortArgument,
LongArgument,
Positional,
Position,
HelpText,
Action,
Required,
Reference,
Accumulate
};
namespace flags {
constexpr static inline add_argument_flags ShortArgument = add_argument_flags::ShortArgument;
@@ -24,19 +34,38 @@ namespace argument_parser::v2 {
constexpr static inline add_argument_flags Required = add_argument_flags::Required;
constexpr static inline add_argument_flags Positional = add_argument_flags::Positional;
constexpr static inline add_argument_flags Position = add_argument_flags::Position;
constexpr static inline add_argument_flags Reference = add_argument_flags::Reference;
constexpr static inline add_argument_flags Accumulate = add_argument_flags::Accumulate;
} // namespace flags
class base_parser : private argument_parser::base_parser {
namespace deducers {
template <typename, typename = void> struct has_value_type : std::false_type {};
template <typename T> struct has_value_type<T, std::void_t<typename T::value_type>> : std::true_type {};
template <typename T> struct is_vector {
static constexpr bool test() {
if constexpr (has_value_type<T>::value) {
return std::is_same_v<T, std::vector<typename T::value_type, typename T::allocator_type>>;
} else {
return false;
}
}
};
template <typename T> constexpr bool is_vector_v = is_vector<T>::test();
} // namespace deducers
class base_parser : argument_parser::base_parser {
public:
template <typename T> using typed_flag_value = std::variant<std::string, parametered_action<T>, bool, int>;
using non_typed_flag_value = std::variant<std::string, non_parametered_action, bool, int>;
template <typename T> using typed_flag_value = std::variant<std::string, action_with_param<T>, bool, int, T *>;
using non_typed_flag_value = std::variant<std::string, action_no_param, bool, int>;
template <typename T> using typed_argument_pair = std::pair<add_argument_flags, typed_flag_value<T>>;
using non_typed_argument_pair = std::pair<add_argument_flags, non_typed_flag_value>;
template <typename T>
void add_argument(std::unordered_map<add_argument_flags, typed_flag_value<T>> const &argument_pairs) {
add_argument_impl<true, parametered_action<T>, T>(argument_pairs);
add_argument_impl<true, action_with_param<T>, T>(argument_pairs);
}
template <typename T> void add_argument(std::initializer_list<typed_argument_pair<T>> const &pairs) {
@@ -60,7 +89,7 @@ namespace argument_parser::v2 {
}
void add_argument(std::unordered_map<add_argument_flags, non_typed_flag_value> const &argument_pairs) {
add_argument_impl<false, non_parametered_action, void>(argument_pairs);
add_argument_impl<false, action_no_param, void>(argument_pairs);
}
argument_parser::base_parser &to_v1() {
@@ -93,7 +122,7 @@ namespace argument_parser::v2 {
void prepare_help_flag(bool should_exit = true) {
add_argument({{flags::ShortArgument, "h"},
{flags::LongArgument, "help"},
{flags::Action, helpers::make_non_parametered_action([this, should_exit]() {
{flags::Action, helpers::make_action([this, should_exit] {
this->display_help(this->current_conventions());
if (should_exit) {
std::exit(0);
@@ -102,6 +131,10 @@ namespace argument_parser::v2 {
{flags::HelpText, "Prints this help text."}});
}
void set_settings(parser_settings const &settings) {
base::set_settings(settings);
}
private:
template <bool IsTyped, typename ActionType, typename T, typename ArgsMap>
void add_argument_impl(ArgsMap const &argument_pairs) {
@@ -116,12 +149,13 @@ namespace argument_parser::v2 {
std::string short_arg, long_arg, help_text;
std::unique_ptr<action_base> action;
bool required = false;
bool accumulates = false;
if (argument_pairs.find(add_argument_flags::ShortArgument) != argument_pairs.end()) {
if (has_flag(argument_pairs, add_argument_flags::ShortArgument)) {
found_params[extended_add_argument_flags::ShortArgument] = true;
short_arg = get_or_throw<std::string>(argument_pairs.at(add_argument_flags::ShortArgument), "short");
}
if (argument_pairs.find(add_argument_flags::LongArgument) != argument_pairs.end()) {
if (has_flag(argument_pairs, add_argument_flags::LongArgument)) {
found_params[extended_add_argument_flags::LongArgument] = true;
long_arg = get_or_throw<std::string>(argument_pairs.at(add_argument_flags::LongArgument), "long");
if (short_arg.empty())
@@ -131,17 +165,72 @@ namespace argument_parser::v2 {
long_arg = "-";
}
if (argument_pairs.find(add_argument_flags::Action) != argument_pairs.end()) {
if (has_flag(argument_pairs, add_argument_flags::Action)) {
found_params[extended_add_argument_flags::Action] = true;
action = get_or_throw<ActionType>(argument_pairs.at(add_argument_flags::Action), "action").clone();
}
if (argument_pairs.find(add_argument_flags::HelpText) != argument_pairs.end()) {
help_text = get_or_throw<std::string>(argument_pairs.at(add_argument_flags::HelpText), "help");
help_text = read_help_text(argument_pairs);
required = read_required(argument_pairs);
bool ref_mode = false;
if (has_flag(argument_pairs, add_argument_flags::Reference)) {
ref_mode = true;
if (!IsTyped) {
throw std::logic_error("Reference argument must be typed");
}
found_params[extended_add_argument_flags::Action] = true;
if constexpr (!std::is_same_v<T, void>) {
auto ref = get_or_throw<T *>(argument_pairs.at(add_argument_flags::Reference), "reference");
if (action) {
throw std::logic_error("Cannot use both action and reference for the same argument");
}
action = make_reference_action(ref);
} else {
throw std::logic_error("Reference argument must not be void");
}
}
if (argument_pairs.find(add_argument_flags::Required) != argument_pairs.end() &&
get_or_throw<bool>(argument_pairs.at(add_argument_flags::Required), "required")) {
required = true;
if (has_flag(argument_pairs, add_argument_flags::Accumulate)) {
if (!IsTyped)
throw std::logic_error("Accumulate argument must be typed");
found_params[extended_add_argument_flags::Action] = true;
accumulates = true;
if constexpr (!std::is_same_v<T, void>) {
if constexpr (std::is_same_v<T, int>) {
action = make_accumulate_action<T>(argument_pairs, ref_mode, short_arg, long_arg);
} else if constexpr (!deducers::is_vector_v<T>) {
throw std::logic_error("Expected vector or integer type");
} else {
if (action && !ref_mode) {
throw std::logic_error("Cannot use both action and accumulate for the same argument");
}
action = make_accumulate_action<T>(argument_pairs, ref_mode, short_arg, long_arg);
}
} else {
throw std::logic_error("Accumulate argument must not be void");
}
}
if (accumulates) {
if constexpr (!std::is_same_v<T, void> && deducers::is_vector_v<T>) {
if (suggest_candidate(found_params) == candidate_type::unknown) {
throw std::runtime_error(
"Could not match any add argument overload to given parameters. Are you "
"missing some required parameter?");
}
if (help_text.empty()) {
help_text = "Accepts repeated values.";
}
base::add_argument<typename T::value_type>(
short_arg, long_arg, help_text,
*static_cast<action_with_param<typename T::value_type> *>(&(*action)), required);
return;
}
}
auto suggested_add = suggest_candidate(found_params);
@@ -209,33 +298,65 @@ namespace argument_parser::v2 {
default:
throw std::runtime_error(
"Could not match the arguments against any overload. The suggested candidate was: " +
std::to_string((int(suggested_add))));
std::to_string(static_cast<int>(suggested_add)));
}
}
}
template <bool IsTyped, typename ActionType, typename T, typename ArgsMap>
void add_positional_argument_impl(ArgsMap const &argument_pairs) {
std::string positional_name =
auto positional_name =
get_or_throw<std::string>(argument_pairs.at(add_argument_flags::Positional), "positional");
std::string help_text;
std::unique_ptr<action_base> action;
bool required = false;
std::optional<int> position = std::nullopt;
bool ref_mode = false;
bool accumulates = false;
if (argument_pairs.find(add_argument_flags::Action) != argument_pairs.end()) {
if (has_flag(argument_pairs, add_argument_flags::Action)) {
action = get_or_throw<ActionType>(argument_pairs.at(add_argument_flags::Action), "action").clone();
}
if (argument_pairs.find(add_argument_flags::HelpText) != argument_pairs.end()) {
help_text = get_or_throw<std::string>(argument_pairs.at(add_argument_flags::HelpText), "help");
std::string help_text = read_help_text(argument_pairs);
required = read_required(argument_pairs);
std::optional<int> position = read_position(argument_pairs);
if (has_flag(argument_pairs, add_argument_flags::Reference)) {
ref_mode = true;
if (!IsTyped) {
throw std::logic_error("Reference argument must be typed");
}
if constexpr (!std::is_same_v<T, void>) {
if (!has_flag(argument_pairs, add_argument_flags::Accumulate)) {
auto ref = get_or_throw<T *>(argument_pairs.at(add_argument_flags::Reference), "reference");
if (action) {
throw std::logic_error("Cannot use both action and reference for the same argument");
}
action = make_reference_action(ref);
}
} else {
throw std::logic_error("Reference argument must not be void");
}
}
if (argument_pairs.find(add_argument_flags::Required) != argument_pairs.end() &&
get_or_throw<bool>(argument_pairs.at(add_argument_flags::Required), "required")) {
required = true;
}
if (argument_pairs.find(add_argument_flags::Position) != argument_pairs.end()) {
position = get_or_throw<int>(argument_pairs.at(add_argument_flags::Position), "position");
if (has_flag(argument_pairs, add_argument_flags::Accumulate)) {
if (!IsTyped)
throw std::logic_error("Accumulate positional argument must be typed");
accumulates = true;
if constexpr (!std::is_same_v<T, void>) {
if constexpr (!deducers::is_vector_v<T>) {
throw std::logic_error("Expected vector (type does not have value_type member)");
} else {
if (action && !ref_mode) {
throw std::logic_error("Cannot use both action and accumulate for the same argument");
}
action = make_accumulate_action<T>(argument_pairs, ref_mode, positional_name);
}
} else {
throw std::logic_error("Accumulate argument must not be void");
}
}
if (help_text.empty()) {
@@ -254,15 +375,24 @@ namespace argument_parser::v2 {
}
}
if (accumulates) {
if constexpr (!std::is_same_v<T, void> && deducers::is_vector_v<T>) {
base::add_positional_accumulator<typename T::value_type>(
positional_name, help_text,
*static_cast<action_with_param<typename T::value_type> *>(&(*action)), required, position);
return;
}
}
if constexpr (IsTyped) {
if (action) {
base::add_positional_argument<T>(positional_name, help_text,
*static_cast<ActionType *>(&(*action)), required, position);
base::add_positional_argument<T>(positional_name, help_text, *static_cast<ActionType *>(&(*action)),
required, position);
} else {
base::template add_positional_argument<T>(positional_name, help_text, required, position);
base::add_positional_argument<T>(positional_name, help_text, required, position);
}
} else {
base::template add_positional_argument<std::string>(positional_name, help_text, required, position);
base::add_positional_argument<std::string>(positional_name, help_text, required, position);
}
}
@@ -273,11 +403,7 @@ namespace argument_parser::v2 {
template <typename T, size_t S>
bool satisfies_at_least_one(std::array<T, S> const &arr, std::unordered_map<T, bool> const &map) {
for (const auto &req : arr) {
if (map.find(req) != map.end())
return true;
}
return false;
return std::any_of(arr.begin(), arr.end(), [&map](T const &entry) { return map.find(entry) != map.end(); });
}
candidate_type suggest_candidate(std::unordered_map<extended_add_argument_flags, bool> const &available_vars) {
@@ -289,8 +415,7 @@ namespace argument_parser::v2 {
if (available_vars.find(extended_add_argument_flags::Action) != available_vars.end()) {
if (available_vars.at(extended_add_argument_flags::IsTyped))
return candidate_type::typed_action;
else
return candidate_type::non_typed_action;
return candidate_type::non_typed_action;
}
if (available_vars.at(extended_add_argument_flags::IsTyped))
@@ -298,6 +423,158 @@ namespace argument_parser::v2 {
return candidate_type::store_boolean;
}
template <typename ArgsMap> static bool has_flag(ArgsMap const &argument_pairs, add_argument_flags flag) {
return argument_pairs.find(flag) != argument_pairs.end();
}
template <typename ArgsMap> std::string read_help_text(ArgsMap const &argument_pairs) {
if (has_flag(argument_pairs, add_argument_flags::HelpText)) {
return get_or_throw<std::string>(argument_pairs.at(add_argument_flags::HelpText), "help");
}
return "";
}
template <typename ArgsMap> bool read_required(ArgsMap const &argument_pairs) {
return has_flag(argument_pairs, add_argument_flags::Required) &&
get_or_throw<bool>(argument_pairs.at(add_argument_flags::Required), "required");
}
template <typename ArgsMap> std::optional<int> read_position(ArgsMap const &argument_pairs) {
if (has_flag(argument_pairs, add_argument_flags::Position)) {
return get_or_throw<int>(argument_pairs.at(add_argument_flags::Position), "position");
}
return std::nullopt;
}
template <typename T, typename T2, typename I>
std::variant<T, T2> get_either_or_throw(typed_flag_value<I> const &v, std::string_view key) {
if (auto p = std::get_if<T>(&v))
return *p;
if (auto p = std::get_if<T2>(&v))
return *p;
throw std::invalid_argument(std::string("variant type mismatch for key: ") + std::string(key));
}
template <typename T> std::unique_ptr<action_base> make_reference_action(T *target) {
return helpers::make_action<T>([target](T const &value) { *target = value; }).clone();
}
template <typename Vector> std::unique_ptr<action_base> make_accumulate_ref_action(Vector *target) {
if constexpr (std::is_same_v<Vector, int>) {
return helpers::make_action([target]() { *target += 1; }).clone();
} else {
using Value = typename Vector::value_type;
return helpers::make_action<Value>([target](Value const &value) { target->emplace_back(value); })
.clone();
}
}
template <typename Vector>
void store_accumulated_on_complete(std::string short_arg, std::string long_arg,
std::shared_ptr<Vector> accumulation_target) {
on_complete(
[this, short_arg = std::move(short_arg), long_arg = std::move(long_arg),
accumulation_target](auto const &) {
if constexpr (std::is_same_v<int, Vector>) {
if (*accumulation_target == 0) {
return;
}
} else {
if (accumulation_target->empty()) {
return;
}
}
const auto sid = this->find_argument_id(short_arg);
const auto lid = this->find_argument_id(long_arg);
if (const auto id = sid ? *sid : (lid ? *lid : -1); id != -1) {
this->ref_stored_arguments()[id] = *accumulation_target;
}
},
true);
}
template <typename Vector>
void store_accumulated_on_complete(std::string positional_name, std::shared_ptr<Vector> accumulation_target) {
on_complete(
[this, positional_name = std::move(positional_name), accumulation_target](auto const &) {
if constexpr (std::is_same_v<int, Vector>) {
if (*accumulation_target == 0) {
return;
}
} else {
if (accumulation_target->empty()) {
return;
}
}
auto id = this->find_argument_id(positional_name);
if (id.has_value()) {
this->ref_stored_arguments()[*id] = *accumulation_target;
}
},
true);
}
template <typename Vector, typename ArgsMap>
std::unique_ptr<action_base> make_accumulate_action(ArgsMap const &argument_pairs, bool ref_mode,
std::string const &short_arg, std::string const &long_arg) {
if (ref_mode) {
auto ref = get_or_throw<Vector *>(argument_pairs.at(add_argument_flags::Reference), "reference");
return make_accumulate_ref_action(ref);
}
auto accumulate =
get_either_or_throw<Vector *, bool>(argument_pairs.at(add_argument_flags::Accumulate), "accumulate");
return std::visit(
[this, short_arg, long_arg](auto &&acc) -> std::unique_ptr<action_base> {
using V = std::decay_t<decltype(acc)>;
if constexpr (std::is_same_v<V, bool>) {
if (!acc) {
throw std::logic_error("Accumulate flag must be true when used as a bool");
}
auto accumulation_target = std::make_shared<Vector>();
store_accumulated_on_complete(short_arg, long_arg, accumulation_target);
return make_accumulate_ref_action(accumulation_target.get());
} else {
return make_accumulate_ref_action(acc);
}
},
accumulate);
}
template <typename Vector, typename ArgsMap>
std::unique_ptr<action_base> make_accumulate_action(ArgsMap const &argument_pairs, bool ref_mode,
std::string const &positional_name) {
if (ref_mode) {
auto ref = get_or_throw<Vector *>(argument_pairs.at(add_argument_flags::Reference), "reference");
return make_accumulate_ref_action(ref);
}
auto accumulate =
get_either_or_throw<Vector *, bool>(argument_pairs.at(add_argument_flags::Accumulate), "accumulate");
return std::visit(
[this, positional_name](auto &&acc) -> std::unique_ptr<action_base> {
using V = std::decay_t<decltype(acc)>;
if constexpr (std::is_same_v<V, bool>) {
if (!acc) {
throw std::logic_error("Accumulate flag must be true when used as a bool");
}
auto accumulation_target = std::make_shared<Vector>();
store_accumulated_on_complete(positional_name, accumulation_target);
return make_accumulate_ref_action(accumulation_target.get());
} else {
return make_accumulate_ref_action(acc);
}
},
accumulate);
}
template <typename T, typename I> T get_or_throw(typed_flag_value<I> const &v, std::string_view key) {
if (auto p = std::get_if<T>(&v))
return *p;

View File

@@ -1,3 +1,4 @@
// ReSharper disable CppFunctionIsNotImplemented
#pragma once
#ifndef PARSING_TRAITS_HPP
#define PARSING_TRAITS_HPP
@@ -10,7 +11,7 @@ namespace argument_parser::parsing_traits {
template <typename T_> struct parser_trait {
using type = T_;
static T_ parse(const std::string &input);
static bool validate(T_ const&);
static bool validate(T_ const &);
static constexpr hint_type format_hint = "value";
static constexpr hint_type purpose_hint = "value";
@@ -50,6 +51,38 @@ namespace argument_parser::parsing_traits {
static constexpr hint_type format_hint = "3.14";
static constexpr hint_type purpose_hint = "double precision floating point number";
};
constexpr hint_type comma = ",";
template <const hint_type *PtrAddr> struct hint_provider {
static constexpr hint_type value = *PtrAddr;
};
template <typename... Providers> struct joiner {
static constexpr auto get_combined() {
constexpr size_t total_len = (std::string_view{Providers::value}.length() + ... + 0);
std::array<char, total_len + 1> arr{};
// ReSharper disable once CppDFAUnreadVariable
size_t offset = 0;
auto append = [&](const hint_type s) {
const std::string_view sv{s};
for (char c : sv)
arr[offset++] = c;
return 0;
};
(append(Providers::value), ...);
arr[total_len] = '\0';
return arr;
}
static constexpr auto storage = get_combined();
static constexpr hint_type value = storage.data();
};
template <typename... Providers> constexpr hint_type concat = joiner<Providers...>::value;
} // namespace argument_parser::parsing_traits
#endif

View File

@@ -16,7 +16,7 @@ namespace argument_parser {
namespace v2 {
class linux_parser : public v2::base_parser {
public:
linux_parser(bool should_exit = true);
linux_parser(parser_settings const &settings = {});
using base_parser::display_help;
};
} // namespace v2

View File

@@ -15,7 +15,7 @@ namespace argument_parser {
namespace v2 {
class macos_parser : public v2::base_parser {
public:
macos_parser(bool should_exit = true);
explicit macos_parser(parser_settings const &settings = {});
using base_parser::display_help;
};
} // namespace v2

View File

@@ -12,7 +12,7 @@ namespace argument_parser {
namespace v2 {
class windows_parser : public v2::base_parser {
public:
windows_parser(bool should_exit = true);
windows_parser(parser_settings const &settings = {});
using base_parser::display_help;
};
} // namespace v2

View File

@@ -1,254 +0,0 @@
#include "macros.h"
#include "traits.hpp"
#include <exception>
#include <memory>
#include <string>
#include <argparse>
#include <fstream>
#include <iostream>
#include <regex>
#include <sstream>
#include <vector>
struct Point {
int x, y;
};
template <> struct argument_parser::parsing_traits::parser_trait<Point> {
static Point parse(const std::string &input) {
auto comma_pos = input.find(',');
if (comma_pos == std::string::npos) {
throw std::runtime_error("Invalid Point format. Expected 'x,y'.");
}
int x = std::stoi(input.substr(0, comma_pos));
int y = std::stoi(input.substr(comma_pos + 1));
return {x, y};
}
static bool validate(Point const& p) {
return p.x >= 0 && p.y >= 0;
}
ARGPARSE_TRAIT_FORMAT_HINT = "x,y";
ARGPARSE_TRAIT_PURPOSE_HINT = "coordinates";
};
template <> struct argument_parser::parsing_traits::parser_trait<std::regex> {
static std::regex parse(const std::string &input) {
return std::regex(input);
}
ARGPARSE_TRAIT_FORMAT_HINT = "regex";
ARGPARSE_TRAIT_PURPOSE_HINT = "regular expression";
};
template <> struct argument_parser::parsing_traits::parser_trait<std::vector<std::string>> {
static std::vector<std::string> parse(const std::string &input) {
std::vector<std::string> result;
std::stringstream ss{input};
std::string item;
while (std::getline(ss, item, ',')) {
result.push_back(item);
}
return result;
}
ARGPARSE_TRAIT_FORMAT_HINT = "string,string,string";
ARGPARSE_TRAIT_PURPOSE_HINT = "list of strings";
};
template <typename VT> struct argument_parser::parsing_traits::parser_trait<std::vector<VT>> {
static std::vector<VT> parse(const std::string &input) {
std::vector<VT> result;
std::stringstream ss{input};
std::string item;
while (std::getline(ss, item, ',')) {
result.push_back(argument_parser::parsing_traits::parser_trait<VT>::parse(item));
}
return result;
}
ARGPARSE_TRAIT_FORMAT_HINT = "VT,VT,VT";
ARGPARSE_TRAIT_PURPOSE_HINT = "list of VT";
};
const std::initializer_list<argument_parser::conventions::convention const *const> conventions = {
&argument_parser::conventions::gnu_argument_convention,
&argument_parser::conventions::gnu_equal_argument_convention,
// &argument_parser::conventions::windows_argument_convention,
// &argument_parser::conventions::windows_equal_argument_convention
};
const auto echo = argument_parser::helpers::make_parametered_action<std::string>(
[](std::string const &text) { std::cout << text << std::endl; });
const auto echo_point = argument_parser::helpers::make_parametered_action<Point>(
[](Point const &point) { std::cout << "Point(" << point.x << ", " << point.y << ")" << std::endl; });
const auto cat = argument_parser::helpers::make_parametered_action<std::string>([](std::string const &file_name) {
std::ifstream file(file_name);
if (!file.is_open()) {
throw std::runtime_error("Could not open file");
}
std::string line;
while (std::getline(file, line)) {
std::cout << line << std::endl;
}
file.close();
});
auto grep(argument_parser::base_parser const &parser, std::string const &filename, std::regex const &pattern) {
if (filename.empty()) {
std::cerr << "Missing filename" << std::endl;
parser.display_help(conventions);
exit(-1);
}
std::ifstream file(filename);
if (!file.is_open()) {
std::cerr << "Could not open file: \"" << filename << '"' << std::endl;
exit(-1);
}
for (std::string line; std::getline(file, line);) {
if (std::regex_search(line, pattern)) {
std::cout << line << std::endl;
}
}
file.close();
}
void run_grep(argument_parser::base_parser const &parser) {
auto filename = parser.get_optional<std::string>("file");
auto pattern = parser.get_optional<std::regex>("grep");
if (filename && pattern) {
grep(parser, filename.value(), pattern.value());
} else if (filename) {
std::cerr << "Missing grep pattern" << std::endl;
parser.display_help(conventions);
exit(-1);
} else if (pattern) {
std::cerr << "Missing filename" << std::endl;
parser.display_help(conventions);
exit(-1);
}
}
void run_store_point(argument_parser::base_parser const &parser) {
auto point = parser.get_optional<Point>("store-point");
if (point) {
std::cout << "Point(" << point->x << ", " << point->y << ")" << std::endl;
}
}
int v2Examples() {
using namespace argument_parser::v2::flags;
argument_parser::v2::parser parser{ false };
parser.add_argument<std::string>(
{{ShortArgument, "e"}, {LongArgument, "echo"}, {Action, echo}, {HelpText, "echoes given variable"}});
parser.add_argument<Point>({{ShortArgument, "ep"}, {LongArgument, "echo-point"}, {Action, echo_point}});
parser.add_argument<std::string>({
// stores string for f/file flag
{ShortArgument, "f"},
{LongArgument, "file"},
{HelpText, "File to grep, required only if using grep"},
// if no action, falls to store operation with given type.
});
parser.add_argument<std::regex>({
// stores string for g/grep flag
{ShortArgument, "g"},
{LongArgument, "grep"},
{HelpText, "Grep pattern, required only if using file"},
// same as 'file' flag
});
parser.add_argument<std::string>(
{{ShortArgument, "c"}, {LongArgument, "cat"}, {Action, cat}, {HelpText, "Prints the content of the file"}}
);
parser.add_argument<Point>({
// { ShortArgument, "sp" }, // now if ShortArgument or LongArgument is missing, it will use it for the other.
{LongArgument, "store-point"},
{Required, true} // makes this flag required
});
parser.add_argument({{ShortArgument, "v"}, {LongArgument, "verbose"}});
parser.add_argument<std::string>({
{Positional, "input"},
{HelpText, "Input file to process"},
{Required, true},
});
parser.add_argument<std::string>({
{Positional, "output"},
{HelpText, "Output file path"},
});
parser.add_argument<std::vector<Point>>({{LongArgument, "points"}, {HelpText, "List of points to store"}});
parser.on_complete(::run_grep);
parser.on_complete(::run_store_point);
parser.on_complete([](argument_parser::base_parser const &p) {
auto input = p.get_optional<std::string>("input");
auto output = p.get_optional<std::string>("output");
if (input) {
std::cout << "Input: " << input.value() << std::endl;
}
if (output) {
std::cout << "Output: " << output.value() << std::endl;
}
});
parser.handle_arguments(conventions);
return 0;
}
auto unique_copy(std::unique_ptr<std::string> ptr) {
std::cout << *ptr << std::endl;
}
auto unique_reference(std::unique_ptr<std::string> const& ptr) {
std::cout << *ptr << std::endl;
}
auto unique_move(std::unique_ptr<std::string>&& ptr) {
std::cout << *ptr << std::endl;
}
template<typename T>
T return_example(std::function<T()> func) {
if constexpr (std::is_same_v<void, T>) {
return func();
} else {
return func();
}
}
template<typename T>
void log_result(std::function<T()> func) {
if constexpr (std::is_same_v<void, T>) {
func();
} else {
std::cout << "result: " << func() << std::endl;
}
}
int main(int argc, char **argv) {
try {
return_example<void>([]{});
return v2Examples();
} catch (std::exception const &e) {
std::cout << e.what() << std::endl;
}
}

View File

@@ -3,12 +3,12 @@
namespace argument_parser::conventions::helpers {
std::string to_lower(std::string s) {
std::transform(s.begin(), s.end(), s.begin(), [](unsigned char c) { return std::tolower(c); });
std::transform(s.begin(), s.end(), s.begin(), [](const unsigned char c) { return std::tolower(c); });
return s;
}
std::string to_upper(std::string s) {
std::transform(s.begin(), s.end(), s.begin(), [](unsigned char c) { return std::toupper(c); });
std::transform(s.begin(), s.end(), s.begin(), [](const unsigned char c) { return std::toupper(c); });
return s;
}
} // namespace argument_parser::conventions::helpers

View File

@@ -10,10 +10,9 @@ namespace argument_parser::conventions::implementations {
parsed_argument gnu_argument_convention::get_argument(std::string const &raw) const {
if (starts_with(raw, long_prec()))
return {argument_type::LONG, raw.substr(2)};
else if (starts_with(raw, short_prec()))
if (starts_with(raw, short_prec()))
return {argument_type::SHORT, raw.substr(1)};
else
return {argument_type::ERROR, "GNU standard convention does not allow arguments without a preceding dash."};
return {argument_type::ERROR, "GNU standard convention does not allow arguments without a preceding dash."};
}
std::string gnu_argument_convention::extract_value(std::string const & /*raw*/) const {
@@ -42,17 +41,17 @@ namespace argument_parser::conventions::implementations {
std::pair<std::string, std::string> gnu_argument_convention::make_help_text(std::string const &short_arg,
std::string const &long_arg,
bool requires_value) const {
std::string s_part = "";
if (short_arg != "-" && short_arg != "") {
bool const requires_value) const {
std::string s_part;
if (short_arg != "-" && !short_arg.empty()) {
s_part += short_prec() + short_arg;
if (requires_value) {
s_part += " <value>";
}
}
std::string l_part = "";
if (long_arg != "-" && long_arg != "") {
std::string l_part;
if (long_arg != "-" && !long_arg.empty()) {
l_part += long_prec() + long_arg;
if (requires_value) {
l_part += " <value>";
@@ -65,18 +64,17 @@ namespace argument_parser::conventions::implementations {
namespace argument_parser::conventions::implementations {
parsed_argument gnu_equal_argument_convention::get_argument(std::string const &raw) const {
auto pos = raw.find('=');
auto arg = pos != std::string::npos ? raw.substr(0, pos) : raw;
const auto pos = raw.find('=');
const auto arg = pos != std::string::npos ? raw.substr(0, pos) : raw;
if (starts_with(arg, long_prec()))
return {argument_type::LONG, arg.substr(2)};
else if (starts_with(arg, short_prec()))
if (starts_with(arg, short_prec()))
return {argument_type::SHORT, arg.substr(1)};
else
return {argument_type::ERROR, "GNU standard convention does not allow arguments without a preceding dash."};
return {argument_type::ERROR, "GNU standard convention does not allow arguments without a preceding dash."};
}
std::string gnu_equal_argument_convention::extract_value(std::string const &raw) const {
auto pos = raw.find('=');
const auto pos = raw.find('=');
if (pos == std::string::npos || pos + 1 >= raw.size())
throw std::runtime_error("Expected value after '='.");
return raw.substr(pos + 1);
@@ -100,17 +98,17 @@ namespace argument_parser::conventions::implementations {
std::pair<std::string, std::string> gnu_equal_argument_convention::make_help_text(std::string const &short_arg,
std::string const &long_arg,
bool requires_value) const {
std::string s_part = "";
if (short_arg != "-" && short_arg != "") {
bool const requires_value) const {
std::string s_part;
if (short_arg != "-" && !short_arg.empty()) {
s_part += short_prec() + short_arg;
if (requires_value) {
s_part += "=<value>";
}
}
std::string l_part = "";
if (long_arg != "-" && long_arg != "") {
std::string l_part;
if (long_arg != "-" && !long_arg.empty()) {
l_part += long_prec() + long_arg;
if (requires_value) {
l_part += "=<value>";

View File

@@ -3,15 +3,14 @@
#include <stdexcept>
namespace argument_parser::conventions::implementations {
windows_argument_convention::windows_argument_convention(bool accept_dash) : accept_dash_(accept_dash) {}
windows_argument_convention::windows_argument_convention(bool const accept_dash) : accept_dash_(accept_dash) {}
parsed_argument windows_argument_convention::get_argument(std::string const &raw) const {
if (raw.empty()) {
return {argument_type::ERROR, "Empty argument token."};
}
const char c0 = raw[0];
const bool ok_prefix = (c0 == '/') || (accept_dash_ && c0 == '-');
if (!ok_prefix) {
if (const bool ok_prefix = (c0 == '/') || (accept_dash_ && c0 == '-'); !ok_prefix) {
return {argument_type::ERROR,
accept_dash_ ? "Windows-style expects options to start with '/' (or '-' in compat mode)."
: "Windows-style expects options to start with '/'."};
@@ -50,18 +49,19 @@ namespace argument_parser::conventions::implementations {
return "/";
}
std::pair<std::string, std::string> windows_argument_convention::make_help_text(std::string const &short_arg, std::string const &long_arg,
bool requires_value) const {
std::string s_part = "";
if (short_arg != "-" && short_arg != "") {
std::pair<std::string, std::string> windows_argument_convention::make_help_text(std::string const &short_arg,
std::string const &long_arg,
bool const requires_value) const {
std::string s_part;
if (short_arg != "-" && !short_arg.empty()) {
s_part += short_prec() + short_arg;
if (requires_value) {
s_part += " <value>";
}
}
std::string l_part = "";
if (long_arg != "-" && long_arg != "") {
std::string l_part;
if (long_arg != "-" && !long_arg.empty()) {
l_part += long_prec() + long_arg;
if (requires_value) {
l_part += " <value>";
@@ -78,15 +78,14 @@ namespace argument_parser::conventions::implementations {
} // namespace argument_parser::conventions::implementations
namespace argument_parser::conventions::implementations {
windows_kv_argument_convention::windows_kv_argument_convention(bool accept_dash) : accept_dash_(accept_dash) {}
windows_kv_argument_convention::windows_kv_argument_convention(bool const accept_dash) : accept_dash_(accept_dash) {}
parsed_argument windows_kv_argument_convention::get_argument(std::string const &raw) const {
if (raw.empty()) {
return {argument_type::ERROR, "Empty argument token."};
}
const char c0 = raw[0];
const bool ok_prefix = (c0 == '/') || (accept_dash_ && c0 == '-');
if (!ok_prefix) {
if (const bool ok_prefix = (c0 == '/') || (accept_dash_ && c0 == '-'); !ok_prefix) {
return {argument_type::ERROR,
accept_dash_ ? "Windows-style expects options to start with '/' (or '-' in compat mode)."
: "Windows-style expects options to start with '/'."};
@@ -129,17 +128,18 @@ namespace argument_parser::conventions::implementations {
}
std::pair<std::string, std::string> windows_kv_argument_convention::make_help_text(std::string const &short_arg,
std::string const &long_arg, bool requires_value) const {
std::string s_part = "";
if (short_arg != "-" && short_arg != "") {
std::string const &long_arg,
bool const requires_value) const {
std::string s_part;
if (short_arg != "-" && !short_arg.empty()) {
s_part += short_prec() + short_arg;
if (requires_value) {
s_part += "=<value>, " + short_prec() + short_arg + ":<value>";
}
}
std::string l_part = "";
if (long_arg != "-" && long_arg != "") {
std::string l_part;
if (long_arg != "-" && !long_arg.empty()) {
l_part += long_prec() + long_arg;
if (requires_value) {
l_part += "=<value>, " + long_prec() + long_arg + ":<value>";

View File

@@ -1,10 +1,13 @@
#include "argument_parser.hpp"
#include "exceptions.hpp"
#include <algorithm>
#include <functional>
#include <iomanip>
#include <iostream>
#include <optional>
#include <sstream>
#include <stdexcept>
#include <string>
#include <thread>
#include <unordered_map>
@@ -13,7 +16,7 @@
class deferred_exec {
public:
deferred_exec(std::function<void()> const &func) : func(func) {}
explicit deferred_exec(std::function<void()> const &func) : func(func) {}
~deferred_exec() {
func();
}
@@ -27,13 +30,12 @@ bool contains(std::unordered_map<std::string, int> const &map, std::string const
}
namespace argument_parser {
argument::argument()
: id(0), name(), action(std::make_unique<non_parametered_action>([]() {})), required(false), invoked(false) {}
argument::argument() : id(0), action(std::make_unique<action_no_param>([] {})), required(false), invoked(false) {}
argument::argument(const argument &other)
: id(other.id), name(other.name), action(other.action->clone()), required(other.required),
invoked(other.invoked), help_text(other.help_text), positional(other.positional),
position_index(other.position_index) {}
positional_accumulator(other.positional_accumulator), position_index(other.position_index) {}
argument &argument::operator=(const argument &other) {
if (this != &other) {
@@ -44,6 +46,7 @@ namespace argument_parser {
invoked = other.invoked;
help_text = other.help_text;
positional = other.positional;
positional_accumulator = other.positional_accumulator;
position_index = other.position_index;
}
return *this;
@@ -69,11 +72,11 @@ namespace argument_parser {
return help_text;
}
void argument::set_required(bool val) {
void argument::set_required(const bool val) {
required = val;
}
void argument::set_invoked(bool val) {
void argument::set_invoked(const bool val) {
invoked = val;
}
@@ -85,20 +88,36 @@ namespace argument_parser {
return positional;
}
bool argument::is_positional_accumulator() const {
return positional_accumulator;
}
std::optional<int> argument::get_position_index() const {
return position_index;
}
void argument::set_positional(bool val) {
void argument::set_positional(const bool val) {
positional = val;
}
void argument::set_position_index(std::optional<int> idx) {
void argument::set_positional_accumulator(const bool val) {
positional_accumulator = val;
}
void argument::set_position_index(const std::optional<int> idx) {
position_index = idx;
}
void base_parser::on_complete(std::function<void(base_parser const &)> const &handler) {
on_complete_events.emplace_back(handler);
void base_parser::on_complete(std::function<void(base_parser const &)> const &action) {
on_complete_events.emplace_back(action);
}
void base_parser::on_complete(std::function<void(base_parser const &)> const &handler, const bool to_start) {
if (to_start) {
on_complete_events.emplace_front(handler);
} else {
on_complete_events.emplace_back(handler);
}
}
std::string
@@ -112,12 +131,17 @@ namespace argument_parser {
auto name_it = reverse_positional_names.find(pos_id);
if (name_it == reverse_positional_names.end())
continue;
auto const &arg = argument_map.at(pos_id);
if (arg.is_required()) {
ss << " <" << name_it->second << ">";
} else {
ss << " [" << name_it->second << "]";
}
if (arg.is_positional_accumulator()) {
ss << "...";
}
}
ss << "\n";
@@ -143,8 +167,8 @@ namespace argument_parser {
std::unordered_set<std::string> hasOnce;
for (auto const &convention : convention_types) {
auto generatedParts = convention->make_help_text(short_arg, long_arg, arg.expects_parameter());
std::string combined = generatedParts.first + "|" + generatedParts.second;
if (hasOnce.find(combined) == hasOnce.end()) {
if (std::string combined = generatedParts.first + "|" + generatedParts.second;
hasOnce.find(combined) == hasOnce.end()) {
parts.push_back(generatedParts);
hasOnce.insert(combined);
@@ -155,24 +179,24 @@ namespace argument_parser {
max_long_len = generatedParts.second.length();
}
} else {
parts.push_back({"", ""}); // trigger empty space in the help text
parts.emplace_back("", ""); // trigger empty space in the help text
}
}
help_lines.push_back({parts, arg.help_text});
}
if (!help_lines.empty()) {
for (auto const &line : help_lines) {
for (const auto &[convention_parts, desc] : help_lines) {
ss << "\t";
for (size_t i = 0; i < line.convention_parts.size(); ++i) {
auto const &parts = line.convention_parts[i];
for (size_t i = 0; i < convention_parts.size(); ++i) {
const auto &[fst, snd] = convention_parts[i];
if (i > 0) {
ss << " ";
}
ss << std::left << std::setw(static_cast<int>(max_short_len)) << parts.first << " "
<< std::setw(static_cast<int>(max_long_len)) << parts.second;
ss << std::left << std::setw(static_cast<int>(max_short_len)) << fst << " "
<< std::setw(static_cast<int>(max_long_len)) << snd;
}
ss << "\t" << line.desc << "\n";
ss << "\t" << desc << "\n";
}
}
@@ -182,10 +206,8 @@ namespace argument_parser {
for (auto const &pos_id : positional_arguments) {
if (pos_id == -1)
continue;
auto name_it = reverse_positional_names.find(pos_id);
if (name_it != reverse_positional_names.end()) {
size_t display_len = name_it->second.length() + 2; // for < >
if (display_len > max_pos_name_len)
if (auto name_it = reverse_positional_names.find(pos_id); name_it != reverse_positional_names.end()) {
if (size_t display_len = name_it->second.length() + 2; display_len > max_pos_name_len)
max_pos_name_len = display_len;
}
}
@@ -208,37 +230,32 @@ namespace argument_parser {
argument &base_parser::get_argument(conventions::parsed_argument const &arg) {
if (arg.first == conventions::argument_type::LONG) {
auto long_pos = long_arguments.find(arg.second);
if (long_pos != long_arguments.end())
if (const auto long_pos = long_arguments.find(arg.second); long_pos != long_arguments.end())
return argument_map.at(long_pos->second);
} else if (arg.first == conventions::argument_type::SHORT) {
auto short_pos = short_arguments.find(arg.second);
if (short_pos != short_arguments.end())
if (const auto short_pos = short_arguments.find(arg.second); short_pos != short_arguments.end())
return argument_map.at(short_pos->second);
} else if (arg.first == conventions::argument_type::INTERCHANGABLE) {
auto long_pos = long_arguments.find(arg.second);
if (long_pos != long_arguments.end())
if (const auto long_pos = long_arguments.find(arg.second); long_pos != long_arguments.end())
return argument_map.at(long_pos->second);
auto short_pos = short_arguments.find(arg.second);
if (short_pos != short_arguments.end())
if (const auto short_pos = short_arguments.find(arg.second); short_pos != short_arguments.end())
return argument_map.at(short_pos->second);
}
throw std::runtime_error("Unknown argument: " + arg.second);
}
void base_parser::enforce_creation_thread() {
void base_parser::enforce_creation_thread() const {
if (std::this_thread::get_id() != this->creation_thread_id.load()) {
throw std::runtime_error("handle_arguments must be called from the main thread");
}
}
bool base_parser::test_conventions(std::initializer_list<conventions::convention const *const> convention_types,
std::unordered_map<std::string, std::string> &values_for_arguments,
std::vector<std::pair<std::string, argument>> &found_arguments,
std::optional<argument> &found_help, std::vector<std::string>::iterator &it,
std::stringstream &error_stream) {
bool
base_parser::test_conventions(const std::initializer_list<conventions::convention const *const> convention_types,
std::vector<found_argument> &found_arguments, std::optional<argument> &found_help,
std::vector<std::string>::iterator &it, std::stringstream &error_stream) {
std::string current_argument = *it;
const std::string current_argument = *it;
for (auto const &convention_type : convention_types) {
auto extracted = convention_type->get_argument(current_argument);
@@ -256,16 +273,17 @@ namespace argument_parser {
return true;
}
found_arguments.emplace_back(extracted.second, corresponding_argument);
found_argument found{extracted.second, corresponding_argument};
if (corresponding_argument.expects_parameter()) {
if (convention_type->requires_next_token() && (it + 1) == parsed_arguments.end()) {
if (convention_type->requires_next_token() && it + 1 == parsed_arguments.end()) {
throw std::runtime_error("Expected value for argument " + extracted.second);
}
values_for_arguments[extracted.second] =
found.value =
convention_type->requires_next_token() ? *(++it) : convention_type->extract_value(*it);
}
found_arguments.emplace_back(std::move(found));
return true;
} catch (const std::runtime_error &e) {
error_stream << "Convention \"" << convention_type->name() << "\" failed with: " << e.what() << "\n";
@@ -275,10 +293,9 @@ namespace argument_parser {
return false;
}
void base_parser::extract_arguments(std::initializer_list<conventions::convention const *const> convention_types,
std::unordered_map<std::string, std::string> &values_for_arguments,
std::vector<std::pair<std::string, argument>> &found_arguments,
std::optional<argument> &found_help) {
void
base_parser::extract_arguments(const std::initializer_list<conventions::convention const *const> convention_types,
std::vector<found_argument> &found_arguments, std::optional<argument> &found_help) {
size_t next_positional_index = 0;
bool force_positional = false;
@@ -290,39 +307,43 @@ namespace argument_parser {
}
if (force_positional) {
if (next_positional_index >= positional_arguments.size()) {
auto slot = next_positional_slot(next_positional_index);
if (!slot.has_value()) {
throw std::runtime_error("Unexpected positional argument: \"" + *it + "\"");
}
int arg_id = positional_arguments[next_positional_index];
int arg_id = positional_arguments[*slot];
argument &pos_arg = argument_map.at(arg_id);
std::string const &pos_name = reverse_positional_names.at(arg_id);
found_arguments.emplace_back(pos_name, pos_arg);
values_for_arguments[pos_name] = *it;
next_positional_index++;
found_arguments.push_back({pos_name, pos_arg, *it});
if (!pos_arg.is_positional_accumulator()) {
next_positional_index = *slot + 1;
}
continue;
}
std::stringstream error_stream;
if (!test_conventions(convention_types, values_for_arguments, found_arguments, found_help, it,
error_stream)) {
if (next_positional_index < positional_arguments.size()) {
int arg_id = positional_arguments[next_positional_index];
if (std::stringstream error_stream;
!test_conventions(convention_types, found_arguments, found_help, it, error_stream)) {
if (auto slot = next_positional_slot(next_positional_index); slot.has_value()) {
int arg_id = positional_arguments[*slot];
argument &pos_arg = argument_map.at(arg_id);
std::string const &pos_name = reverse_positional_names.at(arg_id);
found_arguments.emplace_back(pos_name, pos_arg);
values_for_arguments[pos_name] = *it;
next_positional_index++;
found_arguments.push_back({pos_name, pos_arg, *it});
if (!pos_arg.is_positional_accumulator()) {
next_positional_index = *slot + 1;
}
} else {
throw std::runtime_error("All trials for argument: \n\t\"" + *it + "\"\n failed with: \n" +
error_stream.str());
if (m_settings.ignore_unknown_arguments) {
continue;
}
throw parser::unknown_argument_exception("All trials for argument: \n\t\"" + *it +
"\"\n failed with: \n" + error_stream.str());
}
}
}
}
std::string replace_var(std::string text, const std::string &var_name, const std::string &value) {
std::string placeholder = "${" + var_name + "}";
const std::string placeholder = "${" + var_name + "}";
size_t pos = text.find(placeholder);
while (pos != std::string::npos) {
@@ -332,8 +353,7 @@ namespace argument_parser {
return text;
}
void base_parser::invoke_arguments(std::unordered_map<std::string, std::string> const &values_for_arguments,
std::vector<std::pair<std::string, argument>> &found_arguments,
void base_parser::invoke_arguments(std::vector<found_argument> &found_arguments,
std::optional<argument> const &found_help) {
if (found_help) {
@@ -342,15 +362,15 @@ namespace argument_parser {
}
std::stringstream error_stream;
for (auto &[key, value] : found_arguments) {
for (auto &[key, arg, value] : found_arguments) {
try {
if (value.expects_parameter()) {
value.action->invoke_with_parameter(values_for_arguments.at(key));
if (arg.expects_parameter()) {
arg.action->invoke_with_parameter(value.value());
} else {
value.action->invoke();
arg.action->invoke();
}
value.set_invoked(true);
argument_map.at(value.id).set_invoked(true);
arg.set_invoked(true);
argument_map.at(arg.id).set_invoked(true);
} catch (const std::runtime_error &e) {
std::string err{e.what()};
err = replace_var(err, "KEY", "for " + key);
@@ -358,43 +378,55 @@ namespace argument_parser {
}
}
std::string error_message = error_stream.str();
if (!error_message.empty()) {
if (const std::string error_message = error_stream.str(); !error_message.empty()) {
throw std::runtime_error(error_message);
}
}
void base_parser::handle_arguments(std::initializer_list<conventions::convention const *const> convention_types) {
void
base_parser::handle_arguments(const std::initializer_list<conventions::convention const *const> convention_types) {
enforce_creation_thread();
deferred_exec reset_current_conventions([this]() { this->reset_current_conventions(); });
deferred_exec reset_current_conventions([this] { this->reset_current_conventions(); });
this->current_conventions(convention_types);
std::unordered_map<std::string, std::string> values_for_arguments;
std::vector<std::pair<std::string, argument>> found_arguments;
std::vector<found_argument> found_arguments;
std::optional<argument> found_help = std::nullopt;
extract_arguments(convention_types, values_for_arguments, found_arguments, found_help);
invoke_arguments(values_for_arguments, found_arguments, found_help);
try {
extract_arguments(convention_types, found_arguments, found_help);
invoke_arguments(found_arguments, found_help);
} catch (parser::unknown_argument_exception const &e) {
if (m_settings.should_exit_on_unknown_argument) {
std::exit(1);
}
throw e;
} catch (std::exception const &e) {
if (m_settings.should_exit_on_error) {
std::exit(1);
}
throw e;
}
check_for_required_arguments(convention_types);
fire_on_complete_events();
}
void base_parser::display_help(std::initializer_list<conventions::convention const *const> convention_types) const {
void base_parser::display_help(
const std::initializer_list<conventions::convention const *const> convention_types) const {
std::cout << build_help_text(convention_types);
}
std::optional<int> base_parser::find_argument_id(std::string const &arg) const {
auto long_pos = long_arguments.find(arg);
auto short_post = short_arguments.find(arg);
const auto long_pos = long_arguments.find(arg);
const auto short_post = short_arguments.find(arg);
if (long_pos != long_arguments.end())
return long_pos->second;
if (short_post != short_arguments.end())
return short_post->second;
auto pos_it = positional_name_map.find(arg);
if (pos_it != positional_name_map.end())
if (const auto pos_it = positional_name_map.find(arg); pos_it != positional_name_map.end())
return pos_it->second;
return std::nullopt;
@@ -406,12 +438,12 @@ namespace argument_parser {
}
}
void base_parser::set_argument_status(bool is_required, std::string const &help_text, argument &arg) {
void base_parser::set_argument_status(const bool is_required, std::string const &help_text, argument &arg) {
arg.set_required(is_required);
arg.set_help_text(help_text);
}
void base_parser::place_argument(int id, argument const &arg, std::string const &short_arg,
void base_parser::place_argument(int const id, argument const &arg, std::string const &short_arg,
std::string const &long_arg) {
argument_map[id] = arg;
if (short_arg != "-") {
@@ -430,19 +462,57 @@ namespace argument_parser {
}
}
void base_parser::place_positional_argument(int id, argument const &arg, std::string const &name,
std::optional<int> position) {
void base_parser::assert_can_place_positional(const int id, const std::optional<int> position,
const bool accumulator) const {
const auto existing_accumulator =
std::find_if(positional_arguments.begin(), positional_arguments.end(), [this](int const arg_id) {
if (arg_id == -1) {
return false;
}
return argument_map.at(arg_id).is_positional_accumulator();
});
if (accumulator && existing_accumulator != positional_arguments.end()) {
throw std::runtime_error("Only one positional accumulator is allowed.");
}
if (!accumulator && existing_accumulator != positional_arguments.end()) {
const auto accumulator_slot =
static_cast<size_t>(std::distance(positional_arguments.begin(), existing_accumulator));
if (!position.has_value() || static_cast<size_t>(position.value()) >= accumulator_slot) {
throw std::runtime_error("Positional accumulator must be the last positional argument.");
}
}
if (accumulator && position.has_value()) {
const auto idx = static_cast<size_t>(position.value());
for (size_t i = idx + 1; i < positional_arguments.size(); ++i) {
if (positional_arguments[i] != -1 && positional_arguments[i] != id) {
throw std::runtime_error("Positional accumulator must be the last positional argument.");
}
}
}
}
void base_parser::place_positional_argument(int const id, argument const &arg, std::string const &name,
const std::optional<int> position, const bool accumulator) {
if (position.has_value() && position.value() < 0) {
throw std::runtime_error("Positional argument position cannot be negative.");
}
assert_can_place_positional(id, position, accumulator);
argument_map[id] = arg;
positional_name_map[name] = id;
reverse_positional_names[id] = name;
if (position.has_value()) {
auto idx = static_cast<size_t>(position.value());
const auto idx = static_cast<size_t>(position.value());
if (idx > positional_arguments.size()) {
positional_arguments.resize(idx + 1, -1);
}
if (idx < positional_arguments.size() && positional_arguments[idx] != -1) {
throw std::runtime_error("Position " + std::to_string(idx) + " is already occupied!");
positional_arguments.insert(positional_arguments.begin() + static_cast<std::ptrdiff_t>(idx), id);
return;
}
if (idx == positional_arguments.size()) {
positional_arguments.push_back(id);
@@ -450,10 +520,24 @@ namespace argument_parser {
positional_arguments[idx] = id;
}
} else {
positional_arguments.push_back(id);
if (const auto empty_slot = std::find(positional_arguments.begin(), positional_arguments.end(), -1);
empty_slot != positional_arguments.end()) {
*empty_slot = id;
} else {
positional_arguments.push_back(id);
}
}
}
std::optional<size_t> base_parser::next_positional_slot(size_t const start) const {
for (size_t i = start; i < positional_arguments.size(); ++i) {
if (positional_arguments[i] != -1) {
return i;
}
}
return std::nullopt;
}
std::string get_one_name(std::string const &short_name, std::string const &long_name) {
std::string res{};
if (short_name != "-") {
@@ -500,15 +584,15 @@ namespace argument_parser {
} else {
std::cerr << "\t" << get_one_name(s, l) << ": must be provided as one of [";
for (auto it = convention_types.begin(); it != convention_types.end(); ++it) {
auto generatedParts = (*it)->make_help_text(s, l, p);
std::string help_str = generatedParts.first;
if (!generatedParts.first.empty() && !generatedParts.second.empty()) {
auto [short_part, long_part] = (*it)->make_help_text(s, l, p);
std::string help_str = short_part;
if (!short_part.empty() && !long_part.empty()) {
help_str += " ";
}
help_str += generatedParts.second;
help_str += long_part;
size_t last_not_space = help_str.find_last_not_of(" \t");
if (last_not_space != std::string::npos) {
if (size_t last_not_space = help_str.find_last_not_of(" \t");
last_not_space != std::string::npos) {
help_str.erase(last_not_space + 1);
}
std::cerr << help_str;
@@ -521,7 +605,10 @@ namespace argument_parser {
}
std::cerr << "\n";
display_help(convention_types);
std::exit(1);
if (m_settings.should_exit_on_missing_required) {
std::exit(1);
}
throw std::runtime_error("required arguments not provided");
}
}

View File

@@ -12,7 +12,7 @@ namespace argument_parser {
}
fake_parser::fake_parser(std::string const &program_name, std::initializer_list<std::string> const &arguments)
: fake_parser(program_name, std::vector<std::string>(arguments)) {}
: fake_parser(program_name, std::vector(arguments)) {}
void fake_parser::set_program_name(std::string const &program_name) {
this->program_name = program_name;
@@ -23,28 +23,27 @@ namespace argument_parser {
}
namespace v2 {
fake_parser::fake_parser(std::string program_name, std::vector<std::string> const &arguments) {
set_program_name(program_name);
ref_parsed_args() = arguments;
prepare_help_flag(false);
}
fake_parser::fake_parser(std::string program_name, std::vector<std::string> const &arguments) {
base_parser::set_program_name(std::move(program_name));
ref_parsed_args() = arguments;
prepare_help_flag(false);
}
fake_parser::fake_parser(std::string const &program_name, std::vector<std::string> &&arguments) {
set_program_name(program_name);
ref_parsed_args() = std::move(arguments);
prepare_help_flag(false);
}
fake_parser::fake_parser(std::string program_name, std::vector<std::string> &&arguments) {
base_parser::set_program_name(std::move(program_name));
ref_parsed_args() = std::move(arguments);
prepare_help_flag(false);
}
fake_parser::fake_parser(std::string const &program_name, std::initializer_list<std::string> const &arguments)
: fake_parser(program_name, std::vector<std::string>(arguments)) {
}
fake_parser::fake_parser(std::string program_name, std::initializer_list<std::string> const &arguments)
: fake_parser(std::move(program_name), std::vector(arguments)) {}
void fake_parser::set_program_name(std::string const &program_name) {
set_program_name(program_name);
}
void fake_parser::set_program_name(std::string const &program_name) {
base_parser::set_program_name(program_name);
}
void fake_parser::set_parsed_arguments(std::vector<std::string> const &parsed_arguments) {
ref_parsed_args() = parsed_arguments;
}
}
void fake_parser::set_parsed_arguments(std::vector<std::string> const &parsed_arguments) {
ref_parsed_args() = parsed_arguments;
}
} // namespace v2
} // namespace argument_parser

View File

@@ -15,7 +15,7 @@ namespace argument_parser {
}
namespace v2 {
linux_parser::linux_parser(bool should_exit) {
linux_parser::linux_parser(parser_settings const &settings) {
std::ifstream command_line_file{"/proc/self/cmdline"};
std::string program_name;
std::getline(command_line_file, program_name, '\0');
@@ -25,7 +25,7 @@ namespace argument_parser {
ref_parsed_args().emplace_back(line);
}
prepare_help_flag(should_exit);
prepare_help_flag(settings.should_exit_on_help);
}
} // namespace v2
} // namespace argument_parser

View File

@@ -4,15 +4,20 @@
#include <crt_externs.h>
#define MACOS_GETARGS_LOOP(argc_name, argv_name, before_for, for_body) \
do { \
const int argc_name = *_NSGetArgc(); \
if (char **argv_name = *_NSGetArgv(); argc_name > 0 && argv_name != nullptr && argv_name[0] != nullptr) { \
do { before_for; } while(false); \
for (int i = 1; i < argc_name; ++i) { \
for_body \
} \
} \
#define MACOS_GETARGS_LOOP(argc_name, argv_name, before_for, for_body) \
do { \
const int(argc_name) = *_NSGetArgc(); \
if (char **(argv_name) = *_NSGetArgv(); \
(argc_name) > 0 && (argv_name) != nullptr && (argv_name)[0] != nullptr) { \
do { \
(before_for); \
} while (false); \
for (int i = 1; i < (argc_name); ++i) { \
{ \
for_body \
} \
} \
} \
} while (false)
namespace argument_parser {
@@ -24,12 +29,13 @@ namespace argument_parser {
}
namespace v2 {
macos_parser::macos_parser(bool should_exit) {
MACOS_GETARGS_LOOP(argc, argv, set_program_name(argv[0]), {
if (argv[i] != nullptr)
macos_parser::macos_parser(parser_settings const &settings) {
MACOS_GETARGS_LOOP(argc, argv, set_program_name(argv[0]), {
if (argv[i] != nullptr)
ref_parsed_args().emplace_back(argv[i]);
});
prepare_help_flag(should_exit);
});
prepare_help_flag(settings.should_exit_on_help);
set_settings(settings);
}
} // namespace v2
} // namespace argument_parser

View File

@@ -95,11 +95,11 @@ namespace argument_parser {
} // namespace argument_parser
namespace argument_parser::v2 {
windows_parser::windows_parser(bool should_exit) {
windows_parser::windows_parser(parser_settings const &settings) {
parse_windows_arguments(ref_parsed_args(),
[this](std::string const &program_name) { this->set_program_name(program_name); });
prepare_help_flag(should_exit);
prepare_help_flag(settings.should_exit_on_help);
}
} // namespace argument_parser::v2

View File

@@ -1,420 +0,0 @@
#include <argparse>
#include <array>
#include <cassert>
#include <exception>
#include <functional>
#include <iostream>
#include <string>
#include <vector>
const std::initializer_list<argument_parser::conventions::convention const *const> conventions = {
&argument_parser::conventions::gnu_argument_convention,
&argument_parser::conventions::gnu_equal_argument_convention,
};
namespace v2_test {
class fake_parser : public argument_parser::v2::base_parser {
public:
fake_parser(std::string const &program_name, std::initializer_list<std::string> const &arguments) {
set_program_name(program_name);
ref_parsed_args() = std::vector<std::string>(arguments);
prepare_help_flag();
}
};
} // namespace v2_test
int tests_run = 0;
int tests_passed = 0;
void test_result(const char *name, bool passed) {
tests_run++;
if (passed) {
tests_passed++;
std::cout << " [PASS] " << name << std::endl;
} else {
std::cout << " [FAIL] " << name << std::endl;
}
}
// ============================================================
// V1 Tests (using argument_parser::fake_parser)
// ============================================================
void test_v1_single_positional_store() {
argument_parser::fake_parser parser("test", {"hello"});
parser.add_positional_argument<std::string>("greeting", "A greeting", false);
parser.handle_arguments(conventions);
auto val = parser.get_optional<std::string>("greeting");
test_result("v1: single positional store", val.has_value() && val.value() == "hello");
}
void test_v1_multiple_positionals_ordered() {
argument_parser::fake_parser parser("test", {"alpha", "beta", "gamma"});
parser.add_positional_argument<std::string>("first", "First arg", false);
parser.add_positional_argument<std::string>("second", "Second arg", false);
parser.add_positional_argument<std::string>("third", "Third arg", false);
parser.handle_arguments(conventions);
auto first = parser.get_optional<std::string>("first");
auto second = parser.get_optional<std::string>("second");
auto third = parser.get_optional<std::string>("third");
bool ok = first.has_value() && first.value() == "alpha" && second.has_value() && second.value() == "beta" &&
third.has_value() && third.value() == "gamma";
test_result("v1: multiple positionals preserve order", ok);
}
void test_v1_positional_with_explicit_position() {
argument_parser::fake_parser parser("test", {"first_val", "second_val"});
parser.add_positional_argument<std::string>("second", "Second", false, 1);
parser.add_positional_argument<std::string>("first", "First", false, 0);
parser.handle_arguments(conventions);
auto first = parser.get_optional<std::string>("first");
auto second = parser.get_optional<std::string>("second");
bool ok = first.has_value() && first.value() == "first_val" && second.has_value() && second.value() == "second_val";
test_result("v1: explicit position index", ok);
}
void test_v1_positional_typed_int() {
argument_parser::fake_parser parser("test", {"42"});
parser.add_positional_argument<int>("count", "A count", false);
parser.handle_arguments(conventions);
auto val = parser.get_optional<int>("count");
test_result("v1: positional with int type", val.has_value() && val.value() == 42);
}
void test_v1_positional_with_action() {
std::string captured;
argument_parser::fake_parser parser("test", {"world"});
auto action =
argument_parser::helpers::make_parametered_action<std::string>([&](std::string const &v) { captured = v; });
parser.add_positional_argument<std::string>("name", "A name", action, false);
parser.handle_arguments(conventions);
test_result("v1: positional with action", captured == "world");
}
void test_v1_mixed_named_and_positional() {
argument_parser::fake_parser parser("test", {"--verbose", "true", "myfile.txt"});
parser.add_argument<bool>("v", "verbose", "Verbose mode", false);
parser.add_positional_argument<std::string>("file", "Input file", false);
parser.handle_arguments(conventions);
auto verbose = parser.get_optional<bool>("verbose");
auto file = parser.get_optional<std::string>("file");
bool ok = verbose.has_value() && verbose.value() == true && file.has_value() && file.value() == "myfile.txt";
test_result("v1: mixed named and positional args", ok);
}
void test_v1_positional_after_named() {
argument_parser::fake_parser parser("test", {"-n", "5", "output.txt"});
parser.add_argument<int>("n", "number", "A number", false);
parser.add_positional_argument<std::string>("output", "Output file", false);
parser.handle_arguments(conventions);
auto number = parser.get_optional<int>("number");
auto output = parser.get_optional<std::string>("output");
bool ok = number.has_value() && number.value() == 5 && output.has_value() && output.value() == "output.txt";
test_result("v1: positional after named args", ok);
}
void test_v1_positional_between_named() {
argument_parser::fake_parser parser("test", {"-a", "1", "positional_val", "--beta", "2"});
parser.add_argument<int>("a", "alpha", "Alpha", false);
parser.add_argument<int>("b", "beta", "Beta", false);
parser.add_positional_argument<std::string>("middle", "Middle arg", false);
parser.handle_arguments(conventions);
auto alpha = parser.get_optional<int>("alpha");
auto beta = parser.get_optional<int>("beta");
auto middle = parser.get_optional<std::string>("middle");
bool ok = alpha.has_value() && alpha.value() == 1 && beta.has_value() && beta.value() == 2 && middle.has_value() &&
middle.value() == "positional_val";
test_result("v1: positional between named args", ok);
}
void test_v1_double_dash_separator() {
argument_parser::fake_parser parser("test", {"--", "-not-a-flag"});
parser.add_positional_argument<std::string>("item", "An item", false);
parser.handle_arguments(conventions);
auto val = parser.get_optional<std::string>("item");
test_result("v1: -- separator treats next as positional", val.has_value() && val.value() == "-not-a-flag");
}
void test_v1_double_dash_multiple() {
argument_parser::fake_parser parser("test", {"--name", "hello", "--", "--weird", "-x"});
parser.add_argument<std::string>("n", "name", "A name", false);
parser.add_positional_argument<std::string>("first", "First", false);
parser.add_positional_argument<std::string>("second", "Second", false);
parser.handle_arguments(conventions);
auto name = parser.get_optional<std::string>("name");
auto first = parser.get_optional<std::string>("first");
auto second = parser.get_optional<std::string>("second");
bool ok = name.has_value() && name.value() == "hello" && first.has_value() && first.value() == "--weird" &&
second.has_value() && second.value() == "-x";
test_result("v1: -- separator with multiple positionals", ok);
}
void test_v1_required_positional_missing() {
argument_parser::fake_parser parser("test", {});
parser.add_positional_argument<std::string>("file", "A file", true);
bool threw = false;
try {
// check_for_required_arguments calls std::exit(1) so we can't easily test it
// instead, test that handle_arguments doesn't crash when positionals are provided
parser.handle_arguments(conventions);
} catch (...) {
threw = true;
}
// Note: required check calls std::exit(1), so if we get here the arg wasn't required-checked
// This test just verifies setup doesn't crash. The exit behavior is tested manually.
test_result("v1: required positional setup (no crash)", true);
}
void test_v1_unexpected_positional_throws() {
argument_parser::fake_parser parser("test", {"unexpected"});
// no positional args defined, but a bare token is provided
bool threw = false;
try {
parser.handle_arguments(conventions);
} catch (const std::runtime_error &) {
threw = true;
}
test_result("v1: unexpected positional throws", threw);
}
void test_v1_duplicate_positional_name_throws() {
argument_parser::fake_parser parser("test", {"a", "b"});
parser.add_positional_argument<std::string>("file", "A file", false);
bool threw = false;
try {
parser.add_positional_argument<std::string>("file", "Duplicate", false);
} catch (const std::runtime_error &) {
threw = true;
}
test_result("v1: duplicate positional name throws", threw);
}
void test_v1_positional_on_complete() {
std::string captured_file;
argument_parser::fake_parser parser("test", {"data.csv"});
parser.add_positional_argument<std::string>("file", "Input file", false);
parser.on_complete([&](argument_parser::base_parser const &p) {
auto val = p.get_optional<std::string>("file");
if (val)
captured_file = val.value();
});
parser.handle_arguments(conventions);
test_result("v1: positional accessible in on_complete", captured_file == "data.csv");
}
// ============================================================
// V2 Tests (using v2_test::fake_parser)
// ============================================================
void test_v2_single_positional() {
using namespace argument_parser::v2::flags;
v2_test::fake_parser parser("test", {"hello"});
parser.add_argument<std::string>({{Positional, "greeting"}, {HelpText, "A greeting"}});
parser.handle_arguments(conventions);
auto val = parser.get_optional<std::string>("greeting");
test_result("v2: single positional store", val.has_value() && val.value() == "hello");
}
void test_v2_positional_required() {
using namespace argument_parser::v2::flags;
v2_test::fake_parser parser("test", {"value"});
parser.add_argument<std::string>({{Positional, "arg"}, {Required, true}, {HelpText, "Required arg"}});
parser.handle_arguments(conventions);
auto val = parser.get_optional<std::string>("arg");
test_result("v2: required positional", val.has_value() && val.value() == "value");
}
void test_v2_positional_with_position() {
using namespace argument_parser::v2::flags;
v2_test::fake_parser parser("test", {"first_val", "second_val"});
parser.add_argument<std::string>({{Positional, "second"}, {Position, 1}, {HelpText, "Second"}});
parser.add_argument<std::string>({{Positional, "first"}, {Position, 0}, {HelpText, "First"}});
parser.handle_arguments(conventions);
auto first = parser.get_optional<std::string>("first");
auto second = parser.get_optional<std::string>("second");
bool ok = first.has_value() && first.value() == "first_val" && second.has_value() && second.value() == "second_val";
test_result("v2: positional with explicit Position", ok);
}
void test_v2_positional_typed_int() {
using namespace argument_parser::v2::flags;
v2_test::fake_parser parser("test", {"99"});
parser.add_argument<int>({{Positional, "count"}, {HelpText, "A count"}});
parser.handle_arguments(conventions);
auto val = parser.get_optional<int>("count");
test_result("v2: positional with int type", val.has_value() && val.value() == 99);
}
void test_v2_mixed_named_and_positional() {
using namespace argument_parser::v2::flags;
v2_test::fake_parser parser("test", {"--output", "out.txt", "input.txt"});
parser.add_argument<std::string>({{ShortArgument, "o"}, {LongArgument, "output"}, {HelpText, "Output file"}});
parser.add_argument<std::string>({{Positional, "input"}, {HelpText, "Input file"}});
parser.handle_arguments(conventions);
auto output = parser.get_optional<std::string>("output");
auto input = parser.get_optional<std::string>("input");
bool ok = output.has_value() && output.value() == "out.txt" && input.has_value() && input.value() == "input.txt";
test_result("v2: mixed named and positional", ok);
}
void test_v2_positional_with_action() {
using namespace argument_parser::v2::flags;
std::string captured;
v2_test::fake_parser parser("test", {"world"});
parser.add_argument<std::string>({{Positional, "name"},
{Action, argument_parser::helpers::make_parametered_action<std::string>(
[&](std::string const &v) { captured = v; })},
{HelpText, "A name"}});
parser.handle_arguments(conventions);
test_result("v2: positional with action", captured == "world");
}
void test_v2_double_dash_separator() {
using namespace argument_parser::v2::flags;
v2_test::fake_parser parser("test", {"--", "--not-a-flag"});
parser.add_argument<std::string>({{Positional, "item"}, {HelpText, "An item"}});
parser.handle_arguments(conventions);
auto val = parser.get_optional<std::string>("item");
test_result("v2: -- separator", val.has_value() && val.value() == "--not-a-flag");
}
void test_v2_positional_auto_help_text() {
using namespace argument_parser::v2::flags;
v2_test::fake_parser parser("test", {"42"});
// no HelpText provided — should auto-generate from traits
parser.add_argument<int>({{Positional, "count"}});
parser.handle_arguments(conventions);
auto val = parser.get_optional<int>("count");
test_result("v2: positional auto help text (no crash)", val.has_value() && val.value() == 42);
}
void test_v2_multiple_positionals_and_named() {
using namespace argument_parser::v2::flags;
v2_test::fake_parser parser("test", {"-v", "src.txt", "dst.txt"});
parser.add_argument({{ShortArgument, "v"}, {LongArgument, "verbose"}});
parser.add_argument<std::string>({{Positional, "source"}, {HelpText, "Source"}});
parser.add_argument<std::string>({{Positional, "destination"}, {HelpText, "Destination"}});
parser.handle_arguments(conventions);
auto verbose = parser.get_optional<bool>("verbose");
auto source = parser.get_optional<std::string>("source");
auto dest = parser.get_optional<std::string>("destination");
bool ok = verbose.has_value() && source.has_value() && source.value() == "src.txt" && dest.has_value() &&
dest.value() == "dst.txt";
test_result("v2: multiple positionals with named flag", ok);
}
void test_v2_on_complete_with_positional() {
using namespace argument_parser::v2::flags;
std::string captured;
v2_test::fake_parser parser("test", {"payload"});
parser.add_argument<std::string>({{Positional, "data"}, {HelpText, "Data"}});
parser.on_complete([&](argument_parser::base_parser const &p) {
auto val = p.get_optional<std::string>("data");
if (val)
captured = val.value();
});
parser.handle_arguments(conventions);
test_result("v2: positional accessible in on_complete", captured == "payload");
}
// ============================================================
// Main
// ============================================================
int main() {
std::cout << "=== V1 Positional Argument Tests ===" << std::endl;
std::array<std::function<void()>, 13> v1Tests {
test_v1_single_positional_store,
test_v1_multiple_positionals_ordered,
test_v1_positional_with_explicit_position,
test_v1_positional_typed_int,
test_v1_positional_with_action,
test_v1_mixed_named_and_positional,
test_v1_positional_after_named,
test_v1_positional_between_named,
test_v1_double_dash_separator,
test_v1_double_dash_multiple,
test_v1_unexpected_positional_throws,
test_v1_duplicate_positional_name_throws,
test_v1_positional_on_complete
};
for (auto const& test : v1Tests) {
try {
test();
} catch(std::exception const& e) {
std::cout << "test failed: " << e.what() << std::endl;
}
}
std::cout << "\n=== V2 Positional Argument Tests ===" << std::endl;
std::array<std::function<void()>, 10> v2Tests{
test_v2_single_positional,
test_v2_positional_required,
test_v2_positional_with_position,
test_v2_positional_typed_int,
test_v2_mixed_named_and_positional,
test_v2_positional_with_action,
test_v2_double_dash_separator,
test_v2_positional_auto_help_text,
test_v2_multiple_positionals_and_named,
test_v2_on_complete_with_positional
};
for (auto const& test : v2Tests) {
try {
test();
} catch(std::exception const& e) {
std::cout << "test failed: " << e.what() << std::endl;
}
}
std::cout << "\n=== Results: " << tests_passed << "/" << tests_run << " passed ===" << std::endl;
return (tests_passed == tests_run) ? 0 : 1;
}