diff --git a/.clang-format b/.clang-format
new file mode 100644
index 0000000000000000000000000000000000000000..c5c4c9d1431b6f38baa9d81d1cba883ba1411fa7
--- /dev/null
+++ b/.clang-format
@@ -0,0 +1,148 @@
+---
+Language:        Cpp
+# BasedOnStyle:  Google
+AccessModifierOffset: -1
+AlignAfterOpenBracket: Align
+AlignConsecutiveAssignments: false
+AlignConsecutiveDeclarations: false
+AlignEscapedNewlines: Left
+AlignOperands:   true
+AlignTrailingComments: true
+AllowAllParametersOfDeclarationOnNextLine: true
+AllowShortBlocksOnASingleLine: false
+AllowShortCaseLabelsOnASingleLine: false
+AllowShortFunctionsOnASingleLine: All
+AllowShortIfStatementsOnASingleLine: true
+AllowShortLoopsOnASingleLine: true
+AlwaysBreakAfterDefinitionReturnType: None
+AlwaysBreakAfterReturnType: None
+AlwaysBreakBeforeMultilineStrings: true
+AlwaysBreakTemplateDeclarations: Yes
+BinPackArguments: true
+BinPackParameters: true
+BraceWrapping:   
+  AfterClass:      false
+  AfterControlStatement: false
+  AfterEnum:       false
+  AfterFunction:   false
+  AfterNamespace:  false
+  AfterObjCDeclaration: false
+  AfterStruct:     false
+  AfterUnion:      false
+  AfterExternBlock: false
+  BeforeCatch:     false
+  BeforeElse:      false
+  IndentBraces:    false
+  SplitEmptyFunction: true
+  SplitEmptyRecord: true
+  SplitEmptyNamespace: true
+BreakBeforeBinaryOperators: None
+BreakBeforeBraces: Attach
+BreakBeforeInheritanceComma: false
+BreakInheritanceList: BeforeColon
+BreakBeforeTernaryOperators: true
+BreakConstructorInitializersBeforeComma: false
+BreakConstructorInitializers: BeforeColon
+BreakAfterJavaFieldAnnotations: false
+BreakStringLiterals: true
+ColumnLimit:     80
+CommentPragmas:  '^ IWYU pragma:'
+CompactNamespaces: false
+ConstructorInitializerAllOnOneLineOrOnePerLine: true
+ConstructorInitializerIndentWidth: 4
+ContinuationIndentWidth: 4
+Cpp11BracedListStyle: true
+DerivePointerAlignment: true
+DisableFormat:   false
+ExperimentalAutoDetectBinPacking: false
+FixNamespaceComments: true
+ForEachMacros:   
+  - foreach
+  - Q_FOREACH
+  - BOOST_FOREACH
+IncludeBlocks:   Preserve
+IncludeCategories: 
+  - Regex:           '^<ext/.*\.h>'
+    Priority:        2
+  - Regex:           '^<.*\.h>'
+    Priority:        1
+  - Regex:           '^<.*'
+    Priority:        2
+  - Regex:           '.*'
+    Priority:        3
+IncludeIsMainRegex: '([-_](test|unittest))?$'
+IndentCaseLabels: true
+IndentPPDirectives: None
+IndentWidth:     4
+IndentWrappedFunctionNames: false
+JavaScriptQuotes: Leave
+JavaScriptWrapImports: true
+KeepEmptyLinesAtTheStartOfBlocks: false
+MacroBlockBegin: ''
+MacroBlockEnd:   ''
+MaxEmptyLinesToKeep: 1
+NamespaceIndentation: None
+ObjCBinPackProtocolList: Never
+ObjCBlockIndentWidth: 2
+ObjCSpaceAfterProperty: false
+ObjCSpaceBeforeProtocolList: true
+PenaltyBreakAssignment: 2
+PenaltyBreakBeforeFirstCallParameter: 1
+PenaltyBreakComment: 300
+PenaltyBreakFirstLessLess: 120
+PenaltyBreakString: 1000
+PenaltyBreakTemplateDeclaration: 10
+PenaltyExcessCharacter: 1000000
+PenaltyReturnTypeOnItsOwnLine: 200
+PointerAlignment: Left
+RawStringFormats: 
+  - Language:        Cpp
+    Delimiters:      
+      - cc
+      - CC
+      - cpp
+      - Cpp
+      - CPP
+      - 'c++'
+      - 'C++'
+    CanonicalDelimiter: ''
+    BasedOnStyle:    google
+  - Language:        TextProto
+    Delimiters:      
+      - pb
+      - PB
+      - proto
+      - PROTO
+    EnclosingFunctions: 
+      - EqualsProto
+      - EquivToProto
+      - PARSE_PARTIAL_TEXT_PROTO
+      - PARSE_TEST_PROTO
+      - PARSE_TEXT_PROTO
+      - ParseTextOrDie
+      - ParseTextProtoOrDie
+    CanonicalDelimiter: ''
+    BasedOnStyle:    google
+ReflowComments:  true
+SortIncludes:    true
+SortUsingDeclarations: true
+SpaceAfterCStyleCast: false
+SpaceAfterTemplateKeyword: true
+SpaceBeforeAssignmentOperators: true
+SpaceBeforeCpp11BracedList: false
+SpaceBeforeCtorInitializerColon: true
+SpaceBeforeInheritanceColon: true
+SpaceBeforeParens: ControlStatements
+SpaceBeforeRangeBasedForLoopColon: true
+SpaceInEmptyParentheses: false
+SpacesBeforeTrailingComments: 2
+SpacesInAngles:  false
+SpacesInContainerLiterals: true
+SpacesInCStyleCastParentheses: false
+SpacesInParentheses: false
+SpacesInSquareBrackets: false
+Standard:        Auto
+TabWidth:        8
+UseTab:          Never
+...
+
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
new file mode 100644
index 0000000000000000000000000000000000000000..34e13ad9b1d97598fa472e42392b766957034157
--- /dev/null
+++ b/.gitlab-ci.yml
@@ -0,0 +1,573 @@
+stages:
+    - build
+    - test
+variables:
+    GIT_STRATEGY: clone
+    GIT_SUBMODULE_STRATEGY: recursive
+
+compile_install_g++9:
+    stage: build
+    script:
+        - mkdir build
+        - ./doc/sample_extractor.p6 doc
+        - cd build
+        - CXX=g++-9 CC=gcc-9 cmake -DlibClaPP_BUILD_TESTS=On -DlibClaPP_BUILD_EXAMPLES=On -DlibClaPP_BUILD_DOC_CODE=On -DlibClaPP_BUILD_COVERAGE=On -DlibClaPP_SUBMODULE_DEPENDENCIES=On -DCMAKE_BUILD_TYPE=Release ..
+        - make -j2 VERBOSE=1
+        - make install
+        - echo -e '#include<clapp/main_parser.h>\n#include<clapp/build_info.h>\n#include<iostream>\nint main (int argc, char *argv[]) {\nstd::cout << clapp::build_info::project_name << " v" << clapp::build_info::version << std::endl;\n}' | g++-9 `pkg-config --libs --cflags libclapp` -I../third_party/GSL/include/ --std=c++17 -o out -x c++ -
+    tags:
+        - g++-9
+        - cmake
+    artifacts:
+        paths:
+            - build
+
+compile_release_g++9:
+    stage: build
+    script:
+        - mkdir build
+        - ./doc/sample_extractor.p6 doc
+        - cd build
+        - CXX=g++-9 CC=gcc-9 cmake -DlibClaPP_BUILD_TESTS=On -DlibClaPP_BUILD_EXAMPLES=On -DlibClaPP_BUILD_DOC_CODE=On -DlibClaPP_BUILD_COVERAGE=On -DlibClaPP_SUBMODULE_DEPENDENCIES=On -DCMAKE_BUILD_TYPE=Release ..
+        - make -j2 VERBOSE=1
+    tags:
+        - g++-9
+        - cmake
+    artifacts:
+        paths:
+            - build
+
+test_release_g++9:
+    stage: test
+    script:
+        - cd build
+        - ./tests/libclapp_tests
+        - gcovr -r ../ -e ../third_party/ -e ../tests/ -e ../examples/ --gcov-executable="gcov-9"
+    tags:
+        - g++-9
+        - cmake
+    dependencies:
+        - compile_release_g++9
+
+compile_debug_g++9:
+    stage: build
+    script:
+        - mkdir build
+        - ./doc/sample_extractor.p6 doc
+        - cd build
+        - CXX=g++-9 CC=gcc-9 cmake -DlibClaPP_BUILD_TESTS=On -DlibClaPP_BUILD_EXAMPLES=On -DlibClaPP_BUILD_DOC_CODE=On -DlibClaPP_BUILD_COVERAGE=On -DlibClaPP_SUBMODULE_DEPENDENCIES=On -DCMAKE_BUILD_TYPE=Debug ..
+        - make -j2 VERBOSE=1
+    tags:
+        - g++-9
+        - cmake
+    artifacts:
+        paths:
+            - build
+
+test_debug_g++9:
+    stage: test
+    script:
+        - cd build
+        - ./tests/libclapp_tests
+        - gcovr -r ../ -e ../third_party/ -e ../tests/ -e ../examples/ --gcov-executable="gcov-9"
+    tags:
+        - g++-9
+        - cmake
+    coverage: '/^TOTAL.*\s+(\d+%)$/'
+    dependencies:
+        - compile_debug_g++9
+
+compile_example_release_g++9:
+    stage: build
+    script:
+        - mkdir build
+        - ./doc/sample_extractor.p6 doc
+        - cd build
+        - CXX=g++-9 CC=gcc-9 cmake -DlibClaPP_BUILD_EXAMPLES=On -DlibClaPP_BUILD_DOC_CODE=On -DlibClaPP_SUBMODULE_DEPENDENCIES=On -DCMAKE_BUILD_TYPE=Release ..
+        - make -j2 VERBOSE=1
+    tags:
+        - g++-9
+        - cmake
+    artifacts:
+        paths:
+            - build
+
+compile_install_g++8:
+    stage: build
+    script:
+        - mkdir build
+        - ./doc/sample_extractor.p6 doc
+        - cd build
+        - CXX=g++-8 CC=gcc-8 cmake -DlibClaPP_BUILD_TESTS=On -DlibClaPP_BUILD_EXAMPLES=On -DlibClaPP_BUILD_DOC_CODE=On -DlibClaPP_BUILD_COVERAGE=On -DlibClaPP_SUBMODULE_DEPENDENCIES=On -DCMAKE_BUILD_TYPE=Release ..
+        - make -j2 VERBOSE=1
+        - make install
+        - echo -e '#include<clapp/main_parser.h>\n#include<clapp/build_info.h>\n#include<iostream>\nint main (int argc, char *argv[]) {\nstd::cout << clapp::build_info::project_name << " v" << clapp::build_info::version << std::endl;\n}' | g++-8 `pkg-config --libs --cflags libclapp` -I../third_party/GSL/include/ --std=c++17 -o out -x c++ -
+    tags:
+        - g++-8
+        - cmake
+    artifacts:
+        paths:
+            - build
+
+compile_release_g++8:
+    stage: build
+    script:
+        - mkdir build
+        - ./doc/sample_extractor.p6 doc
+        - cd build
+        - CXX=g++-8 CC=gcc-8 cmake -DlibClaPP_BUILD_TESTS=On -DlibClaPP_BUILD_EXAMPLES=On -DlibClaPP_BUILD_DOC_CODE=On -DlibClaPP_BUILD_COVERAGE=On -DlibClaPP_SUBMODULE_DEPENDENCIES=On -DCMAKE_BUILD_TYPE=Release ..
+        - make -j2 VERBOSE=1
+    tags:
+        - g++-8
+        - cmake
+    artifacts:
+        paths:
+            - build
+
+test_release_g++8:
+    stage: test
+    script:
+        - cd build
+        - ./tests/libclapp_tests
+        - gcovr -r ../ -e ../third_party/ -e ../tests/ -e ../examples/ --gcov-executable="gcov-8"
+    tags:
+        - g++-8
+        - cmake
+    dependencies:
+        - compile_release_g++8
+
+compile_example_release_g++8:
+    stage: build
+    script:
+        - mkdir build
+        - ./doc/sample_extractor.p6 doc
+        - cd build
+        - CXX=g++-8 CC=gcc-8 cmake -DlibClaPP_BUILD_EXAMPLES=On -DlibClaPP_BUILD_DOC_CODE=On -DlibClaPP_SUBMODULE_DEPENDENCIES=On -DCMAKE_BUILD_TYPE=Release ..
+        - make -j2 VERBOSE=1
+    tags:
+        - g++-8
+        - cmake
+    artifacts:
+        paths:
+            - build
+
+compile_debug_g++8:
+    stage: build
+    script:
+        - mkdir build
+        - ./doc/sample_extractor.p6 doc
+        - cd build
+        - CXX=g++-8 CC=gcc-8 cmake -DlibClaPP_BUILD_TESTS=On -DlibClaPP_BUILD_EXAMPLES=On -DlibClaPP_BUILD_DOC_CODE=On -DlibClaPP_BUILD_COVERAGE=On -DlibClaPP_SUBMODULE_DEPENDENCIES=On -DCMAKE_BUILD_TYPE=Debug ..
+        - make -j2 VERBOSE=1
+    tags:
+        - g++-8
+        - cmake
+    artifacts:
+        paths:
+            - build
+
+test_debug_g++8:
+    stage: test
+    script:
+        - cd build
+        - ./tests/libclapp_tests
+        - gcovr -r ../ -e ../third_party/ -e ../tests/ -e ../examples/ --gcov-executable="gcov-8"
+    tags:
+        - g++-8
+        - cmake
+    coverage: '/^TOTAL.*\s+(\d+%)$/'
+    dependencies:
+        - compile_debug_g++8
+
+compile_install_g++7:
+    stage: build
+    script:
+        - mkdir build
+        - ./doc/sample_extractor.p6 doc
+        - cd build
+        - CXX=g++-7 CC=gcc-7 cmake -DlibClaPP_BUILD_TESTS=On -DlibClaPP_BUILD_EXAMPLES=On -DlibClaPP_BUILD_DOC_CODE=On -DlibClaPP_BUILD_COVERAGE=On -DlibClaPP_SUBMODULE_DEPENDENCIES=On -DCMAKE_BUILD_TYPE=Release ..
+        - make -j2 VERBOSE=1
+        - make install
+        - echo -e '#include<clapp/main_parser.h>\n#include<clapp/build_info.h>\n#include<iostream>\nint main (int argc, char *argv[]) {\nstd::cout << clapp::build_info::project_name << " v" << clapp::build_info::version << std::endl;\n}' | g++-7 `pkg-config --libs --cflags libclapp` -I../third_party/GSL/include/ --std=c++17 -o out -x c++ -
+    tags:
+        - g++-7
+        - cmake
+    artifacts:
+        paths:
+            - build
+
+compile_release_g++7:
+    stage: build
+    script:
+        - mkdir build
+        - ./doc/sample_extractor.p6 doc
+        - cd build
+        - CXX=g++-7 CC=gcc-7 cmake -DlibClaPP_BUILD_TESTS=On -DlibClaPP_BUILD_EXAMPLES=On -DlibClaPP_BUILD_DOC_CODE=On -DlibClaPP_BUILD_COVERAGE=On -DlibClaPP_SUBMODULE_DEPENDENCIES=On -DCMAKE_BUILD_TYPE=Release ..
+        - make -j2 VERBOSE=1
+    tags:
+        - g++-7
+        - cmake
+    artifacts:
+        paths:
+            - build
+
+test_release_g++7:
+    stage: test
+    script:
+        - cd build
+        - ./tests/libclapp_tests
+        - gcovr -r ../ -e ../third_party/ -e ../tests/ -e ../examples/ --gcov-executable="gcov-7"
+    tags:
+        - g++-7
+        - cmake
+    dependencies:
+        - compile_release_g++7
+
+compile_debug_g++7:
+    stage: build
+    script:
+        - mkdir build
+        - ./doc/sample_extractor.p6 doc
+        - cd build
+        - CXX=g++-7 CC=gcc-7 cmake -DlibClaPP_BUILD_TESTS=On -DlibClaPP_BUILD_EXAMPLES=On -DlibClaPP_BUILD_DOC_CODE=On -DlibClaPP_BUILD_COVERAGE=On -DlibClaPP_SUBMODULE_DEPENDENCIES=On -DCMAKE_BUILD_TYPE=Debug ..
+        - make -j2 VERBOSE=1
+    tags:
+        - g++-7
+        - cmake
+    artifacts:
+        paths:
+            - build
+
+test_debug_g++7:
+    stage: test
+    script:
+        - cd build
+        - ./tests/libclapp_tests
+        - gcovr -r ../ -e ../third_party/ -e ../tests/ -e ../examples/ --gcov-executable="gcov-7"
+    tags:
+        - g++-7
+        - cmake
+    coverage: '/^TOTAL.*\s+(\d+%)$/'
+    dependencies:
+        - compile_debug_g++7
+
+compile_example_release_g++7:
+    stage: build
+    script:
+        - mkdir build
+        - ./doc/sample_extractor.p6 doc
+        - cd build
+        - CXX=g++-7 CC=gcc-7 cmake -DlibClaPP_BUILD_EXAMPLES=On -DlibClaPP_BUILD_DOC_CODE=On -DlibClaPP_SUBMODULE_DEPENDENCIES=On -DCMAKE_BUILD_TYPE=Release ..
+        - make -j2 VERBOSE=1
+    tags:
+        - g++-7
+        - cmake
+    artifacts:
+        paths:
+            - build
+
+compile_install_clang++9:
+    stage: build
+    script:
+        - mkdir build
+        - ./doc/sample_extractor.p6 doc
+        - cd build
+        - CXX=clang++-9 CC=clang-9 cmake -DlibClaPP_BUILD_TESTS=On -DlibClaPP_BUILD_DOC_CODE=On -DlibClaPP_BUILD_EXAMPLES=On -DlibClaPP_BUILD_COVERAGE=On -DlibClaPP_SUBMODULE_DEPENDENCIES=On -DCMAKE_BUILD_TYPE=Release ..
+        - make -j2 VERBOSE=1
+        - make install
+        - echo -e '#include<clapp/main_parser.h>\n#include<clapp/build_info.h>\n#include<iostream>\nint main (int argc, char *argv[]) {\nstd::cout << clapp::build_info::project_name << " v" << clapp::build_info::version << std::endl;\n}' | clang++-9 `pkg-config --libs --cflags libclapp` -I../third_party/GSL/include/ --std=c++17 -o out -x c++ -
+    tags:
+        - clang++-9
+        - cmake
+    artifacts:
+        paths:
+            - build
+
+compile_debug_clang++9:
+    stage: build
+    script:
+        - mkdir build
+        - ./doc/sample_extractor.p6 doc
+        - cd build
+        - CXX=clang++-9 CC=clang-9 cmake -DlibClaPP_BUILD_TESTS=On -DlibClaPP_BUILD_DOC_CODE=On -DlibClaPP_BUILD_EXAMPLES=On -DlibClaPP_BUILD_COVERAGE=On -DlibClaPP_SUBMODULE_DEPENDENCIES=On -DCMAKE_BUILD_TYPE=Debug ..
+        - make -j2 VERBOSE=1
+    tags:
+        - clang++-9
+        - cmake
+    artifacts:
+        paths:
+            - build
+
+test_debug_clang++9:
+    stage: test
+    script:
+        - cd build
+        - ./tests/libclapp_tests
+        - gcovr -r ../ -e ../third_party/ -e ../tests/ -e ../examples/ --gcov-executable="llvm-cov-9 gcov"
+    tags:
+        - clang++-9
+        - cmake
+    coverage: '/^TOTAL.*\s+(\d+%)$/'
+    dependencies:
+        - compile_debug_clang++9
+
+clang-tidy-9:
+    stage: build
+    script:
+        - mkdir build
+        - ./doc/sample_extractor.p6 doc
+        - cd build
+        - CXX=clang++-9 CC=clang-9 cmake -DCLANG_TIDY=clang-tidy-9 -DlibClaPP_BUILD_DOC_CODE=On -DlibClaPP_BUILD_TESTS=On -DlibClaPP_BUILD_EXAMPLES=On -DlibClaPP_SUBMODULE_DEPENDENCIES=On -DlibClaPP_BUILD_COVERAGE=On -DCMAKE_BUILD_TYPE=Debug -DlibClaPP_CLANG_TIDY=On ..
+        - make VERBOSE=1
+    tags:
+        - clang++-8
+        - cmake
+    artifacts:
+        paths:
+            - build
+
+compile_release_clang++9:
+    stage: build
+    script:
+        - mkdir build
+        - ./doc/sample_extractor.p6 doc
+        - cd build
+        - CXX=clang++-9 CC=clang-9 cmake -DlibClaPP_BUILD_TESTS=On -DlibClaPP_BUILD_DOC_CODE=On -DlibClaPP_BUILD_EXAMPLES=On -DlibClaPP_BUILD_COVERAGE=On -DlibClaPP_SUBMODULE_DEPENDENCIES=On -DCMAKE_BUILD_TYPE=Release ..
+        - make -j2 VERBOSE=1
+    tags:
+        - clang++-9
+        - cmake
+    artifacts:
+        paths:
+            - build
+
+test_release_clang++9:
+    stage: test
+    script:
+        - cd build
+        - ./tests/libclapp_tests
+        - gcovr -r ../ -e ../third_party/ -e ../tests/ -e ../examples/ --gcov-executable="llvm-cov-9 gcov"
+    tags:
+        - clang++-9
+        - cmake
+    dependencies:
+        - compile_release_clang++9
+
+compile_example_release_clang++9:
+    stage: build
+    script:
+        - mkdir build
+        - ./doc/sample_extractor.p6 doc
+        - cd build
+        - CXX=clang++-9 CC=clang-9 cmake -DlibClaPP_BUILD_EXAMPLES=On -DlibClaPP_BUILD_DOC_CODE=On -DlibClaPP_SUBMODULE_DEPENDENCIES=On -DCMAKE_BUILD_TYPE=Release ..
+        - make -j2 VERBOSE=1
+    tags:
+        - clang++-9
+        - cmake
+    artifacts:
+        paths:
+            - build
+
+compile_install_clang++8:
+    stage: build
+    script:
+        - mkdir build
+        - ./doc/sample_extractor.p6 doc
+        - cd build
+        - CXX=clang++-8 CC=clang-8 cmake -DlibClaPP_BUILD_TESTS=On -DlibClaPP_BUILD_DOC_CODE=On -DlibClaPP_BUILD_EXAMPLES=On -DlibClaPP_BUILD_COVERAGE=On -DlibClaPP_SUBMODULE_DEPENDENCIES=On -DCMAKE_BUILD_TYPE=Release ..
+        - make -j2 VERBOSE=1
+        - make install
+        - echo -e '#include<clapp/main_parser.h>\n#include<clapp/build_info.h>\n#include<iostream>\nint main (int argc, char *argv[]) {\nstd::cout << clapp::build_info::project_name << " v" << clapp::build_info::version << std::endl;\n}' | clang++-8 `pkg-config --libs --cflags libclapp` -I../third_party/GSL/include/ --std=c++17 -o out -x c++ -
+    tags:
+        - clang++-8
+        - cmake
+    artifacts:
+        paths:
+            - build
+
+compile_debug_clang++8:
+    stage: build
+    script:
+        - mkdir build
+        - ./doc/sample_extractor.p6 doc
+        - cd build
+        - CXX=clang++-8 CC=clang-8 cmake -DlibClaPP_BUILD_TESTS=On -DlibClaPP_BUILD_DOC_CODE=On -DlibClaPP_BUILD_EXAMPLES=On -DlibClaPP_BUILD_COVERAGE=On -DlibClaPP_SUBMODULE_DEPENDENCIES=On -DCMAKE_BUILD_TYPE=Debug ..
+        - make -j2 VERBOSE=1
+    tags:
+        - clang++-8
+        - cmake
+    artifacts:
+        paths:
+            - build
+
+test_debug_clang++8:
+    stage: test
+    script:
+        - cd build
+        - ./tests/libclapp_tests
+        - gcovr -r ../ -e ../third_party/ -e ../tests/ -e ../examples/ --gcov-executable="llvm-cov-8 gcov"
+    tags:
+        - clang++-8
+        - cmake
+    coverage: '/^TOTAL.*\s+(\d+%)$/'
+    dependencies:
+        - compile_debug_clang++8
+
+clang-tidy-8:
+    stage: build
+    script:
+        - mkdir build
+        - ./doc/sample_extractor.p6 doc
+        - cd build
+        - CXX=clang++-8 CC=clang-8 cmake -DCLANG_TIDY=clang-tidy-8 -DlibClaPP_BUILD_DOC_CODE=On -DlibClaPP_BUILD_TESTS=On -DlibClaPP_BUILD_EXAMPLES=On -DlibClaPP_SUBMODULE_DEPENDENCIES=On -DlibClaPP_BUILD_COVERAGE=On -DCMAKE_BUILD_TYPE=Debug -DlibClaPP_CLANG_TIDY=On ..
+        - make VERBOSE=1
+    tags:
+        - clang++-8
+        - cmake
+    artifacts:
+        paths:
+            - build
+
+compile_release_clang++8:
+    stage: build
+    script:
+        - mkdir build
+        - ./doc/sample_extractor.p6 doc
+        - cd build
+        - CXX=clang++-8 CC=clang-8 cmake -DlibClaPP_BUILD_TESTS=On -DlibClaPP_BUILD_DOC_CODE=On -DlibClaPP_BUILD_EXAMPLES=On -DlibClaPP_BUILD_COVERAGE=On -DlibClaPP_SUBMODULE_DEPENDENCIES=On -DCMAKE_BUILD_TYPE=Release ..
+        - make -j2 VERBOSE=1
+    tags:
+        - clang++-8
+        - cmake
+    artifacts:
+        paths:
+            - build
+
+test_release_clang++8:
+    stage: test
+    script:
+        - cd build
+        - ./tests/libclapp_tests
+        - gcovr -r ../ -e ../third_party/ -e ../tests/ -e ../examples/ --gcov-executable="llvm-cov-8 gcov"
+    tags:
+        - clang++-8
+        - cmake
+    dependencies:
+        - compile_release_clang++8
+
+compile_example_release_clang++8:
+    stage: build
+    script:
+        - mkdir build
+        - ./doc/sample_extractor.p6 doc
+        - cd build
+        - CXX=clang++-8 CC=clang-8 cmake -DlibClaPP_BUILD_DOC_CODE=On -DlibClaPP_BUILD_EXAMPLES=On -DlibClaPP_SUBMODULE_DEPENDENCIES=On -DCMAKE_BUILD_TYPE=Release ..
+        - make -j2 VERBOSE=1
+    tags:
+        - clang++-8
+        - cmake
+    artifacts:
+        paths:
+            - build
+
+compile_install_clang++7:
+    stage: build
+    script:
+        - mkdir build
+        - ./doc/sample_extractor.p6 doc
+        - cd build
+        - CXX=clang++-7 CC=clang-7 cmake -DlibClaPP_BUILD_TESTS=On -DlibClaPP_BUILD_DOC_CODE=On -DlibClaPP_BUILD_EXAMPLES=On -DlibClaPP_BUILD_COVERAGE=On -DlibClaPP_SUBMODULE_DEPENDENCIES=On -DCMAKE_BUILD_TYPE=Release ..
+        - make -j2 VERBOSE=1
+        - make install
+        - echo -e '#include<clapp/main_parser.h>\n#include<clapp/build_info.h>\n#include<iostream>\nint main (int argc, char *argv[]) {\nstd::cout << clapp::build_info::project_name << " v" << clapp::build_info::version << std::endl;\n}' | clang++-7 `pkg-config --libs --cflags libclapp` -I../third_party/GSL/include/ --std=c++17 -o out -x c++ -
+    tags:
+        - clang++-7
+        - cmake
+    artifacts:
+        paths:
+            - build
+
+compile_debug_clang++7:
+    stage: build
+    script:
+        - mkdir build
+        - ./doc/sample_extractor.p6 doc
+        - cd build
+        - CXX=clang++-7 CC=clang-7 cmake -DlibClaPP_BUILD_TESTS=On -DlibClaPP_BUILD_DOC_CODE=On -DlibClaPP_BUILD_EXAMPLES=On -DlibClaPP_SUBMODULE_DEPENDENCIES=On -DlibClaPP_BUILD_COVERAGE=On -DCMAKE_BUILD_TYPE=Debug ..
+        - make -j2 VERBOSE=1
+    tags:
+        - clang++-7
+        - cmake
+    artifacts:
+        paths:
+            - build
+
+test_debug_clang++7:
+    stage: test
+    script:
+        - cd build
+        - ./tests/libclapp_tests
+        - gcovr -r ../ -e ../third_party/ -e ../tests/ -e ../examples/ --gcov-executable="llvm-cov-7 gcov"
+    tags:
+        - clang++-7
+        - cmake
+    coverage: '/^TOTAL.*\s+(\d+%)$/'
+    dependencies:
+        - compile_debug_clang++7
+
+clang-tidy-7:
+    stage: build
+    script:
+        - mkdir build
+        - ./doc/sample_extractor.p6 doc
+        - cd build
+        - CXX=clang++-7 CC=clang-7 cmake -DCLANG_TIDY=clang-tidy-7 -DlibClaPP_BUILD_DOC_CODE=On -DlibClaPP_BUILD_TESTS=On -DlibClaPP_BUILD_EXAMPLES=On -DlibClaPP_SUBMODULE_DEPENDENCIES=On -DlibClaPP_BUILD_COVERAGE=On -DCMAKE_BUILD_TYPE=Debug -DlibClaPP_CLANG_TIDY=On ..
+        - make VERBOSE=1
+    tags:
+        - clang++-7
+        - cmake
+    artifacts:
+        paths:
+            - build
+
+compile_release_clang++7:
+    stage: build
+    script:
+        - mkdir build
+        - ./doc/sample_extractor.p6 doc
+        - cd build
+        - CXX=clang++-7 CC=clang-7 cmake -DlibClaPP_BUILD_TESTS=On -DlibClaPP_BUILD_DOC_CODE=On -DlibClaPP_BUILD_EXAMPLES=On -DlibClaPP_BUILD_COVERAGE=On -DlibClaPP_SUBMODULE_DEPENDENCIES=On -DCMAKE_BUILD_TYPE=Release ..
+        - make -j2 VERBOSE=1
+    tags:
+        - clang++-7
+        - cmake
+    artifacts:
+        paths:
+            - build
+
+test_release_clang++7:
+    stage: test
+    script:
+        - cd build
+        - ./tests/libclapp_tests
+        - gcovr -r ../ -e ../third_party/ -e ../tests/ -e ../examples/ --gcov-executable="llvm-cov-7 gcov"
+    tags:
+        - clang++-7
+        - cmake
+    dependencies:
+        - compile_release_clang++7
+
+compile_example_release_clang++7:
+    stage: build
+    script:
+        - mkdir build
+        - ./doc/sample_extractor.p6 doc
+        - cd build
+        - CXX=clang++-7 CC=clang-7 cmake -DlibClaPP_BUILD_DOC_CODE=On -DlibClaPP_BUILD_EXAMPLES=On -DlibClaPP_SUBMODULE_DEPENDENCIES=On -DCMAKE_BUILD_TYPE=Release ..
+        - make -j2 VERBOSE=1
+    tags:
+        - clang++-7
+        - cmake
+    artifacts:
+        paths:
+            - build
diff --git a/CMakeLists.txt b/CMakeLists.txt
index d122937143d4a39f33825774d0006b45156db667..a9f856ef49993615ce857aaec3854273828b18e0 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -13,13 +13,17 @@ if (NOT CMAKE_BUILD_TYPE)
 endif()
 set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
 
-set(CMAKE_CXX_FLAGS_DEBUG "-ggdb -O0 -DGSL_THROW_ON_CONTRACT_VIOLATION")
+set(CMAKE_CXX_FLAGS_DEBUG "-ggdb -O0")
 set(CMAKE_CXX_FLAGS_RELEASE "-O2 -DNDEBUG")
 
 if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU")
-    add_compile_options(-Wall -Wextra -Wunreachable-code -Wfloat-equal -Wunused-parameter -Werror -pedantic-errors -Wcast-align -Wcast-qual -Wpointer-arith -Wwrite-strings -Wstrict-overflow=1 -Wformat=2 -Wlogical-op -Wduplicated-cond -Wduplicated-branches -Wdouble-promotion -Wold-style-cast -Wshadow -Wuninitialized -Wwrite-strings)
+    list(APPEND libClaPP_TEST_COMPILE_OPTIONS -Wall -Wextra -Wunreachable-code -Wfloat-equal -Wunused-parameter -Werror -pedantic-errors -Wcast-align -Wcast-qual -Wpointer-arith -Wwrite-strings -Wstrict-overflow=1 -Wformat=2 -Wlogical-op -Wduplicated-cond -Wduplicated-branches -Wdouble-promotion -Wold-style-cast -Wshadow -Wwrite-strings -Wuninitialized -Wconversion -Wunused -Wno-missing-braces -Wnarrowing -Wconversion-null -Wuseless-cast -Wsign-conversion -Wrestrict -Wnull-dereference)
+    set(libClaPP_COMPILE_OPTIONS ${libClaPP_TEST_COMPILE_OPTIONS})
+    list(APPEND libClaPP_COMPILE_OPTIONS -Weffc++)
 elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang")
-    add_compile_options(-Weverything -Wno-c++98-compat -Wno-c++98-compat-pedantic -Wno-padded)
+    list(APPEND libClaPP_COMPILE_OPTIONS -Weverything -Wno-c++98-compat -Wno-c++98-compat-pedantic -Wno-padded -Werror -pedantic-errors)
+    set(libClaPP_TEST_COMPILE_OPTIONS ${libClaPP_COMPILE_OPTIONS})
+    list(APPEND libClaPP_TEST_COMPILE_OPTION -Wno-global-constructors)
 endif()
 
 set(CMAKE_CXX_STANDARD 17)
@@ -31,20 +35,40 @@ if(COMPILER_SUPPORTS_FILESYSTEM)
     set(libClaPP_FS_LINKER_FLAG "-lstdc++fs")
 endif()
 
+option(libClaPP_CLANG_TIDY "Clang-Tidy." Off)
 option(libClaPP_BUILD_TESTS "Build tests." Off)
+option(libClaPP_BUILD_EXAMPLES "Build examples." Off)
+option(libClaPP_BUILD_DOC_CODE "Build doc code." Off)
 option(libClaPP_BUILD_COVERAGE "Cretae gcov build." Off)
 option(libClaPP_SUBMODULE_DEPENDENCIES "Use dependencies from git submodules." Off)
 
 if(libClaPP_BUILD_COVERAGE)
     set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fprofile-arcs -ftest-coverage")
     set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fprofile-arcs -ftest-coverage")
-    set(CMAKE_EXE_LINKER_FLAGS "-fprofile-arcs -ftest-coverage")
+    set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS}  -fprofile-arcs -ftest-coverage")
+endif()
+
+if(libClaPP_CLANG_TIDY)
+    if(NOT CLANG_TIDY)
+        find_program(CLANG_TIDY NAMES "clang-tidy" DOC "Path to clang-tidy executable")
+
+        if(NOT CLANG_TIDY)
+            message(STATUS "clang-tidy not found.")
+        else()
+            message(STATUS "found CLANG_TIDY by find_program().")
+        endif()
+    endif()
+
+    if(CLANG_TIDY)
+        message(STATUS "clang-tidy found: ${CLANG_TIDY}")
+        set(CLANG_TIDY_ARGS "${CLANG_TIDY}" "-header-filter=src/include;-warnings-as-errors=*;-checks=*,-google-runtime-references,-fuchsia-default-arguments,-fuchsia-overloaded-operator,-cert-dcl50-cpp,-clang-diagnostic-unused-command-line-argument")
+    endif()
 endif()
 
 find_package(PkgConfig REQUIRED)
 
 set (project_VERSION_MAJOR 0)
-set (project_VERSION_MINOR 0)
+set (project_VERSION_MINOR 1)
 set (project_VERSION_PATCH_LEVEL 0)
 string(TIMESTAMP project_CONFIG_TIME "%Y-%m-%d %H:%M:%S")
 
@@ -57,9 +81,17 @@ if(libClaPP_SUBMODULE_DEPENDENCIES)
         add_subdirectory("${THIRD_PARTY_DIR}/GSL" EXCLUDE_FROM_ALL)
     endif()
     target_link_libraries(libClaPP_GSL INTERFACE GSL)
+    if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang")
+        target_compile_options(libClaPP_GSL INTERFACE -Wno-error=weak-vtables)
+    endif()
     if(libClaPP_BUILD_TESTS)
-        if(NOT TARGET gtest_main)
+        if(NOT TARGET gmock_main)
             add_subdirectory("${THIRD_PARTY_DIR}/googletest" EXCLUDE_FROM_ALL)
+            add_library(libClaPP_gmock_main INTERFACE)
+            target_link_libraries(libClaPP_gmock_main INTERFACE gmock_main)
+            if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang")
+                target_compile_options(libClaPP_gmock_main INTERFACE -Wno-error=used-but-marked-unused -Wno-error=covered-switch-default -Wno-error=global-constructors -Wno-gnu-zero-variadic-macro-arguments)
+            endif()
         endif()
     endif()
 else()
@@ -73,20 +105,22 @@ else()
         target_include_directories(libClaPP_GSL INTERFACE ${libClaPP_GSL_INCLUDE_DIR}/..)
     endif()
     if(libClaPP_BUILD_TESTS)
-        pkg_check_modules(GTEST REQUIRED gtest>=1.10.0)
-        add_library(gtest_main INTERFACE)
-        target_include_directories(gtest_main INTERFACE ${GTEST_INCLUDE_DIRS})
-        target_link_libraries(gtest_main INTERFACE ${GTEST_LIBRARIES})
-
-        pkg_check_modules(GMOCK REQUIRED gmock>=1.10.0)
-        add_library(gmock INTERFACE)
-        target_include_directories(gmock INTERFACE ${GMOCK_INCLUDE_DIRS})
-        target_link_libraries(gmock INTERFACE ${GMOCK_LIBRARIES})
+        pkg_check_modules(GMOCK REQUIRED gmock_main>=1.10.0)
+        add_library(libClaPP_gmock_main INTERFACE)
+        target_include_directories(libClaPP_gmock_main SYSTEM INTERFACE ${GMOCK_INCLUDE_DIRS})
+        target_link_libraries(libClaPP_gmock_main INTERFACE ${GMOCK_LIBRARIES})
     endif()
 endif()
 
 add_subdirectory(src)
-add_subdirectory(examples)
+
+if(libClaPP_BUILD_EXAMPLES)
+    add_subdirectory(examples)
+endif()
+
+if(libClaPP_BUILD_DOC_CODE)
+    add_subdirectory(doc)
+endif()
 
 configure_file(${CMAKE_CURRENT_SOURCE_DIR}/libclapp.pc.in ${CMAKE_CURRENT_BINARY_DIR}/libclapp.pc @ONLY)
 install(FILES ${CMAKE_CURRENT_BINARY_DIR}/libclapp.pc DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig/")
diff --git a/README.md b/README.md
index d33514647bb7565e01070b21d468426783a0ef03..405d568dbff58e0b86fec0e94401f05bf5a93591 100644
--- a/README.md
+++ b/README.md
@@ -1,13 +1,15 @@
-      _____  _        _____   _____  
-     / ____|| | __ _ |  __ \ |  __ \ 
+      _____  _        _____   _____
+     / ____|| | __ _ |  __ \ |  __ \
     | |     | |/ _` || |__) || |__) |
-    | |     |_|\__,_||  ___/ |  ___/ 
-    | |____          | |     | |     
-     \_____|         |_|     |_|     
+    | |     |_|\__,_||  ___/ |  ___/
+    | |____          | |     | |
+     \_____|         |_|     |_|
 
 
 libClaPP:
 =========
+[![pipeline](../badges/master/pipeline.svg)](../commits/master/)
+[![coverage](../badges/master/coverage.svg)](../commits/master/)
 
 libClaPP is an open source command line argument processing library for C++.
 It supports the processing of GNU options (long and short options) as well as positional arguments.
@@ -36,6 +38,27 @@ But if all dependencies are installed on your system, the following steps are su
     cmake ..
     make
 
+Build the library in debug mode with submodules, unitests and check code coverage:
+
+    git clone --recurse-submodules https://git.libclapp.org/libclapp/clapp.git
+    mkdir build
+    cd build
+    cmake -DlibClaPP_BUILD_TESTS=On -DlibClaPP_BUILD_COVERAGE=On -DlibClaPP_SUBMODULE_DEPENDENCIES=On -DCMAKE_BUILD_TYPE=Debug ..
+    make
+    ./tests/libclapp_tests
+    gcovr -r ../ -e ../third_party/ -e ../tests/ -e ../examples/
+
+Build the library in debug mode with submodules, unitests, examples and check code coverage:
+
+    git clone --recurse-submodules https://git.libclapp.org/libclapp/clapp.git
+    mkdir build
+    cd build
+    cmake -DlibClaPP_BUILD_TESTS=On -DlibClaPP_BUILD_EXAMPLES=On -DlibClaPP_BUILD_COVERAGE=On -DlibClaPP_SUBMODULE_DEPENDENCIES=On -DCMAKE_BUILD_TYPE=Debug ..
+    make
+    ./tests/libclapp_tests
+    gcovr -r ../ -e ../third_party/ -e ../tests/ -e ../examples/
+
+
 ### Install the library
 
 To install the library on your system, type the following command.
@@ -69,3 +92,8 @@ Coverage:
 
     cmake -DlibClaPP_BUILD_COVERAGE=On -DCMAKE_BUILD_TYPE=Debug -DlibClaPP_SUBMODULE_DEPENDENCIES=On -DlibClaPP_BUILD_TESTS=On ..
     gcovr -r ../ -e ../third_party/ -e ../tests/ -e ../examples/
+
+Clang-Tidy:
+-----------
+
+    CXX=clang++-7 CC=clang-7 cmake -DCLANG_TIDY=clang-tidy-7 -DlibClaPP_BUILD_TESTS=On -DlibClaPP_BUILD_EXAMPLES=On -DlibClaPP_SUBMODULE_DEPENDENCIES=On -DlibClaPP_BUILD_COVERAGE=On -DCMAKE_BUILD_TYPE=Debug -DlibClaPP_CLANG_TIDY=On ..
diff --git a/doc/CMakeLists.txt b/doc/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..1303f2699a3733fbd5fc251c011a3316dbfdaa02
--- /dev/null
+++ b/doc/CMakeLists.txt
@@ -0,0 +1,6 @@
+add_executable(libclapp_doc_simple_main_parser simple_main_parser.cpp)
+target_link_libraries(libclapp_doc_simple_main_parser clapp)
+target_compile_options(libclapp_doc_simple_main_parser PRIVATE ${libClaPP_COMPILE_OPTIONS})
+if(CLANG_TIDY)
+  set_target_properties(libclapp_doc_simple_main_parser PROPERTIES CXX_CLANG_TIDY "${CLANG_TIDY_ARGS}")
+endif()
diff --git a/doc/doc.md b/doc/doc.md
index 5555f0e36ac70ac0fdf8c6648fe61d3da75d8c9e..606b5d46bfcbf31fe8a2f3ee1afce4b9bc20c075 100644
--- a/doc/doc.md
+++ b/doc/doc.md
@@ -185,3 +185,55 @@ This classes must contain at least one of these methods:
     The returend strig will be appended to the description of the argumen or option.
 * `void validate(const T &value, const std::string &param_name) const;`
     If this method is defined, it will be called during the `validate()`-call of the parser.
+
+Parser:
+=======
+
+Main Parser:
+------------
+The main parser is the key element of the libClaPP CLI parsing library.
+Typically, it is a derivied class with the base class `clapp::parser::basic_main_parser_t`.
+One of the benefit of the libClaPP library is that all arguments and options can be registered 
+as class members.
+Thus, by simply passing around a reference (or a pointer) to the main parser, all parsed options 
+or arguments are condensed in a class instance.
+
+[//]:#begin_cpp_listing_simple_main_parser
+```c++
+#include <clapp/argument.h>
+#include <clapp/main_parser.h>
+#include <clapp/option.h>
+
+class cli_parser_t : public clapp::basic_main_parser_t {
+   public:
+    cli_parser_t(int argc, const char *const *argv) {
+        parse_and_validate(argc, argv);
+    }
+
+    ~cli_parser_t() override;
+
+    clapp::help_option_t help{*this, "help", 'h', "Show help options."};
+
+    clapp::string_argument_t string_arg{*this, "string-arg", "String argument"};
+
+    //explicitly delete copy/move-ctors and assignmet operators 
+    explicit cli_parser_t(const cli_parser_t &) = delete;
+    explicit cli_parser_t(cli_parser_t &&) noexcept = delete;
+    cli_parser_t &operator=(const cli_parser_t &) = delete;
+    cli_parser_t &operator=(cli_parser_t &&) noexcept = delete;
+};
+
+cli_parser_t::~cli_parser_t() = default;
+
+int main(int argc, char *argv[]) {
+    try {
+        cli_parser_t cp{argc, argv};  // parses and validates cli-arguments
+
+        Expects(cp.string_arg);  // parser ensures mandatory arguments are given
+        std::cout << "string-arg: " << cp.string_arg.value() << std::endl;
+    } catch (std::exception &e) {
+        std::cout << "Caught Exception: " << e.what() << std::endl;
+    }
+}
+```
+[//]:#end_cpp_listing_simple_main_parser
diff --git a/doc/sample_extractor.p6 b/doc/sample_extractor.p6
new file mode 100755
index 0000000000000000000000000000000000000000..dd71a33c5934d4b32824edd5dfbd7215d015c95c
--- /dev/null
+++ b/doc/sample_extractor.p6
@@ -0,0 +1,28 @@
+#!/usr/bin/perl6
+
+sub MAIN($doc_dir) {
+    chdir $doc_dir;
+    for dir(test => /^ .* \. cpp$/) -> $file {
+        say "Removing " ~ $file;
+        unlink($file);
+    }
+    
+    my Str $listing_name;
+    
+    for 'doc.md'.IO.lines -> $line {
+        if $listing_name and $line ~~ /^^\[\/\/\]\:\#end_cpp_listing_$<name>=[\w+]$$/ {
+            $listing_name eq $<name>.Str 
+                or die "Unexpected listing name. Expected: '" 
+                    ~ $listing_name ~ "' received: '" ~ $<name>.Str ~ "'.";
+            $listing_name=Nil;
+        }
+        if $listing_name {
+            unless $line ~~ /^^\`\`\`/ {
+                spurt $listing_name ~ ".cpp", $line ~ "\n", :append;
+            }
+        }
+        if !$listing_name and $line ~~ /^^\[\/\/\]\:\#begin_cpp_listing_$<name>=[\w+]$$/ {
+            $listing_name = $<name>.Str;
+        }
+    }
+}
diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt
index 0936bb0d92bff3ed7ac5da6a5f7e815576e7e175..3c8ceabb7bb17da5f04ef44869817c3925590117 100644
--- a/examples/CMakeLists.txt
+++ b/examples/CMakeLists.txt
@@ -1,13 +1,25 @@
 if(libClaPP_FS_LINKER_FLAG)
     add_executable(libclapp_example_full full_example.cpp)
     target_link_libraries(libclapp_example_full clapp ${libClaPP_FS_LINKER_FLAG})
+    target_compile_options(libclapp_example_full PRIVATE ${libClaPP_COMPILE_OPTIONS})
+    if(CLANG_TIDY)
+      set_target_properties(libclapp_example_full PROPERTIES CXX_CLANG_TIDY "${CLANG_TIDY_ARGS}")
+    endif()
     install(TARGETS libclapp_example_full DESTINATION ${CMAKE_INSTALL_BINDIR})
 endif()
 
 add_executable(libclapp_example_short short_example.cpp)
 target_link_libraries(libclapp_example_short clapp)
+target_compile_options(libclapp_example_short PRIVATE ${libClaPP_COMPILE_OPTIONS})
+if(CLANG_TIDY)
+  set_target_properties(libclapp_example_short PROPERTIES CXX_CLANG_TIDY "${CLANG_TIDY_ARGS}")
+endif()
 install(TARGETS libclapp_example_short DESTINATION ${CMAKE_INSTALL_BINDIR})
 
 add_executable(libclapp_example_sub_parser sub_parser_example.cpp)
 target_link_libraries(libclapp_example_sub_parser clapp)
+target_compile_options(libclapp_example_sub_parser PRIVATE ${libClaPP_COMPILE_OPTIONS})
+if(CLANG_TIDY)
+  set_target_properties(libclapp_example_sub_parser PROPERTIES CXX_CLANG_TIDY "${CLANG_TIDY_ARGS}")
+endif()
 install(TARGETS libclapp_example_sub_parser DESTINATION ${CMAKE_INSTALL_BINDIR})
diff --git a/examples/full_example.cpp b/examples/full_example.cpp
index e55267ef869f42b7ddfd37478144c1686343d817..7597be2465a0c3000a5dc0204d1d20b5cc5c6427 100644
--- a/examples/full_example.cpp
+++ b/examples/full_example.cpp
@@ -7,7 +7,8 @@
 #include <iostream>
 
 using string_argument_t = clapp::basic_argument_t<std::string>;
-using variadic_string_argument_t = clapp::basic_variadic_argument_t<std::string>;
+using variadic_string_argument_t =
+    clapp::basic_variadic_argument_t<std::string>;
 using int_argument_t = clapp::basic_argument_t<std::int32_t>;
 using variadic_int_argument_t = clapp::basic_variadic_argument_t<std::int32_t>;
 
@@ -38,16 +39,29 @@ struct restriction_t {
 
 class cli_parser_t : public clapp::basic_main_parser_t {
    public:
-    using clapp::basic_main_parser_t::basic_main_parser_t;
+    cli_parser_t() = default;
+
     cli_parser_t(int argc, const char *const *argv) : cli_parser_t{} {
         parse(argc, argv);
     }
-    ~cli_parser_t();
+
+    explicit cli_parser_t(const cli_parser_t &) = delete;
+    explicit cli_parser_t(cli_parser_t &&) noexcept = delete;
+    cli_parser_t &operator=(const cli_parser_t &) = delete;
+    cli_parser_t &operator=(cli_parser_t &&) noexcept = delete;
+
+    ~cli_parser_t() override;
 
     class cmd1_parser_t : public clapp::basic_sub_parser_t {
        public:
         using clapp::basic_sub_parser_t::basic_sub_parser_t;
-        ~cmd1_parser_t();
+
+        explicit cmd1_parser_t(const cmd1_parser_t &) = delete;
+        explicit cmd1_parser_t(cmd1_parser_t &&) noexcept = delete;
+        cmd1_parser_t &operator=(const cmd1_parser_t &) = delete;
+        cmd1_parser_t &operator=(cmd1_parser_t &&) noexcept = delete;
+
+        ~cmd1_parser_t() override;
         clapp::bool_option_t help{*this, "help", 'h', "Show help options."};
         clapp::bool_option_t short_bool{*this, 'b', "Short bool option.",
                                         purpose_t::mandatory};
@@ -56,25 +70,30 @@ class cli_parser_t : public clapp::basic_main_parser_t {
         entry_argument_t entry_arg{*this, "entry-arg", "Entry argument",
                                    entry_value_constraint_t{}};
 
-        int_argument_t int_arg{
-            *this, "int-arg", "Int argument",
-            clapp::min_max_value_t{std::int32_t{5}, std::int32_t{10}}};
-        string_argument_t string_arg_x{*this, "string-arg-x",
-                                       "String argument x", purpose_t::optional,
-                                       clapp::default_value_t{"abaa"}};
+        int_argument_t int_arg{*this, "int-arg", "Int argument",
+                               clapp::min_max_value_t<std::int32_t>{5, 10}};
+        string_argument_t string_arg_x{
+            *this, "string-arg-x", "String argument x", purpose_t::optional,
+            clapp::default_value_t<std::string>{"abaa"}};
     };
 
     class cmd2_parser_t : public clapp::basic_sub_parser_t {
        public:
         using clapp::basic_sub_parser_t::basic_sub_parser_t;
-        ~cmd2_parser_t();
+
+        explicit cmd2_parser_t(const cmd2_parser_t &) = delete;
+        explicit cmd2_parser_t(cmd2_parser_t &&) noexcept = delete;
+        cmd2_parser_t &operator=(const cmd2_parser_t &) = delete;
+        cmd2_parser_t &operator=(cmd2_parser_t &&) noexcept = delete;
+
+        ~cmd2_parser_t() override;
         clapp::bool_option_t help{*this, "help", 'h', "Show help options."};
         string_argument_t string_arg_x{
             *this, "string-arg-x", "String argument x", purpose_t::optional,
-            clapp::default_value_t{"default-string-arg-x"}};
+            clapp::default_value_t<std::string>{"default-string-arg-x"}};
         variadic_int_argument_t int_arg{
             *this, "variadic-int-arg", "Int argument", purpose_t::optional,
-            clapp::min_max_value_t{std::int32_t{5}, std::int32_t{10}}};
+            clapp::min_max_value_t<std::int32_t>{5, 10}};
         // string_argument_t string_arg_y{*this, "string-arg-y", "String
         // argument x", purpose_t::optional};//TODO: ensure that this throws
     };
@@ -102,13 +121,27 @@ class cli_parser_t : public clapp::basic_main_parser_t {
         purpose_t::mandatory,
         restriction_t{}};
 
-    clapp::count_option_t verbose{
-        *this,
-        "verbose",
-        'v',
-        "Verbose option.",
-        clapp::min_max_value_t{std::size_t{0}, std::size_t{7}},
-        clapp::default_value_t{std::size_t{2}}};
+    clapp::hours_param_option_t hours{
+        *this, "hours", "hours option.",
+        clapp::default_value_t<std::chrono::hours>{std::chrono::hours{100}}};
+    clapp::min_param_option_t minutes{
+        *this, "minutes", "minutes option.",
+        clapp::min_max_value_t<std::chrono::minutes>{std::chrono::minutes{0},
+                                                     std::chrono::minutes{7}}};
+    clapp::sec_param_option_t seconds{*this, "seconds", "seconds option."};
+    clapp::ms_param_option_t milliseconds{*this, "milliseconds",
+                                          "milliseconds option."};
+    clapp::us_param_option_t microseconds{*this, "microseconds",
+                                          "microseconds option."};
+    clapp::ns_param_option_t nanoseconds{*this, "nanoseconds",
+                                         "nanoseconds option."};
+
+    clapp::count_option_t verbose{*this,
+                                  "verbose",
+                                  'v',
+                                  "Verbose option.",
+                                  clapp::min_max_value_t<std::size_t>{0, 7},
+                                  clapp::default_value_t<std::size_t>{2}};
     clapp::count_option_t count{*this, 'c', "Count option ",
                                 purpose_t::mandatory};
 
@@ -132,19 +165,19 @@ class cli_parser_t : public clapp::basic_main_parser_t {
     int32_param_t mandatory_int{*this, "mandatory-int", "Mandatory Int option.",
                                 purpose_t::mandatory};
     int32_param_t default_int{*this, "default-int", "Default Int option.",
-                              clapp::default_value_t{10}};
+                              clapp::default_value_t<std::int32_t>{10}};
     int32_param_t optional_int{*this, "optional-int", "Optional Int option."};
 
     int32_param_t constrained_int{
         *this, "constrained-int",
         'f',   "Constrained default Int option.",
-        15,    clapp::min_max_value_t{std::int32_t{10}, std::int32_t{20}}};
+        15,    clapp::min_max_value_t<std::int32_t>{10, 20}};
 };
 
 std::ostream &operator<<(std::ostream &out, const entry_t &e);
 
 template <>
-entry_t clapp::value::convert_value<entry_t>(const std::string_view param);
+entry_t clapp::value::convert_value<entry_t>(std::string_view param);
 
 void process_cmd1(const cli_parser_t::cmd1_parser_t &cmd1);
 
@@ -167,11 +200,11 @@ template <>
 entry_t clapp::value::convert_value<entry_t>(const std::string_view param) {
     if (param == "entry1") {
         return entry_t::entry1;
-    } else if (param == "entry2") {
+    }
+    if (param == "entry2") {
         return entry_t::entry2;
-    } else {
-        throw std::runtime_error("Invalid enum param type.");
     }
+    throw std::runtime_error("Invalid enum param type.");
 }
 
 void process_cmd1(const cli_parser_t::cmd1_parser_t &cmd1) {
@@ -235,119 +268,126 @@ void process_cmd2(const cli_parser_t::cmd2_parser_t &cmd2) {
 }
 
 int main(int argc, char *argv[]) {
-    std::cout << clapp::project_name << " " << clapp::version << " ("
-              << clapp::git_branch << ":" << clapp::git_hash << "), "
-              << clapp::build_type << std::endl;
-    // create parser and parse the args
-    // cli_parser_t cp;
-    // cp.parse(argc, argv);
-    // or do it all in one.
-    cli_parser_t cp{argc, argv};
-
-    if (!cp) {
-        std::cout << "Parsing failed!" << std::endl;
-        return EXIT_FAILURE;
-    }
-
-    if (cp.help) {
-        std::cout << "Usage: \n"
-                  << cp.get_executable() << ' ' << cp.gen_help_msg();
-        return EXIT_SUCCESS;
-    }
+    try {
+        std::cout << clapp::project_name << " " << clapp::version << " ("
+                  << clapp::git_branch << ":" << clapp::git_hash << "), "
+                  << clapp::build_type << std::endl;
+        // create parser and parse the args
+        // cli_parser_t cp;
+        // cp.parse(argc, argv);
+        // or do it all in one.
+        cli_parser_t cp{argc, argv};
+
+        if (!cp) {
+            std::cout << "Parsing failed!" << std::endl;
+            return EXIT_FAILURE;
+        }
 
-    cp.validate();
+        if (cp.help) {
+            std::cout << "Usage: \n"
+                      << cp.get_executable() << ' ' << cp.gen_help_msg();
+            return EXIT_SUCCESS;
+        }
 
-    if (cp.string_arg) {
-        std::cout << "string-arg: " << cp.string_arg.value() << "\n";
-    } else {
-        std::cout << "string-arg: not given\n";
-    }
+        cp.validate();
 
-    if (cp.verbose) {
-        std::cout << "verbose: " << cp.verbose.value() << "\n";
-    } else {
-        std::cout << "verbose: not given\n";
-    }
+        if (cp.string_arg) {
+            std::cout << "string-arg: " << cp.string_arg.value() << "\n";
+        } else {
+            std::cout << "string-arg: not given\n";
+        }
 
-    if (cp.string_param) {
-        std::cout << "string_param: '" << cp.string_param.value() << "'\n";
-    } else {
-        std::cout << "string_param: not given\n";
-    }
+        if (cp.verbose) {
+            std::cout << "verbose: " << cp.verbose.value() << "\n";
+        } else {
+            std::cout << "verbose: not given\n";
+        }
 
-    if (cp.string_vector_param) {
-        std::cout << "string_vector_param (size: "
-                  << cp.string_vector_param.value().size() << "): ";
-        for (auto &val : cp.string_vector_param.value()) {
-            std::cout << val << ", ";
+        if (cp.string_param) {
+            std::cout << "string_param: '" << cp.string_param.value() << "'\n";
+        } else {
+            std::cout << "string_param: not given\n";
         }
-        std::cout << "\n";
 
-    } else {
-        std::cout << "string_vector_param: not given\n";
-    }
+        if (cp.string_vector_param) {
+            std::cout << "string_vector_param (size: "
+                      << cp.string_vector_param.value().size() << "): ";
+            for (auto &val : cp.string_vector_param.value()) {
+                std::cout << val << ", ";
+            }
+            std::cout << "\n";
 
-    if (cp.short_bool.given()) {
-        std::cout << "short_bool: given\n";
-    } else {
-        std::cout << "short_bool: not given\n";
-    }
+        } else {
+            std::cout << "string_vector_param: not given\n";
+        }
 
-    if (cp.restricted_bool) {
-        std::cout << "restricted_bool: given\n";
-    } else {
-        std::cout << "restricted_bool: not given\n";
-    }
+        if (cp.short_bool.given()) {
+            std::cout << "short_bool: given\n";
+        } else {
+            std::cout << "short_bool: not given\n";
+        }
 
-    if (cp.long_bool) {
-        std::cout << "long_bool: " << cp.long_bool.value() << "\n";
-    } else {
-        std::cout << "long_bool: not given\n";
-    }
+        if (cp.restricted_bool) {
+            std::cout << "restricted_bool: given\n";
+        } else {
+            std::cout << "restricted_bool: not given\n";
+        }
 
-    if (cp.count) {
-        std::cout << "count: " << cp.count.value() << "\n";
-    } else {
-        std::cout << "count: not given\n";
-    }
+        if (cp.long_bool) {
+            std::cout << "long_bool: " << cp.long_bool.value() << "\n";
+        } else {
+            std::cout << "long_bool: not given\n";
+        }
+
+        if (cp.count) {
+            std::cout << "count: " << cp.count.value() << "\n";
+        } else {
+            std::cout << "count: not given\n";
+        }
 
 #ifdef CLAPP_FS_AVAIL
-    if (cp.test_file) {
-        std::cout << "test-file: " << cp.test_file.value() << "\n";
-    } else {
-        std::cout << "test-file: not given\n";
-    }
+        if (cp.test_file) {
+            std::cout << "test-file: " << cp.test_file.value() << "\n";
+        } else {
+            std::cout << "test-file: not given\n";
+        }
 #else
-    std::cout << "without fs\n";
+        std::cout << "without fs\n";
 #endif
 
-    if (cp.constrained_int) {
-        std::cout << "constrained_int: " << cp.constrained_int.value() << "\n";
-    } else {
-        std::cout << "constrained_int: not given\n";
-    }
-    std::cout << "mandatory_bool: " << cp.mandatory_bool.value() << "\n";
+        if (cp.constrained_int) {
+            std::cout << "constrained_int: " << cp.constrained_int.value()
+                      << "\n";
+        } else {
+            std::cout << "constrained_int: not given\n";
+        }
+        std::cout << "mandatory_bool: " << cp.mandatory_bool.value() << "\n";
 
-    std::cout << "mandatory_int: " << cp.mandatory_int.value() << "\n";
-    std::cout << "default_int: " << cp.default_int.value() << "\n";
+        std::cout << "mandatory_int: " << cp.mandatory_int.value() << "\n";
+        std::cout << "default_int: " << cp.default_int.value() << "\n";
 
-    if (cp.optional_int) {
-        std::cout << "optional_int: " << cp.optional_int.value() << "\n";
-    }
+        if (cp.optional_int) {
+            std::cout << "optional_int: " << cp.optional_int.value() << "\n";
+        }
 
-    std::cout << "entry_param: '" << cp.entry_param.value() << "'\n";
+        std::cout << "entry_param: '" << cp.entry_param.value() << "'\n";
 
-    if (cp.cmd1) {
-        std::cout << "cmd1 given" << std::endl;
-        process_cmd1(cp.cmd1);
-    } else {
-        std::cout << "cmd1 not given" << std::endl;
-    }
+        if (cp.cmd1) {
+            std::cout << "cmd1 given" << std::endl;
+            process_cmd1(cp.cmd1);
+        } else {
+            std::cout << "cmd1 not given" << std::endl;
+        }
 
-    if (cp.cmd2) {
-        std::cout << "cmd2 given" << std::endl;
-        process_cmd2(cp.cmd2);
-    } else {
-        std::cout << "cmd2 not given" << std::endl;
+        if (cp.cmd2) {
+            std::cout << "cmd2 given" << std::endl;
+            process_cmd2(cp.cmd2);
+        } else {
+            std::cout << "cmd2 not given" << std::endl;
+        }
+    } catch (clapp::exception::clapp_exception_t &e) {
+        std::cout << "Caught ClaPP-Exception: " << e.what() << std::endl;
+    } catch (std::exception &e) {
+        std::cout << "Caught Exception: " << e.what() << std::endl;
     }
 }
diff --git a/examples/short_example.cpp b/examples/short_example.cpp
index 4d7de97966cdbfc8e342d60d4e6373b4b2ac4257..2b18a70da0a0e87227c1d2930502bfd3f73aa17a 100644
--- a/examples/short_example.cpp
+++ b/examples/short_example.cpp
@@ -1,31 +1,60 @@
 #include <clapp/argument.h>
+#include <clapp/build_info.h>
 #include <clapp/main_parser.h>
 #include <clapp/option.h>
 #include <iostream>
 
+[[noreturn]] void print_version_and_exit();
+
+[[noreturn]] void print_version_and_exit() {
+    std::cout << clapp::build_info::project_name << " "
+              << clapp::build_info::version << "" << std::endl;
+    exit(EXIT_SUCCESS);
+}
+
 class cli_parser_t : public clapp::basic_main_parser_t {
    public:
-    using clapp::basic_main_parser_t::basic_main_parser_t;
-    ~cli_parser_t();
-    cli_parser_t(int argc, const char *const *argv) : cli_parser_t{} {
-        parse(argc, argv);
+    cli_parser_t(int argc, const char *const *argv) {
+        parse_and_validate(argc, argv);
     }
 
-    clapp::bool_option_t help{*this, "help", 'h', "Show help options."};
+    explicit cli_parser_t(const cli_parser_t &) = delete;
+    explicit cli_parser_t(cli_parser_t &&) noexcept = delete;
+    cli_parser_t &operator=(const cli_parser_t &) = delete;
+    cli_parser_t &operator=(cli_parser_t &&) noexcept = delete;
+
+    ~cli_parser_t() override;
+
+    // if help is given, help is printed and exit(EXIT_SUCCESS) is called
+    clapp::help_option_t help{*this, std::vector<std::string>{"help", "usage"},
+                              std::vector<char>{'h', '?'},
+                              "Show help options."};
 
+    // if version is given, print_version_and_exit() is called.
+    clapp::bool_option_t version{
+        *this, std::vector<std::string>{"version", "vers"}, 'v',
+        "Show version info",
+        clapp::value::found_func_t{print_version_and_exit}};
+
+    // first mandatory argument of string
     clapp::string_argument_t string_arg{*this, "string-arg", "String argument"};
 
+    // second mandatory argument of int32_t
     clapp::int32_argument_t int_arg{
         *this, "int-arg", "Int argument",
-        clapp::min_max_value_t{std::int32_t{5}, std::int32_t{10}}};
+        clapp::min_max_value_t<std::int32_t>{5, 10}};
 
+    // third optional variadic arguments of strings
     clapp::variadic_string_argument_t variadic_string_arg{
         *this, "variadic-string-arg", "Variadic String argument",
         purpose_t::optional};
 
+    // mandatory string option
     clapp::string_param_option_t string_param{
-        *this, "string", 's', "String option 1.", purpose_t::mandatory};
+        *this, "string", std::vector<char>{'s', '1'}, "String option 1.",
+        purpose_t::mandatory};
 
+    // optional string option (multiple string vectors)
     clapp::vector_string_param_option_t string_vector_param{
         *this, "string-vector", "String vector param."};
 };
@@ -33,54 +62,46 @@ class cli_parser_t : public clapp::basic_main_parser_t {
 cli_parser_t::~cli_parser_t() = default;
 
 int main(int argc, char *argv[]) {
-    cli_parser_t cp{argc, argv};
-
-    if (cp.help) {
-        std::cout << "Usage: \n"
-                  << cp.get_executable() << ' ' << cp.gen_help_msg();
-        return EXIT_SUCCESS;
-    }
-
-    cp.validate();
-
-    if (cp.string_arg) {
-        std::cout << "string-arg: " << cp.string_arg.value() << "\n";
-    } else {
-        std::cout << "string-arg: not given\n";
-    }
-
-    if (cp.int_arg) {
-        std::cout << "int-arg: " << cp.int_arg.value() << "\n";
-    } else {
-        std::cout << "int-arg: not given\n";
-    }
-
-    if (cp.variadic_string_arg) {
-        std::cout << "variadic-string-arg (size: "
-                  << cp.variadic_string_arg.value().size() << "): ";
-        for (auto &val : cp.variadic_string_arg.value()) {
-            std::cout << val << ", ";
+    try {
+        cli_parser_t cp{argc, argv};  // parses and validates cli-arguments
+
+        Expects(cp.string_arg);  // parser ensures mandatory arguments are given
+        std::cout << "string-arg: " << cp.string_arg.value() << std::endl;
+
+        Expects(cp.int_arg);  // parser ensures mandatory arguments are given
+        std::cout << "int-arg: " << cp.int_arg.value() << std::endl;
+
+        if (cp.variadic_string_arg) {  // if variadic_string_arg is given
+            std::cout << "variadic-string-arg (size: "
+                      << cp.variadic_string_arg.value().size() << "): ";
+            // iterate over the vector of arguments
+            for (auto &val : cp.variadic_string_arg.value()) {
+                std::cout << val << ", ";
+            }
+            std::cout << std::endl;
+        } else {
+            std::cout << "variadic-string-arg: not given" << std::endl;
         }
-        std::cout << "\n";
-    } else {
-        std::cout << "variadic-string-arg: not given\n";
-    }
-
-    if (cp.string_param) {
-        std::cout << "string_param: '" << cp.string_param.value() << "'\n";
-    } else {
-        std::cout << "string_param: not given\n";
-    }
 
-    if (cp.string_vector_param) {
-        std::cout << "string_vector_param (size: "
-                  << cp.string_vector_param.value().size() << "): ";
-        for (auto &val : cp.string_vector_param.value()) {
-            std::cout << val << ", ";
+        Expects(cp.string_param);  // The parser ensures that mandatory options
+                                   // are given
+        std::cout << "string_param: '" << cp.string_param.value() << "'"
+                  << std::endl;
+
+        if (cp.string_vector_param) {  // if string_vector_param is given
+            std::cout << "string_vector_param (size: "
+                      << cp.string_vector_param.value().size() << "): ";
+            // iterate over the vector of options
+            for (auto &val : cp.string_vector_param.value()) {
+                std::cout << val << ", ";
+            }
+            std::cout << std::endl;
+        } else {
+            std::cout << "string_vector_param: not given" << std::endl;
         }
-        std::cout << "\n";
-
-    } else {
-        std::cout << "string_vector_param: not given\n";
+    } catch (clapp::exception::clapp_exception_t &e) {
+        std::cout << "Caught ClaPP-Exception: " << e.what() << std::endl;
+    } catch (std::exception &e) {
+        std::cout << "Caught Exception: " << e.what() << std::endl;
     }
 }
diff --git a/examples/sub_parser_example.cpp b/examples/sub_parser_example.cpp
index 4b38c9a4799200dbbeedba763a08ae2f091c7ce9..a4d59e90e4c00306a320c86f3a2d01be9a12e49e 100644
--- a/examples/sub_parser_example.cpp
+++ b/examples/sub_parser_example.cpp
@@ -1,60 +1,108 @@
 #include <clapp/argument.h>
+#include <clapp/build_info.h>
 #include <clapp/main_parser.h>
 #include <clapp/option.h>
 #include <clapp/sub_parser.h>
 #include <iostream>
 
+[[noreturn]] void print_version_and_exit();
+
+[[noreturn]] void print_version_and_exit() {
+    std::cout << clapp::build_info::project_name << " "
+              << clapp::build_info::version << "" << std::endl;
+    exit(EXIT_SUCCESS);
+}
+
 class cli_parser_t : public clapp::basic_main_parser_t {
    public:
     using clapp::basic_main_parser_t::basic_main_parser_t;
+    cli_parser_t() = default;
+
     cli_parser_t(int argc, const char *const *argv) : cli_parser_t{} {
-        parse(argc, argv);
+        parse_and_validate(argc, argv);
     }
 
-    ~cli_parser_t();
+    explicit cli_parser_t(const cli_parser_t &) = delete;
+    explicit cli_parser_t(cli_parser_t &&) noexcept = delete;
+    cli_parser_t &operator=(const cli_parser_t &) = delete;
+    cli_parser_t &operator=(cli_parser_t &&) noexcept = delete;
+
+    ~cli_parser_t() override;
 
-    clapp::bool_option_t help{*this, "help", 'h', "Show help options."};
+    // if help is given, help is printed and exit(EXIT_SUCCESS) is called
+    clapp::help_option_t help{*this, "help", 'h', "Show help options."};
 
-    clapp::count_option_t verbose{
-        *this,
-        "verbose",
-        'v',
-        "Verbose option.",
-        clapp::min_max_value_t{std::size_t{0}, std::size_t{7}},
-        clapp::default_value_t{std::size_t{2}}};
+    // if version is given, print_version_and_exit() is called.
+    clapp::bool_option_t version{
+        *this, "version", "Show version info",
+        clapp::value::found_func_t{print_version_and_exit}};
 
+    clapp::count_option_t verbose{*this,
+                                  "verbose",
+                                  'v',
+                                  "Verbose option.",
+                                  clapp::min_max_value_t<std::size_t>{0, 7},
+                                  clapp::default_value_t<std::size_t>{2}};
+
+    // first subparser declaration
     class first_parser_t : public clapp::basic_sub_parser_t {
        public:
         using clapp::basic_sub_parser_t::basic_sub_parser_t;
-        ~first_parser_t();
-        clapp::bool_option_t help{*this, "help", 'h', "Show help options."};
+
+        explicit first_parser_t(const first_parser_t &) = delete;
+        explicit first_parser_t(first_parser_t &&) noexcept = delete;
+        first_parser_t &operator=(const first_parser_t &) = delete;
+        first_parser_t &operator=(first_parser_t &&) noexcept = delete;
+
+        ~first_parser_t() override;
+
+        // if a help message for subparser is required, define it...
+        clapp::help_option_t help{*this, "help", 'h', "Show help options."};
+
         clapp::bool_option_t short_bool{*this, 'b', "Short bool option.",
                                         purpose_t::mandatory};
 
+        clapp::double_param_option_t double_opt{
+            *this, 'd', "Double param option",
+            clapp::default_value_t<double>{10}};
+
         clapp::string_param_option_t string{*this, 's', "String param option."};
 
         clapp::string_argument_t string_arg_x{
             *this, "string-arg-x", "String argument x", purpose_t::optional,
-            clapp::default_value_t{"abaa"}};
+            clapp::default_value_t<std::string>{"abaa"}};
     };
 
+    // second subparser declaration
     class second_parser_t : public clapp::basic_sub_parser_t {
        public:
         using clapp::basic_sub_parser_t::basic_sub_parser_t;
-        ~second_parser_t();
-        clapp::bool_option_t help{*this, "help", 'h', "Show help options."};
+
+        explicit second_parser_t(const second_parser_t &) = delete;
+        explicit second_parser_t(second_parser_t &&) noexcept = delete;
+        second_parser_t &operator=(const second_parser_t &) = delete;
+        second_parser_t &operator=(second_parser_t &&) noexcept = delete;
+
+        ~second_parser_t() override;
+
+        // if a help message for subparser is required, define it...
+        clapp::help_option_t help{*this, "help", 'h', "Show help options."};
 
         clapp::int32_argument_t int_arg{
             *this, "int-arg", "Int argument",
-            clapp::min_max_value_t{std::int32_t{5}, std::int32_t{10}}};
+            clapp::min_max_value_t<std::int32_t>{5, 10}};
+
+        clapp::double_argument_t double_arg{
+            *this, "double-arg", "Double argument",
+            clapp::min_max_value_t<double>{-1000., 1000.}};
 
         clapp::string_argument_t string_arg_x{
             *this, "string-arg-x", "String argument x", purpose_t::optional,
-            clapp::default_value_t{"default-string-arg-x"}};
+            clapp::default_value_t<std::string>{"default-string-arg-x"}};
 
         clapp::variadic_int64_argument_t variadic_int_arg{
             *this, "variadic-int-arg", "Int argument", purpose_t::optional,
-            clapp::min_max_value_t{std::int64_t{5}, std::int64_t{10}}};
+            clapp::min_max_value_t<std::int64_t>{5, 10}};
     };
 
     first_parser_t first{*this, "first", "First parser."};
@@ -69,21 +117,10 @@ void process_first(const cli_parser_t::first_parser_t &first);
 void process_second(const cli_parser_t::second_parser_t &second);
 
 void process_first(const cli_parser_t::first_parser_t &first) {
-    if (first.help) {
-        std::cout << "Usage: \n"
-                  << first.get_sub_parser_name() << ' ' << first.gen_help_msg();
-        return;
-    }
-
-    first.validate();
-
-    if (first.short_bool) {
-        std::cout << "short_bool: " << first.short_bool.value() << "\n";
-    } else {
-        std::cout << "short_bool: not given\n";
-    }
+    Expects(first.short_bool);  // parser ensures mandatory options are given
+    std::cout << "short_bool: " << first.short_bool.value() << "\n";
 
-    if (first.string) {
+    if (first.string) {  // if optional string option is given
         std::cout << "string: " << first.string.value() << "\n";
     } else {
         std::cout << "string: not given\n";
@@ -94,31 +131,25 @@ void process_first(const cli_parser_t::first_parser_t &first) {
     } else {
         std::cout << "string-arg-x: not given\n";
     }
+
+    Expects(first.double_opt);
+    std::cout << "double-opt: " << first.double_opt.value() << "\n";
 }
 
 void process_second(const cli_parser_t::second_parser_t &second) {
-    if (second.help) {
-        std::cout << "Usage: \n"
-                  << second.get_sub_parser_name() << ' '
-                  << second.gen_help_msg();
-        return;
-    }
+    Expects(second.int_arg);  // parser ensures mandatory arguments are given
+    std::cout << "int-arg: " << second.int_arg.value() << "\n";
 
-    second.validate();
+    Expects(second.double_arg);  // parser ensures mandatory arguments are given
+    std::cout << "double-arg: " << second.double_arg.value() << "\n";
 
-    if (second.int_arg) {
-        std::cout << "int-arg: " << second.int_arg.value() << "\n";
-    } else {
-        std::cout << "int-arg: not given\n";
-    }
-
-    if (second.string_arg_x) {
+    if (second.string_arg_x) {  // if optional string-arg-x option is given
         std::cout << "string-arg-x: " << second.string_arg_x.value() << "\n";
     } else {
         std::cout << "string-arg-x: not given\n";
     }
 
-    if (second.variadic_int_arg) {
+    if (second.variadic_int_arg) {  // if optional variadic int argument given
         std::cout << "variadic-int-arg (size: "
                   << second.variadic_int_arg.value().size() << "): ";
         for (auto &val : second.variadic_int_arg.value()) {
@@ -131,33 +162,31 @@ void process_second(const cli_parser_t::second_parser_t &second) {
 }
 
 int main(int argc, char *argv[]) {
-    cli_parser_t cp{argc, argv};
-
-    if (cp.help) {
-        std::cout << "Usage: \n"
-                  << cp.get_executable() << ' ' << cp.gen_help_msg();
-        return EXIT_SUCCESS;
-    }
+    try {
+        cli_parser_t cp{argc, argv};
 
-    cp.validate();
-
-    if (cp.verbose) {
-        std::cout << "verbose: " << cp.verbose.value() << "\n";
-    } else {
-        std::cout << "verbose: not given\n";
-    }
+        if (cp.verbose) {  // if the optional verbose option is given
+            std::cout << "verbose: " << cp.verbose.value() << "\n";
+        } else {
+            std::cout << "verbose: not given\n";
+        }
 
-    if (cp.first) {
-        std::cout << "first parser active" << std::endl;
-        process_first(cp.first);
-    } else {
-        std::cout << "first parser not active" << std::endl;
-    }
+        if (cp.first) {  // if the first sub-parser is selected
+            std::cout << "first parser active" << std::endl;
+            process_first(cp.first);
+        } else {
+            std::cout << "first parser not active" << std::endl;
+        }
 
-    if (cp.second) {
-        std::cout << "second parser active" << std::endl;
-        process_second(cp.second);
-    } else {
-        std::cout << "second parser not active" << std::endl;
+        if (cp.second) {  // if the first sub-parser is selected
+            std::cout << "second parser active" << std::endl;
+            process_second(cp.second);
+        } else {
+            std::cout << "second parser not active" << std::endl;
+        }
+    } catch (clapp::exception::clapp_exception_t &e) {
+        std::cout << "Caught ClaPP-Exception: " << e.what() << std::endl;
+    } catch (std::exception &e) {
+        std::cout << "Caught Exception: " << e.what() << std::endl;
     }
 }
diff --git a/libclapp.pc.in b/libclapp.pc.in
index 43a02197f62a90163167348285c389129952f427..edb5617b3324ab1e08c43d9a4a068b0acbb16085 100644
--- a/libclapp.pc.in
+++ b/libclapp.pc.in
@@ -5,5 +5,5 @@ Name: @CMAKE_PROJECT_NAME@
 Description: libClaPP is an open source command line argument processing library for C++.
 Version: @project_VERSION_MAJOR@.@project_VERSION_MINOR@.@project_VERSION_PATCH_LEVEL@
 Requires: 
-Libs: -L${libdir} -lclapp
+Libs: -L${libdir} -lclapp @libClaPP_FS_LINKER_FLAG@
 Cflags: -I${includedir}
diff --git a/src/clapp/CMakeLists.txt b/src/clapp/CMakeLists.txt
index d386b4d71f28a372217d960e08cb7bd49fdb19c6..291ce40ac8b5f07cab6273a612e6de26915d4ea4 100644
--- a/src/clapp/CMakeLists.txt
+++ b/src/clapp/CMakeLists.txt
@@ -1,11 +1,15 @@
-add_library(clapp SHARED option.cpp value.cpp parser.cpp main_parser.cpp sub_parser.cpp)
-target_include_directories(clapp PUBLIC
-  $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/../include>
-  $<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}/../include>
-  $<INSTALL_INTERFACE:../include>
-)
+add_library(clapp SHARED option.cpp value.cpp parser.cpp main_parser.cpp sub_parser.cpp exception.cpp)
+set(libClaPP_INCLUDE_DIRS $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/../include>
+    $<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}/../include>)
+target_include_directories(clapp PRIVATE ${libClaPP_INCLUDE_DIRS}
+  SYSTEM INTERFACE ${libClaPP_INCLUDE_DIRS} $<INSTALL_INTERFACE:../include>)
 target_link_libraries(clapp PUBLIC libClaPP_GSL ${libClaPP_FS_LINKER_FLAG})
+target_compile_options(clapp PRIVATE ${libClaPP_COMPILE_OPTIONS})
 set_target_properties(clapp PROPERTIES VERSION "${project_VERSION_MAJOR}.${project_VERSION_MINOR}" POSITION_INDEPENDENT_CODE ON)
+if(CLANG_TIDY)
+  set_target_properties(clapp PROPERTIES CXX_CLANG_TIDY "${CLANG_TIDY_ARGS}")
+endif()
+
 install(TARGETS clapp LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
 
 if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU")
diff --git a/src/clapp/exception.cpp b/src/clapp/exception.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..78332ccd5803f3be3e1f47e1dbe21bc51e053997
--- /dev/null
+++ b/src/clapp/exception.cpp
@@ -0,0 +1,178 @@
+///////////////////////////////////////////////////////////////////////////////
+//
+// Copyright (c) 2020 Martin Wölzer
+//
+// This code is licensed under the MIT License (MIT).
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+// SOFTWARE.
+///////////////////////////////////////////////////////////////////////////////
+
+#include <clapp/exception.h>
+
+clapp::exception::clapp_exception_t::clapp_exception_t(const char* message)
+    : std::runtime_error(message) {}
+clapp::exception::clapp_exception_t::clapp_exception_t(
+    const std::string& message)
+    : std::runtime_error(message) {}
+clapp::exception::clapp_exception_t::clapp_exception_t(
+    const clapp_exception_t&) = default;
+clapp::exception::clapp_exception_t& clapp::exception::clapp_exception_t::
+operator=(const clapp_exception_t&) = default;
+clapp::exception::clapp_exception_t::clapp_exception_t(
+    clapp_exception_t&&) noexcept = default;
+clapp::exception::clapp_exception_t& clapp::exception::clapp_exception_t::
+operator=(clapp_exception_t&&) noexcept = default;
+clapp::exception::clapp_exception_t::~clapp_exception_t() noexcept = default;
+
+clapp::exception::invalid_value_t::invalid_value_t(const char* message)
+    : clapp_exception_t(message) {}
+clapp::exception::invalid_value_t::invalid_value_t(const std::string& message)
+    : clapp_exception_t(message) {}
+clapp::exception::invalid_value_t::invalid_value_t(const invalid_value_t&) =
+    default;
+clapp::exception::invalid_value_t& clapp::exception::invalid_value_t::operator=(
+    const invalid_value_t&) = default;
+clapp::exception::invalid_value_t::invalid_value_t(invalid_value_t&&) noexcept =
+    default;
+clapp::exception::invalid_value_t& clapp::exception::invalid_value_t::operator=(
+    invalid_value_t&&) noexcept = default;
+clapp::exception::invalid_value_t::~invalid_value_t() noexcept = default;
+
+clapp::exception::value_undefined_t::value_undefined_t(const char* message)
+    : clapp_exception_t(message) {}
+clapp::exception::value_undefined_t::value_undefined_t(
+    const std::string& message)
+    : clapp_exception_t(message) {}
+clapp::exception::value_undefined_t::value_undefined_t(
+    const value_undefined_t&) = default;
+clapp::exception::value_undefined_t& clapp::exception::value_undefined_t::
+operator=(const value_undefined_t&) = default;
+clapp::exception::value_undefined_t::value_undefined_t(
+    value_undefined_t&&) noexcept = default;
+clapp::exception::value_undefined_t& clapp::exception::value_undefined_t::
+operator=(value_undefined_t&&) noexcept = default;
+clapp::exception::value_undefined_t::~value_undefined_t() noexcept = default;
+
+clapp::exception::out_of_range_t::out_of_range_t(const char* message)
+    : invalid_value_t(message) {}
+clapp::exception::out_of_range_t::out_of_range_t(const std::string& message)
+    : invalid_value_t(message) {}
+clapp::exception::out_of_range_t::out_of_range_t(const out_of_range_t&) =
+    default;
+clapp::exception::out_of_range_t& clapp::exception::out_of_range_t::operator=(
+    const out_of_range_t&) = default;
+clapp::exception::out_of_range_t::out_of_range_t(out_of_range_t&&) noexcept =
+    default;
+clapp::exception::out_of_range_t& clapp::exception::out_of_range_t::operator=(
+    out_of_range_t&&) noexcept = default;
+clapp::exception::out_of_range_t::~out_of_range_t() noexcept = default;
+
+clapp::exception::path_does_not_exist_t::path_does_not_exist_t(
+    const char* message)
+    : invalid_value_t(message) {}
+clapp::exception::path_does_not_exist_t::path_does_not_exist_t(
+    const std::string& message)
+    : invalid_value_t(message) {}
+clapp::exception::path_does_not_exist_t::path_does_not_exist_t(
+    const path_does_not_exist_t&) = default;
+clapp::exception::path_does_not_exist_t&
+clapp::exception::path_does_not_exist_t::operator=(
+    const path_does_not_exist_t&) = default;
+clapp::exception::path_does_not_exist_t::path_does_not_exist_t(
+    path_does_not_exist_t&&) noexcept = default;
+clapp::exception::path_does_not_exist_t&
+clapp::exception::path_does_not_exist_t::operator=(
+    path_does_not_exist_t&&) noexcept = default;
+clapp::exception::path_does_not_exist_t::~path_does_not_exist_t() noexcept =
+    default;
+
+clapp::exception::option_exception_t::option_exception_t(const char* message)
+    : clapp_exception_t(message) {}
+clapp::exception::option_exception_t::option_exception_t(
+    const std::string& message)
+    : clapp_exception_t(message) {}
+clapp::exception::option_exception_t::option_exception_t(
+    const option_exception_t&) = default;
+clapp::exception::option_exception_t& clapp::exception::option_exception_t::
+operator=(const option_exception_t&) = default;
+clapp::exception::option_exception_t::option_exception_t(
+    option_exception_t&&) noexcept = default;
+clapp::exception::option_exception_t& clapp::exception::option_exception_t::
+operator=(option_exception_t&&) noexcept = default;
+clapp::exception::option_exception_t::~option_exception_t() noexcept = default;
+
+clapp::exception::option_param_exception_t::option_param_exception_t(
+    const char* message)
+    : option_exception_t(message) {}
+clapp::exception::option_param_exception_t::option_param_exception_t(
+    const std::string& message)
+    : option_exception_t(message) {}
+clapp::exception::option_param_exception_t::option_param_exception_t(
+    const option_param_exception_t&) = default;
+clapp::exception::option_param_exception_t&
+clapp::exception::option_param_exception_t::operator=(
+    const option_param_exception_t&) = default;
+clapp::exception::option_param_exception_t::option_param_exception_t(
+    option_param_exception_t&&) noexcept = default;
+clapp::exception::option_param_exception_t&
+clapp::exception::option_param_exception_t::operator=(
+    option_param_exception_t&&) noexcept = default;
+clapp::exception::option_param_exception_t::
+    ~option_param_exception_t() noexcept = default;
+
+clapp::exception::argument_exception_t::argument_exception_t(
+    const char* message)
+    : clapp_exception_t(message) {}
+clapp::exception::argument_exception_t::argument_exception_t(
+    const std::string& message)
+    : clapp_exception_t(message) {}
+clapp::exception::argument_exception_t::argument_exception_t(
+    const argument_exception_t&) = default;
+clapp::exception::argument_exception_t& clapp::exception::argument_exception_t::
+operator=(const argument_exception_t&) = default;
+clapp::exception::argument_exception_t::argument_exception_t(
+    argument_exception_t&&) noexcept = default;
+clapp::exception::argument_exception_t& clapp::exception::argument_exception_t::
+operator=(argument_exception_t&&) noexcept = default;
+clapp::exception::argument_exception_t::~argument_exception_t() noexcept =
+    default;
+
+clapp::exception::parser_exception_t::parser_exception_t(const char* message)
+    : clapp_exception_t(message) {}
+clapp::exception::parser_exception_t::parser_exception_t(
+    const std::string& message)
+    : clapp_exception_t(message) {}
+clapp::exception::parser_exception_t::parser_exception_t(
+    const parser_exception_t&) = default;
+clapp::exception::parser_exception_t& clapp::exception::parser_exception_t::
+operator=(const parser_exception_t&) = default;
+clapp::exception::parser_exception_t::parser_exception_t(
+    parser_exception_t&&) noexcept = default;
+clapp::exception::parser_exception_t& clapp::exception::parser_exception_t::
+operator=(parser_exception_t&&) noexcept = default;
+clapp::exception::parser_exception_t::~parser_exception_t() noexcept = default;
+
+clapp::exception::sub_parser_exception_t::sub_parser_exception_t(
+    const char* message)
+    : clapp_exception_t(message) {}
+clapp::exception::sub_parser_exception_t::sub_parser_exception_t(
+    const std::string& message)
+    : clapp_exception_t(message) {}
+clapp::exception::sub_parser_exception_t::sub_parser_exception_t(
+    const sub_parser_exception_t&) = default;
+clapp::exception::sub_parser_exception_t&
+clapp::exception::sub_parser_exception_t::operator=(
+    const sub_parser_exception_t&) = default;
+clapp::exception::sub_parser_exception_t::sub_parser_exception_t(
+    sub_parser_exception_t&&) noexcept = default;
+clapp::exception::sub_parser_exception_t&
+clapp::exception::sub_parser_exception_t::operator=(
+    sub_parser_exception_t&&) noexcept = default;
+clapp::exception::sub_parser_exception_t::~sub_parser_exception_t() noexcept =
+    default;
diff --git a/src/clapp/main_parser.cpp b/src/clapp/main_parser.cpp
index 003ee878480fcbf41ebe4c98c57cc65ea307b0a0..722f8ae347364123072605d2a82fdffed2165ce3 100644
--- a/src/clapp/main_parser.cpp
+++ b/src/clapp/main_parser.cpp
@@ -38,3 +38,13 @@ void clapp::parser::basic_main_parser_t::parse(const arg_t& arg) {
     executable = *it;
     parse(it + 1, arg.cend());
 }
+
+void clapp::parser::basic_main_parser_t::parse_and_validate(
+    int argc, const char* const* argv) {
+    parse(argc, argv);
+    validate_recursive();
+}
+
+std::string clapp::parser::basic_main_parser_t::gen_help_prefix() const {
+    return basic_parser_t::gen_help_prefix() + get_executable();
+}
diff --git a/src/clapp/option.cpp b/src/clapp/option.cpp
index bf24d5ef3b70ebc1936908a8831f806c6177f5cd..c7926ef59a6e76ddb22e2e8a56c04433acf32ca4 100644
--- a/src/clapp/option.cpp
+++ b/src/clapp/option.cpp
@@ -21,7 +21,7 @@ void clapp::option::check_long_option(const std::string_view option) {
         ss << "Whitespaces and equalsigns are not allowed in long options: "
               "option='"
            << option << "'";
-        throw std::runtime_error(ss.str());
+        throw clapp::exception::option_exception_t(ss.str());
     }
 }
 
@@ -36,7 +36,7 @@ void clapp::option::check_short_option(const char option) {
             ss << "Whitespaces and equalsigns are not allowed in short "
                   "options: option='"
                << option << "'";
-            throw std::runtime_error(ss.str());
+            throw clapp::exception::option_exception_t(ss.str());
         }
         default:
             return;
@@ -56,11 +56,9 @@ clapp::option::bool_option_t::create_callbacks(bool_option_t* inst) {
 void clapp::option::bool_option_t::found_entry() {
     _given = true;
     _value = true;
-}
-
-clapp::option::bool_option_t::operator bool() const {
-    Expects(_value.has_value());
-    return _value.value();
+    for (auto& found_func : _found) {
+        found_func.found();
+    }
 }
 
 clapp::option::bool_option_t::~bool_option_t() = default;
@@ -78,11 +76,9 @@ clapp::option::count_option_t::create_callbacks(count_option_t* inst) {
 void clapp::option::count_option_t::found_entry() {
     _given = true;
     _value = _value.value() + 1;
-}
-
-clapp::option::count_option_t::operator bool() const {
-    Expects(_value.has_value());
-    return _value.value() > 0;
+    for (auto& found_func : _found) {
+        found_func.found();
+    }
 }
 
 clapp::option::count_option_t::~count_option_t() = default;
diff --git a/src/clapp/parser.cpp b/src/clapp/parser.cpp
index 19abf6526e6941335346c1c7f21a430eed64165c..7abf014d71da22a2c308363ee0524567f949c4b1 100644
--- a/src/clapp/parser.cpp
+++ b/src/clapp/parser.cpp
@@ -14,15 +14,11 @@
 ///////////////////////////////////////////////////////////////////////////////
 
 #include <clapp/parser.h>
+#include <clapp/sub_parser.h>
 #include <iostream>
 #include <sstream>
 #include <utility>
 
-clapp::parser::arg_t::arg_t(const char* const* argv, int argc)
-    : base{gsl::make_span(argv, argc)} {
-    Expects(argv[argc] == nullptr);
-}
-
 clapp::parser::basic_parser_t::basic_parser_t() = default;
 
 clapp::parser::basic_parser_t::sub_parsers_map_t&
@@ -84,11 +80,11 @@ void clapp::parser::basic_parser_t::reg(reg_sub_parser_conf_t&& config) {
         max_option_string_size = config.sub_parser_name.size();
     }
 
-    if (get_optional_argument_descriptions().size() > 0) {
+    if (!get_optional_argument_descriptions().empty()) {
         std::stringstream ss;
         ss << "Can't register sub-parser '" << config.sub_parser_name
            << "' as optional arguments are already registered.";
-        throw std::runtime_error(ss.str());
+        throw clapp::exception::sub_parser_exception_t(ss.str());
     }
 
     const std::size_t num_arguments{get_arguments().size()};
@@ -97,7 +93,7 @@ void clapp::parser::basic_parser_t::reg(reg_sub_parser_conf_t&& config) {
         std::stringstream ss;
         ss << "Can't register sub-parser '" << config.sub_parser_name
            << "' as a variadic argument is already registered.";
-        throw std::runtime_error(ss.str());
+        throw clapp::exception::sub_parser_exception_t(ss.str());
     }
 
     auto found_sub_parser{get_sub_parsers().find(config.sub_parser_name)};
@@ -105,14 +101,17 @@ void clapp::parser::basic_parser_t::reg(reg_sub_parser_conf_t&& config) {
         std::stringstream ss;
         ss << "Can't register sub-parser '" << config.sub_parser_name
            << "' as a sub-parser with this name is already registered.";
-        throw std::runtime_error(ss.str());
+        throw clapp::exception::sub_parser_exception_t(ss.str());
     }
 
     get_sub_parser_descriptions().push_back(
         {config.sub_parser_name, std::move(config.description)});
 
-    get_sub_parsers().emplace(std::move(config.sub_parser_name),
-                              std::move(config.parse));
+    get_sub_parsers().emplace(std::move(config.sub_parser_name), config.parser);
+}
+
+std::string clapp::parser::basic_parser_t::gen_help_prefix() const {
+    return "Usage: \n";
 }
 
 std::string clapp::parser::basic_parser_t::gen_help_msg() const {
@@ -122,7 +121,7 @@ std::string clapp::parser::basic_parser_t::gen_help_msg() const {
     std::string optional_option_desc;
     std::string mandatory_argument_desc;
     std::string optional_argument_desc;
-    if (mandatory_option_descriptions.size() > 0) {
+    if (!mandatory_option_descriptions.empty()) {
         mandatory_option_desc = "\nMandatory Options:\n";
         for (const option_description_container_t& desc_cont :
              mandatory_option_descriptions) {
@@ -141,7 +140,7 @@ std::string clapp::parser::basic_parser_t::gen_help_msg() const {
         }
     }
 
-    if (optional_option_descriptions.size() > 0) {
+    if (!optional_option_descriptions.empty()) {
         optional_option_desc = "\nOptional Options:\n";
         for (const option_description_container_t& desc_cont :
              optional_option_descriptions) {
@@ -160,7 +159,7 @@ std::string clapp::parser::basic_parser_t::gen_help_msg() const {
         }
     }
 
-    if (mandatory_argument_descriptions.size() > 0) {
+    if (!mandatory_argument_descriptions.empty()) {
         mandatory_argument_desc = "\nMandatory Arguments:\n";
 
         for (const argument_description_container_t& desc_cont :
@@ -180,7 +179,7 @@ std::string clapp::parser::basic_parser_t::gen_help_msg() const {
         }
     }
 
-    if (optional_argument_descriptions.size() > 0) {
+    if (!optional_argument_descriptions.empty()) {
         optional_argument_desc = "\nOptional Arguments:\n";
 
         for (const argument_description_container_t& desc_cont :
@@ -201,7 +200,7 @@ std::string clapp::parser::basic_parser_t::gen_help_msg() const {
         }
     }
 
-    if (sub_parser_descriptions.size() > 0) {
+    if (!sub_parser_descriptions.empty()) {
         sub_parser_desc = "\nAvailable sub-parsers:\n";
 
         for (const sub_parser_description_container_t& desc_cont :
@@ -227,20 +226,19 @@ clapp::basic_parser_t::process_parse_result(
     if (it == parse_result.it) {
         if (parse_result.short_option) {
             std::stringstream ss;
-            ss << "Invalid (sub-)parser line option '-"
+            ss << "Invalid (sub-)parser option '-"
                << parse_result.short_option.value() << "'";
-            throw std::runtime_error(ss.str());
-        } else if (parse_result.long_option) {
+            throw clapp::exception::option_exception_t(ss.str());
+        }
+        if (parse_result.long_option) {
             std::stringstream ss;
-            ss << "Invalid (sub-)parser line option '--"
+            ss << "Invalid (sub-)parser option '--"
                << parse_result.long_option.value() << "'";
-            throw std::runtime_error(ss.str());
-        } else {
-            return it + 1;  // ignore unknonwn arg
+            throw clapp::exception::option_exception_t(ss.str());
         }
-    } else {
-        return parse_result.it;
+        return it + 1;  // ignore unknonwn arg
     }
+    return parse_result.it;
 }
 
 void clapp::parser::basic_parser_t::parse(arg_iterator begin,
@@ -257,12 +255,12 @@ clapp::parser::basic_parser_t::parse_result_t clapp::basic_parser_t::parse(
     if (option.size() >= 3 && option[0] == '-' && option[1] == '-') {
         option.remove_prefix(2);
         return parse_long(option, it, end);
-    } else if (option.size() >= 2 && option[0] == '-') {
+    }
+    if (option.size() >= 2 && option[0] == '-') {
         option.remove_prefix(1);
         return parse_short(option, it, end);
-    } else {
-        return parse_arg(option, it, end);
     }
+    return parse_arg(option, it, end);
 }
 
 void clapp::parser::basic_parser_t::validate() const {
@@ -271,6 +269,15 @@ void clapp::parser::basic_parser_t::validate() const {
     }
 }
 
+void clapp::parser::basic_parser_t::validate_recursive() const {
+    validate();
+    for (const auto& elem : sub_parsers) {
+        if (elem.second) {
+            elem.second.validate_recursive();
+        }
+    }
+}
+
 clapp::parser::basic_parser_t::parse_result_t
 clapp::parser::basic_parser_t::parse_arg(const std::string_view option,
                                          arg_iterator it, arg_iterator end) {
@@ -293,9 +300,9 @@ clapp::parser::basic_parser_t::parse_arg(const std::string_view option,
     if (found_sub_parser == get_sub_parsers().end()) {
         std::stringstream ss;
         ss << "Unknown argument/sub-parser '" << option << "'.";
-        throw std::runtime_error(ss.str());
+        throw clapp::exception::clapp_exception_t(ss.str());
     }
-    found_sub_parser->second(it + 1, end);
+    found_sub_parser->second.sub_parse(it + 1, end);
     return parse_result_t{end, std::nullopt, std::nullopt};
 }
 
@@ -310,38 +317,36 @@ clapp::parser::basic_parser_t::parse_long(const std::string_view option,
         return parse_result_t{it, std::nullopt, std::string{opt}};
     }
     if (equal_index != std::string_view::npos) {
-        if (std::holds_alternative<long_opt_param_func_t>(found->second)) {
-            std::get<long_opt_param_func_t>(found->second)(
-                opt, std::string_view{&(option[equal_index + 1])});
-            return parse_result_t{it + 1, std::nullopt, std::nullopt};
-        } else {
+        if (!std::holds_alternative<long_opt_param_func_t>(found->second)) {
             std::stringstream ss;
             ss << "No param allowed for option '--" << opt << "'";
-            throw std::runtime_error(ss.str());
+            throw clapp::exception::option_param_exception_t(ss.str());
         }
-    } else {
-        if (std::holds_alternative<long_opt_param_func_t>(found->second)) {
-            if (it + 1 != end) {
-                it++;
-                std::get<long_opt_param_func_t>(found->second)(
-                    opt, std::string_view{*(it)});
-                return parse_result_t{it + 1, std::nullopt, std::nullopt};
-            } else {
-                std::stringstream ss;
-                ss << "No param given for option '--" << opt << "'";
-                throw std::runtime_error(ss.str());
-            }
-        } else {
-            Expects(std::holds_alternative<long_opt_func_t>(found->second));
-            std::get<long_opt_func_t>(found->second)(opt);
-            return parse_result_t{it + 1, std::nullopt, std::nullopt};
+        std::get<long_opt_param_func_t>(found->second)(
+            opt, std::string_view{&(option[equal_index + 1])});
+        return parse_result_t{it + 1, std::nullopt, std::nullopt};
+    }
+    if (std::holds_alternative<long_opt_param_func_t>(found->second)) {
+        if (it + 1 == end) {
+            std::stringstream ss;
+            ss << "No param given for option '--" << opt << "'";
+            throw clapp::exception::option_param_exception_t(ss.str());
         }
+
+        it++;
+        std::get<long_opt_param_func_t>(found->second)(opt,
+                                                       std::string_view{*(it)});
+        return parse_result_t{it + 1, std::nullopt, std::nullopt};
     }
+    Expects(std::holds_alternative<long_opt_func_t>(found->second));
+    std::get<long_opt_func_t>(found->second)(opt);
+    return parse_result_t{it + 1, std::nullopt, std::nullopt};
 }
 
 clapp::parser::basic_parser_t::parse_result_t
 clapp::parser::basic_parser_t::parse_short(const std::string_view option,
                                            arg_iterator it, arg_iterator end) {
+    Expects(!option.empty());
     const std::size_t equal_index{option.find_first_of("=")};
     for (std::size_t i{0}; i < option.size(); i++) {
         auto found{get_short_options().find(option[i])};
@@ -349,42 +354,49 @@ clapp::parser::basic_parser_t::parse_short(const std::string_view option,
             return parse_result_t{it, option[i], std::nullopt};
         }
         if (equal_index == i + 1) {
-            if (std::holds_alternative<short_opt_param_func_t>(found->second)) {
-                std::get<short_opt_param_func_t>(found->second)(
-                    option[i], std::string_view{&(option[equal_index + 1])});
-                return parse_result_t{it + 1, std::nullopt, std::nullopt};
-            } else {
+            if (!std::holds_alternative<short_opt_param_func_t>(
+                    found->second)) {
                 std::stringstream ss;
                 ss << "No param allowed for option '-" << option[i] << "'";
-                throw std::runtime_error(ss.str());
+                throw clapp::exception::option_param_exception_t(ss.str());
             }
-        } else if (i == option.size() - 1) {
+            std::get<short_opt_param_func_t>(found->second)(
+                option[i], std::string_view{&(option[equal_index + 1])});
+            return parse_result_t{it + 1, std::nullopt, std::nullopt};
+        }
+        if (i == option.size() - 1) {
             if (std::holds_alternative<short_opt_param_func_t>(found->second)) {
-                if (it + 1 != end) {
-                    it++;
-                    std::get<short_opt_param_func_t>(found->second)(
-                        option[i], std::string_view{*(it)});
-                    return parse_result_t{it + 1, std::nullopt, std::nullopt};
-                } else {
+                if (it + 1 == end) {
                     std::stringstream ss;
                     ss << "No param given for option '-" << option[i] << "'";
-                    throw std::runtime_error(ss.str());
+                    throw clapp::exception::option_param_exception_t(ss.str());
                 }
-            } else {
-                std::get<short_opt_func_t>(found->second)(option[i]);
+                it++;
+                std::get<short_opt_param_func_t>(found->second)(
+                    option[i], std::string_view{*(it)});
                 return parse_result_t{it + 1, std::nullopt, std::nullopt};
             }
-        } else {
-            if (std::holds_alternative<short_opt_param_func_t>(found->second)) {
-                std::stringstream ss;
-                ss << "No param given for option '-" << option[i] << "'";
-                throw std::runtime_error(ss.str());
-            } else {
-                std::get<short_opt_func_t>(found->second)(option[i]);
-            }
+            std::get<short_opt_func_t>(found->second)(option[i]);
+            return parse_result_t{it + 1, std::nullopt, std::nullopt};
         }
+        if (std::holds_alternative<short_opt_param_func_t>(found->second)) {
+            std::stringstream ss;
+            ss << "No param given for option '-" << option[i] << "'";
+            throw clapp::exception::option_param_exception_t(ss.str());
+        }
+        std::get<short_opt_func_t>(found->second)(option[i]);
     }
-    return parse_result_t{it + 1, std::nullopt, std::nullopt};
+    throw clapp::exception::clapp_exception_t(
+        "This exection should not be thrown.");
+}
+
+clapp::value::found_func_t
+clapp::parser::basic_parser_t::gen_func_print_help_and_exit(
+    const int exit_code) const {
+    return found_func_t{[this, exit_code]() {
+        std::cout << gen_help_prefix() + " " + gen_help_msg();
+        exit(exit_code);
+    }};
 }
 
 clapp::parser::basic_parser_t::~basic_parser_t() = default;
diff --git a/src/clapp/sub_parser.cpp b/src/clapp/sub_parser.cpp
index 3f7dcf118e5645e4cfffa305a65c51edf40d0079..244a41fb7d2784f768795a7ce4c7ce99aba834ca 100644
--- a/src/clapp/sub_parser.cpp
+++ b/src/clapp/sub_parser.cpp
@@ -37,16 +37,15 @@ void clapp::parser::basic_sub_parser_t::sub_parse(arg_iterator begin,
 }
 
 clapp::parser::basic_sub_parser_t::basic_sub_parser_t(
-    clapp::basic_parser_t& parser, const std::string& _sub_parser_name,
-    const std::string& _description, bool _parse_parent)
-    : basic_parser_t{},
-      parent_parser{parser},
-      sub_parser_name{_sub_parser_name},
-      description{_description},
-      parse_parent{_parse_parent} {
-    parser.reg(
-        reg_sub_parser_conf_t{[this](arg_iterator begin, arg_iterator end) {
-                                  return this->sub_parse(begin, end);
-                              },
-                              sub_parser_name, description});
+    clapp::basic_parser_t& parser, std::string sub_parser_name_arg,
+    std::string description_arg, bool parse_parent_arg)
+    : parent_parser{parser},
+      sub_parser_name{std::move(sub_parser_name_arg)},
+      description{std::move(description_arg)},
+      parse_parent{parse_parent_arg} {
+    parser.reg(reg_sub_parser_conf_t{*this, sub_parser_name, description});
+}
+
+std::string clapp::parser::basic_sub_parser_t::gen_help_prefix() const {
+    return parent_parser.gen_help_prefix() + ' ' + sub_parser_name;
 }
diff --git a/src/clapp/value.cpp b/src/clapp/value.cpp
index 1074f470e43efe00eb97b8f2bec1db8d9b1d757c..6439f00a643874cc5684bcdb843692f22034170d 100644
--- a/src/clapp/value.cpp
+++ b/src/clapp/value.cpp
@@ -14,13 +14,18 @@
 ///////////////////////////////////////////////////////////////////////////////
 
 #include <clapp/value.h>
-#include <stdexcept>
+#include <iostream>
 #include <string>
 
 template <typename T>
-static T convert_uint(const std::string_view param);
+static T convert_uint(std::string_view param);
 template <typename T>
-static T convert_int(const std::string_view param);
+static T convert_int(std::string_view param);
+
+clapp::value::found_func_t::found_func_t(func_t&& func_arg)
+    : func{std::move(func_arg)} {}
+
+void clapp::value::found_func_t::found() { func(); }
 
 template <>
 std::string clapp::value::convert_value<std::string>(
@@ -76,6 +81,78 @@ std::uint64_t clapp::value::convert_value<std::uint64_t>(
     return convert_uint<std::uint64_t>(param);
 }
 
+template <>
+std::chrono::nanoseconds clapp::value::convert_value<std::chrono::nanoseconds>(
+    const std::string_view param) {
+    return std::chrono::nanoseconds{convert_uint<std::uint64_t>(param)};
+}
+
+template <>
+std::chrono::microseconds
+clapp::value::convert_value<std::chrono::microseconds>(
+    const std::string_view param) {
+    return std::chrono::microseconds{convert_uint<std::uint64_t>(param)};
+}
+
+template <>
+std::chrono::milliseconds
+clapp::value::convert_value<std::chrono::milliseconds>(
+    const std::string_view param) {
+    return std::chrono::milliseconds{convert_uint<std::uint64_t>(param)};
+}
+
+template <>
+std::chrono::seconds clapp::value::convert_value<std::chrono::seconds>(
+    const std::string_view param) {
+    return std::chrono::seconds{convert_uint<std::uint64_t>(param)};
+}
+
+template <>
+std::chrono::minutes clapp::value::convert_value<std::chrono::minutes>(
+    const std::string_view param) {
+    return std::chrono::minutes{convert_uint<std::uint64_t>(param)};
+}
+
+template <>
+std::chrono::hours clapp::value::convert_value<std::chrono::hours>(
+    const std::string_view param) {
+    return std::chrono::hours{convert_uint<std::uint64_t>(param)};
+}
+
+template <>
+float clapp::value::convert_value<float>(const std::string_view param) {
+    try {
+        return std::stof(std::string{param}, nullptr);
+    } catch (std::out_of_range& e) {
+        std::stringstream ss;
+        ss << "convert_value: value " << param << " is out of float-range. ("
+           << e.what() << ")";
+        throw clapp::exception::out_of_range_t{ss.str()};
+    } catch (std::invalid_argument& e) {
+        std::stringstream ss;
+        ss << "convert_value: value " << param << " is invalid. (" << e.what()
+           << ")";
+        throw clapp::exception::invalid_value_t{ss.str()};
+    }
+}
+
+template <>
+double clapp::value::convert_value<double>(const std::string_view param) {
+    try {
+        return std::stod(std::string{param}, nullptr);
+    } catch (std::out_of_range& e) {
+        std::stringstream ss;
+        ss << "convert_value: value " << param << " is out of double-range. ("
+           << e.what() << ")";
+        throw clapp::exception::out_of_range_t{ss.str()};
+    } catch (std::invalid_argument& e) {
+        std::stringstream ss;
+        ss << "convert_value: value " << param << " is invalid. (" << e.what()
+           << ")";
+        throw clapp::exception::invalid_value_t{ss.str()};
+    }
+}
+
 #ifdef CLAPP_FS_AVAIL
 template <>
 clapp::fs::path clapp::value::convert_value<clapp::fs::path>(
@@ -87,13 +164,13 @@ std::string clapp::path_exists_t::append_restriction_text() {
     return "existing path";
 }
 
-void clapp::path_exists_t::validate(const clapp::fs::path &path,
-                                    const std::string &param_name) const {
+void clapp::path_exists_t::validate(const clapp::fs::path& path,
+                                    const std::string& param_name) const {
     if (!clapp::fs::exists(path)) {
         std::stringstream ss;
         ss << "CLI value " << path << " for '" << param_name
            << "' does not exist.";
-        throw std::runtime_error(ss.str());
+        throw clapp::exception::path_does_not_exist_t(ss.str());
     }
 }
 #endif
@@ -104,14 +181,28 @@ static T convert_uint(const std::string_view param) {
                   "Integral template parameter required.");
     static_assert(std::is_unsigned<T>::value,
                   "Unsigned template parameter required.");
-    const std::uint64_t value{std::stoull(std::string{param}, 0, 0)};
-    if (value > std::numeric_limits<T>::max()) {
+    try {
+        const std::uint64_t value{std::stoull(std::string{param}, nullptr, 0)};
+
+        if (value > std::numeric_limits<T>::max()) {
+            std::stringstream ss;
+            ss << "convert_value: value " << value << " is bigger than max "
+               << std::numeric_limits<T>::max();
+            throw clapp::exception::out_of_range_t{ss.str()};
+        }
+        return static_cast<T>(value);
+    } catch (std::out_of_range& e) {
         std::stringstream ss;
-        ss << "convert_value: value " << value << " is bigger than max "
-           << std::numeric_limits<T>::max();
-        throw std::out_of_range{ss.str()};
+        ss << "convert_value: value " << param << " is out of range. ("
+           << std::numeric_limits<T>::min() << " and "
+           << std::numeric_limits<T>::max() << ", " << e.what() << ")";
+        throw clapp::exception::out_of_range_t{ss.str()};
+    } catch (std::invalid_argument& e) {
+        std::stringstream ss;
+        ss << "convert_value: value " << param << " is invalid. (" << e.what()
+           << ")";
+        throw clapp::exception::invalid_value_t{ss.str()};
     }
-    return static_cast<T>(value);
 }
 
 template <typename T>
@@ -120,18 +211,33 @@ static T convert_int(const std::string_view param) {
                   "Integral template parameter required.");
     static_assert(std::is_signed<T>::value,
                   "Signed template parameter required.");
-    const std::int64_t value{std::stoll(std::string{param}, 0, 0)};
-    if (value > std::numeric_limits<T>::max()) {
+    try {
+        const std::int64_t value{std::stoll(std::string{param}, nullptr, 0)};
+
+        if (value > std::numeric_limits<T>::max()) {
+            std::stringstream ss;
+            ss << "convert_value: value " << value << " is bigger than max "
+               << std::numeric_limits<T>::max();
+            throw clapp::exception::out_of_range_t{ss.str()};
+        }
+
+        if (value < std::numeric_limits<T>::min()) {
+            std::stringstream ss;
+            ss << "convert_value: value " << value << " is smaller than min "
+               << std::numeric_limits<T>::max();
+            throw clapp::exception::out_of_range_t{ss.str()};
+        }
+        return static_cast<T>(value);
+    } catch (std::out_of_range& e) {
         std::stringstream ss;
-        ss << "convert_value: value " << value << " is bigger than max "
-           << std::numeric_limits<T>::max();
-        throw std::out_of_range{ss.str()};
-    }
-    if (value < std::numeric_limits<T>::min()) {
+        ss << "convert_value: value " << param << " is out of range. ("
+           << std::numeric_limits<T>::min() << " and "
+           << std::numeric_limits<T>::max() << ", " << e.what() << ")";
+        throw clapp::exception::out_of_range_t{ss.str()};
+    } catch (std::invalid_argument& e) {
         std::stringstream ss;
-        ss << "convert_value: value " << value << " is smaller than min "
-           << std::numeric_limits<T>::max();
-        throw std::out_of_range{ss.str()};
+        ss << "convert_value: value " << param << " is invalid. (" << e.what()
+           << ")";
+        throw clapp::exception::invalid_value_t{ss.str()};
     }
-    return static_cast<T>(value);
 }
diff --git a/src/include/clapp/CMakeLists.txt b/src/include/clapp/CMakeLists.txt
index 246ecb8c20adab5ca3af83496aabae520ced5cd3..9edbc1b8c69feb5ceed8e355a844e01aa65cc09c 100644
--- a/src/include/clapp/CMakeLists.txt
+++ b/src/include/clapp/CMakeLists.txt
@@ -2,6 +2,7 @@ configure_file(build_info.h.in build_info.h)
 install(FILES  "${CMAKE_CURRENT_SOURCE_DIR}/argument.h"
                "${CMAKE_CURRENT_SOURCE_DIR}/argument.hpp"
                "${CMAKE_CURRENT_BINARY_DIR}/build_info.h"
+               "${CMAKE_CURRENT_SOURCE_DIR}/exception.h"
                "${CMAKE_CURRENT_SOURCE_DIR}/filesystem.h"
                "${CMAKE_CURRENT_SOURCE_DIR}/main_parser.h"
                "${CMAKE_CURRENT_SOURCE_DIR}/option.h"
diff --git a/src/include/clapp/argument.h b/src/include/clapp/argument.h
index 793db1f7f25dda8b725b889b87c1bd62eefd0c04..a9468da409d0b5f90f59e0ddc3ff4a690f24d842 100644
--- a/src/include/clapp/argument.h
+++ b/src/include/clapp/argument.h
@@ -13,11 +13,12 @@
 // SOFTWARE.
 ///////////////////////////////////////////////////////////////////////////////
 
-#ifndef LIBCLAPP_ARGUMENT_H
-#define LIBCLAPP_ARGUMENT_H
+#ifndef CLAPP_ARGUMENT_H
+#define CLAPP_ARGUMENT_H
 
 #include <clapp/parser.h>
 #include <clapp/type_traits.h>
+#include <clapp/value.h>
 #include <functional>
 #include <optional>
 #include <string>
@@ -53,16 +54,18 @@ template <typename T>
 struct arg_params_t {
     using validate_func_t =
         std::function<void(const T&, const std::string& option_string)>;
-    std::vector<std::string> restrictions;
-    std::vector<validate_func_t> validate_funcs;
+    std::vector<std::string> restrictions{};
+    std::vector<validate_func_t> validate_funcs{};
     basic_parser_t::purpose_t purpose{basic_parser_t::purpose_t::mandatory};
-    std::optional<T> default_value;
+    std::optional<T> default_value{};
+    std::vector<clapp::value::found_func_t> found{};
 };
 
 template <typename T, typename ARG_CONF>
 struct arg_conf_container_t {
     ARG_CONF arg_conf;
     std::optional<T> default_value;
+    std::vector<clapp::value::found_func_t> found;
 };
 
 template <typename T>
@@ -103,9 +106,9 @@ class basic_argument_t {
     template <typename... Params>
     basic_argument_t(basic_parser_t& parser, const std::string& argument_name,
                      const std::string& description, Params&&... parameters);
-    explicit operator bool() const;
+    constexpr explicit operator bool() const noexcept;
     T value() const;
-    bool given() const;
+    constexpr bool given() const noexcept;
 
    protected:
     void validate() const;
@@ -114,7 +117,8 @@ class basic_argument_t {
    private:
     static callbacks_t create_callbacks(basic_argument_t<T>* inst);
 
-    std::optional<T> _value;
+    std::vector<clapp::value::found_func_t> _found{};
+    std::optional<T> _value{};
     bool _given{false};
 };
 
@@ -129,9 +133,9 @@ class basic_variadic_argument_t {
                               const std::string& argument_name,
                               const std::string& description,
                               Params&&... parameters);
-    explicit operator bool() const;
+    constexpr explicit operator bool() const noexcept;
     std::vector<T> value() const;
-    bool given() const;
+    constexpr bool given() const noexcept;
 
     static std::string variadic_argument_restrictions();
 
@@ -142,7 +146,8 @@ class basic_variadic_argument_t {
    private:
     static callbacks_t create_callbacks(basic_variadic_argument_t<T>* inst);
 
-    std::vector<T> _value;
+    std::vector<clapp::value::found_func_t> _found{};
+    std::vector<T> _value{};
     bool _given{false};
 };
 
@@ -158,6 +163,14 @@ using int32_argument_t = clapp::basic_argument_t<std::int32_t>;
 using uint32_argument_t = clapp::basic_argument_t<std::uint32_t>;
 using int64_argument_t = clapp::basic_argument_t<std::int64_t>;
 using uint64_argument_t = clapp::basic_argument_t<std::uint64_t>;
+using double_argument_t = clapp::basic_argument_t<double>;
+using float_argument_t = clapp::basic_argument_t<float>;
+using ns_argument_t = clapp::basic_argument_t<std::chrono::nanoseconds>;
+using us_argument_t = clapp::basic_argument_t<std::chrono::microseconds>;
+using ms_argument_t = clapp::basic_argument_t<std::chrono::milliseconds>;
+using sec_argument_t = clapp::basic_argument_t<std::chrono::seconds>;
+using min_argument_t = clapp::basic_argument_t<std::chrono::minutes>;
+using hours_argument_t = clapp::basic_argument_t<std::chrono::hours>;
 
 using variadic_string_argument_t =
     clapp::basic_variadic_argument_t<std::string>;
@@ -180,6 +193,20 @@ using variadic_int64_argument_t =
     clapp::basic_variadic_argument_t<std::int64_t>;
 using variadic_uint64_argument_t =
     clapp::basic_variadic_argument_t<std::uint64_t>;
+using variadic_double_argument_t = clapp::basic_variadic_argument_t<double>;
+using variadic_float_argument_t = clapp::basic_variadic_argument_t<float>;
+using variadic_ns_argument_t =
+    clapp::basic_variadic_argument_t<std::chrono::nanoseconds>;
+using variadic_us_argument_t =
+    clapp::basic_variadic_argument_t<std::chrono::microseconds>;
+using variadic_ms_argument_t =
+    clapp::basic_variadic_argument_t<std::chrono::milliseconds>;
+using variadic_sec_argument_t =
+    clapp::basic_variadic_argument_t<std::chrono::seconds>;
+using variadic_min_argument_t =
+    clapp::basic_variadic_argument_t<std::chrono::minutes>;
+using variadic_hours_argument_t =
+    clapp::basic_variadic_argument_t<std::chrono::hours>;
 
 }  // namespace argument
 
diff --git a/src/include/clapp/argument.hpp b/src/include/clapp/argument.hpp
index 7a0c5ff1e465c90cd742eab768c9b4b770e93b30..28b4b8bafeba42971c9f7cbc07d326064c1dc211 100644
--- a/src/include/clapp/argument.hpp
+++ b/src/include/clapp/argument.hpp
@@ -13,8 +13,8 @@
 // SOFTWARE.
 ///////////////////////////////////////////////////////////////////////////////
 
-#ifndef LIBCLAPP_ARGUMENT_HPP
-#define LIBCLAPP_ARGUMENT_HPP
+#ifndef CLAPP_ARGUMENT_HPP
+#define CLAPP_ARGUMENT_HPP
 
 #include <clapp/argument.h>
 #include <clapp/value.h>
@@ -26,7 +26,8 @@ inline void clapp::argument::gen_arg_conf_process_params(arg_params_t<T>&) {}
 template <typename T, typename Param>
 void clapp::argument::gen_arg_conf_process_params(arg_params_t<T>& arg_params,
                                                   Param&& param) {
-    if constexpr (has_append_restriction_text<typename std::decay<Param>::type>::value) {
+    if constexpr (has_append_restriction_text<
+                      typename std::decay<Param>::type>::value) {
         arg_params.restrictions.push_back(param.append_restriction_text());
     }
     if constexpr (has_default_value<typename std::decay<Param>::type>::value) {
@@ -38,7 +39,11 @@ void clapp::argument::gen_arg_conf_process_params(arg_params_t<T>& arg_params,
                 param.validate(value, argument_name);
             });
     }
-    if constexpr (std::is_same<typename std::decay<Param>::type, basic_parser_t::purpose_t>::value) {
+    if constexpr (has_found<typename std::decay<Param>::type>::value) {
+        arg_params.found.push_back(param);
+    }
+    if constexpr (std::is_same<typename std::decay<Param>::type,
+                               basic_parser_t::purpose_t>::value) {
         arg_params.purpose = param;
     }
 }
@@ -66,7 +71,7 @@ clapp::argument::gen_arg_validate_func(
                 argument_name, validate_funcs = std::move(validate_funcs)]() {
             if (purpose == basic_parser_t::purpose_t::mandatory && given_func) {
                 if (!given_func.value()()) {
-                    throw std::runtime_error(
+                    throw clapp::exception::argument_exception_t(
                         std::string{"Mandatory argument '"} + argument_name +
                         "' not given.");
                 }
@@ -93,7 +98,7 @@ clapp::argument::gen_arg_validate_func(
                 }
             } else {
                 if (validate_funcs.size() > 0) {
-                    throw std::runtime_error(
+                    throw clapp::exception::argument_exception_t(
                         std::string{"Cannot validate argument '"} +
                         argument_name + "' without a given value function.");
                 }
@@ -126,7 +131,8 @@ clapp::argument::gen_arg_conf(CALLBACKS&& callbacks,
     arg_params_t<T> arg_params;
     if (std::is_same<ARG_CONF, basic_parser_t::variadic_arg_conf_t>::value) {
         arg_params.restrictions.push_back(
-            clapp::argument::basic_variadic_argument_t<T>::variadic_argument_restrictions());
+            clapp::argument::basic_variadic_argument_t<
+                T>::variadic_argument_restrictions());
     }
     gen_arg_conf_process_params(arg_params,
                                 std::forward<Params>(parameters)...);
@@ -146,7 +152,7 @@ clapp::argument::gen_arg_conf(CALLBACKS&& callbacks,
             std::forward<CALLBACKS>(callbacks), argument_name,
             std::move(arg_params.validate_funcs), description + restriction,
             arg_params.purpose),
-        arg_params.default_value};
+        arg_params.default_value, std::move(arg_params.found)};
 }
 
 template <typename T>
@@ -171,20 +177,25 @@ clapp::argument::basic_argument_t<T>::basic_argument_t(
         std::forward<Params>(parameters)...)};
     parser.reg(std::move(conf.arg_conf));
     _value = conf.default_value;
+    _found = std::move(conf.found);
 }
 
 template <typename T>
-clapp::argument::basic_argument_t<T>::operator bool() const {
+constexpr clapp::argument::basic_argument_t<T>::operator bool() const noexcept {
     return _value.has_value();
 }
 
 template <typename T>
 T clapp::argument::basic_argument_t<T>::value() const {
-    return _value.value();
+    if (_value) {
+        return _value.value();
+    }
+    throw clapp::exception::value_undefined_t{
+        "Requested argument value is not defined."};
 }
 
 template <typename T>
-bool clapp::argument::basic_argument_t<T>::given() const {
+constexpr bool clapp::argument::basic_argument_t<T>::given() const noexcept {
     return _given;
 }
 
@@ -193,6 +204,9 @@ void clapp::argument::basic_argument_t<T>::found_entry(
     const std::string_view argument) {
     _given = true;
     _value = clapp::value::convert_value<T>(argument);
+    for (auto& found_func : _found) {
+        found_func.found();
+    }
 }
 
 template <typename T>
@@ -219,13 +233,15 @@ clapp::argument::basic_variadic_argument_t<T>::basic_variadic_argument_t(
         std::stringstream ss;
         ss << "No default value for variadic argument '"
            << conf.arg_conf.argument_name << "' allowed.";
-        throw std::runtime_error(ss.str());
+        throw clapp::exception::argument_exception_t(ss.str());
     }
+    _found = std::move(conf.found);
     parser.reg(std::move(conf.arg_conf));
 }
 
 template <typename T>
-clapp::argument::basic_variadic_argument_t<T>::operator bool() const {
+constexpr clapp::argument::basic_variadic_argument_t<T>::operator bool() const
+    noexcept {
     return _value.size() > 0;
 }
 
@@ -235,7 +251,8 @@ std::vector<T> clapp::argument::basic_variadic_argument_t<T>::value() const {
 }
 
 template <typename T>
-bool clapp::argument::basic_variadic_argument_t<T>::given() const {
+constexpr bool clapp::argument::basic_variadic_argument_t<T>::given() const
+    noexcept {
     return _given;
 }
 
@@ -244,10 +261,14 @@ void clapp::argument::basic_variadic_argument_t<T>::found_entry(
     const std::string_view argument) {
     _given = true;
     _value.push_back(clapp::value::convert_value<T>(argument));
+    for (auto& found_func : _found) {
+        found_func.found();
+    }
 }
 
 template <typename T>
-std::string clapp::argument::basic_variadic_argument_t<T>::variadic_argument_restrictions() {
+std::string clapp::argument::basic_variadic_argument_t<
+    T>::variadic_argument_restrictions() {
     return "variadic argument";
 }
 
diff --git a/src/include/clapp/build_info.h.in b/src/include/clapp/build_info.h.in
index 0ae7248bf302f3cc0dddfeeee32ba6619d556fe6..d092878eecf03ef0e9a71a079e1b2179d3335e30 100644
--- a/src/include/clapp/build_info.h.in
+++ b/src/include/clapp/build_info.h.in
@@ -13,8 +13,8 @@
 // SOFTWARE.
 ///////////////////////////////////////////////////////////////////////////////
 
-#ifndef LIBCLAPP_BUILD_INFO_H
-#define LIBCLAPP_BUILD_INFO_H
+#ifndef CLAPP_BUILD_INFO_H
+#define CLAPP_BUILD_INFO_H
 
 #include <cstddef>
 #include <cstdint>
diff --git a/src/include/clapp/exception.h b/src/include/clapp/exception.h
new file mode 100644
index 0000000000000000000000000000000000000000..acf6535040cbd9109461c86a23dc968e013d7d7c
--- /dev/null
+++ b/src/include/clapp/exception.h
@@ -0,0 +1,136 @@
+///////////////////////////////////////////////////////////////////////////////
+//
+// Copyright (c) 2020 Martin Wölzer
+//
+// This code is licensed under the MIT License (MIT).
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+// SOFTWARE.
+///////////////////////////////////////////////////////////////////////////////
+
+#ifndef CLAPP_EXCEPTION_H
+#define CLAPP_EXCEPTION_H
+
+#include <stdexcept>
+
+namespace clapp {
+inline namespace exception {
+class clapp_exception_t : public std::runtime_error {
+   public:
+    explicit clapp_exception_t(const char* message);
+    explicit clapp_exception_t(const std::string& message);
+    clapp_exception_t(const clapp_exception_t&);
+    clapp_exception_t& operator=(const clapp_exception_t&);
+    clapp_exception_t(clapp_exception_t&&) noexcept;
+    clapp_exception_t& operator=(clapp_exception_t&&) noexcept;
+    ~clapp_exception_t() noexcept override;
+};
+
+class invalid_value_t : public clapp_exception_t {
+   public:
+    explicit invalid_value_t(const char* message);
+    explicit invalid_value_t(const std::string& message);
+    invalid_value_t(const invalid_value_t&);
+    invalid_value_t& operator=(const invalid_value_t&);
+    invalid_value_t(invalid_value_t&&) noexcept;
+    invalid_value_t& operator=(invalid_value_t&&) noexcept;
+    ~invalid_value_t() noexcept override;
+};
+
+class value_undefined_t : public clapp_exception_t {
+   public:
+    explicit value_undefined_t(const char* message);
+    explicit value_undefined_t(const std::string& message);
+    value_undefined_t(const value_undefined_t&);
+    value_undefined_t& operator=(const value_undefined_t&);
+    value_undefined_t(value_undefined_t&&) noexcept;
+    value_undefined_t& operator=(value_undefined_t&&) noexcept;
+    ~value_undefined_t() noexcept override;
+};
+
+class out_of_range_t : public invalid_value_t {
+   public:
+    explicit out_of_range_t(const char* message);
+    explicit out_of_range_t(const std::string& message);
+    out_of_range_t(const out_of_range_t&);
+    out_of_range_t& operator=(const out_of_range_t&);
+    out_of_range_t(out_of_range_t&&) noexcept;
+    out_of_range_t& operator=(out_of_range_t&&) noexcept;
+    ~out_of_range_t() noexcept override;
+};
+
+class path_does_not_exist_t : public invalid_value_t {
+   public:
+    explicit path_does_not_exist_t(const char* message);
+    explicit path_does_not_exist_t(const std::string& message);
+    path_does_not_exist_t(const path_does_not_exist_t&);
+    path_does_not_exist_t& operator=(const path_does_not_exist_t&);
+    path_does_not_exist_t(path_does_not_exist_t&&) noexcept;
+    path_does_not_exist_t& operator=(path_does_not_exist_t&&) noexcept;
+    ~path_does_not_exist_t() noexcept override;
+};
+
+class option_exception_t : public clapp_exception_t {
+   public:
+    explicit option_exception_t(const char* message);
+    explicit option_exception_t(const std::string& message);
+    option_exception_t(const option_exception_t&);
+    option_exception_t& operator=(const option_exception_t&);
+    option_exception_t(option_exception_t&&) noexcept;
+    option_exception_t& operator=(option_exception_t&&) noexcept;
+    ~option_exception_t() noexcept override;
+};
+
+class option_param_exception_t : public option_exception_t {
+   public:
+    explicit option_param_exception_t(const char* message);
+    explicit option_param_exception_t(const std::string& message);
+    option_param_exception_t(const option_param_exception_t&);
+    option_param_exception_t& operator=(const option_param_exception_t&);
+    option_param_exception_t(option_param_exception_t&&) noexcept;
+    option_param_exception_t& operator=(option_param_exception_t&&) noexcept;
+    ~option_param_exception_t() noexcept override;
+};
+
+class argument_exception_t : public clapp_exception_t {
+   public:
+    explicit argument_exception_t(const char* message);
+    explicit argument_exception_t(const std::string& message);
+    argument_exception_t(const argument_exception_t&);
+    argument_exception_t& operator=(const argument_exception_t&);
+    argument_exception_t(argument_exception_t&&) noexcept;
+    argument_exception_t& operator=(argument_exception_t&&) noexcept;
+    ~argument_exception_t() noexcept override;
+};
+
+class parser_exception_t : public clapp_exception_t {
+   public:
+    explicit parser_exception_t(const char* message);
+    explicit parser_exception_t(const std::string& message);
+    parser_exception_t(const parser_exception_t&);
+    parser_exception_t& operator=(const parser_exception_t&);
+    parser_exception_t(parser_exception_t&&) noexcept;
+    parser_exception_t& operator=(parser_exception_t&&) noexcept;
+    ~parser_exception_t() noexcept override;
+};
+
+class sub_parser_exception_t : public clapp_exception_t {
+   public:
+    explicit sub_parser_exception_t(const char* message);
+    explicit sub_parser_exception_t(const std::string& message);
+    sub_parser_exception_t(const sub_parser_exception_t&);
+    sub_parser_exception_t& operator=(const sub_parser_exception_t&);
+    sub_parser_exception_t(sub_parser_exception_t&&) noexcept;
+    sub_parser_exception_t& operator=(sub_parser_exception_t&&) noexcept;
+    ~sub_parser_exception_t() noexcept override;
+};
+
+}  // namespace exception
+}  // namespace clapp
+
+#endif
diff --git a/src/include/clapp/filesystem.h b/src/include/clapp/filesystem.h
index 9fd47326a7e1f4219e0d3d8ee6bdd5b00aa470b0..b9ee69685fcfe17609305887c9b796ae88fa1552 100644
--- a/src/include/clapp/filesystem.h
+++ b/src/include/clapp/filesystem.h
@@ -13,20 +13,24 @@
 // SOFTWARE.
 ///////////////////////////////////////////////////////////////////////////////
 
-#ifndef LIBCLAPP_FILESYSTEM_H
-#define LIBCLAPP_FILESYSTEM_H
+#ifndef CLAPP_FILESYSTEM_H
+#define CLAPP_FILESYSTEM_H
 
 #if __has_include(<filesystem>)
+
 #include <filesystem>
 namespace clapp {
 namespace fs = std::filesystem;
-}
+}  // namespace clapp
+
 #define CLAPP_FS_AVAIL
 #elif __has_include(<experimental/filesystem>)
+
 #include <experimental/filesystem>
 namespace clapp {
 namespace fs = std::experimental::filesystem;
-}
+}  // namespace clapp
+
 #define CLAPP_FS_AVAIL
 #endif
 
diff --git a/src/include/clapp/main_parser.h b/src/include/clapp/main_parser.h
index c0e9f9f44f7a564f2028cb1922a0c3b0a58af638..01f671441befa83ff82b203ca11ed59a11237001 100644
--- a/src/include/clapp/main_parser.h
+++ b/src/include/clapp/main_parser.h
@@ -13,8 +13,8 @@
 // SOFTWARE.
 ///////////////////////////////////////////////////////////////////////////////
 
-#ifndef LIBCLAPP_MAIN_PARSER_H
-#define LIBCLAPP_MAIN_PARSER_H
+#ifndef CLAPP_MAIN_PARSER_H
+#define CLAPP_MAIN_PARSER_H
 
 #include <clapp/parser.h>
 
@@ -30,11 +30,15 @@ class basic_main_parser_t : public basic_parser_t {
     void parse(int argc, const char* const* argv);
     void parse(const arg_t& arg);
 
+    void parse_and_validate(int argc, const char* const* argv);
+
     explicit operator bool() const;
     std::string get_executable() const;
 
+    std::string gen_help_prefix() const override;
+
    private:
-    std::optional<std::string> executable;
+    std::optional<std::string> executable{};
 };
 
 }  // namespace parser
diff --git a/src/include/clapp/option.h b/src/include/clapp/option.h
index 193aa8152ba93206e3f8ab0314d003e52cd4563a..4aaf84102b2569841606c964ccfc6b68c08e6cf7 100644
--- a/src/include/clapp/option.h
+++ b/src/include/clapp/option.h
@@ -13,11 +13,12 @@
 // SOFTWARE.
 ///////////////////////////////////////////////////////////////////////////////
 
-#ifndef LIBCLAPP_CLI_OPTION_H
-#define LIBCLAPP_CLI_OPTION_H
+#ifndef CLAPP_OPTION_H
+#define CLAPP_OPTION_H
 
 #include <clapp/filesystem.h>
 #include <clapp/parser.h>
+#include <clapp/value.h>
 #include <functional>
 #include <optional>
 #include <string>
@@ -59,23 +60,28 @@ struct option_vector_param_callbacks_t {
     std::optional<vector_value_func_t<T>> value;
 };
 
-void check_short_option(const char option);
-void check_long_option(const std::string_view option);
+void check_short_option(char option);
+void check_long_option(std::string_view option);
+
+template <typename T>
+inline std::vector<std::string> gen_string_vec(const std::vector<T>& vec);
 
 template <typename T>
 struct opt_params_t {
     using validate_func_t =
         std::function<void(const T&, const std::string& option_string)>;
-    std::vector<std::string> restrictions;
-    std::vector<validate_func_t> validate_funcs;
+    std::vector<std::string> restrictions{};
+    std::vector<validate_func_t> validate_funcs{};
     basic_parser_t::purpose_t purpose{basic_parser_t::purpose_t::optional};
-    std::optional<T> default_value;
+    std::optional<T> default_value{};
+    std::vector<clapp::value::found_func_t> found{};
 };
 
 template <typename T, typename OPT_CONF>
 struct opt_conf_container_t {
     OPT_CONF opt_conf;
     std::optional<T> default_value;
+    std::vector<clapp::value::found_func_t> found;
 };
 
 template <typename T>
@@ -85,19 +91,24 @@ class basic_param_option_t {
     using opt_conf_t = basic_parser_t::opt_scalar_param_conf_t;
 
     template <typename... Params>
-    basic_param_option_t(basic_parser_t& parser, Params... parameters);
+    explicit basic_param_option_t(basic_parser_t& parser, Params... parameters);
+    explicit basic_param_option_t(const basic_param_option_t&) = delete;
+    explicit basic_param_option_t(basic_param_option_t&&) noexcept = delete;
+    basic_param_option_t& operator=(const basic_param_option_t&) = delete;
+    basic_param_option_t& operator=(basic_param_option_t&&) noexcept = delete;
 
-    explicit operator bool() const;
+    inline explicit operator bool() const;
     T value() const;
-    bool given() const;
+    constexpr bool given() const noexcept;
 
     virtual ~basic_param_option_t();
 
    protected:
-    void found_entry(const std::string_view param);
+    void found_entry(std::string_view param);
     static callbacks_t create_callbacks(basic_param_option_t<T>* inst);
 
-    std::optional<T> _value;
+    std::vector<clapp::value::found_func_t> _found{};
+    std::optional<T> _value{};
     bool _given{false};
 };
 
@@ -108,19 +119,29 @@ class basic_vector_param_option_t {
     using opt_conf_t = basic_parser_t::opt_vector_param_conf_t;
 
     template <typename... Params>
-    basic_vector_param_option_t(basic_parser_t& parser, Params... parameters);
-
-    explicit operator bool() const;
+    explicit basic_vector_param_option_t(basic_parser_t& parser,
+                                         Params... parameters);
+    explicit basic_vector_param_option_t(const basic_vector_param_option_t&) =
+        delete;
+    explicit basic_vector_param_option_t(
+        basic_vector_param_option_t&&) noexcept = delete;
+    basic_vector_param_option_t& operator=(const basic_vector_param_option_t&) =
+        delete;
+    basic_vector_param_option_t& operator=(
+        basic_vector_param_option_t&&) noexcept = delete;
+
+    inline explicit operator bool() const;
     std::vector<T> value() const;
-    bool given() const;
+    constexpr bool given() const noexcept;
 
     virtual ~basic_vector_param_option_t();
 
    protected:
-    void found_entry(const std::string_view param);
+    void found_entry(std::string_view param);
     static callbacks_t create_callbacks(basic_vector_param_option_t<T>* inst);
 
-    std::vector<T> _value;
+    std::vector<clapp::value::found_func_t> _found{};
+    std::vector<T> _value{};
     bool _given{false};
 };
 
@@ -133,6 +154,10 @@ class basic_option_t {
     template <typename... Params>
     basic_option_t(basic_parser_t& parser, callbacks_t&& callbacks,
                    Params&&... parameters);
+    explicit basic_option_t(const basic_option_t&) = delete;
+    explicit basic_option_t(basic_option_t&&) noexcept = delete;
+    basic_option_t& operator=(const basic_option_t&) = delete;
+    basic_option_t& operator=(basic_option_t&&) noexcept = delete;
 
     virtual ~basic_option_t();
 
@@ -140,32 +165,57 @@ class basic_option_t {
     bool given() const;
 
    protected:
-    std::optional<T> _value;
+    std::vector<clapp::value::found_func_t> _found{};
+    std::optional<T> _value{};
     bool _given{false};
 };
 
-class bool_option_t final : public basic_option_t<bool> {
+class bool_option_t : public basic_option_t<bool> {
    public:
     template <typename... Params>
-    bool_option_t(basic_parser_t& parser, Params... parameters);
+    explicit bool_option_t(basic_parser_t& parser, Params... parameters);
+    explicit bool_option_t(const bool_option_t&) = delete;
+    explicit bool_option_t(bool_option_t&&) noexcept = delete;
+    bool_option_t& operator=(const bool_option_t&) = delete;
+    bool_option_t& operator=(bool_option_t&&) noexcept = delete;
 
     ~bool_option_t() override;
 
-    explicit operator bool() const;
+    inline explicit operator bool() const;
 
    private:
     void found_entry();
     static callbacks_t create_callbacks(bool_option_t* inst);
 };
 
-class count_option_t final : public basic_option_t<std::uint32_t> {
+template <int EXIT_CODE>
+class basic_help_option_t : public bool_option_t {
    public:
     template <typename... Params>
-    count_option_t(basic_parser_t& parser, Params... parameters);
+    explicit basic_help_option_t(basic_parser_t& parser, Params... parameters);
+    explicit basic_help_option_t(const basic_help_option_t&) = delete;
+    explicit basic_help_option_t(basic_help_option_t&&) noexcept = delete;
+    basic_help_option_t& operator=(const basic_help_option_t&) = delete;
+    basic_help_option_t& operator=(basic_help_option_t&&) noexcept = delete;
+
+    ~basic_help_option_t() override;
+
+    static value::found_func_t gen_func_print_help_and_exit(
+        basic_parser_t& parser);
+};
+
+class count_option_t : public basic_option_t<std::uint32_t> {
+   public:
+    template <typename... Params>
+    explicit count_option_t(basic_parser_t& parser, Params... parameters);
+    explicit count_option_t(const count_option_t&) = delete;
+    explicit count_option_t(count_option_t&&) noexcept = delete;
+    count_option_t& operator=(const count_option_t&) = delete;
+    count_option_t& operator=(count_option_t&&) noexcept = delete;
 
     ~count_option_t() override;
 
-    explicit operator bool() const;
+    inline explicit operator bool() const;
 
    private:
     void found_entry();
@@ -173,7 +223,7 @@ class count_option_t final : public basic_option_t<std::uint32_t> {
 };
 
 template <typename T>
-inline void gen_opt_conf_process_params(opt_params_t<T>&);
+inline void gen_opt_conf_process_params(opt_params_t<T>& opt_params);
 
 template <typename T, typename Param>
 void gen_opt_conf_process_params(opt_params_t<T>& opt_params, Param&& param);
@@ -183,50 +233,50 @@ void gen_opt_conf_process_params(opt_params_t<T>& opt_params, Param&& param,
                                  Params&&... parameters);
 
 template <typename short_option_func_t>
-std::optional<basic_parser_t::basic_short_opt_conf_t<short_option_func_t>>
-gen_short_option(short_option_func_t&& sof,
-                 const std::optional<char> short_option);
+std::vector<basic_parser_t::basic_short_opt_conf_t<short_option_func_t>>
+gen_short_option(short_option_func_t&& sof, std::vector<char> short_option);
 
 template <typename long_option_func_t>
-std::optional<basic_parser_t::basic_long_opt_conf_t<long_option_func_t>>
+std::vector<basic_parser_t::basic_long_opt_conf_t<long_option_func_t>>
 gen_long_option(long_option_func_t&& lof,
-                const std::optional<std::string>& long_option);
+                const std::vector<std::string>& long_option);
 
 template <typename T, typename VALUE_FUNC>
 std::optional<basic_parser_t::validate_func_t> gen_opt_validate_func(
     std::optional<VALUE_FUNC>&& vf, std::optional<has_value_func_t>&& hvf,
     std::optional<given_func_t>&& gf,
     std::vector<typename opt_params_t<T>::validate_func_t>&& validate_funcs,
-    const std::string& option_string, const basic_parser_t::purpose_t purpose);
+    const std::string& option_string, basic_parser_t::purpose_t purpose);
 
-template <typename T, typename OPT_CONF, typename CALLBACKS>
-OPT_CONF gen_opt_conf(
-    CALLBACKS&& callbacks, const std::optional<std::string>& long_option,
-    const std::optional<char> short_option,
-    std::vector<typename opt_params_t<T>::validate_func_t>&& validate_funcs,
-    const std::string& description, basic_parser_t::purpose_t purpose);
-
-template <typename T, typename OPT_CONF, typename CALLBACKS, typename... Params>
-opt_conf_container_t<T, OPT_CONF> gen_opt_conf(
-    CALLBACKS&& callbacks, const std::optional<std::string>& long_option,
-    const std::optional<char> short_option, const std::string& description,
-    Params&&... parameters);
-
-template <typename T, typename OPT_CONF, typename CALLBACKS, typename... Params>
+template <typename T, typename OPT_CONF, typename CALLBACKS, typename T1,
+          typename... Params>
 opt_conf_container_t<T, OPT_CONF> gen_opt_conf(CALLBACKS&& callbacks,
-                                               const char short_option,
+                                               T1&& single_option,
+                                               const std::string& description,
                                                Params&&... parameters);
 
-template <typename T, typename OPT_CONF, typename CALLBACKS, typename... Params>
+template <typename T, typename OPT_CONF, typename CALLBACKS, typename T1,
+          typename T2, typename... Params>
 opt_conf_container_t<T, OPT_CONF> gen_opt_conf(CALLBACKS&& callbacks,
-                                               const std::string& long_option,
+                                               T1&& long_option,
+                                               T2&& short_option,
+                                               const std::string& description,
                                                Params&&... parameters);
 
 template <typename T, typename OPT_CONF, typename CALLBACKS, typename... Params>
-opt_conf_container_t<T, OPT_CONF> gen_opt_conf(CALLBACKS&& callbacks,
-                                               const std::string& long_option,
-                                               const char short_option,
-                                               Params&&... parameters);
+opt_conf_container_t<T, OPT_CONF> gen_opt_conf1(
+    CALLBACKS&& callbacks, const std::vector<std::string>& long_option,
+    const std::vector<char>& short_option, const std::string& description,
+    Params&&... parameters);
+
+template <typename T, typename OPT_CONF, typename CALLBACKS>
+OPT_CONF gen_opt_conf2(
+    CALLBACKS&& callbacks, const std::vector<std::string>& long_option,
+    const std::vector<char>& short_option,
+    std::vector<typename opt_params_t<T>::validate_func_t>&& validate_funcs,
+    const std::string& description, basic_parser_t::purpose_t purpose);
+
+using help_option_t = basic_help_option_t<EXIT_SUCCESS>;
 
 using string_param_option_t = clapp::basic_param_option_t<std::string>;
 
@@ -242,6 +292,16 @@ using int32_param_option_t = clapp::basic_param_option_t<std::int32_t>;
 using uint32_param_option_t = clapp::basic_param_option_t<std::uint32_t>;
 using int64_param_option_t = clapp::basic_param_option_t<std::int64_t>;
 using uint64_param_option_t = clapp::basic_param_option_t<std::uint64_t>;
+using double_param_option_t = clapp::basic_param_option_t<double>;
+using float_param_option_t = clapp::basic_param_option_t<float>;
+using ns_param_option_t = clapp::basic_param_option_t<std::chrono::nanoseconds>;
+using us_param_option_t =
+    clapp::basic_param_option_t<std::chrono::microseconds>;
+using ms_param_option_t =
+    clapp::basic_param_option_t<std::chrono::milliseconds>;
+using sec_param_option_t = clapp::basic_param_option_t<std::chrono::seconds>;
+using min_param_option_t = clapp::basic_param_option_t<std::chrono::minutes>;
+using hours_param_option_t = clapp::basic_param_option_t<std::chrono::hours>;
 
 using vector_string_param_option_t =
     clapp::basic_vector_param_option_t<std::string>;
@@ -267,6 +327,22 @@ using vector_int64_param_option_t =
     clapp::basic_vector_param_option_t<std::int64_t>;
 using vector_uint64_param_option_t =
     clapp::basic_vector_param_option_t<std::uint64_t>;
+using vector_double_param_option_t = clapp::basic_vector_param_option_t<double>;
+using vector_float_param_option_t = clapp::basic_vector_param_option_t<float>;
+using vector_double_param_option_t = clapp::basic_vector_param_option_t<double>;
+using vector_float_param_option_t = clapp::basic_vector_param_option_t<float>;
+using vector_ns_param_option_t =
+    clapp::basic_vector_param_option_t<std::chrono::nanoseconds>;
+using vector_us_param_option_t =
+    clapp::basic_vector_param_option_t<std::chrono::microseconds>;
+using vector_ms_param_option_t =
+    clapp::basic_vector_param_option_t<std::chrono::milliseconds>;
+using vector_sec_param_option_t =
+    clapp::basic_vector_param_option_t<std::chrono::seconds>;
+using vector_min_param_option_t =
+    clapp::basic_vector_param_option_t<std::chrono::minutes>;
+using vector_hours_param_option_t =
+    clapp::basic_vector_param_option_t<std::chrono::hours>;
 
 }  // namespace option
 
diff --git a/src/include/clapp/option.hpp b/src/include/clapp/option.hpp
index 413b174435622b280009a8c4a0e666ada94b86cd..b64bf1df52518486decd9731526157fd39a96097 100644
--- a/src/include/clapp/option.hpp
+++ b/src/include/clapp/option.hpp
@@ -13,8 +13,8 @@
 // SOFTWARE.
 ///////////////////////////////////////////////////////////////////////////////
 
-#ifndef LIBCLAPP_CLI_OPTION_HPP
-#define LIBCLAPP_CLI_OPTION_HPP
+#ifndef CLAPP_OPTION_HPP
+#define CLAPP_OPTION_HPP
 
 #include <clapp/option.h>
 #include <clapp/type_traits.h>
@@ -23,7 +23,8 @@
 #include <sstream>
 
 template <typename T>
-inline void clapp::option::gen_opt_conf_process_params(opt_params_t<T>&) {}
+inline void clapp::option::gen_opt_conf_process_params([
+    [maybe_unused]] opt_params_t<T>& opt_params) {}
 
 template <typename T, typename Param>
 void clapp::option::gen_opt_conf_process_params(opt_params_t<T>& opt_params,
@@ -41,6 +42,9 @@ void clapp::option::gen_opt_conf_process_params(opt_params_t<T>& opt_params,
                 param.validate(value, option_string);
             });
     }
+    if constexpr (has_found<typename std::decay<Param>::type>::value) {
+        opt_params.found.push_back(param);
+    }
     if constexpr (std::is_same<typename std::decay<Param>::type,
                                basic_parser_t::purpose_t>::value) {
         opt_params.purpose = param;
@@ -57,28 +61,35 @@ void clapp::option::gen_opt_conf_process_params(opt_params_t<T>& opt_params,
 }
 
 template <typename short_option_func_t>
-std::optional<
-    clapp::basic_parser_t::basic_short_opt_conf_t<short_option_func_t>>
+std::vector<clapp::basic_parser_t::basic_short_opt_conf_t<short_option_func_t>>
 clapp::option::gen_short_option(short_option_func_t&& sof,
-                                const std::optional<char> short_option) {
-    if (short_option) {
-        check_short_option(short_option.value());
-        return basic_parser_t::basic_short_opt_conf_t<short_option_func_t>{
-            short_option.value(), std::forward<short_option_func_t>(sof)};
+                                const std::vector<char> short_option) {
+    std::vector<
+        clapp::basic_parser_t::basic_short_opt_conf_t<short_option_func_t>>
+        ret;
+    for (auto opt : short_option) {
+        check_short_option(opt);
+        ret.emplace_back(
+            basic_parser_t::basic_short_opt_conf_t<short_option_func_t>{opt,
+                                                                        sof});
     }
-    return std::nullopt;
+    return ret;
 }
 
 template <typename long_option_func_t>
-std::optional<clapp::basic_parser_t::basic_long_opt_conf_t<long_option_func_t>>
+std::vector<clapp::basic_parser_t::basic_long_opt_conf_t<long_option_func_t>>
 clapp::option::gen_long_option(long_option_func_t&& lof,
-                               const std::optional<std::string>& long_option) {
-    if (long_option) {
-        check_long_option(long_option.value());
-        return basic_parser_t::basic_long_opt_conf_t<long_option_func_t>{
-            long_option.value(), std::forward<long_option_func_t>(lof)};
+                               const std::vector<std::string>& long_option) {
+    std::vector<
+        clapp::basic_parser_t::basic_long_opt_conf_t<long_option_func_t>>
+        ret;
+    for (auto opt : long_option) {
+        check_long_option(opt);
+        ret.emplace_back(
+            basic_parser_t::basic_long_opt_conf_t<long_option_func_t>{opt,
+                                                                      lof});
     }
-    return std::nullopt;
+    return ret;
 }
 
 template <typename T, typename VALUE_FUNC>
@@ -95,7 +106,7 @@ clapp::option::gen_opt_validate_func(
                 option_string, validate_funcs = std::move(validate_funcs)]() {
             if (purpose == basic_parser_t::purpose_t::mandatory && given_func) {
                 if (!given_func.value()()) {
-                    throw std::runtime_error(
+                    throw clapp::exception::option_param_exception_t(
                         std::string{"Mandatory parameter for option '"} +
                         option_string + "' not given.");
                 }
@@ -120,7 +131,7 @@ clapp::option::gen_opt_validate_func(
                 }
             } else {
                 if (validate_funcs.size() > 0) {
-                    throw std::runtime_error(
+                    throw clapp::exception::option_param_exception_t(
                         std::string{"Cannot validate option '"} +
                         option_string +
                         "' without a given value and has_value function.");
@@ -131,30 +142,88 @@ clapp::option::gen_opt_validate_func(
     return std::nullopt;
 }
 
-template <typename T, typename OPT_CONF, typename CALLBACKS>
-OPT_CONF clapp::option::gen_opt_conf(
-    CALLBACKS&& callbacks, const std::optional<std::string>& long_option,
-    const std::optional<char> short_option,
-    std::vector<typename opt_params_t<T>::validate_func_t>&& validate_funcs,
-    const std::string& description, basic_parser_t::purpose_t purpose) {
-    std::string option_string{
-        OPT_CONF::create_option_string(short_option, long_option)};
-    return OPT_CONF{gen_short_option(std::move(callbacks.soh), short_option),
-                    gen_long_option(std::move(callbacks.loh), long_option),
-                    gen_opt_validate_func<T>(std::move(callbacks.value),
-                                             std::move(callbacks.has_value),
-                                             std::move(callbacks.given),
-                                             std::move(validate_funcs),
-                                             option_string, purpose),
-                    std::move(option_string),
-                    description,
-                    purpose};
+template <typename T>
+inline std::vector<std::string> clapp::option::gen_string_vec(
+    const std::vector<T>& vec) {
+    return std::vector<std::string>{vec.begin(), vec.end()};
 }
 
-template <typename T, typename OPT_CONF, typename CALLBACKS, typename... Params>
+template <>
+inline std::vector<std::string> clapp::option::gen_string_vec(
+    const std::vector<std::string>& vec) {
+    return vec;
+}
+
+template <typename T, typename OPT_CONF, typename CALLBACKS, typename T1,
+          typename... Params>
 clapp::option::opt_conf_container_t<T, OPT_CONF> clapp::option::gen_opt_conf(
-    CALLBACKS&& callbacks, const std::optional<std::string>& long_option,
-    const std::optional<char> short_option, const std::string& description,
+    CALLBACKS&& callbacks, T1&& single_option, const std::string& description,
+    Params&&... parameters) {
+    if constexpr (clapp::is_vector<typename std::decay<T1>::type>::value) {
+        if constexpr (std::is_same<typename std::decay<T1>::type,
+                                   std::vector<char>>::value) {
+            return gen_opt_conf1<T, OPT_CONF>(
+                std::forward<CALLBACKS>(callbacks), std::vector<std::string>{},
+                std::forward<T1>(single_option), std::move(description),
+                std::forward<Params>(parameters)...);
+        }
+        return gen_opt_conf1<T, OPT_CONF>(
+            std::forward<CALLBACKS>(callbacks),
+            gen_string_vec(std::forward<T1>(single_option)),
+            std::vector<char>{}, std::move(description),
+            std::forward<Params>(parameters)...);
+    }
+    if constexpr (std::is_same<typename std::decay<T1>::type, char>::value) {
+        return gen_opt_conf1<T, OPT_CONF>(
+            std::forward<CALLBACKS>(callbacks), std::vector<std::string>{},
+            std::vector<char>{std::forward<T1>(single_option)},
+            std::move(description), std::forward<Params>(parameters)...);
+    }
+    return gen_opt_conf1<T, OPT_CONF>(
+        std::forward<CALLBACKS>(callbacks),
+        std::vector<std::string>{std::string{std::forward<T1>(single_option)}},
+        std::vector<char>{}, std::move(description),
+        std::forward<Params>(parameters)...);
+}
+
+template <typename T, typename OPT_CONF, typename CALLBACKS, typename T1,
+          typename T2, typename... Params>
+clapp::option::opt_conf_container_t<T, OPT_CONF> clapp::option::gen_opt_conf(
+    CALLBACKS&& callbacks, T1&& long_option, T2&& short_option,
+    const std::string& description, Params&&... parameters) {
+    if constexpr (clapp::is_vector<typename std::decay<T1>::type>::value &&
+                  clapp::is_vector<typename std::decay<T2>::type>::value) {
+        return gen_opt_conf1<T, OPT_CONF>(
+            std::forward<CALLBACKS>(callbacks),
+            gen_string_vec(std::forward<T1>(long_option)),
+            std::forward<T2>(short_option), std::move(description),
+            std::forward<Params>(parameters)...);
+    }
+    if constexpr (clapp::is_vector<typename std::decay<T1>::type>::value) {
+        return gen_opt_conf1<T, OPT_CONF>(
+            std::forward<CALLBACKS>(callbacks),
+            gen_string_vec(std::forward<T1>(long_option)),
+            std::vector<char>{std::forward<T2>(short_option)},
+            std::move(description), std::forward<Params>(parameters)...);
+    }
+    if constexpr (clapp::is_vector<typename std::decay<T2>::type>::value) {
+        return gen_opt_conf1<T, OPT_CONF>(
+            std::forward<CALLBACKS>(callbacks),
+            std::vector<std::string>{std::forward<T1>(long_option)},
+            std::forward<T2>(short_option), std::move(description),
+            std::forward<Params>(parameters)...);
+    }
+    return gen_opt_conf1<T, OPT_CONF>(
+        std::forward<CALLBACKS>(callbacks),
+        std::vector<std::string>{std::forward<T1>(long_option)},
+        std::vector<char>{std::forward<T2>(short_option)},
+        std::move(description), std::forward<Params>(parameters)...);
+}
+
+template <typename T, typename OPT_CONF, typename CALLBACKS, typename... Params>
+clapp::option::opt_conf_container_t<T, OPT_CONF> clapp::option::gen_opt_conf1(
+    CALLBACKS&& callbacks, const std::vector<std::string>& long_option,
+    const std::vector<char>& short_option, const std::string& description,
     Params&&... parameters) {
     opt_params_t<T> opt_params;
     if (std::is_same<
@@ -172,42 +241,36 @@ clapp::option::opt_conf_container_t<T, OPT_CONF> clapp::option::gen_opt_conf(
         [](const std::string& a, const std::string& b) -> std::string {
             return a + (a.length() > 0 && b.length() > 0 ? ", " : "") + b;
         })};
-    if (restriction.size() > 0) {
+    if (!restriction.empty()) {
         restriction = " (" + restriction + ")";
     }
 
     return opt_conf_container_t<T, OPT_CONF>{
-        gen_opt_conf<T, OPT_CONF>(
+        gen_opt_conf2<T, OPT_CONF>(
             std::forward<CALLBACKS>(callbacks), long_option, short_option,
             std::move(opt_params.validate_funcs), description + restriction,
             opt_params.purpose),
-        opt_params.default_value};
-}
-
-template <typename T, typename OPT_CONF, typename CALLBACKS, typename... Params>
-clapp::option::opt_conf_container_t<T, OPT_CONF> clapp::option::gen_opt_conf(
-    CALLBACKS&& callbacks, const char short_option, Params&&... parameters) {
-    return gen_opt_conf<T, OPT_CONF>(
-        std::forward<CALLBACKS>(callbacks), std::nullopt,
-        std::make_optional(short_option), std::forward<Params>(parameters)...);
+        opt_params.default_value, std::move(opt_params.found)};
 }
 
-template <typename T, typename OPT_CONF, typename CALLBACKS, typename... Params>
-clapp::option::opt_conf_container_t<T, OPT_CONF> clapp::option::gen_opt_conf(
-    CALLBACKS&& callbacks, const std::string& long_option,
-    Params&&... parameters) {
-    return gen_opt_conf<T, OPT_CONF>(
-        std::forward<CALLBACKS>(callbacks), std::make_optional(long_option),
-        std::nullopt, std::forward<Params>(parameters)...);
-}
-
-template <typename T, typename OPT_CONF, typename CALLBACKS, typename... Params>
-clapp::option::opt_conf_container_t<T, OPT_CONF> clapp::option::gen_opt_conf(
-    CALLBACKS&& callbacks, const std::string& long_option,
-    const char short_option, Params&&... parameters) {
-    return gen_opt_conf<T, OPT_CONF>(
-        std::forward<CALLBACKS>(callbacks), std::make_optional(long_option),
-        std::make_optional(short_option), std::forward<Params>(parameters)...);
+template <typename T, typename OPT_CONF, typename CALLBACKS>
+OPT_CONF clapp::option::gen_opt_conf2(
+    CALLBACKS&& callbacks, const std::vector<std::string>& long_option,
+    const std::vector<char>& short_option,
+    std::vector<typename opt_params_t<T>::validate_func_t>&& validate_funcs,
+    const std::string& description, basic_parser_t::purpose_t purpose) {
+    std::string option_string{
+        OPT_CONF::create_option_string(short_option, long_option)};
+    return OPT_CONF{gen_short_option(std::move(callbacks.soh), short_option),
+                    gen_long_option(std::move(callbacks.loh), long_option),
+                    gen_opt_validate_func<T>(std::move(callbacks.value),
+                                             std::move(callbacks.has_value),
+                                             std::move(callbacks.given),
+                                             std::move(validate_funcs),
+                                             option_string, purpose),
+                    std::move(option_string),
+                    description,
+                    purpose};
 }
 
 template <typename T>
@@ -218,6 +281,7 @@ clapp::option::basic_param_option_t<T>::basic_param_option_t(
         create_callbacks(this), std::forward<Params>(parameters)...)};
     parser.reg(std::move(conf.opt_conf));
     _value = conf.default_value;
+    _found = std::move(conf.found);
 }
 
 template <typename T>
@@ -243,20 +307,27 @@ void clapp::option::basic_param_option_t<T>::found_entry(
     const std::string_view param) {
     _given = true;
     _value = convert_value<T>(param);
+    for (auto& found_func : _found) {
+        found_func.found();
+    }
 }
 
 template <typename T>
-clapp::option::basic_param_option_t<T>::operator bool() const {
+inline clapp::option::basic_param_option_t<T>::operator bool() const {
     return _value.has_value();
 }
 
 template <typename T>
 T clapp::option::basic_param_option_t<T>::value() const {
-    return _value.value();
+    if (_value) {
+        return _value.value();
+    }
+    throw clapp::exception::value_undefined_t{
+        "Requested value is not defined."};
 }
 
 template <typename T>
-bool clapp::option::basic_param_option_t<T>::given() const {
+constexpr bool clapp::option::basic_param_option_t<T>::given() const noexcept {
     return _given;
 }
 
@@ -270,9 +341,10 @@ clapp::option::basic_vector_param_option_t<T>::basic_vector_param_option_t(
         std::stringstream ss;
         ss << "No default value for vector based param option '"
            << conf.opt_conf.option_string << "' possible.";
-        throw std::runtime_error(ss.str());
+        throw clapp::exception::option_param_exception_t(ss.str());
     }
     parser.reg(std::move(conf.opt_conf));
+    _found = std::move(conf.found);
 }
 
 template <typename T>
@@ -299,10 +371,13 @@ void clapp::option::basic_vector_param_option_t<T>::found_entry(
     const std::string_view param) {
     _given = true;
     _value.push_back(convert_value<T>(param));
+    for (auto& found_func : _found) {
+        found_func.found();
+    }
 }
 
 template <typename T>
-clapp::option::basic_vector_param_option_t<T>::operator bool() const {
+inline clapp::option::basic_vector_param_option_t<T>::operator bool() const {
     return _value.size() > 0;
 }
 
@@ -312,7 +387,8 @@ std::vector<T> clapp::option::basic_vector_param_option_t<T>::value() const {
 }
 
 template <typename T>
-bool clapp::option::basic_vector_param_option_t<T>::given() const {
+constexpr bool clapp::option::basic_vector_param_option_t<T>::given() const
+    noexcept {
     return _given;
 }
 
@@ -325,6 +401,7 @@ clapp::option::basic_option_t<T>::basic_option_t(clapp::basic_parser_t& parser,
         callbacks, std::forward<Params>(parameters)...)};
     parser.reg(std::move(conf.opt_conf));
     _value = conf.default_value;
+    _found = std::move(conf.found);
 }
 
 template <typename T>
@@ -332,7 +409,11 @@ clapp::option::basic_option_t<T>::~basic_option_t() = default;
 
 template <typename T>
 T clapp::option::basic_option_t<T>::value() const {
-    return _value.value();
+    if (_value) {
+        return _value.value();
+    }
+    throw clapp::exception::value_undefined_t{
+        "Requested option value is not defined."};
 }
 
 template <typename T>
@@ -350,6 +431,23 @@ clapp::option::bool_option_t::bool_option_t(clapp::basic_parser_t& parser,
     }
 }
 
+template <int EXIT_CODE>
+template <typename... Params>
+clapp::option::basic_help_option_t<EXIT_CODE>::basic_help_option_t(
+    basic_parser_t& parser, Params... parameters)
+    : bool_option_t{parser, std::forward<Params>(parameters)...,
+                    gen_func_print_help_and_exit(parser)} {}
+
+template <int EXIT_CODE>
+clapp::value::found_func_t
+clapp::option::basic_help_option_t<EXIT_CODE>::gen_func_print_help_and_exit(
+    basic_parser_t& parser) {
+    return parser.gen_func_print_help_and_exit(EXIT_CODE);
+}
+
+template <int EXIT_CODE>
+clapp::option::basic_help_option_t<EXIT_CODE>::~basic_help_option_t() = default;
+
 template <typename... Params>
 clapp::option::count_option_t::count_option_t(clapp::basic_parser_t& parser,
                                               Params... parameters)
@@ -360,4 +458,14 @@ clapp::option::count_option_t::count_option_t(clapp::basic_parser_t& parser,
     }
 }
 
+inline clapp::option::bool_option_t::operator bool() const {
+    Expects(_value.has_value());
+    return _value.value();
+}
+
+inline clapp::option::count_option_t::operator bool() const {
+    Expects(_value.has_value());
+    return _value.value() > 0;
+}
+
 #endif
diff --git a/src/include/clapp/parser.h b/src/include/clapp/parser.h
index b474aa90f74f69e829983c535c0252bc27827886..a0d3c4e0f9167d5c6199a22f63ff18067a4fedf0 100644
--- a/src/include/clapp/parser.h
+++ b/src/include/clapp/parser.h
@@ -13,9 +13,10 @@
 // SOFTWARE.
 ///////////////////////////////////////////////////////////////////////////////
 
-#ifndef LIBCLAPP_PARSER_H
-#define LIBCLAPP_PARSER_H
+#ifndef CLAPP_PARSER_H
+#define CLAPP_PARSER_H
 
+#include <clapp/value.h>
 #include <functional>
 #include <gsl/gsl_assert>
 #include <gsl/span>
@@ -30,15 +31,15 @@ struct arg_t : public gsl::span<const char* const> {
    public:
     using base = gsl::span<const char* const>;
 
-    arg_t(const char* const* argv, int argc);
+    inline arg_t(const char* const* argv, int argc);
 };
 
+class basic_sub_parser_t;
+
 class basic_parser_t {
    public:
     using arg_iterator = arg_t::const_iterator;
     using long_opt_func_t = std::function<void(const std::string_view option)>;
-    using parse_func_t =
-        std::function<void(arg_iterator begin, arg_iterator end)>;
     using long_opt_param_func_t = std::function<void(
         const std::string_view option, const std::string_view param)>;
     using short_opt_func_t = std::function<void(const char option)>;
@@ -74,23 +75,23 @@ class basic_parser_t {
               option_type_t option_type>
     struct reg_option_conf_t {
         static std::string create_option_string(
-            const std::optional<char> short_option,
-            const std::optional<std::string>& long_option);
+            std::vector<char> short_option,
+            const std::vector<std::string>& long_option);
         static std::string create_option_string(const std::string& long_option);
-        static std::string create_option_string(const char short_option);
+        static std::string create_option_string(char short_option);
 
         using long_opt_conf_t = basic_long_opt_conf_t<long_option_func_t>;
         using short_opt_conf_t = basic_short_opt_conf_t<short_option_func_t>;
-        std::optional<short_opt_conf_t> short_option;
-        std::optional<long_opt_conf_t> long_option;
+        std::vector<short_opt_conf_t> short_option;
+        std::vector<long_opt_conf_t> long_option;
         std::optional<validate_func_t> validate_func;
         std::string option_string;
         std::string description;
-        purpose_t purpose;
+        purpose_t purpose{purpose_t::optional};
     };
 
     struct reg_sub_parser_conf_t {
-        parse_func_t parse;
+        basic_sub_parser_t& parser;
         std::string sub_parser_name;
         std::string description;
     };
@@ -101,7 +102,7 @@ class basic_parser_t {
         std::string argument_name;
         std::string description;
         std::optional<validate_func_t> validate_func;
-        purpose_t purpose;
+        purpose_t purpose{purpose_t::mandatory};
     };
 
     using single_arg_conf_t = reg_argument_conf_t<argument_type_t::single>;
@@ -121,7 +122,8 @@ class basic_parser_t {
     using short_opt_variant_t =
         std::variant<short_opt_func_t, short_opt_param_func_t>;
 
-    using sub_parsers_map_t = std::map<std::string, parse_func_t, std::less<>>;
+    using sub_parsers_map_t =
+        std::map<std::string, basic_sub_parser_t&, std::less<>>;
     using arguments_vector_t = std::vector<arg_conf_t>;
     using optional_argument_t = std::optional<arg_conf_t>;
     using long_options_map_t =
@@ -155,6 +157,11 @@ class basic_parser_t {
 
    public:
     basic_parser_t();
+    explicit basic_parser_t(const basic_parser_t&) = delete;
+    explicit basic_parser_t(basic_parser_t&&) noexcept = delete;
+    basic_parser_t& operator=(const basic_parser_t&) = delete;
+    basic_parser_t& operator=(basic_parser_t&&) noexcept = delete;
+
     virtual ~basic_parser_t();
 
     template <typename short_option_func_t, typename long_option_func_t>
@@ -176,7 +183,7 @@ class basic_parser_t {
         std::optional<std::string> long_option;
     };
 
-    arg_iterator process_parse_result(const arg_iterator it,
+    arg_iterator process_parse_result(arg_iterator it,
                                       const parse_result_t& parse_result) const;
     void parse(arg_iterator begin, arg_iterator end);
     parse_result_t parse(std::string_view option, arg_iterator it,
@@ -184,7 +191,11 @@ class basic_parser_t {
 
     void validate() const;
 
+    void validate_recursive() const;
+
+    virtual std::string gen_help_prefix() const;
     std::string gen_help_msg() const;
+    value::found_func_t gen_func_print_help_and_exit(int exit_code) const;
 
     std::size_t get_num_processed_arguments() const;
 
@@ -202,24 +213,24 @@ class basic_parser_t {
     argument_descriptions_vec_t& get_optional_argument_descriptions();
 
    private:
-    parse_result_t parse_arg(const std::string_view option, arg_iterator it,
+    parse_result_t parse_arg(std::string_view option, arg_iterator it,
                              arg_iterator end);
-    parse_result_t parse_long(const std::string_view option, arg_iterator it,
+    parse_result_t parse_long(std::string_view option, arg_iterator it,
                               arg_iterator end);
-    parse_result_t parse_short(const std::string_view option, arg_iterator it,
+    parse_result_t parse_short(std::string_view option, arg_iterator it,
                                arg_iterator end);
 
-    sub_parsers_map_t sub_parsers;
-    long_options_map_t long_options;
-    short_options_map_t short_options;
-    arguments_vector_t arguments;
-    optional_argument_t optional_argument;
-    validate_func_vec_t validate_functions;
-    option_descriptions_vec_t mandatory_option_descriptions;
-    option_descriptions_vec_t optional_option_descriptions;
-    sub_parser_descriptions_vec_t sub_parser_descriptions;
-    argument_descriptions_vec_t mandatory_argument_descriptions;
-    argument_descriptions_vec_t optional_argument_descriptions;
+    sub_parsers_map_t sub_parsers{};
+    long_options_map_t long_options{};
+    short_options_map_t short_options{};
+    arguments_vector_t arguments{};
+    optional_argument_t optional_argument{};
+    validate_func_vec_t validate_functions{};
+    option_descriptions_vec_t mandatory_option_descriptions{};
+    option_descriptions_vec_t optional_option_descriptions{};
+    sub_parser_descriptions_vec_t sub_parser_descriptions{};
+    argument_descriptions_vec_t mandatory_argument_descriptions{};
+    argument_descriptions_vec_t optional_argument_descriptions{};
     std::size_t max_option_string_size{0};
     std::size_t num_processed_arguments{0};
 };
diff --git a/src/include/clapp/parser.hpp b/src/include/clapp/parser.hpp
index f1ebc21a108d47e683dddbde8d9b2610efaf755d..7cac92a98e9c6ad9bf082b6a9482d3fe0412bdee 100644
--- a/src/include/clapp/parser.hpp
+++ b/src/include/clapp/parser.hpp
@@ -13,12 +13,16 @@
 // SOFTWARE.
 ///////////////////////////////////////////////////////////////////////////////
 
-#ifndef LIBCLAPP_PARSER_HPP
-#define LIBCLAPP_PARSER_HPP
+#ifndef CLAPP_PARSER_HPP
+#define CLAPP_PARSER_HPP
 
 #include <clapp/parser.h>
+#include <numeric>
 #include <sstream>
 
+inline clapp::parser::arg_t::arg_t(const char* const* argv, int argc)
+    : base{gsl::make_span(argv, argc)} {}
+
 template <typename short_option_func_t, typename long_option_func_t>
 constexpr bool clapp::parser::basic_parser_t::is_param_opt() {
     return std::is_same<short_option_func_t, short_opt_param_func_t>::value &&
@@ -30,33 +34,32 @@ template <typename short_option_func_t, typename long_option_func_t,
 void clapp::parser::basic_parser_t::reg(
     reg_option_conf_t<short_option_func_t, long_option_func_t, option_type>&&
         config) {
-    if (config.option_string.size() == 0) {
-        throw std::runtime_error("Option string '' is too short.");
-    }
-
-    if (config.short_option) {
-        std::pair<short_options_map_t::iterator, bool> ret{
-            get_short_options().emplace(
-                std::move(config.short_option.value().option),
-                std::move(config.short_option.value().func))};
-        if (!ret.second) {
-            std::stringstream ss;
-            ss << "Can't register option '"
-               << config.short_option.value().option
-               << "' as it is already registered.";
-            throw std::runtime_error(ss.str());
+    Expects(config.option_string.size() > 1);
+
+    if (!config.short_option.empty()) {
+        for (auto& so : config.short_option) {
+            std::pair<short_options_map_t::iterator, bool> ret{
+                get_short_options().emplace(std::move(so.option),
+                                            std::move(so.func))};
+            if (!ret.second) {
+                std::stringstream ss;
+                ss << "Can't register option '" << so.option
+                   << "' as it is already registered.";
+                throw clapp::exception::option_exception_t(ss.str());
+            }
         }
     }
-    if (config.long_option) {
-        std::pair<long_options_map_t::iterator, bool> ret{
-            get_long_options().emplace(
-                std::move(config.long_option.value().option),
-                std::move(config.long_option.value().func))};
-        if (!ret.second) {
-            std::stringstream ss;
-            ss << "Can't register option '" << config.long_option.value().option
-               << "' as it is already registered.";
-            throw std::runtime_error(ss.str());
+    if (!config.long_option.empty()) {
+        for (auto& lo : config.long_option) {
+            std::pair<long_options_map_t::iterator, bool> ret{
+                get_long_options().emplace(std::move(lo.option),
+                                           std::move(lo.func))};
+            if (!ret.second) {
+                std::stringstream ss;
+                ss << "Can't register option '" << lo.option
+                   << "' as it is already registered.";
+                throw clapp::exception::option_exception_t(ss.str());
+            }
         }
     }
 
@@ -89,7 +92,7 @@ void clapp::parser::basic_parser_t::reg(
     if (config.argument_name.size() == 0) {
         std::stringstream ss;
         ss << "Argument name '" << config.argument_name << "' is too short.";
-        throw std::runtime_error(ss.str());
+        throw clapp::exception::argument_exception_t(ss.str());
     }
 
     const std::size_t num_arguments{get_arguments().size()};
@@ -98,7 +101,7 @@ void clapp::parser::basic_parser_t::reg(
         std::stringstream ss;
         ss << "Can't register argument '" << config.argument_name
            << "' when variadic arguments are already registered.";
-        throw std::runtime_error(ss.str());
+        throw clapp::exception::argument_exception_t(ss.str());
     }
 
     if (max_option_string_size < config.argument_name.size()) {
@@ -106,21 +109,21 @@ void clapp::parser::basic_parser_t::reg(
     }
 
     if (config.purpose == purpose_t::mandatory) {
-        if (get_optional_argument_descriptions().size() > 0) {
+        if (!get_optional_argument_descriptions().empty()) {
             std::stringstream ss;
             ss << "Can't register mandatory argument '" << config.argument_name
                << "' when optional arguments are already registered.";
-            throw std::runtime_error(ss.str());
+            throw clapp::exception::argument_exception_t(ss.str());
         }
         get_mandatory_argument_descriptions().push_back(
             {config.argument_name, std::move(config.description),
              argument_type});
     } else {
-        if (get_sub_parser_descriptions().size() > 0) {
+        if (!get_sub_parser_descriptions().empty()) {
             std::stringstream ss;
             ss << "Can't register optional argument '" << config.argument_name
                << "' when a sub-parser is already registered.";
-            throw std::runtime_error(ss.str());
+            throw clapp::exception::argument_exception_t(ss.str());
         }
         get_optional_argument_descriptions().push_back(
             {config.argument_name, std::move(config.description),
@@ -156,18 +159,25 @@ template <typename short_option_func_t, typename long_option_func_t,
           clapp::parser::basic_parser_t::option_type_t option_type>
 std::string clapp::parser::basic_parser_t::reg_option_conf_t<
     short_option_func_t, long_option_func_t,
-    option_type>::create_option_string(const std::optional<char> short_option,
-                                       const std::optional<std::string>&
+    option_type>::create_option_string(const std::vector<char> short_option,
+                                       const std::vector<std::string>&
                                            long_option) {
     std::string option_string;
-    if (short_option) {
-        option_string += create_option_string(short_option.value());
+    if (!short_option.empty()) {
+        option_string = std::accumulate(
+            short_option.begin(), short_option.end(), std::string(),
+            [](const std::string& a, const char b) -> std::string {
+                std::string opt_str{create_option_string(b)};
+                return a + (a.length() > 0 ? "|" : "") + opt_str;
+            });
     }
-    if (long_option) {
-        if (option_string != "") {
-            option_string += "|";
-        }
-        option_string += create_option_string(long_option.value());
+    if (!long_option.empty()) {
+        option_string = std::accumulate(
+            long_option.begin(), long_option.end(), option_string,
+            [](const std::string& a, const std::string& b) -> std::string {
+                std::string opt_str{create_option_string(b)};
+                return a + (a.length() > 0 ? "|" : "") + opt_str;
+            });
     }
     return option_string;
 }
diff --git a/src/include/clapp/sub_parser.h b/src/include/clapp/sub_parser.h
index 8db5f4e2a84749aec63bf95852de7a8dc5c5f37c..9d2423ffa8bdce0cdc0e76dff0468e162d541e10 100644
--- a/src/include/clapp/sub_parser.h
+++ b/src/include/clapp/sub_parser.h
@@ -13,8 +13,8 @@
 // SOFTWARE.
 ///////////////////////////////////////////////////////////////////////////////
 
-#ifndef LIBCLAPP_SUB_PARSER_H
-#define LIBCLAPP_SUB_PARSER_H
+#ifndef CLAPP_SUB_PARSER_H
+#define CLAPP_SUB_PARSER_H
 
 #include <clapp/parser.h>
 
@@ -23,14 +23,14 @@ namespace clapp {
 inline namespace parser {
 class basic_sub_parser_t : public basic_parser_t {
    public:
-    basic_sub_parser_t(basic_parser_t& parser,
-                       const std::string& sub_parser_name,
-                       const std::string& description,
-                       bool _parse_parent = true);
+    basic_sub_parser_t(basic_parser_t& parser, std::string sub_parser_name_arg,
+                       std::string description_arg,
+                       bool parse_parent_arg = true);
     ~basic_sub_parser_t() override;
     explicit operator bool() const;
     std::string get_sub_parser_name() const;
     void sub_parse(arg_iterator begin, arg_iterator end);
+    std::string gen_help_prefix() const override;
 
    private:
     basic_parser_t& parent_parser;
diff --git a/src/include/clapp/sub_parser.hpp b/src/include/clapp/sub_parser.hpp
index 1b237ab10e6bb3ad35ef96b3fe05ce168c77b59d..b3a2c54bc3289bfc2093066ccddbcb91fdc20751 100644
--- a/src/include/clapp/sub_parser.hpp
+++ b/src/include/clapp/sub_parser.hpp
@@ -13,8 +13,8 @@
 // SOFTWARE.
 ///////////////////////////////////////////////////////////////////////////////
 
-#ifndef LIBCLAPP_SUB_PARSER_HPP
-#define LIBCLAPP_SUB_PARSER_HPP
+#ifndef CLAPP_SUB_PARSER_HPP
+#define CLAPP_SUB_PARSER_HPP
 
 #include <clapp/sub_parser.h>
 
diff --git a/src/include/clapp/type_traits.h b/src/include/clapp/type_traits.h
index d036d54fa182a8b9b8d36fcedefd3729f95eba87..ea5a84904ccfff23255762b3390f11719055829c 100644
--- a/src/include/clapp/type_traits.h
+++ b/src/include/clapp/type_traits.h
@@ -13,22 +13,38 @@
 // SOFTWARE.
 ///////////////////////////////////////////////////////////////////////////////
 
-#ifndef LIBCLAPP_TYPE_TRAITS_H
-#define LIBCLAPP_TYPE_TRAITS_H
+#ifndef CLAPP_TYPE_TRAITS_H
+#define CLAPP_TYPE_TRAITS_H
 
 #include <type_traits>
 
+#include <chrono>
 #include <iostream>
 #include <type_traits>
+#include <vector>
 
 namespace clapp {
 
 inline namespace type_traits {
+template <typename T>
+struct is_chrono_duration : std::false_type {};
+
+template <typename Rep, typename Period>
+struct is_chrono_duration<std::chrono::duration<Rep, Period>> : std::true_type {
+};
+
+template <typename T>
+struct is_vector : std::false_type {};
+
+template <typename T, typename A>
+struct is_vector<std::vector<T, A>> : std::true_type {};
+
 template <typename T>
 class has_append_restriction_text {
    private:
     template <typename C>
-    constexpr static bool test(decltype(&C::append_restriction_text)) {
+    constexpr static bool test([
+        [maybe_unused]] decltype(&C::append_restriction_text) func) {
         return true;
     }
     template <typename C>
@@ -44,7 +60,7 @@ template <typename T>
 class has_validate {
    private:
     template <typename C>
-    constexpr static bool test(decltype(&C::validate)) {
+    constexpr static bool test([[maybe_unused]] decltype(&C::validate) func) {
         return true;
     }
     template <typename C>
@@ -60,7 +76,24 @@ template <typename T>
 class has_default_value {
    private:
     template <typename C>
-    constexpr static bool test(decltype(&C::default_value)) {
+    constexpr static bool test([
+        [maybe_unused]] decltype(&C::default_value) func) {
+        return true;
+    }
+    template <typename C>
+    constexpr static bool test(...) {
+        return false;
+    }
+
+   public:
+    constexpr static bool value{test<T>(nullptr)};
+};
+
+template <typename T>
+class has_found {
+   private:
+    template <typename C>
+    constexpr static bool test([[maybe_unused]] decltype(&C::found) func) {
         return true;
     }
     template <typename C>
diff --git a/src/include/clapp/value.h b/src/include/clapp/value.h
index 3eac3df6c55131061825e3f893c47165fdacd261..9a3c846a0de47916efba888bf68d5accb7b9ba89 100644
--- a/src/include/clapp/value.h
+++ b/src/include/clapp/value.h
@@ -13,10 +13,11 @@
 // SOFTWARE.
 ///////////////////////////////////////////////////////////////////////////////
 
-#ifndef LIBCLAPP_VALUE_H
-#define LIBCLAPP_VALUE_H
+#ifndef CLAPP_VALUE_H
+#define CLAPP_VALUE_H
 
 #include <clapp/filesystem.h>
+#include <functional>
 #include <string>
 #include <string_view>
 
@@ -24,11 +25,17 @@ namespace clapp {
 
 inline namespace value {
 template <typename T>
-T convert_value(const std::string_view param);
+T convert_value(std::string_view param);
+
+template <typename T>
+constexpr const char *get_chrono_postfix() noexcept;
+
+template <typename T>
+std::string to_string(const T &value);
 
 template <typename T>
 struct default_value_t {
-    constexpr default_value_t(T _value);
+    explicit default_value_t(T _value);
 
     std::string append_restriction_text() const;
 
@@ -41,7 +48,7 @@ struct default_value_t {
 template <typename T>
 class min_max_value_t {
    public:
-    constexpr min_max_value_t(T _min, T _max);
+    min_max_value_t(T _min, T _max);
 
     std::string append_restriction_text() const;
 
@@ -52,6 +59,17 @@ class min_max_value_t {
     T max;
 };
 
+class found_func_t {
+   public:
+    using func_t = std::function<void()>;
+    explicit found_func_t(func_t &&func_arg);
+
+    void found();
+
+   private:
+    func_t func;
+};
+
 #ifdef CLAPP_FS_AVAIL
 class path_exists_t {
    public:
diff --git a/src/include/clapp/value.hpp b/src/include/clapp/value.hpp
index c595ff4bc2ffe395960882a6d84e583228e02123..41eae0a8b542f7da94d290d9c52c936a0822f3d3 100644
--- a/src/include/clapp/value.hpp
+++ b/src/include/clapp/value.hpp
@@ -13,36 +13,62 @@
 // SOFTWARE.
 ///////////////////////////////////////////////////////////////////////////////
 
-#ifndef LIBCLAPP_VALUE_HPP
-#define LIBCLAPP_VALUE_HPP
+#ifndef CLAPP_VALUE_HPP
+#define CLAPP_VALUE_HPP
 
+#include <clapp/exception.h>
+#include <clapp/type_traits.h>
 #include <clapp/value.h>
 #include <gsl/gsl_assert>
 #include <sstream>
 
 template <typename T>
-constexpr clapp::value::default_value_t<T>::default_value_t(T _value)
-    : value{_value} {}
+clapp::value::default_value_t<T>::default_value_t(T _value) : value{_value} {}
 
 template <typename T>
-std::string clapp::value::default_value_t<T>::append_restriction_text() const {
+constexpr const char* clapp::value::get_chrono_postfix() noexcept {
+    const char* postfix{""};
+    if constexpr (std::is_same<T, std::chrono::nanoseconds>::value) {
+        postfix = "ns";
+    } else if constexpr (std::is_same<T, std::chrono::microseconds>::value) {
+        postfix = "µs";
+    } else if constexpr (std::is_same<T, std::chrono::milliseconds>::value) {
+        postfix = "ms";
+    } else if constexpr (std::is_same<T, std::chrono::seconds>::value) {
+        postfix = "s";
+    } else if constexpr (std::is_same<T, std::chrono::minutes>::value) {
+        postfix = "m";
+    } else if constexpr (std::is_same<T, std::chrono::hours>::value) {
+        postfix = "h";
+    }
+    return postfix;
+}
+
+template <typename T>
+std::string clapp::value::to_string(const T& value) {
     std::stringstream ss;
-    ss << "default-value: ";
     if constexpr (std::is_integral<T>::value) {
         ss << std::to_string(value);
+    } else if constexpr (is_chrono_duration<T>::value) {
+        ss << value.count() << get_chrono_postfix<T>();
     } else {
         ss << value;
     }
     return ss.str();
 }
 
+template <typename T>
+std::string clapp::value::default_value_t<T>::append_restriction_text() const {
+    return "default-value: " + to_string(value);
+}
+
 template <typename T>
 T clapp::value::default_value_t<T>::default_value() const {
     return value;
 }
 
 template <typename T>
-constexpr clapp::value::min_max_value_t<T>::min_max_value_t(T _min, T _max)
+clapp::value::min_max_value_t<T>::min_max_value_t(T _min, T _max)
     : min{_min}, max{_max} {
     Expects(min < max);
 }
@@ -50,80 +76,89 @@ constexpr clapp::value::min_max_value_t<T>::min_max_value_t(T _min, T _max)
 template <typename T>
 std::string clapp::value::min_max_value_t<T>::append_restriction_text() const {
     std::stringstream ss;
-    if constexpr (std::is_integral<T>::value) {
-        ss << "constraint: [" << std::to_string(min) << ","
-           << std::to_string(max) << "]";
-    } else {
-        ss << "constraint: [" << min << "," << max << "]";
-    }
+    ss << "constraint: [" << to_string(min) << "," << to_string(max) << "]";
     return ss.str();
 }
 
 template <typename T>
 void clapp::value::min_max_value_t<T>::validate(
-    const T &value, const std::string &param_name) const {
+    const T& value, const std::string& param_name) const {
     if (value < min) {
-        std::stringstream ss;
-        ss << "CLI value for '" << param_name << "' is too low (";
-        if constexpr (std::is_integral<T>::value) {
-            ss << std::to_string(value) << " < " << std::to_string(min) << ")";
-        } else {
-            ss << value << " < " << min << ")";
-        }
-        throw std::out_of_range{ss.str()};
+        throw clapp::exception::out_of_range_t{
+            "CLI value for '" + param_name + "' is too low (" +
+            to_string(value) + " < " + to_string(min) + ")"};
     }
     if (value > max) {
-        std::stringstream ss;
-        ss << "CLI value for '" << param_name << "' is too high (";
-        if constexpr (std::is_integral<T>::value) {
-            ss << std::to_string(value) << " < " << std::to_string(min) << ")";
-        } else {
-            ss << value << " > " << min << ")";
-        }
-        throw std::out_of_range{ss.str()};
+        throw clapp::exception::out_of_range_t{
+            "CLI value for '" + param_name + "' is too high (" +
+            to_string(value) + " > " + to_string(max) + ")"};
     }
 }
 
 template <>
-std::string clapp::value::convert_value<std::string>(
-    const std::string_view param);
+std::string clapp::value::convert_value<std::string>(std::string_view param);
 
 template <>
-std::int8_t clapp::value::convert_value<std::int8_t>(
-    const std::string_view param);
+std::int8_t clapp::value::convert_value<std::int8_t>(std::string_view param);
 
 template <>
-std::int16_t clapp::value::convert_value<std::int16_t>(
-    const std::string_view param);
+std::int16_t clapp::value::convert_value<std::int16_t>(std::string_view param);
 
 template <>
-std::int32_t clapp::value::convert_value<std::int32_t>(
-    const std::string_view param);
+std::int32_t clapp::value::convert_value<std::int32_t>(std::string_view param);
 
 template <>
-std::int64_t clapp::value::convert_value<std::int64_t>(
-    const std::string_view param);
+std::int64_t clapp::value::convert_value<std::int64_t>(std::string_view param);
 
 template <>
-std::uint8_t clapp::value::convert_value<std::uint8_t>(
-    const std::string_view param);
+std::uint8_t clapp::value::convert_value<std::uint8_t>(std::string_view param);
 
 template <>
 std::uint16_t clapp::value::convert_value<std::uint16_t>(
-    const std::string_view param);
+    std::string_view param);
 
 template <>
 std::uint32_t clapp::value::convert_value<std::uint32_t>(
-    const std::string_view param);
+    std::string_view param);
 
 template <>
 std::uint64_t clapp::value::convert_value<std::uint64_t>(
-    const std::string_view param);
+    std::string_view param);
+
+template <>
+std::chrono::nanoseconds clapp::value::convert_value<std::chrono::nanoseconds>(
+    std::string_view param);
+
+template <>
+std::chrono::microseconds
+clapp::value::convert_value<std::chrono::microseconds>(std::string_view param);
+
+template <>
+std::chrono::milliseconds
+clapp::value::convert_value<std::chrono::milliseconds>(std::string_view param);
+
+template <>
+std::chrono::seconds clapp::value::convert_value<std::chrono::seconds>(
+    std::string_view param);
+
+template <>
+std::chrono::minutes clapp::value::convert_value<std::chrono::minutes>(
+    std::string_view param);
+
+template <>
+std::chrono::hours clapp::value::convert_value<std::chrono::hours>(
+    std::string_view param);
+
+template <>
+float clapp::value::convert_value<float>(std::string_view param);
+
+template <>
+double clapp::value::convert_value<double>(std::string_view param);
 
 #ifdef CLAPP_FS_AVAIL
 template <>
 clapp::fs::path clapp::value::convert_value<clapp::fs::path>(
-    const std::string_view param);
+    std::string_view param);
 #endif
 
 #endif
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
index b12defb915a8263af9d3c996f2364cf9c0b2b39a..74a3ae8a341218a028d18d482f5764fa74d3a079 100644
--- a/tests/CMakeLists.txt
+++ b/tests/CMakeLists.txt
@@ -1,5 +1,10 @@
-set(sources value.cpp argument.cpp option.cpp parser.cpp sub_parser.cpp main_parser.cpp)
+set(sources value.cpp argument.cpp option.cpp parser.cpp sub_parser.cpp main_parser.cpp exception.cpp)
 
 add_executable(libclapp_tests ${sources})
-target_link_libraries(libclapp_tests gtest_main gmock clapp)
+target_link_libraries(libclapp_tests libClaPP_gmock_main clapp)
+target_compile_options(libclapp_tests PRIVATE ${libClaPP_TEST_COMPILE_OPTIONS})
 install(TARGETS libclapp_tests DESTINATION ${CMAKE_INSTALL_BINDIR})
+if(CLANG_TIDY)
+  set_target_properties(libclapp_tests PROPERTIES CXX_CLANG_TIDY "${CLANG_TIDY_ARGS},-llvm-include-order,-clang-diagnostic-global-constructors,-hicpp-special-member-functions,-cppcoreguidelines-owning-memory,-cert-err58-cpp,-cppcoreguidelines-special-member-functions,-clang-diagnostic-used-but-marked-unused,-cppcoreguidelines-pro-type-vararg,-clang-diagnostic-covered-switch-default,-hicpp-vararg,-fuchsia-statically-constructed-objects,-cppcoreguidelines-avoid-goto,-hicpp-avoid-goto")
+endif()
+
diff --git a/tests/argument.cpp b/tests/argument.cpp
index a4deee713bd4ad91a9e0f40a4b58f6ff44f9828c..ca4b9997f4e5b82d32d1207127a0d5634a71e3a6 100644
--- a/tests/argument.cpp
+++ b/tests/argument.cpp
@@ -1,44 +1,133 @@
 #include <clapp/argument.h>
+#include <clapp/sub_parser.h>
 #include <gmock/gmock.h>
 
-class test_parser_t : public clapp::parser::basic_parser_t {
-    public:
+class argument_test_parser_t : public clapp::parser::basic_parser_t {
+   public:
     using clapp::parser::basic_parser_t::purpose_t;
 
+    using clapp::parser::basic_parser_t::argument_descriptions_vec_t;
     using clapp::parser::basic_parser_t::argument_type_t;
     using clapp::parser::basic_parser_t::arguments_vector_t;
-    using clapp::parser::basic_parser_t::argument_descriptions_vec_t;
     using clapp::parser::basic_parser_t::validate_func_vec_t;
 
     using clapp::parser::basic_parser_t::get_arguments;
     using clapp::parser::basic_parser_t::get_mandatory_argument_descriptions;
     using clapp::parser::basic_parser_t::get_optional_argument_descriptions;
     using clapp::parser::basic_parser_t::get_validate_functions;
+
+   public:
+    ~argument_test_parser_t() override;
+};
+
+argument_test_parser_t::~argument_test_parser_t() = default;
+
+class simple_argument_sub_parser_t : public clapp::parser::basic_sub_parser_t {
+    using clapp::parser::basic_sub_parser_t::basic_sub_parser_t;
+
+   public:
+    ~simple_argument_sub_parser_t() override;
 };
 
+simple_argument_sub_parser_t::~simple_argument_sub_parser_t() = default;
+
+TEST(argument, basic_argument_construct_to_short_argument_name_throws) {
+    const std::string arg_name;
+    const std::string arg_desc{"description"};
+    argument_test_parser_t tp;
+    ASSERT_THROW((clapp::argument::string_argument_t{tp, arg_name, arg_desc}),
+                 clapp::exception::argument_exception_t);
+}
+
+TEST(
+    argument,
+    basic_argument_construct_variadic_argument_and_register_another_argument_throws) {
+    const std::string variadic_arg_name{"variadic"};
+    const std::string arg_name{"arg"};
+    const std::string arg_desc{"description"};
+    argument_test_parser_t tp;
+    clapp::argument::basic_variadic_argument_t<std::int32_t> arg{
+        tp, variadic_arg_name, arg_desc};
+    ASSERT_THROW((clapp::argument::string_argument_t{tp, arg_name, arg_desc}),
+                 clapp::exception::argument_exception_t);
+}
+
+TEST(
+    argument,
+    basic_argument_construct_optional_argument_and_register_mandatory_argument_throws) {
+    const std::string optional_arg_name{"variadic"};
+    const std::string arg_name{"arg"};
+    const std::string arg_desc{"description"};
+    argument_test_parser_t tp;
+    clapp::argument::basic_argument_t<std::int32_t> arg{
+        tp, optional_arg_name, arg_desc,
+        argument_test_parser_t::purpose_t::optional};
+    ASSERT_THROW((clapp::argument::string_argument_t{tp, arg_name, arg_desc}),
+                 clapp::exception::argument_exception_t);
+}
+
+TEST(argument,
+     basic_argument_construct_sub_parser_and_register_argument_throws) {
+    const std::string arg_name{"arg"};
+    const std::string arg_desc{"description"};
+    argument_test_parser_t tp;
+    simple_argument_sub_parser_t sub_parser{tp, "sub-parser",
+                                            "Sub parser desc"};
+    ASSERT_THROW((clapp::argument::string_argument_t{
+                     tp, arg_name, arg_desc,
+                     argument_test_parser_t::purpose_t::optional}),
+                 clapp::exception::argument_exception_t);
+}
+
+TEST(argument, basic_argument_found_func_long) {
+    const std::string arg_name{"arg"};
+    const std::string arg_desc{"description"};
+
+    std::stringstream ss;
+    argument_test_parser_t tp;
+    clapp::argument::basic_argument_t<std::string> arg{
+        tp, arg_name, arg_desc,
+        clapp::value::found_func_t{[&ss]() { ss << "this is a test"; }}};
+    argument_test_parser_t::arguments_vector_t args{tp.get_arguments()};
+    argument_test_parser_t::argument_descriptions_vec_t descs{
+        tp.get_mandatory_argument_descriptions()};
+
+    ASSERT_THAT(args.size(), testing::Eq(1));
+    ASSERT_THAT(args[0].argument, testing::StrEq(arg_name));
+    ASSERT_THAT(args[0].argument_type,
+                testing::Eq(argument_test_parser_t::argument_type_t::single));
+
+    args[0].func("argument-value");
+    ASSERT_THAT(ss.str(), testing::StrEq("this is a test"));
+}
+
 TEST(argument, basic_argument_construct_simple) {
     const std::string arg_name{"argument name"};
     const std::string arg_desc{"description"};
-    test_parser_t tp;
+    argument_test_parser_t tp;
     clapp::argument::basic_argument_t<std::int32_t> arg{tp, arg_name, arg_desc};
-    test_parser_t::arguments_vector_t args{tp.get_arguments()};
-    test_parser_t::argument_descriptions_vec_t descs{tp.get_mandatory_argument_descriptions()};
-    test_parser_t::validate_func_vec_t validate_funcs{tp.get_validate_functions()};
+    argument_test_parser_t::arguments_vector_t args{tp.get_arguments()};
+    argument_test_parser_t::argument_descriptions_vec_t descs{
+        tp.get_mandatory_argument_descriptions()};
+    argument_test_parser_t::validate_func_vec_t validate_funcs{
+        tp.get_validate_functions()};
     ASSERT_THAT(validate_funcs.size(), testing::Eq(1));
 
     ASSERT_THAT(descs.size(), testing::Eq(1));
     ASSERT_THAT(descs[0].argument_string, testing::StrEq(arg_name));
     ASSERT_THAT(descs[0].description, testing::StrEq(arg_desc));
-    ASSERT_THAT(descs[0].argument_type, testing::Eq(test_parser_t::argument_type_t::single));
+    ASSERT_THAT(descs[0].argument_type,
+                testing::Eq(argument_test_parser_t::argument_type_t::single));
 
     ASSERT_THAT(args.size(), testing::Eq(1));
-    ASSERT_THAT(args[0].argument,testing::StrEq(arg_name));
-    ASSERT_THAT(args[0].argument_type, testing::Eq(test_parser_t::argument_type_t::single));
+    ASSERT_THAT(args[0].argument, testing::StrEq(arg_name));
+    ASSERT_THAT(args[0].argument_type,
+                testing::Eq(argument_test_parser_t::argument_type_t::single));
 
     ASSERT_THAT(static_cast<bool>(arg), testing::Eq(false));
     ASSERT_THAT(arg.given(), testing::Eq(false));
-    ASSERT_THROW(arg.value(), std::exception);
-    ASSERT_THROW(validate_funcs[0](), std::runtime_error);
+    ASSERT_THROW(arg.value(), clapp::exception::value_undefined_t);
+    ASSERT_THROW(validate_funcs[0](), clapp::exception::argument_exception_t);
 
     args[0].func("12345");
     ASSERT_THAT(static_cast<bool>(arg), testing::Eq(true));
@@ -50,27 +139,31 @@ TEST(argument, basic_argument_construct_simple) {
 TEST(argument, basic_argument_construct_mandatory) {
     const std::string arg_name{"argument name"};
     const std::string arg_desc{"description"};
-    test_parser_t tp;
-    clapp::argument::basic_argument_t<std::string> arg{tp, arg_name, arg_desc, 
-        test_parser_t::purpose_t::mandatory};
-    test_parser_t::arguments_vector_t args{tp.get_arguments()};
-    test_parser_t::argument_descriptions_vec_t descs{tp.get_mandatory_argument_descriptions()};
-    test_parser_t::validate_func_vec_t validate_funcs{tp.get_validate_functions()};
+    argument_test_parser_t tp;
+    clapp::argument::basic_argument_t<std::string> arg{
+        tp, arg_name, arg_desc, argument_test_parser_t::purpose_t::mandatory};
+    argument_test_parser_t::arguments_vector_t args{tp.get_arguments()};
+    argument_test_parser_t::argument_descriptions_vec_t descs{
+        tp.get_mandatory_argument_descriptions()};
+    argument_test_parser_t::validate_func_vec_t validate_funcs{
+        tp.get_validate_functions()};
     ASSERT_THAT(validate_funcs.size(), testing::Eq(1));
 
     ASSERT_THAT(descs.size(), testing::Eq(1));
     ASSERT_THAT(descs[0].argument_string, testing::StrEq(arg_name));
     ASSERT_THAT(descs[0].description, testing::StrEq(arg_desc));
-    ASSERT_THAT(descs[0].argument_type, testing::Eq(test_parser_t::argument_type_t::single));
+    ASSERT_THAT(descs[0].argument_type,
+                testing::Eq(argument_test_parser_t::argument_type_t::single));
 
     ASSERT_THAT(args.size(), testing::Eq(1));
-    ASSERT_THAT(args[0].argument,testing::StrEq(arg_name));
-    ASSERT_THAT(args[0].argument_type, testing::Eq(test_parser_t::argument_type_t::single));
+    ASSERT_THAT(args[0].argument, testing::StrEq(arg_name));
+    ASSERT_THAT(args[0].argument_type,
+                testing::Eq(argument_test_parser_t::argument_type_t::single));
 
     ASSERT_THAT(static_cast<bool>(arg), testing::Eq(false));
     ASSERT_THAT(arg.given(), testing::Eq(false));
-    ASSERT_THROW(arg.value(), std::exception);
-    ASSERT_THROW(validate_funcs[0](), std::runtime_error);
+    ASSERT_THROW(arg.value(), clapp::exception::value_undefined_t);
+    ASSERT_THROW(validate_funcs[0](), clapp::exception::argument_exception_t);
 
     args[0].func("string-arg");
     ASSERT_THAT(static_cast<bool>(arg), testing::Eq(true));
@@ -82,26 +175,30 @@ TEST(argument, basic_argument_construct_mandatory) {
 TEST(argument, basic_argument_construct_optional) {
     const std::string arg_name{"argument name"};
     const std::string arg_desc{"description"};
-    test_parser_t tp;
-    clapp::argument::basic_argument_t<std::int32_t> arg{tp, arg_name, arg_desc, 
-        test_parser_t::purpose_t::optional};
-    test_parser_t::arguments_vector_t args{tp.get_arguments()};
-    test_parser_t::argument_descriptions_vec_t descs{tp.get_optional_argument_descriptions()};
-    test_parser_t::validate_func_vec_t validate_funcs{tp.get_validate_functions()};
+    argument_test_parser_t tp;
+    clapp::argument::basic_argument_t<std::int32_t> arg{
+        tp, arg_name, arg_desc, argument_test_parser_t::purpose_t::optional};
+    argument_test_parser_t::arguments_vector_t args{tp.get_arguments()};
+    argument_test_parser_t::argument_descriptions_vec_t descs{
+        tp.get_optional_argument_descriptions()};
+    argument_test_parser_t::validate_func_vec_t validate_funcs{
+        tp.get_validate_functions()};
     ASSERT_THAT(validate_funcs.size(), testing::Eq(0));
 
     ASSERT_THAT(descs.size(), testing::Eq(1));
     ASSERT_THAT(descs[0].argument_string, testing::StrEq(arg_name));
     ASSERT_THAT(descs[0].description, testing::StrEq(arg_desc));
-    ASSERT_THAT(descs[0].argument_type, testing::Eq(test_parser_t::argument_type_t::single));
+    ASSERT_THAT(descs[0].argument_type,
+                testing::Eq(argument_test_parser_t::argument_type_t::single));
 
     ASSERT_THAT(args.size(), testing::Eq(1));
-    ASSERT_THAT(args[0].argument,testing::StrEq(arg_name));
-    ASSERT_THAT(args[0].argument_type, testing::Eq(test_parser_t::argument_type_t::single));
+    ASSERT_THAT(args[0].argument, testing::StrEq(arg_name));
+    ASSERT_THAT(args[0].argument_type,
+                testing::Eq(argument_test_parser_t::argument_type_t::single));
 
     ASSERT_THAT(static_cast<bool>(arg), testing::Eq(false));
     ASSERT_THAT(arg.given(), testing::Eq(false));
-    ASSERT_THROW(arg.value(), std::exception);
+    ASSERT_THROW(arg.value(), clapp::exception::value_undefined_t);
 
     args[0].func("12345");
     ASSERT_THAT(static_cast<bool>(arg), testing::Eq(true));
@@ -112,22 +209,30 @@ TEST(argument, basic_argument_construct_optional) {
 TEST(argument, basic_argument_construct_optional_default_value) {
     const std::string arg_name{"argument name"};
     const std::string arg_desc{"description"};
-    test_parser_t tp;
+    argument_test_parser_t tp;
     const clapp::value::default_value_t<std::int8_t> default_value{10};
-    clapp::argument::basic_argument_t<std::int8_t> arg{tp, arg_name, arg_desc, test_parser_t::purpose_t::optional, default_value};
-    test_parser_t::arguments_vector_t args{tp.get_arguments()};
-    test_parser_t::argument_descriptions_vec_t descs{tp.get_optional_argument_descriptions()};
-    test_parser_t::validate_func_vec_t validate_funcs{tp.get_validate_functions()};
+    clapp::argument::basic_argument_t<std::int8_t> arg{
+        tp, arg_name, arg_desc, argument_test_parser_t::purpose_t::optional,
+        default_value};
+    argument_test_parser_t::arguments_vector_t args{tp.get_arguments()};
+    argument_test_parser_t::argument_descriptions_vec_t descs{
+        tp.get_optional_argument_descriptions()};
+    argument_test_parser_t::validate_func_vec_t validate_funcs{
+        tp.get_validate_functions()};
     ASSERT_THAT(validate_funcs.size(), testing::Eq(0));
 
     ASSERT_THAT(descs.size(), testing::Eq(1));
     ASSERT_THAT(descs[0].argument_string, testing::StrEq(arg_name));
-    ASSERT_THAT(descs[0].description, testing::StrEq(arg_desc + " (" + default_value.append_restriction_text() + ")"));
-    ASSERT_THAT(descs[0].argument_type, testing::Eq(test_parser_t::argument_type_t::single));
+    ASSERT_THAT(descs[0].description,
+                testing::StrEq(arg_desc + " (" +
+                               default_value.append_restriction_text() + ")"));
+    ASSERT_THAT(descs[0].argument_type,
+                testing::Eq(argument_test_parser_t::argument_type_t::single));
 
     ASSERT_THAT(args.size(), testing::Eq(1));
-    ASSERT_THAT(args[0].argument,testing::StrEq(arg_name));
-    ASSERT_THAT(args[0].argument_type, testing::Eq(test_parser_t::argument_type_t::single));
+    ASSERT_THAT(args[0].argument, testing::StrEq(arg_name));
+    ASSERT_THAT(args[0].argument_type,
+                testing::Eq(argument_test_parser_t::argument_type_t::single));
 
     ASSERT_THAT(static_cast<bool>(arg), testing::Eq(true));
     ASSERT_THAT(arg.given(), testing::Eq(false));
@@ -143,28 +248,40 @@ TEST(argument, basic_argument_construct_mandatory_default_value) {
     const std::string arg_name{"argument name"};
     const std::string arg_desc{"desciption"};
     const std::uint32_t default_value{1123};
-    test_parser_t tp;
-    clapp::argument::basic_argument_t<std::uint32_t> arg{tp, arg_name, arg_desc, test_parser_t::purpose_t::optional, clapp::value::default_value_t<std::uint32_t>{default_value}};
-    test_parser_t::arguments_vector_t args{tp.get_arguments()};
-    test_parser_t::argument_descriptions_vec_t descs{tp.get_optional_argument_descriptions()};
-    test_parser_t::validate_func_vec_t validate_funcs{tp.get_validate_functions()};
+    argument_test_parser_t tp;
+    clapp::argument::basic_argument_t<std::uint32_t> arg{
+        tp, arg_name, arg_desc, argument_test_parser_t::purpose_t::optional,
+        clapp::value::default_value_t<std::uint32_t>{default_value}};
+    argument_test_parser_t::arguments_vector_t args{tp.get_arguments()};
+    argument_test_parser_t::argument_descriptions_vec_t descs{
+        tp.get_optional_argument_descriptions()};
+    argument_test_parser_t::validate_func_vec_t validate_funcs{
+        tp.get_validate_functions()};
     ASSERT_THAT(validate_funcs.size(), testing::Eq(0));
 
     ASSERT_THAT(descs.size(), testing::Eq(1));
     ASSERT_THAT(descs[0].argument_string, testing::StrEq(arg_name));
-    ASSERT_THAT(descs[0].description, testing::StrEq(arg_desc + " (" + clapp::value::default_value_t{default_value}.append_restriction_text() + ")"));
-    ASSERT_THAT(descs[0].argument_type, testing::Eq(test_parser_t::argument_type_t::single));
+    ASSERT_THAT(descs[0].description,
+                testing::StrEq(
+                    arg_desc + " (" +
+                    clapp::value::default_value_t<std::uint32_t>{default_value}
+                        .append_restriction_text() +
+                    ")"));
+    ASSERT_THAT(descs[0].argument_type,
+                testing::Eq(argument_test_parser_t::argument_type_t::single));
 
     ASSERT_THAT(args.size(), testing::Eq(1));
-    ASSERT_THAT(args[0].argument,testing::StrEq(arg_name));
-    ASSERT_THAT(args[0].argument_type, testing::Eq(test_parser_t::argument_type_t::single));
+    ASSERT_THAT(args[0].argument, testing::StrEq(arg_name));
+    ASSERT_THAT(args[0].argument_type,
+                testing::Eq(argument_test_parser_t::argument_type_t::single));
 
     ASSERT_THAT(static_cast<bool>(arg), testing::Eq(true));
     ASSERT_THAT(arg.given(), testing::Eq(false));
     ASSERT_THAT(arg.value(), testing::Eq(default_value));
 
-    ASSERT_THROW(args[0].func("0x123412341234"), std::out_of_range);
-    ASSERT_THROW(args[0].func("-0x12341234"), std::out_of_range);
+    ASSERT_THROW(args[0].func("0x123412341234"),
+                 clapp::exception::out_of_range_t);
+    ASSERT_THROW(args[0].func("-0x12341234"), clapp::exception::out_of_range_t);
 
     args[0].func("0x12341234");
     ASSERT_THAT(static_cast<bool>(arg), testing::Eq(true));
@@ -175,24 +292,37 @@ TEST(argument, basic_argument_construct_mandatory_default_value) {
 TEST(argument, basic_argument_construct_optional_default_value_min_max) {
     const std::string arg_name{"argument name"};
     const std::string arg_desc{"desciption"};
-    test_parser_t tp;
+    argument_test_parser_t tp;
     const clapp::value::default_value_t<std::int64_t> default_value{0x3};
     const clapp::value::min_max_value_t<std::int64_t> min_max{-2, 10};
-    clapp::argument::basic_argument_t<std::int64_t> arg{tp, arg_name, arg_desc, test_parser_t::purpose_t::optional, default_value, min_max};
-    test_parser_t::arguments_vector_t args{tp.get_arguments()};
-    test_parser_t::argument_descriptions_vec_t descs{tp.get_optional_argument_descriptions()};
-    test_parser_t::validate_func_vec_t validate_funcs{tp.get_validate_functions()};
+    clapp::argument::basic_argument_t<std::int64_t> arg{
+        tp,
+        arg_name,
+        arg_desc,
+        argument_test_parser_t::purpose_t::optional,
+        default_value,
+        min_max};
+    argument_test_parser_t::arguments_vector_t args{tp.get_arguments()};
+    argument_test_parser_t::argument_descriptions_vec_t descs{
+        tp.get_optional_argument_descriptions()};
+    argument_test_parser_t::validate_func_vec_t validate_funcs{
+        tp.get_validate_functions()};
     ASSERT_THAT(validate_funcs.size(), testing::Eq(1));
     ASSERT_NO_THROW(validate_funcs[0]());
 
     ASSERT_THAT(descs.size(), testing::Eq(1));
     ASSERT_THAT(descs[0].argument_string, testing::StrEq(arg_name));
-    ASSERT_THAT(descs[0].description, testing::StrEq(arg_desc + " (" + default_value.append_restriction_text() + ", " + min_max.append_restriction_text() + ")"));
-    ASSERT_THAT(descs[0].argument_type, testing::Eq(test_parser_t::argument_type_t::single));
+    ASSERT_THAT(descs[0].description,
+                testing::StrEq(arg_desc + " (" +
+                               default_value.append_restriction_text() + ", " +
+                               min_max.append_restriction_text() + ")"));
+    ASSERT_THAT(descs[0].argument_type,
+                testing::Eq(argument_test_parser_t::argument_type_t::single));
 
     ASSERT_THAT(args.size(), testing::Eq(1));
-    ASSERT_THAT(args[0].argument,testing::StrEq(arg_name));
-    ASSERT_THAT(args[0].argument_type, testing::Eq(test_parser_t::argument_type_t::single));
+    ASSERT_THAT(args[0].argument, testing::StrEq(arg_name));
+    ASSERT_THAT(args[0].argument_type,
+                testing::Eq(argument_test_parser_t::argument_type_t::single));
 
     ASSERT_THAT(static_cast<bool>(arg), testing::Eq(true));
     ASSERT_THAT(arg.given(), testing::Eq(false));
@@ -205,42 +335,52 @@ TEST(argument, basic_argument_construct_optional_default_value_min_max) {
     ASSERT_NO_THROW(validate_funcs[0]());
 
     args[0].func("-3");
-    ASSERT_THROW(validate_funcs[0](), std::out_of_range);
+    ASSERT_THROW(validate_funcs[0](), clapp::exception::out_of_range_t);
 
     args[0].func("-11");
-    ASSERT_THROW(validate_funcs[0](), std::out_of_range);
+    ASSERT_THROW(validate_funcs[0](), clapp::exception::out_of_range_t);
 }
 
 TEST(argument, basic_argument_construct_mandatory_path_exists) {
     const std::string arg_name1{"arg"};
     const std::string arg_desc1{"desc"};
-    test_parser_t tp;
-    clapp::argument::basic_argument_t<clapp::fs::path> arg{tp, arg_name1, arg_desc1, test_parser_t::purpose_t::optional, clapp::value::path_exists_t{}};
-
-    test_parser_t::arguments_vector_t args{tp.get_arguments()};
-    test_parser_t::argument_descriptions_vec_t descs{tp.get_optional_argument_descriptions()};
-    test_parser_t::validate_func_vec_t validate_funcs{tp.get_validate_functions()};
+    argument_test_parser_t tp;
+    clapp::argument::basic_argument_t<clapp::fs::path> arg{
+        tp, arg_name1, arg_desc1, argument_test_parser_t::purpose_t::optional,
+        clapp::value::path_exists_t{}};
+
+    argument_test_parser_t::arguments_vector_t args{tp.get_arguments()};
+    argument_test_parser_t::argument_descriptions_vec_t descs{
+        tp.get_optional_argument_descriptions()};
+    argument_test_parser_t::validate_func_vec_t validate_funcs{
+        tp.get_validate_functions()};
     ASSERT_THAT(validate_funcs.size(), testing::Eq(1));
     ASSERT_NO_THROW(validate_funcs[0]());
 
     ASSERT_THAT(descs.size(), testing::Eq(1));
     ASSERT_THAT(descs[0].argument_string, testing::StrEq(arg_name1));
-    ASSERT_THAT(descs[0].description, testing::StrEq(arg_desc1 + " (" + clapp::value::path_exists_t{}.append_restriction_text() + ")"));
-    ASSERT_THAT(descs[0].argument_type, testing::Eq(test_parser_t::argument_type_t::single));
+    ASSERT_THAT(
+        descs[0].description,
+        testing::StrEq(arg_desc1 + " (" +
+                       clapp::value::path_exists_t{}.append_restriction_text() +
+                       ")"));
+    ASSERT_THAT(descs[0].argument_type,
+                testing::Eq(argument_test_parser_t::argument_type_t::single));
 
     ASSERT_THAT(args.size(), testing::Eq(1));
-    ASSERT_THAT(args[0].argument,testing::StrEq(arg_name1));
-    ASSERT_THAT(args[0].argument_type, testing::Eq(test_parser_t::argument_type_t::single));
+    ASSERT_THAT(args[0].argument, testing::StrEq(arg_name1));
+    ASSERT_THAT(args[0].argument_type,
+                testing::Eq(argument_test_parser_t::argument_type_t::single));
 
     ASSERT_THAT(static_cast<bool>(arg), testing::Eq(false));
     ASSERT_THAT(arg.given(), testing::Eq(false));
-    ASSERT_THROW(arg.value(), std::bad_optional_access);
+    ASSERT_THROW(arg.value(), clapp::exception::value_undefined_t);
 
     args[0].func("/tmp/xxx/yyy/zz");
     ASSERT_THAT(static_cast<bool>(arg), testing::Eq(true));
     ASSERT_THAT(arg.given(), testing::Eq(true));
     ASSERT_THAT(arg.value().string(), testing::StrEq("/tmp/xxx/yyy/zz"));
-    ASSERT_THROW(validate_funcs[0](), std::runtime_error);
+    ASSERT_THROW(validate_funcs[0](), clapp::exception::path_does_not_exist_t);
 
     args[0].func("/tmp");
     ASSERT_THAT(static_cast<bool>(arg), testing::Eq(true));
@@ -249,25 +389,63 @@ TEST(argument, basic_argument_construct_mandatory_path_exists) {
     ASSERT_NO_THROW(validate_funcs[0]());
 }
 
+TEST(argument, basic_variadic_argument_found_func_long) {
+    const std::string arg_name{"arg"};
+    const std::string arg_desc{"description"};
+
+    std::stringstream ss;
+    argument_test_parser_t tp;
+    clapp::argument::basic_variadic_argument_t<std::string> arg{
+        tp, arg_name, arg_desc,
+        clapp::value::found_func_t{[&ss]() { ss << "this is a test"; }}};
+    argument_test_parser_t::arguments_vector_t args{tp.get_arguments()};
+    argument_test_parser_t::argument_descriptions_vec_t descs{
+        tp.get_mandatory_argument_descriptions()};
+
+    ASSERT_THAT(args.size(), testing::Eq(1));
+    ASSERT_THAT(args[0].argument, testing::StrEq(arg_name));
+    ASSERT_THAT(args[0].argument_type,
+                testing::Eq(argument_test_parser_t::argument_type_t::variadic));
+
+    args[0].func("argument-value");
+    args[0].func("argument-value 2");
+
+    ASSERT_THAT(static_cast<bool>(arg), testing::Eq(true));
+    ASSERT_THAT(arg.given(), testing::Eq(true));
+    ASSERT_THAT(arg.value(), testing::Eq(std::vector<std::string>{
+                                 "argument-value", "argument-value 2"}));
+    ASSERT_THAT(ss.str(), testing::StrEq("this is a testthis is a test"));
+}
+
 TEST(argument, basic_variadic_argument_construct_simple) {
     const std::string arg_name{"argument name"};
     const std::string arg_desc{"desc"};
-    test_parser_t tp;
-    clapp::argument::basic_variadic_argument_t<std::int32_t> arg{tp, arg_name, arg_desc};
-    test_parser_t::arguments_vector_t args{tp.get_arguments()};
-    test_parser_t::argument_descriptions_vec_t descs{tp.get_mandatory_argument_descriptions()};
-    test_parser_t::validate_func_vec_t validate_funcs{tp.get_validate_functions()};
+    argument_test_parser_t tp;
+    clapp::argument::basic_variadic_argument_t<std::int32_t> arg{tp, arg_name,
+                                                                 arg_desc};
+    argument_test_parser_t::arguments_vector_t args{tp.get_arguments()};
+    argument_test_parser_t::argument_descriptions_vec_t descs{
+        tp.get_mandatory_argument_descriptions()};
+    argument_test_parser_t::validate_func_vec_t validate_funcs{
+        tp.get_validate_functions()};
     ASSERT_THAT(validate_funcs.size(), testing::Eq(1));
-    ASSERT_THROW(validate_funcs[0](), std::runtime_error);
+    ASSERT_THROW(validate_funcs[0](), clapp::exception::argument_exception_t);
 
     ASSERT_THAT(descs.size(), testing::Eq(1));
     ASSERT_THAT(descs[0].argument_string, testing::StrEq(arg_name));
-    ASSERT_THAT(descs[0].description, testing::StrEq(arg_desc + " (" + clapp::basic_variadic_argument_t<std::int32_t>::variadic_argument_restrictions() + ")"));
-    ASSERT_THAT(descs[0].argument_type, testing::Eq(test_parser_t::argument_type_t::variadic));
+    ASSERT_THAT(
+        descs[0].description,
+        testing::StrEq(arg_desc + " (" +
+                       clapp::basic_variadic_argument_t<
+                           std::int32_t>::variadic_argument_restrictions() +
+                       ")"));
+    ASSERT_THAT(descs[0].argument_type,
+                testing::Eq(argument_test_parser_t::argument_type_t::variadic));
 
     ASSERT_THAT(args.size(), testing::Eq(1));
-    ASSERT_THAT(args[0].argument,testing::StrEq(arg_name));
-    ASSERT_THAT(args[0].argument_type, testing::Eq(test_parser_t::argument_type_t::variadic));
+    ASSERT_THAT(args[0].argument, testing::StrEq(arg_name));
+    ASSERT_THAT(args[0].argument_type,
+                testing::Eq(argument_test_parser_t::argument_type_t::variadic));
 
     ASSERT_THAT(static_cast<bool>(arg), testing::Eq(false));
     ASSERT_THAT(arg.given(), testing::Eq(false));
@@ -283,7 +461,8 @@ TEST(argument, basic_variadic_argument_construct_simple) {
     args[0].func("1234");
     ASSERT_THAT(static_cast<bool>(arg), testing::Eq(true));
     ASSERT_THAT(arg.given(), testing::Eq(true));
-    ASSERT_THAT(arg.value(), testing::Eq(std::vector<std::int32_t>{12345, 1234}));
+    ASSERT_THAT(arg.value(),
+                testing::Eq(std::vector<std::int32_t>{12345, 1234}));
 
     ASSERT_NO_THROW(validate_funcs[0]());
 }
@@ -291,22 +470,31 @@ TEST(argument, basic_variadic_argument_construct_simple) {
 TEST(argument, basic_variadic_argument_construct_simple_optional) {
     const std::string arg_name{"argument name"};
     const std::string arg_desc{"desc"};
-    test_parser_t tp;
-    clapp::argument::basic_variadic_argument_t<std::string> arg{tp, arg_name, arg_desc, 
-        test_parser_t::purpose_t::optional};
-    test_parser_t::arguments_vector_t args{tp.get_arguments()};
-    test_parser_t::argument_descriptions_vec_t descs{tp.get_optional_argument_descriptions()};
-    test_parser_t::validate_func_vec_t validate_funcs{tp.get_validate_functions()};
+    argument_test_parser_t tp;
+    clapp::argument::basic_variadic_argument_t<std::string> arg{
+        tp, arg_name, arg_desc, argument_test_parser_t::purpose_t::optional};
+    argument_test_parser_t::arguments_vector_t args{tp.get_arguments()};
+    argument_test_parser_t::argument_descriptions_vec_t descs{
+        tp.get_optional_argument_descriptions()};
+    argument_test_parser_t::validate_func_vec_t validate_funcs{
+        tp.get_validate_functions()};
     ASSERT_THAT(validate_funcs.size(), testing::Eq(0));
 
     ASSERT_THAT(descs.size(), testing::Eq(1));
     ASSERT_THAT(descs[0].argument_string, testing::StrEq(arg_name));
-    ASSERT_THAT(descs[0].description, testing::StrEq(arg_desc + " (" + clapp::basic_variadic_argument_t<std::int32_t>::variadic_argument_restrictions() + ")"));
-    ASSERT_THAT(descs[0].argument_type, testing::Eq(test_parser_t::argument_type_t::variadic));
+    ASSERT_THAT(
+        descs[0].description,
+        testing::StrEq(arg_desc + " (" +
+                       clapp::basic_variadic_argument_t<
+                           std::int32_t>::variadic_argument_restrictions() +
+                       ")"));
+    ASSERT_THAT(descs[0].argument_type,
+                testing::Eq(argument_test_parser_t::argument_type_t::variadic));
 
     ASSERT_THAT(args.size(), testing::Eq(1));
-    ASSERT_THAT(args[0].argument,testing::StrEq(arg_name));
-    ASSERT_THAT(args[0].argument_type, testing::Eq(test_parser_t::argument_type_t::variadic));
+    ASSERT_THAT(args[0].argument, testing::StrEq(arg_name));
+    ASSERT_THAT(args[0].argument_type,
+                testing::Eq(argument_test_parser_t::argument_type_t::variadic));
 
     ASSERT_THAT(static_cast<bool>(arg), testing::Eq(false));
     ASSERT_THAT(arg.given(), testing::Eq(false));
@@ -320,31 +508,42 @@ TEST(argument, basic_variadic_argument_construct_simple_optional) {
     args[0].func("aba");
     ASSERT_THAT(static_cast<bool>(arg), testing::Eq(true));
     ASSERT_THAT(arg.given(), testing::Eq(true));
-    ASSERT_THAT(arg.value(), testing::Eq(std::vector<std::string>{"ccc", "aba"}));
+    ASSERT_THAT(arg.value(),
+                testing::Eq(std::vector<std::string>{"ccc", "aba"}));
 }
 
 TEST(argument, basic_variadic_argument_construct_simple_optional_min_max) {
     const std::string arg_name{"argument name"};
     const std::string arg_desc{"desc"};
     const clapp::value::min_max_value_t<std::int64_t> min_max{-2, 10};
-    test_parser_t tp;
-    clapp::argument::basic_variadic_argument_t<std::int64_t> arg{tp, arg_name, arg_desc, min_max, 
-        test_parser_t::purpose_t::optional};
-    test_parser_t::arguments_vector_t args{tp.get_arguments()};
-    test_parser_t::argument_descriptions_vec_t descs{tp.get_optional_argument_descriptions()};
-    test_parser_t::validate_func_vec_t validate_funcs{tp.get_validate_functions()};
+    argument_test_parser_t tp;
+    clapp::argument::basic_variadic_argument_t<std::int64_t> arg{
+        tp, arg_name, arg_desc, min_max,
+        argument_test_parser_t::purpose_t::optional};
+    argument_test_parser_t::arguments_vector_t args{tp.get_arguments()};
+    argument_test_parser_t::argument_descriptions_vec_t descs{
+        tp.get_optional_argument_descriptions()};
+    argument_test_parser_t::validate_func_vec_t validate_funcs{
+        tp.get_validate_functions()};
 
     ASSERT_THAT(validate_funcs.size(), testing::Eq(1));
     ASSERT_NO_THROW(validate_funcs[0]());
 
     ASSERT_THAT(descs.size(), testing::Eq(1));
     ASSERT_THAT(descs[0].argument_string, testing::StrEq(arg_name));
-    ASSERT_THAT(descs[0].description, testing::StrEq(arg_desc + " (" + clapp::basic_variadic_argument_t<std::int64_t>::variadic_argument_restrictions() + ", " + min_max.append_restriction_text() + ")"));
-    ASSERT_THAT(descs[0].argument_type, testing::Eq(test_parser_t::argument_type_t::variadic));
+    ASSERT_THAT(
+        descs[0].description,
+        testing::StrEq(arg_desc + " (" +
+                       clapp::basic_variadic_argument_t<
+                           std::int64_t>::variadic_argument_restrictions() +
+                       ", " + min_max.append_restriction_text() + ")"));
+    ASSERT_THAT(descs[0].argument_type,
+                testing::Eq(argument_test_parser_t::argument_type_t::variadic));
 
     ASSERT_THAT(args.size(), testing::Eq(1));
-    ASSERT_THAT(args[0].argument,testing::StrEq(arg_name));
-    ASSERT_THAT(args[0].argument_type, testing::Eq(test_parser_t::argument_type_t::variadic));
+    ASSERT_THAT(args[0].argument, testing::StrEq(arg_name));
+    ASSERT_THAT(args[0].argument_type,
+                testing::Eq(argument_test_parser_t::argument_type_t::variadic));
 
     ASSERT_THAT(static_cast<bool>(arg), testing::Eq(false));
     ASSERT_THAT(arg.given(), testing::Eq(false));
@@ -367,13 +566,15 @@ TEST(argument, basic_variadic_argument_construct_simple_optional_min_max) {
     ASSERT_THAT(arg.given(), testing::Eq(true));
     ASSERT_THAT(arg.value(), testing::Eq(std::vector<std::int64_t>{8, 6, 12}));
 
-    ASSERT_THROW(validate_funcs[0](), std::out_of_range);
+    ASSERT_THROW(validate_funcs[0](), clapp::exception::out_of_range_t);
 }
 
 TEST(argument, basic_variadic_argument_construct_simple_default_value_throws) {
     const std::string arg_name{"argument name"};
     const std::string arg_desc{"desc"};
     const clapp::value::default_value_t<std::int8_t> default_value{10};
-    test_parser_t tp;
-    ASSERT_THROW((clapp::argument::basic_variadic_argument_t<std::int64_t>{tp, arg_name, arg_desc, default_value}), std::runtime_error);
+    argument_test_parser_t tp;
+    ASSERT_THROW((clapp::argument::basic_variadic_argument_t<std::int64_t>{
+                     tp, arg_name, arg_desc, default_value}),
+                 clapp::exception::argument_exception_t);
 }
diff --git a/tests/clapp.cpp b/tests/clapp.cpp
deleted file mode 100644
index 84862be4af4f0f66454784b8bb283b93382cd941..0000000000000000000000000000000000000000
--- a/tests/clapp.cpp
+++ /dev/null
@@ -1,9 +0,0 @@
-#include <gmock/gmock.h>
-#include <cassert>
-#include <iterator>
-#include <map>
-#include <utility>
-
-TEST(DummyTest, Desc) {
-    ASSERT_THAT(true, testing::Eq(true));
-}
diff --git a/tests/exception.cpp b/tests/exception.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..9499696ac993aac4537699a72037f69db2520be4
--- /dev/null
+++ b/tests/exception.cpp
@@ -0,0 +1,172 @@
+#include <clapp/exception.h>
+#include <gmock/gmock.h>
+
+TEST(exception, clapp_exception_t) {
+    const std::string msg{"message"};
+    const std::string msg2{"message2"};
+
+    clapp::exception::clapp_exception_t ce1{msg.c_str()};
+    clapp::exception::clapp_exception_t ce2{msg2};
+
+    clapp::exception::clapp_exception_t ce3{ce1};
+    clapp::exception::clapp_exception_t ce4{std::move(ce1)};
+
+    ce4 = ce3;
+    ce3 = std::move(ce2);
+
+    ASSERT_THAT(ce3.what(), testing::StrEq(msg2));
+    ASSERT_THAT(ce4.what(), testing::StrEq(msg));
+}
+
+TEST(exception, invalid_value_t) {
+    const std::string msg{"message"};
+    const std::string msg2{"message2"};
+
+    clapp::exception::invalid_value_t ce1{msg.c_str()};
+    clapp::exception::invalid_value_t ce2{msg2};
+
+    clapp::exception::invalid_value_t ce3{ce1};
+    clapp::exception::invalid_value_t ce4{std::move(ce1)};
+
+    ce4 = ce3;
+    ce3 = std::move(ce2);
+
+    ASSERT_THAT(ce3.what(), testing::StrEq(msg2));
+    ASSERT_THAT(ce4.what(), testing::StrEq(msg));
+}
+
+TEST(exception, value_undefined_t) {
+    const std::string msg{"message"};
+    const std::string msg2{"message2"};
+
+    clapp::exception::value_undefined_t ce1{msg.c_str()};
+    clapp::exception::value_undefined_t ce2{msg2};
+
+    clapp::exception::value_undefined_t ce3{ce1};
+    clapp::exception::value_undefined_t ce4{std::move(ce1)};
+
+    ce4 = ce3;
+    ce3 = std::move(ce2);
+
+    ASSERT_THAT(ce3.what(), testing::StrEq(msg2));
+    ASSERT_THAT(ce4.what(), testing::StrEq(msg));
+}
+
+TEST(exception, out_of_range_t) {
+    const std::string msg{"message"};
+    const std::string msg2{"message2"};
+
+    clapp::exception::out_of_range_t ce1{msg.c_str()};
+    clapp::exception::out_of_range_t ce2{msg2};
+
+    clapp::exception::out_of_range_t ce3{ce1};
+    clapp::exception::out_of_range_t ce4{std::move(ce1)};
+
+    ce4 = ce3;
+    ce3 = std::move(ce2);
+
+    ASSERT_THAT(ce3.what(), testing::StrEq(msg2));
+    ASSERT_THAT(ce4.what(), testing::StrEq(msg));
+}
+
+TEST(exception, path_does_not_exist_t) {
+    const std::string msg{"message"};
+    const std::string msg2{"message2"};
+
+    clapp::exception::path_does_not_exist_t ce1{msg.c_str()};
+    clapp::exception::path_does_not_exist_t ce2{msg2};
+
+    clapp::exception::path_does_not_exist_t ce3{ce1};
+    clapp::exception::path_does_not_exist_t ce4{std::move(ce1)};
+
+    ce4 = ce3;
+    ce3 = std::move(ce2);
+
+    ASSERT_THAT(ce3.what(), testing::StrEq(msg2));
+    ASSERT_THAT(ce4.what(), testing::StrEq(msg));
+}
+
+TEST(exception, option_exception_t) {
+    const std::string msg{"message"};
+    const std::string msg2{"message2"};
+
+    clapp::exception::option_exception_t ce1{msg.c_str()};
+    clapp::exception::option_exception_t ce2{msg2};
+
+    clapp::exception::option_exception_t ce3{ce1};
+    clapp::exception::option_exception_t ce4{std::move(ce1)};
+
+    ce4 = ce3;
+    ce3 = std::move(ce2);
+
+    ASSERT_THAT(ce3.what(), testing::StrEq(msg2));
+    ASSERT_THAT(ce4.what(), testing::StrEq(msg));
+}
+
+TEST(exception, option_param_exception_t) {
+    const std::string msg{"message"};
+    const std::string msg2{"message2"};
+
+    clapp::exception::option_param_exception_t ce1{msg.c_str()};
+    clapp::exception::option_param_exception_t ce2{msg2};
+
+    clapp::exception::option_param_exception_t ce3{ce1};
+    clapp::exception::option_param_exception_t ce4{std::move(ce1)};
+
+    ce4 = ce3;
+    ce3 = std::move(ce2);
+
+    ASSERT_THAT(ce3.what(), testing::StrEq(msg2));
+    ASSERT_THAT(ce4.what(), testing::StrEq(msg));
+}
+
+TEST(exception, argument_exception_t) {
+    const std::string msg{"message"};
+    const std::string msg2{"message2"};
+
+    clapp::exception::argument_exception_t ce1{msg.c_str()};
+    clapp::exception::argument_exception_t ce2{msg2};
+
+    clapp::exception::argument_exception_t ce3{ce1};
+    clapp::exception::argument_exception_t ce4{std::move(ce1)};
+
+    ce4 = ce3;
+    ce3 = std::move(ce2);
+
+    ASSERT_THAT(ce3.what(), testing::StrEq(msg2));
+    ASSERT_THAT(ce4.what(), testing::StrEq(msg));
+}
+
+TEST(exception, parser_exception_t) {
+    const std::string msg{"message"};
+    const std::string msg2{"message2"};
+
+    clapp::exception::parser_exception_t ce1{msg.c_str()};
+    clapp::exception::parser_exception_t ce2{msg2};
+
+    clapp::exception::parser_exception_t ce3{ce1};
+    clapp::exception::parser_exception_t ce4{std::move(ce1)};
+
+    ce4 = ce3;
+    ce3 = std::move(ce2);
+
+    ASSERT_THAT(ce3.what(), testing::StrEq(msg2));
+    ASSERT_THAT(ce4.what(), testing::StrEq(msg));
+}
+
+TEST(exception, sub_parser_exception_t) {
+    const std::string msg{"message"};
+    const std::string msg2{"message2"};
+
+    clapp::exception::sub_parser_exception_t ce1{msg.c_str()};
+    clapp::exception::sub_parser_exception_t ce2{msg2};
+
+    clapp::exception::sub_parser_exception_t ce3{ce1};
+    clapp::exception::sub_parser_exception_t ce4{std::move(ce1)};
+
+    ce4 = ce3;
+    ce3 = std::move(ce2);
+
+    ASSERT_THAT(ce3.what(), testing::StrEq(msg2));
+    ASSERT_THAT(ce4.what(), testing::StrEq(msg));
+}
diff --git a/tests/main_parser.cpp b/tests/main_parser.cpp
index 8af92d6a423249452b9abf8aec650c752287dee3..a210b2835df5546aad6fa18e32aa41e93d7e0928 100644
--- a/tests/main_parser.cpp
+++ b/tests/main_parser.cpp
@@ -4,13 +4,32 @@
 #include <gmock/gmock.h>
 
 class empty_main_parser_t : public clapp::parser::basic_main_parser_t {
+   public:
+    ~empty_main_parser_t() override;
 };
 
+empty_main_parser_t::~empty_main_parser_t() = default;
+
 TEST(main_parser, construct_main_parser) {
     constexpr const char* const argv[]{"main", nullptr};
     empty_main_parser_t tp;
 
-    tp.parse(1, argv);
+    tp.parse(1, static_cast<const char* const*>(argv));
     ASSERT_THAT(static_cast<bool>(tp), testing::Eq(true));
     ASSERT_THAT(tp.get_executable(), testing::StrEq("main"));
 }
+
+TEST(parser, construct_main_parser_and_gen_help_prefix) {
+    constexpr const char* const argv[]{"main", nullptr};
+    empty_main_parser_t tp;
+
+    tp.parse(1, static_cast<const char* const*>(argv));
+    ASSERT_THAT(tp.gen_help_prefix(), testing::StrEq("Usage: \nmain"));
+}
+
+TEST(parser, construct_main_parser_parse_and_validate) {
+    constexpr const char* const argv[]{"main", nullptr};
+    empty_main_parser_t tp;
+
+    tp.parse_and_validate(1, static_cast<const char* const*>(argv));
+}
diff --git a/tests/option.cpp b/tests/option.cpp
index b5bed47872239e02e0eedab195eaaf03d47e5050..62a305f969cb5bb8b11db779fcdd76f544c443cc 100644
--- a/tests/option.cpp
+++ b/tests/option.cpp
@@ -1,8 +1,9 @@
 #include <clapp/option.h>
 #include <gmock/gmock.h>
 
-class test_parser_t : public clapp::parser::basic_parser_t {
+class option_test_parser_t : public clapp::parser::basic_parser_t {
    public:
+    ~option_test_parser_t() override;
     using clapp::parser::basic_parser_t::purpose_t;
 
     using clapp::parser::basic_parser_t::option_type_t;
@@ -27,18 +28,91 @@ class test_parser_t : public clapp::parser::basic_parser_t {
     using clapp::parser::basic_parser_t::get_validate_functions;
 };
 
+option_test_parser_t::~option_test_parser_t() = default;
+
+class test_option_t : public clapp::option::basic_option_t<std::int32_t> {
+   public:
+    template <typename... Params>
+    explicit test_option_t(clapp::parser::basic_parser_t& parser,
+                           Params... parameters);
+    ~test_option_t() override;
+
+    inline explicit operator bool() const;
+
+   private:
+    void found_entry();
+    static callbacks_t create_callbacks(test_option_t* inst);
+};
+
+template <typename... Params>
+test_option_t::test_option_t(clapp::basic_parser_t& parser,
+                             Params... parameters)
+    : clapp::basic_option_t<std::int32_t>{parser, create_callbacks(this),
+                                          std::forward<Params>(parameters)...} {
+}
+
+void test_option_t::found_entry() {
+    _given = true;
+    _value = _value.value() + 1;
+    for (auto& found_func : _found) {
+        found_func.found();
+    }
+}
+
+inline test_option_t::operator bool() const { return _value.value() != 0; }
+
+test_option_t::~test_option_t() = default;
+
+test_option_t::callbacks_t test_option_t::create_callbacks(
+    test_option_t* inst) {
+    return callbacks_t{
+        [inst](const std::string_view /*option*/) { inst->found_entry(); },
+        [inst](const char /*option*/) { inst->found_entry(); },
+        [inst]() { return inst->given(); },
+        [inst]() { return static_cast<bool>(*inst); },
+        [inst]() { return inst->value(); }};
+}
+
+TEST(option, basic_option_construct_value_throws) {
+    const std::string long_opt{"long"};
+    const std::string opt_desc{"description"};
+    option_test_parser_t tp;
+    test_option_t opt{tp, long_opt, opt_desc};
+    ASSERT_THROW(opt.value(), clapp::exception::value_undefined_t);
+}
+
+TEST(option, bool_option_construct_long_option_twice_throws) {
+    const std::string long_opt{"long"};
+    const std::string opt_desc{"description"};
+    option_test_parser_t tp;
+    clapp::option::bool_option_t opt{tp, long_opt, opt_desc};
+    ASSERT_THROW((clapp::option::bool_option_t{tp, long_opt, opt_desc}),
+                 clapp::exception::option_exception_t);
+}
+
+TEST(option, bool_option_construct_short_option_twice_throws) {
+    const char short_opt{'s'};
+    const std::string opt_desc{"description"};
+    option_test_parser_t tp;
+    clapp::option::bool_option_t opt{tp, short_opt, opt_desc};
+    ASSERT_THROW((clapp::option::bool_option_t{tp, short_opt, opt_desc}),
+                 clapp::exception::option_exception_t);
+}
+
 TEST(option, bool_option_construct_long) {
     const std::string long_opt_name{"long"};
     const std::string opt_desc{"description"};
 
-    test_parser_t tp;
+    option_test_parser_t tp;
     clapp::option::bool_option_t opt{tp, long_opt_name, opt_desc};
-    test_parser_t::long_options_map_t long_options{tp.get_long_options()};
-    test_parser_t::short_options_map_t short_options{tp.get_short_options()};
+    option_test_parser_t::long_options_map_t long_options{
+        tp.get_long_options()};
+    option_test_parser_t::short_options_map_t short_options{
+        tp.get_short_options()};
     ASSERT_THAT(tp.get_mandatory_option_descriptions().size(), testing::Eq(0));
-    test_parser_t::option_descriptions_vec_t descs{
+    option_test_parser_t::option_descriptions_vec_t descs{
         tp.get_optional_option_descriptions()};
-    test_parser_t::validate_func_vec_t validate_funcs{
+    option_test_parser_t::validate_func_vec_t validate_funcs{
         tp.get_validate_functions()};
     ASSERT_THAT(validate_funcs.size(), testing::Eq(0));
 
@@ -47,18 +121,18 @@ TEST(option, bool_option_construct_long) {
                 testing::StrEq(std::string{"--"} + long_opt_name));
     ASSERT_THAT(descs[0].description, testing::StrEq(opt_desc));
     ASSERT_THAT(descs[0].option_type,
-                testing::Eq(test_parser_t::option_type_t::scalar));
+                testing::Eq(option_test_parser_t::option_type_t::scalar));
 
     ASSERT_THAT(short_options.size(), testing::Eq(0));
     ASSERT_THAT(long_options.size(), testing::Eq(1));
     ASSERT_THAT(long_options.count(long_opt_name), testing::Eq(1));
-    test_parser_t::long_opt_variant_t long_opt_func_variant{
+    option_test_parser_t::long_opt_variant_t long_opt_func_variant{
         long_options.find(long_opt_name)->second};
-    ASSERT_THAT(std::holds_alternative<test_parser_t::long_opt_func_t>(
+    ASSERT_THAT(std::holds_alternative<option_test_parser_t::long_opt_func_t>(
                     long_opt_func_variant),
                 testing::Eq(true));
-    test_parser_t::long_opt_func_t long_opt_func{
-        std::get<test_parser_t::long_opt_func_t>(long_opt_func_variant)};
+    option_test_parser_t::long_opt_func_t long_opt_func{
+        std::get<option_test_parser_t::long_opt_func_t>(long_opt_func_variant)};
 
     ASSERT_THAT(static_cast<bool>(opt), testing::Eq(false));
     ASSERT_THAT(opt.given(), testing::Eq(false));
@@ -74,14 +148,16 @@ TEST(option, bool_option_construct_short) {
     const char short_opt_name{'o'};
     const std::string opt_desc{"description"};
 
-    test_parser_t tp;
+    option_test_parser_t tp;
     clapp::option::bool_option_t opt{tp, short_opt_name, opt_desc};
-    test_parser_t::long_options_map_t long_options{tp.get_long_options()};
-    test_parser_t::short_options_map_t short_options{tp.get_short_options()};
+    option_test_parser_t::long_options_map_t long_options{
+        tp.get_long_options()};
+    option_test_parser_t::short_options_map_t short_options{
+        tp.get_short_options()};
     ASSERT_THAT(tp.get_mandatory_option_descriptions().size(), testing::Eq(0));
-    test_parser_t::option_descriptions_vec_t descs{
+    option_test_parser_t::option_descriptions_vec_t descs{
         tp.get_optional_option_descriptions()};
-    test_parser_t::validate_func_vec_t validate_funcs{
+    option_test_parser_t::validate_func_vec_t validate_funcs{
         tp.get_validate_functions()};
     ASSERT_THAT(validate_funcs.size(), testing::Eq(0));
 
@@ -90,18 +166,19 @@ TEST(option, bool_option_construct_short) {
                 testing::StrEq(std::string{"-"} + short_opt_name));
     ASSERT_THAT(descs[0].description, testing::StrEq(opt_desc));
     ASSERT_THAT(descs[0].option_type,
-                testing::Eq(test_parser_t::option_type_t::scalar));
+                testing::Eq(option_test_parser_t::option_type_t::scalar));
 
     ASSERT_THAT(long_options.size(), testing::Eq(0));
     ASSERT_THAT(short_options.size(), testing::Eq(1));
     ASSERT_THAT(short_options.count(short_opt_name), testing::Eq(1));
-    test_parser_t::short_opt_variant_t short_opt_func_variant{
+    option_test_parser_t::short_opt_variant_t short_opt_func_variant{
         short_options.find(short_opt_name)->second};
-    ASSERT_THAT(std::holds_alternative<test_parser_t::short_opt_func_t>(
+    ASSERT_THAT(std::holds_alternative<option_test_parser_t::short_opt_func_t>(
                     short_opt_func_variant),
                 testing::Eq(true));
-    test_parser_t::short_opt_func_t short_opt_func{
-        std::get<test_parser_t::short_opt_func_t>(short_opt_func_variant)};
+    option_test_parser_t::short_opt_func_t short_opt_func{
+        std::get<option_test_parser_t::short_opt_func_t>(
+            short_opt_func_variant)};
 
     ASSERT_THAT(static_cast<bool>(opt), testing::Eq(false));
     ASSERT_THAT(opt.given(), testing::Eq(false));
@@ -122,18 +199,20 @@ TEST(option, bool_option_construct_long_and_short) {
     const char short_opt_name2{'n'};
     const std::string opt_desc2{"description2"};
 
-    test_parser_t tp;
+    option_test_parser_t tp;
 
     clapp::option::bool_option_t opt1{tp, long_opt_name1, short_opt_name1,
                                       opt_desc1};
     clapp::option::bool_option_t opt2{tp, long_opt_name2, short_opt_name2,
                                       opt_desc2};
-    test_parser_t::long_options_map_t long_options{tp.get_long_options()};
-    test_parser_t::short_options_map_t short_options{tp.get_short_options()};
+    option_test_parser_t::long_options_map_t long_options{
+        tp.get_long_options()};
+    option_test_parser_t::short_options_map_t short_options{
+        tp.get_short_options()};
     ASSERT_THAT(tp.get_mandatory_option_descriptions().size(), testing::Eq(0));
-    test_parser_t::option_descriptions_vec_t descs{
+    option_test_parser_t::option_descriptions_vec_t descs{
         tp.get_optional_option_descriptions()};
-    test_parser_t::validate_func_vec_t validate_funcs{
+    option_test_parser_t::validate_func_vec_t validate_funcs{
         tp.get_validate_functions()};
     ASSERT_THAT(validate_funcs.size(), testing::Eq(0));
 
@@ -143,49 +222,53 @@ TEST(option, bool_option_construct_long_and_short) {
                                long_opt_name1));
     ASSERT_THAT(descs[0].description, testing::StrEq(opt_desc1));
     ASSERT_THAT(descs[0].option_type,
-                testing::Eq(test_parser_t::option_type_t::scalar));
+                testing::Eq(option_test_parser_t::option_type_t::scalar));
     ASSERT_THAT(descs[1].option_string,
                 testing::StrEq(std::string{"-"} + short_opt_name2 + "|--" +
                                long_opt_name2));
     ASSERT_THAT(descs[1].description, testing::StrEq(opt_desc2));
     ASSERT_THAT(descs[1].option_type,
-                testing::Eq(test_parser_t::option_type_t::scalar));
+                testing::Eq(option_test_parser_t::option_type_t::scalar));
 
     ASSERT_THAT(long_options.size(), testing::Eq(2));
     ASSERT_THAT(long_options.count(long_opt_name1), testing::Eq(1));
     ASSERT_THAT(long_options.count(long_opt_name2), testing::Eq(1));
-    test_parser_t::long_opt_variant_t long_opt_func_variant1{
+    option_test_parser_t::long_opt_variant_t long_opt_func_variant1{
         long_options.find(long_opt_name1)->second};
-    test_parser_t::long_opt_variant_t long_opt_func_variant2{
+    option_test_parser_t::long_opt_variant_t long_opt_func_variant2{
         long_options.find(long_opt_name2)->second};
-    ASSERT_THAT(std::holds_alternative<test_parser_t::long_opt_func_t>(
+    ASSERT_THAT(std::holds_alternative<option_test_parser_t::long_opt_func_t>(
                     long_opt_func_variant1),
                 testing::Eq(true));
-    ASSERT_THAT(std::holds_alternative<test_parser_t::long_opt_func_t>(
+    ASSERT_THAT(std::holds_alternative<option_test_parser_t::long_opt_func_t>(
                     long_opt_func_variant2),
                 testing::Eq(true));
-    test_parser_t::long_opt_func_t long_opt_func1{
-        std::get<test_parser_t::long_opt_func_t>(long_opt_func_variant1)};
-    test_parser_t::long_opt_func_t long_opt_func2{
-        std::get<test_parser_t::long_opt_func_t>(long_opt_func_variant2)};
+    option_test_parser_t::long_opt_func_t long_opt_func1{
+        std::get<option_test_parser_t::long_opt_func_t>(
+            long_opt_func_variant1)};
+    option_test_parser_t::long_opt_func_t long_opt_func2{
+        std::get<option_test_parser_t::long_opt_func_t>(
+            long_opt_func_variant2)};
 
     ASSERT_THAT(short_options.size(), testing::Eq(2));
     ASSERT_THAT(short_options.count(short_opt_name1), testing::Eq(1));
     ASSERT_THAT(short_options.count(short_opt_name2), testing::Eq(1));
-    test_parser_t::short_opt_variant_t short_opt_func_variant1{
+    option_test_parser_t::short_opt_variant_t short_opt_func_variant1{
         short_options.find(short_opt_name1)->second};
-    test_parser_t::short_opt_variant_t short_opt_func_variant2{
+    option_test_parser_t::short_opt_variant_t short_opt_func_variant2{
         short_options.find(short_opt_name2)->second};
-    ASSERT_THAT(std::holds_alternative<test_parser_t::short_opt_func_t>(
+    ASSERT_THAT(std::holds_alternative<option_test_parser_t::short_opt_func_t>(
                     short_opt_func_variant1),
                 testing::Eq(true));
-    ASSERT_THAT(std::holds_alternative<test_parser_t::short_opt_func_t>(
+    ASSERT_THAT(std::holds_alternative<option_test_parser_t::short_opt_func_t>(
                     short_opt_func_variant2),
                 testing::Eq(true));
-    test_parser_t::short_opt_func_t short_opt_func1{
-        std::get<test_parser_t::short_opt_func_t>(short_opt_func_variant1)};
-    test_parser_t::short_opt_func_t short_opt_func2{
-        std::get<test_parser_t::short_opt_func_t>(short_opt_func_variant2)};
+    option_test_parser_t::short_opt_func_t short_opt_func1{
+        std::get<option_test_parser_t::short_opt_func_t>(
+            short_opt_func_variant1)};
+    option_test_parser_t::short_opt_func_t short_opt_func2{
+        std::get<option_test_parser_t::short_opt_func_t>(
+            short_opt_func_variant2)};
 
     ASSERT_THAT(static_cast<bool>(opt1), testing::Eq(false));
     ASSERT_THAT(opt1.given(), testing::Eq(false));
@@ -206,19 +289,50 @@ TEST(option, bool_option_construct_long_and_short) {
     ASSERT_THAT(opt2.value(), testing::Eq(true));
 }
 
+TEST(option, bool_option_found_func_long) {
+    const std::string long_opt_name{"long"};
+    const std::string opt_desc{"description"};
+
+    std::stringstream ss;
+
+    option_test_parser_t tp;
+    clapp::option::bool_option_t opt{
+        tp, long_opt_name, opt_desc, option_test_parser_t::purpose_t::mandatory,
+        clapp::value::found_func_t{[&ss]() { ss << "this is a test"; }}};
+    option_test_parser_t::short_options_map_t short_options{
+        tp.get_short_options()};
+    option_test_parser_t::long_options_map_t long_options{
+        tp.get_long_options()};
+    ASSERT_THAT(short_options.size(), testing::Eq(0));
+    ASSERT_THAT(long_options.size(), testing::Eq(1));
+    ASSERT_THAT(long_options.count(long_opt_name), testing::Eq(1));
+    option_test_parser_t::long_opt_variant_t long_opt_func_variant{
+        long_options.find(long_opt_name)->second};
+    ASSERT_THAT(std::holds_alternative<option_test_parser_t::long_opt_func_t>(
+                    long_opt_func_variant),
+                testing::Eq(true));
+    option_test_parser_t::long_opt_func_t long_opt_func{
+        std::get<option_test_parser_t::long_opt_func_t>(long_opt_func_variant)};
+    long_opt_func(long_opt_name);
+    ASSERT_THAT(ss.str(), testing::StrEq("this is a test"));
+}
+
 TEST(option, bool_option_construct_long_mandatory) {
     const std::string long_opt_name{"long"};
     const std::string opt_desc{"description"};
 
-    test_parser_t tp;
-    clapp::option::bool_option_t opt{tp, long_opt_name, opt_desc,
-                                     test_parser_t::purpose_t::mandatory};
-    test_parser_t::long_options_map_t long_options{tp.get_long_options()};
-    test_parser_t::short_options_map_t short_options{tp.get_short_options()};
+    option_test_parser_t tp;
+    clapp::option::bool_option_t opt{
+        tp, long_opt_name, opt_desc,
+        option_test_parser_t::purpose_t::mandatory};
+    option_test_parser_t::long_options_map_t long_options{
+        tp.get_long_options()};
+    option_test_parser_t::short_options_map_t short_options{
+        tp.get_short_options()};
     ASSERT_THAT(tp.get_optional_option_descriptions().size(), testing::Eq(0));
-    test_parser_t::option_descriptions_vec_t descs{
+    option_test_parser_t::option_descriptions_vec_t descs{
         tp.get_mandatory_option_descriptions()};
-    test_parser_t::validate_func_vec_t validate_funcs{
+    option_test_parser_t::validate_func_vec_t validate_funcs{
         tp.get_validate_functions()};
     ASSERT_THAT(validate_funcs.size(), testing::Eq(1));
 
@@ -227,23 +341,24 @@ TEST(option, bool_option_construct_long_mandatory) {
                 testing::StrEq(std::string{"--"} + long_opt_name));
     ASSERT_THAT(descs[0].description, testing::StrEq(opt_desc));
     ASSERT_THAT(descs[0].option_type,
-                testing::Eq(test_parser_t::option_type_t::scalar));
+                testing::Eq(option_test_parser_t::option_type_t::scalar));
 
     ASSERT_THAT(short_options.size(), testing::Eq(0));
     ASSERT_THAT(long_options.size(), testing::Eq(1));
     ASSERT_THAT(long_options.count(long_opt_name), testing::Eq(1));
-    test_parser_t::long_opt_variant_t long_opt_func_variant{
+    option_test_parser_t::long_opt_variant_t long_opt_func_variant{
         long_options.find(long_opt_name)->second};
-    ASSERT_THAT(std::holds_alternative<test_parser_t::long_opt_func_t>(
+    ASSERT_THAT(std::holds_alternative<option_test_parser_t::long_opt_func_t>(
                     long_opt_func_variant),
                 testing::Eq(true));
-    test_parser_t::long_opt_func_t long_opt_func{
-        std::get<test_parser_t::long_opt_func_t>(long_opt_func_variant)};
+    option_test_parser_t::long_opt_func_t long_opt_func{
+        std::get<option_test_parser_t::long_opt_func_t>(long_opt_func_variant)};
 
     ASSERT_THAT(static_cast<bool>(opt), testing::Eq(false));
     ASSERT_THAT(opt.given(), testing::Eq(false));
     ASSERT_THAT(opt.value(), testing::Eq(false));
-    ASSERT_THROW(validate_funcs[0](), std::exception);
+    ASSERT_THROW(validate_funcs[0](),
+                 clapp::exception::option_param_exception_t);
 
     long_opt_func(long_opt_name);
     ASSERT_THAT(static_cast<bool>(opt), testing::Eq(true));
@@ -252,18 +367,85 @@ TEST(option, bool_option_construct_long_mandatory) {
     ASSERT_NO_THROW(validate_funcs[0]());
 }
 
+TEST(option, help_option_construct_long) {
+    const std::string long_opt_name{"long"};
+    const char short_opt_name{'s'};
+    const std::string opt_desc{"description"};
+
+    option_test_parser_t tp;
+    clapp::option::help_option_t opt{tp, long_opt_name, short_opt_name,
+                                     opt_desc};
+    option_test_parser_t::long_options_map_t long_options{
+        tp.get_long_options()};
+    option_test_parser_t::short_options_map_t short_options{
+        tp.get_short_options()};
+    ASSERT_THAT(tp.get_mandatory_option_descriptions().size(), testing::Eq(0));
+    option_test_parser_t::option_descriptions_vec_t descs{
+        tp.get_optional_option_descriptions()};
+    ASSERT_THAT(tp.get_validate_functions().size(), testing::Eq(0));
+
+    ASSERT_THAT(descs.size(), testing::Eq(1));
+    ASSERT_THAT(descs[0].option_string,
+                testing::StrEq(std::string{"-"} + short_opt_name + "|--" +
+                               long_opt_name));
+    ASSERT_THAT(descs[0].description, testing::StrEq(opt_desc));
+    ASSERT_THAT(descs[0].option_type,
+                testing::Eq(option_test_parser_t::option_type_t::scalar));
+
+    ASSERT_THAT(short_options.size(), testing::Eq(1));
+    ASSERT_THAT(short_options.count(short_opt_name), testing::Eq(1));
+    option_test_parser_t::short_opt_variant_t short_opt_func_variant{
+        short_options.find(short_opt_name)->second};
+    ASSERT_THAT(long_options.size(), testing::Eq(1));
+    ASSERT_THAT(long_options.count(long_opt_name), testing::Eq(1));
+    option_test_parser_t::long_opt_variant_t long_opt_func_variant{
+        long_options.find(long_opt_name)->second};
+
+    ASSERT_THAT(std::holds_alternative<option_test_parser_t::long_opt_func_t>(
+                    long_opt_func_variant),
+                testing::Eq(true));
+    option_test_parser_t::long_opt_func_t long_opt_func{
+        std::get<option_test_parser_t::long_opt_func_t>(long_opt_func_variant)};
+
+    ASSERT_THAT(std::holds_alternative<option_test_parser_t::short_opt_func_t>(
+                    short_opt_func_variant),
+                testing::Eq(true));
+    option_test_parser_t::short_opt_func_t short_opt_func{
+        std::get<option_test_parser_t::short_opt_func_t>(
+            short_opt_func_variant)};
+
+    ASSERT_THAT(static_cast<bool>(opt), testing::Eq(false));
+    ASSERT_THAT(opt.given(), testing::Eq(false));
+    ASSERT_THAT(opt.value(), testing::Eq(false));
+
+    testing::internal::CaptureStdout();
+    ASSERT_EXIT(long_opt_func(long_opt_name), ::testing::ExitedWithCode(0), "");
+    ASSERT_THAT(testing::internal::GetCapturedStdout(),
+                testing::StrEq("Usage: \n [-s|--long] \n\nOptional Options:\n  "
+                               "-s|--long description\n"));
+
+    testing::internal::CaptureStdout();
+    ASSERT_EXIT(short_opt_func(short_opt_name), ::testing::ExitedWithCode(0),
+                "");
+    ASSERT_THAT(testing::internal::GetCapturedStdout(),
+                testing::StrEq("Usage: \n [-s|--long] \n\nOptional Options:\n  "
+                               "-s|--long description\n"));
+}
+
 TEST(option, count_option_construct_long) {
     const std::string long_opt_name{"long"};
     const std::string opt_desc{"description 2"};
 
-    test_parser_t tp;
+    option_test_parser_t tp;
     clapp::option::count_option_t opt{tp, long_opt_name, opt_desc};
-    test_parser_t::long_options_map_t long_options{tp.get_long_options()};
-    test_parser_t::short_options_map_t short_options{tp.get_short_options()};
+    option_test_parser_t::long_options_map_t long_options{
+        tp.get_long_options()};
+    option_test_parser_t::short_options_map_t short_options{
+        tp.get_short_options()};
     ASSERT_THAT(tp.get_mandatory_option_descriptions().size(), testing::Eq(0));
-    test_parser_t::option_descriptions_vec_t descs{
+    option_test_parser_t::option_descriptions_vec_t descs{
         tp.get_optional_option_descriptions()};
-    test_parser_t::validate_func_vec_t validate_funcs{
+    option_test_parser_t::validate_func_vec_t validate_funcs{
         tp.get_validate_functions()};
     ASSERT_THAT(validate_funcs.size(), testing::Eq(0));
 
@@ -272,18 +454,18 @@ TEST(option, count_option_construct_long) {
                 testing::StrEq(std::string{"--"} + long_opt_name));
     ASSERT_THAT(descs[0].description, testing::StrEq(opt_desc));
     ASSERT_THAT(descs[0].option_type,
-                testing::Eq(test_parser_t::option_type_t::scalar));
+                testing::Eq(option_test_parser_t::option_type_t::scalar));
 
     ASSERT_THAT(short_options.size(), testing::Eq(0));
     ASSERT_THAT(long_options.size(), testing::Eq(1));
     ASSERT_THAT(long_options.count(long_opt_name), testing::Eq(1));
-    test_parser_t::long_opt_variant_t long_opt_func_variant{
+    option_test_parser_t::long_opt_variant_t long_opt_func_variant{
         long_options.find(long_opt_name)->second};
-    ASSERT_THAT(std::holds_alternative<test_parser_t::long_opt_func_t>(
+    ASSERT_THAT(std::holds_alternative<option_test_parser_t::long_opt_func_t>(
                     long_opt_func_variant),
                 testing::Eq(true));
-    test_parser_t::long_opt_func_t long_opt_func{
-        std::get<test_parser_t::long_opt_func_t>(long_opt_func_variant)};
+    option_test_parser_t::long_opt_func_t long_opt_func{
+        std::get<option_test_parser_t::long_opt_func_t>(long_opt_func_variant)};
 
     ASSERT_THAT(static_cast<bool>(opt), testing::Eq(false));
     ASSERT_THAT(opt.given(), testing::Eq(false));
@@ -304,14 +486,16 @@ TEST(option, count_option_construct_short) {
     const char short_opt_name{'i'};
     const std::string opt_desc{"description 3"};
 
-    test_parser_t tp;
+    option_test_parser_t tp;
     clapp::option::count_option_t opt{tp, short_opt_name, opt_desc};
-    test_parser_t::long_options_map_t long_options{tp.get_long_options()};
-    test_parser_t::short_options_map_t short_options{tp.get_short_options()};
+    option_test_parser_t::long_options_map_t long_options{
+        tp.get_long_options()};
+    option_test_parser_t::short_options_map_t short_options{
+        tp.get_short_options()};
     ASSERT_THAT(tp.get_mandatory_option_descriptions().size(), testing::Eq(0));
-    test_parser_t::option_descriptions_vec_t descs{
+    option_test_parser_t::option_descriptions_vec_t descs{
         tp.get_optional_option_descriptions()};
-    test_parser_t::validate_func_vec_t validate_funcs{
+    option_test_parser_t::validate_func_vec_t validate_funcs{
         tp.get_validate_functions()};
     ASSERT_THAT(validate_funcs.size(), testing::Eq(0));
 
@@ -320,18 +504,19 @@ TEST(option, count_option_construct_short) {
                 testing::StrEq(std::string{"-"} + short_opt_name));
     ASSERT_THAT(descs[0].description, testing::StrEq(opt_desc));
     ASSERT_THAT(descs[0].option_type,
-                testing::Eq(test_parser_t::option_type_t::scalar));
+                testing::Eq(option_test_parser_t::option_type_t::scalar));
 
     ASSERT_THAT(long_options.size(), testing::Eq(0));
     ASSERT_THAT(short_options.size(), testing::Eq(1));
     ASSERT_THAT(short_options.count(short_opt_name), testing::Eq(1));
-    test_parser_t::short_opt_variant_t short_opt_func_variant{
+    option_test_parser_t::short_opt_variant_t short_opt_func_variant{
         short_options.find(short_opt_name)->second};
-    ASSERT_THAT(std::holds_alternative<test_parser_t::short_opt_func_t>(
+    ASSERT_THAT(std::holds_alternative<option_test_parser_t::short_opt_func_t>(
                     short_opt_func_variant),
                 testing::Eq(true));
-    test_parser_t::short_opt_func_t short_opt_func{
-        std::get<test_parser_t::short_opt_func_t>(short_opt_func_variant)};
+    option_test_parser_t::short_opt_func_t short_opt_func{
+        std::get<option_test_parser_t::short_opt_func_t>(
+            short_opt_func_variant)};
 
     ASSERT_THAT(static_cast<bool>(opt), testing::Eq(false));
     ASSERT_THAT(opt.given(), testing::Eq(false));
@@ -354,18 +539,20 @@ TEST(option, count_option_construct_long_and_short) {
     const char short_opt_name2{'n'};
     const std::string opt_desc2{"description2"};
 
-    test_parser_t tp;
+    option_test_parser_t tp;
 
     clapp::option::count_option_t opt1{tp, long_opt_name1, short_opt_name1,
                                        opt_desc1};
     clapp::option::count_option_t opt2{tp, long_opt_name2, short_opt_name2,
                                        opt_desc2};
-    test_parser_t::long_options_map_t long_options{tp.get_long_options()};
-    test_parser_t::short_options_map_t short_options{tp.get_short_options()};
+    option_test_parser_t::long_options_map_t long_options{
+        tp.get_long_options()};
+    option_test_parser_t::short_options_map_t short_options{
+        tp.get_short_options()};
     ASSERT_THAT(tp.get_mandatory_option_descriptions().size(), testing::Eq(0));
-    test_parser_t::option_descriptions_vec_t descs{
+    option_test_parser_t::option_descriptions_vec_t descs{
         tp.get_optional_option_descriptions()};
-    test_parser_t::validate_func_vec_t validate_funcs{
+    option_test_parser_t::validate_func_vec_t validate_funcs{
         tp.get_validate_functions()};
     ASSERT_THAT(validate_funcs.size(), testing::Eq(0));
 
@@ -375,49 +562,53 @@ TEST(option, count_option_construct_long_and_short) {
                                long_opt_name1));
     ASSERT_THAT(descs[0].description, testing::StrEq(opt_desc1));
     ASSERT_THAT(descs[0].option_type,
-                testing::Eq(test_parser_t::option_type_t::scalar));
+                testing::Eq(option_test_parser_t::option_type_t::scalar));
     ASSERT_THAT(descs[1].option_string,
                 testing::StrEq(std::string{"-"} + short_opt_name2 + "|--" +
                                long_opt_name2));
     ASSERT_THAT(descs[1].description, testing::StrEq(opt_desc2));
     ASSERT_THAT(descs[1].option_type,
-                testing::Eq(test_parser_t::option_type_t::scalar));
+                testing::Eq(option_test_parser_t::option_type_t::scalar));
 
     ASSERT_THAT(long_options.size(), testing::Eq(2));
     ASSERT_THAT(long_options.count(long_opt_name1), testing::Eq(1));
     ASSERT_THAT(long_options.count(long_opt_name2), testing::Eq(1));
-    test_parser_t::long_opt_variant_t long_opt_func_variant1{
+    option_test_parser_t::long_opt_variant_t long_opt_func_variant1{
         long_options.find(long_opt_name1)->second};
-    test_parser_t::long_opt_variant_t long_opt_func_variant2{
+    option_test_parser_t::long_opt_variant_t long_opt_func_variant2{
         long_options.find(long_opt_name2)->second};
-    ASSERT_THAT(std::holds_alternative<test_parser_t::long_opt_func_t>(
+    ASSERT_THAT(std::holds_alternative<option_test_parser_t::long_opt_func_t>(
                     long_opt_func_variant1),
                 testing::Eq(true));
-    ASSERT_THAT(std::holds_alternative<test_parser_t::long_opt_func_t>(
+    ASSERT_THAT(std::holds_alternative<option_test_parser_t::long_opt_func_t>(
                     long_opt_func_variant2),
                 testing::Eq(true));
-    test_parser_t::long_opt_func_t long_opt_func1{
-        std::get<test_parser_t::long_opt_func_t>(long_opt_func_variant1)};
-    test_parser_t::long_opt_func_t long_opt_func2{
-        std::get<test_parser_t::long_opt_func_t>(long_opt_func_variant2)};
+    option_test_parser_t::long_opt_func_t long_opt_func1{
+        std::get<option_test_parser_t::long_opt_func_t>(
+            long_opt_func_variant1)};
+    option_test_parser_t::long_opt_func_t long_opt_func2{
+        std::get<option_test_parser_t::long_opt_func_t>(
+            long_opt_func_variant2)};
 
     ASSERT_THAT(short_options.size(), testing::Eq(2));
     ASSERT_THAT(short_options.count(short_opt_name1), testing::Eq(1));
     ASSERT_THAT(short_options.count(short_opt_name2), testing::Eq(1));
-    test_parser_t::short_opt_variant_t short_opt_func_variant1{
+    option_test_parser_t::short_opt_variant_t short_opt_func_variant1{
         short_options.find(short_opt_name1)->second};
-    test_parser_t::short_opt_variant_t short_opt_func_variant2{
+    option_test_parser_t::short_opt_variant_t short_opt_func_variant2{
         short_options.find(short_opt_name2)->second};
-    ASSERT_THAT(std::holds_alternative<test_parser_t::short_opt_func_t>(
+    ASSERT_THAT(std::holds_alternative<option_test_parser_t::short_opt_func_t>(
                     short_opt_func_variant1),
                 testing::Eq(true));
-    ASSERT_THAT(std::holds_alternative<test_parser_t::short_opt_func_t>(
+    ASSERT_THAT(std::holds_alternative<option_test_parser_t::short_opt_func_t>(
                     short_opt_func_variant2),
                 testing::Eq(true));
-    test_parser_t::short_opt_func_t short_opt_func1{
-        std::get<test_parser_t::short_opt_func_t>(short_opt_func_variant1)};
-    test_parser_t::short_opt_func_t short_opt_func2{
-        std::get<test_parser_t::short_opt_func_t>(short_opt_func_variant2)};
+    option_test_parser_t::short_opt_func_t short_opt_func1{
+        std::get<option_test_parser_t::short_opt_func_t>(
+            short_opt_func_variant1)};
+    option_test_parser_t::short_opt_func_t short_opt_func2{
+        std::get<option_test_parser_t::short_opt_func_t>(
+            short_opt_func_variant2)};
 
     ASSERT_THAT(static_cast<bool>(opt1), testing::Eq(false));
     ASSERT_THAT(opt1.given(), testing::Eq(false));
@@ -446,15 +637,17 @@ TEST(option, count_option_construct_short_min_max) {
     const char short_opt_name{'i'};
     const std::string opt_desc{"description 3"};
 
-    test_parser_t tp;
-    clapp::value::min_max_value_t min_max{0, 5};
+    option_test_parser_t tp;
+    clapp::value::min_max_value_t<std::uint32_t> min_max{0, 5};
     clapp::option::count_option_t opt{tp, short_opt_name, opt_desc, min_max};
-    test_parser_t::long_options_map_t long_options{tp.get_long_options()};
-    test_parser_t::short_options_map_t short_options{tp.get_short_options()};
+    option_test_parser_t::long_options_map_t long_options{
+        tp.get_long_options()};
+    option_test_parser_t::short_options_map_t short_options{
+        tp.get_short_options()};
     ASSERT_THAT(tp.get_mandatory_option_descriptions().size(), testing::Eq(0));
-    test_parser_t::option_descriptions_vec_t descs{
+    option_test_parser_t::option_descriptions_vec_t descs{
         tp.get_optional_option_descriptions()};
-    test_parser_t::validate_func_vec_t validate_funcs{
+    option_test_parser_t::validate_func_vec_t validate_funcs{
         tp.get_validate_functions()};
     ASSERT_THAT(validate_funcs.size(), testing::Eq(1));
 
@@ -465,18 +658,19 @@ TEST(option, count_option_construct_short_min_max) {
                 testing::StrEq(opt_desc + " (" +
                                min_max.append_restriction_text() + ")"));
     ASSERT_THAT(descs[0].option_type,
-                testing::Eq(test_parser_t::option_type_t::scalar));
+                testing::Eq(option_test_parser_t::option_type_t::scalar));
 
     ASSERT_THAT(long_options.size(), testing::Eq(0));
     ASSERT_THAT(short_options.size(), testing::Eq(1));
     ASSERT_THAT(short_options.count(short_opt_name), testing::Eq(1));
-    test_parser_t::short_opt_variant_t short_opt_func_variant{
+    option_test_parser_t::short_opt_variant_t short_opt_func_variant{
         short_options.find(short_opt_name)->second};
-    ASSERT_THAT(std::holds_alternative<test_parser_t::short_opt_func_t>(
+    ASSERT_THAT(std::holds_alternative<option_test_parser_t::short_opt_func_t>(
                     short_opt_func_variant),
                 testing::Eq(true));
-    test_parser_t::short_opt_func_t short_opt_func{
-        std::get<test_parser_t::short_opt_func_t>(short_opt_func_variant)};
+    option_test_parser_t::short_opt_func_t short_opt_func{
+        std::get<option_test_parser_t::short_opt_func_t>(
+            short_opt_func_variant)};
 
     ASSERT_THAT(static_cast<bool>(opt), testing::Eq(false));
     ASSERT_THAT(opt.given(), testing::Eq(false));
@@ -502,23 +696,25 @@ TEST(option, count_option_construct_short_min_max) {
     ASSERT_THAT(static_cast<bool>(opt), testing::Eq(true));
     ASSERT_THAT(opt.given(), testing::Eq(true));
     ASSERT_THAT(opt.value(), testing::Eq(6));
-    ASSERT_THROW(validate_funcs[0](), std::exception);
+    ASSERT_THROW(validate_funcs[0](), clapp::exception::out_of_range_t);
 }
 
 TEST(option, count_option_construct_long_default) {
     const std::string long_opt_name{"long"};
     const std::string opt_desc{"desc"};
 
-    test_parser_t tp;
-    clapp::value::default_value_t default_value{4};
+    option_test_parser_t tp;
+    clapp::value::default_value_t<std::uint32_t> default_value{4};
     clapp::option::count_option_t opt{tp, long_opt_name, opt_desc,
                                       default_value};
-    test_parser_t::long_options_map_t long_options{tp.get_long_options()};
-    test_parser_t::short_options_map_t short_options{tp.get_short_options()};
+    option_test_parser_t::long_options_map_t long_options{
+        tp.get_long_options()};
+    option_test_parser_t::short_options_map_t short_options{
+        tp.get_short_options()};
     ASSERT_THAT(tp.get_mandatory_option_descriptions().size(), testing::Eq(0));
-    test_parser_t::option_descriptions_vec_t descs{
+    option_test_parser_t::option_descriptions_vec_t descs{
         tp.get_optional_option_descriptions()};
-    test_parser_t::validate_func_vec_t validate_funcs{
+    option_test_parser_t::validate_func_vec_t validate_funcs{
         tp.get_validate_functions()};
     ASSERT_THAT(validate_funcs.size(), testing::Eq(0));
 
@@ -529,18 +725,18 @@ TEST(option, count_option_construct_long_default) {
                 testing::StrEq(opt_desc + " (" +
                                default_value.append_restriction_text() + ")"));
     ASSERT_THAT(descs[0].option_type,
-                testing::Eq(test_parser_t::option_type_t::scalar));
+                testing::Eq(option_test_parser_t::option_type_t::scalar));
 
     ASSERT_THAT(short_options.size(), testing::Eq(0));
     ASSERT_THAT(long_options.size(), testing::Eq(1));
     ASSERT_THAT(long_options.count(long_opt_name), testing::Eq(1));
-    test_parser_t::long_opt_variant_t long_opt_func_variant{
+    option_test_parser_t::long_opt_variant_t long_opt_func_variant{
         long_options.find(long_opt_name)->second};
-    ASSERT_THAT(std::holds_alternative<test_parser_t::long_opt_func_t>(
+    ASSERT_THAT(std::holds_alternative<option_test_parser_t::long_opt_func_t>(
                     long_opt_func_variant),
                 testing::Eq(true));
-    test_parser_t::long_opt_func_t long_opt_func{
-        std::get<test_parser_t::long_opt_func_t>(long_opt_func_variant)};
+    option_test_parser_t::long_opt_func_t long_opt_func{
+        std::get<option_test_parser_t::long_opt_func_t>(long_opt_func_variant)};
 
     ASSERT_THAT(static_cast<bool>(opt), testing::Eq(true));
     ASSERT_THAT(opt.given(), testing::Eq(false));
@@ -552,19 +748,61 @@ TEST(option, count_option_construct_long_default) {
     ASSERT_THAT(opt.value(), testing::Eq(5));
 }
 
+TEST(option, count_option_found_func_short) {
+    const char short_opt_name{'c'};
+    const std::string opt_desc{"description"};
+
+    std::stringstream ss;
+    option_test_parser_t tp;
+    clapp::option::count_option_t opt{
+        tp, short_opt_name, opt_desc,
+        option_test_parser_t::purpose_t::mandatory,
+        clapp::value::found_func_t{[&ss]() { ss << "this is another test"; }}};
+    option_test_parser_t::short_options_map_t short_options{
+        tp.get_short_options()};
+    option_test_parser_t::long_options_map_t long_options{
+        tp.get_long_options()};
+    ASSERT_THAT(long_options.size(), testing::Eq(0));
+    ASSERT_THAT(short_options.size(), testing::Eq(1));
+    ASSERT_THAT(short_options.count(short_opt_name), testing::Eq(1));
+    option_test_parser_t::short_opt_variant_t short_opt_func_variant{
+        short_options.find(short_opt_name)->second};
+    ASSERT_THAT(std::holds_alternative<option_test_parser_t::short_opt_func_t>(
+                    short_opt_func_variant),
+                testing::Eq(true));
+    option_test_parser_t::short_opt_func_t short_opt_func{
+        std::get<option_test_parser_t::short_opt_func_t>(
+            short_opt_func_variant)};
+
+    ASSERT_THAT(static_cast<bool>(opt), testing::Eq(false));
+    ASSERT_THAT(opt.given(), testing::Eq(false));
+    ASSERT_THAT(opt.value(), testing::Eq(0));
+    ASSERT_THAT(ss.str(), testing::StrEq(""));
+
+    short_opt_func(short_opt_name);
+    ASSERT_THAT(ss.str(), testing::StrEq("this is another test"));
+
+    ASSERT_THAT(static_cast<bool>(opt), testing::Eq(true));
+    ASSERT_THAT(opt.given(), testing::Eq(true));
+    ASSERT_THAT(opt.value(), testing::Eq(1));
+}
+
 TEST(option, count_option_construct_short_mandatory) {
     const char short_opt_name{'i'};
     const std::string opt_desc{"description 3"};
 
-    test_parser_t tp;
-    clapp::option::count_option_t opt{tp, short_opt_name, opt_desc,
-                                      test_parser_t::purpose_t::mandatory};
-    test_parser_t::long_options_map_t long_options{tp.get_long_options()};
-    test_parser_t::short_options_map_t short_options{tp.get_short_options()};
+    option_test_parser_t tp;
+    clapp::option::count_option_t opt{
+        tp, short_opt_name, opt_desc,
+        option_test_parser_t::purpose_t::mandatory};
+    option_test_parser_t::long_options_map_t long_options{
+        tp.get_long_options()};
+    option_test_parser_t::short_options_map_t short_options{
+        tp.get_short_options()};
     ASSERT_THAT(tp.get_optional_option_descriptions().size(), testing::Eq(0));
-    test_parser_t::option_descriptions_vec_t descs{
+    option_test_parser_t::option_descriptions_vec_t descs{
         tp.get_mandatory_option_descriptions()};
-    test_parser_t::validate_func_vec_t validate_funcs{
+    option_test_parser_t::validate_func_vec_t validate_funcs{
         tp.get_validate_functions()};
     ASSERT_THAT(validate_funcs.size(), testing::Eq(1));
 
@@ -573,23 +811,24 @@ TEST(option, count_option_construct_short_mandatory) {
                 testing::StrEq(std::string{"-"} + short_opt_name));
     ASSERT_THAT(descs[0].description, testing::StrEq(opt_desc));
     ASSERT_THAT(descs[0].option_type,
-                testing::Eq(test_parser_t::option_type_t::scalar));
+                testing::Eq(option_test_parser_t::option_type_t::scalar));
 
     ASSERT_THAT(long_options.size(), testing::Eq(0));
     ASSERT_THAT(short_options.size(), testing::Eq(1));
     ASSERT_THAT(short_options.count(short_opt_name), testing::Eq(1));
-    test_parser_t::short_opt_variant_t short_opt_func_variant{
+    option_test_parser_t::short_opt_variant_t short_opt_func_variant{
         short_options.find(short_opt_name)->second};
-    ASSERT_THAT(std::holds_alternative<test_parser_t::short_opt_func_t>(
+    ASSERT_THAT(std::holds_alternative<option_test_parser_t::short_opt_func_t>(
                     short_opt_func_variant),
                 testing::Eq(true));
-    test_parser_t::short_opt_func_t short_opt_func{
-        std::get<test_parser_t::short_opt_func_t>(short_opt_func_variant)};
+    option_test_parser_t::short_opt_func_t short_opt_func{
+        std::get<option_test_parser_t::short_opt_func_t>(
+            short_opt_func_variant)};
 
     ASSERT_THAT(static_cast<bool>(opt), testing::Eq(false));
     ASSERT_THAT(opt.given(), testing::Eq(false));
     ASSERT_THAT(opt.value(), testing::Eq(0));
-    ASSERT_THROW(validate_funcs[0](), std::exception);
+    ASSERT_THROW(validate_funcs[0](), clapp::exception::option_exception_t);
 
     short_opt_func(short_opt_name);
     short_opt_func(short_opt_name);
@@ -604,14 +843,16 @@ TEST(option, string_param_option_construct_short) {
     const char short_opt_name{'s'};
     const std::string opt_desc{"string description"};
 
-    test_parser_t tp;
+    option_test_parser_t tp;
     clapp::option::string_param_option_t opt{tp, short_opt_name, opt_desc};
-    test_parser_t::long_options_map_t long_options{tp.get_long_options()};
-    test_parser_t::short_options_map_t short_options{tp.get_short_options()};
+    option_test_parser_t::long_options_map_t long_options{
+        tp.get_long_options()};
+    option_test_parser_t::short_options_map_t short_options{
+        tp.get_short_options()};
     ASSERT_THAT(tp.get_mandatory_option_descriptions().size(), testing::Eq(0));
-    test_parser_t::option_descriptions_vec_t descs{
+    option_test_parser_t::option_descriptions_vec_t descs{
         tp.get_optional_option_descriptions()};
-    test_parser_t::validate_func_vec_t validate_funcs{
+    option_test_parser_t::validate_func_vec_t validate_funcs{
         tp.get_validate_functions()};
     ASSERT_THAT(validate_funcs.size(), testing::Eq(0));
 
@@ -620,23 +861,24 @@ TEST(option, string_param_option_construct_short) {
                 testing::StrEq(std::string{"-"} + short_opt_name + "=<arg>"));
     ASSERT_THAT(descs[0].description, testing::StrEq(opt_desc));
     ASSERT_THAT(descs[0].option_type,
-                testing::Eq(test_parser_t::option_type_t::scalar));
+                testing::Eq(option_test_parser_t::option_type_t::scalar));
 
     ASSERT_THAT(long_options.size(), testing::Eq(0));
     ASSERT_THAT(short_options.size(), testing::Eq(1));
     ASSERT_THAT(short_options.count(short_opt_name), testing::Eq(1));
-    test_parser_t::short_opt_variant_t short_opt_func_variant{
+    option_test_parser_t::short_opt_variant_t short_opt_func_variant{
         short_options.find(short_opt_name)->second};
-    ASSERT_THAT(std::holds_alternative<test_parser_t::short_opt_param_func_t>(
-                    short_opt_func_variant),
-                testing::Eq(true));
-    test_parser_t::short_opt_param_func_t short_opt_func{
-        std::get<test_parser_t::short_opt_param_func_t>(
+    ASSERT_THAT(
+        std::holds_alternative<option_test_parser_t::short_opt_param_func_t>(
+            short_opt_func_variant),
+        testing::Eq(true));
+    option_test_parser_t::short_opt_param_func_t short_opt_func{
+        std::get<option_test_parser_t::short_opt_param_func_t>(
             short_opt_func_variant)};
 
     ASSERT_THAT(static_cast<bool>(opt), testing::Eq(false));
     ASSERT_THAT(opt.given(), testing::Eq(false));
-    ASSERT_THROW(opt.value(), std::exception);
+    ASSERT_THROW(opt.value(), clapp::exception::value_undefined_t);
 
     short_opt_func(short_opt_name, "string");
     ASSERT_THAT(static_cast<bool>(opt), testing::Eq(true));
@@ -644,19 +886,61 @@ TEST(option, string_param_option_construct_short) {
     ASSERT_THAT(opt.value(), testing::StrEq("string"));
 }
 
+TEST(option, string_param_option_found_func_short) {
+    const char short_opt_name{'s'};
+    const std::string opt_desc{"description"};
+
+    std::stringstream ss;
+    option_test_parser_t tp;
+    clapp::option::string_param_option_t opt{
+        tp, short_opt_name, opt_desc,
+        option_test_parser_t::purpose_t::mandatory,
+        clapp::value::found_func_t{
+            [&ss]() { ss << "this is another test for params"; }}};
+    option_test_parser_t::short_options_map_t short_options{
+        tp.get_short_options()};
+    option_test_parser_t::long_options_map_t long_options{
+        tp.get_long_options()};
+    ASSERT_THAT(long_options.size(), testing::Eq(0));
+    ASSERT_THAT(short_options.size(), testing::Eq(1));
+    ASSERT_THAT(short_options.count(short_opt_name), testing::Eq(1));
+    option_test_parser_t::short_opt_variant_t short_opt_func_variant{
+        short_options.find(short_opt_name)->second};
+    ASSERT_THAT(
+        std::holds_alternative<option_test_parser_t::short_opt_param_func_t>(
+            short_opt_func_variant),
+        testing::Eq(true));
+    option_test_parser_t::short_opt_param_func_t short_opt_func{
+        std::get<option_test_parser_t::short_opt_param_func_t>(
+            short_opt_func_variant)};
+
+    ASSERT_THAT(static_cast<bool>(opt), testing::Eq(false));
+    ASSERT_THAT(opt.given(), testing::Eq(false));
+    ASSERT_THAT(ss.str(), testing::StrEq(""));
+    short_opt_func(short_opt_name, "param");
+
+    ASSERT_THAT(ss.str(), testing::StrEq("this is another test for params"));
+    ASSERT_THAT(static_cast<bool>(opt), testing::Eq(true));
+    ASSERT_THAT(opt.given(), testing::Eq(true));
+    ASSERT_THAT(opt.value(), testing::StrEq("param"));
+}
+
 TEST(option, string_param_option_construct_long_mandatory) {
     const std::string long_opt_name{"string"};
     const std::string opt_desc{"string description"};
 
-    test_parser_t tp;
+    option_test_parser_t tp;
     clapp::option::string_param_option_t opt{
-        tp, long_opt_name, opt_desc, test_parser_t::purpose_t::mandatory};
-    test_parser_t::long_options_map_t long_options{tp.get_long_options()};
-    test_parser_t::short_options_map_t short_options{tp.get_short_options()};
+        tp, long_opt_name, opt_desc,
+        option_test_parser_t::purpose_t::mandatory};
+    option_test_parser_t::long_options_map_t long_options{
+        tp.get_long_options()};
+    option_test_parser_t::short_options_map_t short_options{
+        tp.get_short_options()};
     ASSERT_THAT(tp.get_optional_option_descriptions().size(), testing::Eq(0));
-    test_parser_t::option_descriptions_vec_t descs{
+    option_test_parser_t::option_descriptions_vec_t descs{
         tp.get_mandatory_option_descriptions()};
-    test_parser_t::validate_func_vec_t validate_funcs{
+    option_test_parser_t::validate_func_vec_t validate_funcs{
         tp.get_validate_functions()};
     ASSERT_THAT(validate_funcs.size(), testing::Eq(1));
 
@@ -665,23 +949,25 @@ TEST(option, string_param_option_construct_long_mandatory) {
                 testing::StrEq(std::string{"--"} + long_opt_name + "=<arg>"));
     ASSERT_THAT(descs[0].description, testing::StrEq(opt_desc));
     ASSERT_THAT(descs[0].option_type,
-                testing::Eq(test_parser_t::option_type_t::scalar));
+                testing::Eq(option_test_parser_t::option_type_t::scalar));
 
     ASSERT_THAT(short_options.size(), testing::Eq(0));
     ASSERT_THAT(long_options.size(), testing::Eq(1));
     ASSERT_THAT(long_options.count(long_opt_name), testing::Eq(1));
-    test_parser_t::long_opt_variant_t long_opt_func_variant{
+    option_test_parser_t::long_opt_variant_t long_opt_func_variant{
         long_options.find(long_opt_name)->second};
-    ASSERT_THAT(std::holds_alternative<test_parser_t::long_opt_param_func_t>(
-                    long_opt_func_variant),
-                testing::Eq(true));
-    test_parser_t::long_opt_param_func_t long_opt_func{
-        std::get<test_parser_t::long_opt_param_func_t>(long_opt_func_variant)};
+    ASSERT_THAT(
+        std::holds_alternative<option_test_parser_t::long_opt_param_func_t>(
+            long_opt_func_variant),
+        testing::Eq(true));
+    option_test_parser_t::long_opt_param_func_t long_opt_func{
+        std::get<option_test_parser_t::long_opt_param_func_t>(
+            long_opt_func_variant)};
 
     ASSERT_THAT(static_cast<bool>(opt), testing::Eq(false));
     ASSERT_THAT(opt.given(), testing::Eq(false));
-    ASSERT_THROW(opt.value(), std::exception);
-    ASSERT_THROW(validate_funcs[0](), std::exception);
+    ASSERT_THROW(opt.value(), clapp::exception::value_undefined_t);
+    ASSERT_THROW(validate_funcs[0](), clapp::exception::option_exception_t);
 
     long_opt_func(long_opt_name, "string");
     ASSERT_THAT(static_cast<bool>(opt), testing::Eq(true));
@@ -698,21 +984,23 @@ TEST(option, int_param_option_construct_long_and_short_mandatory) {
     const int min_value{25};
     const int max_value{55};
 
-    test_parser_t tp;
+    option_test_parser_t tp;
     clapp::option::int64_param_option_t opt{
         tp,
         long_opt_name,
         short_opt_name,
         opt_desc,
-        test_parser_t::purpose_t::mandatory,
-        clapp::value::default_value_t{default_value},
-        clapp::value::min_max_value_t{min_value, max_value}};
-    test_parser_t::long_options_map_t long_options{tp.get_long_options()};
-    test_parser_t::short_options_map_t short_options{tp.get_short_options()};
+        option_test_parser_t::purpose_t::mandatory,
+        clapp::value::default_value_t<std::int64_t>{default_value},
+        clapp::value::min_max_value_t<std::int64_t>{min_value, max_value}};
+    option_test_parser_t::long_options_map_t long_options{
+        tp.get_long_options()};
+    option_test_parser_t::short_options_map_t short_options{
+        tp.get_short_options()};
     ASSERT_THAT(tp.get_optional_option_descriptions().size(), testing::Eq(0));
-    test_parser_t::option_descriptions_vec_t descs{
+    option_test_parser_t::option_descriptions_vec_t descs{
         tp.get_mandatory_option_descriptions()};
-    test_parser_t::validate_func_vec_t validate_funcs{
+    option_test_parser_t::validate_func_vec_t validate_funcs{
         tp.get_validate_functions()};
     ASSERT_THAT(validate_funcs.size(), testing::Eq(1));
 
@@ -722,48 +1010,52 @@ TEST(option, int_param_option_construct_long_and_short_mandatory) {
                                std::string{"|--"} + long_opt_name + "=<arg>"));
     ASSERT_THAT(
         descs[0].description,
-        testing::StrEq(opt_desc + " (" +
-                       clapp::value::default_value_t{default_value}
-                           .append_restriction_text() +
-                       ", " +
-                       clapp::value::min_max_value_t{min_value, max_value}
-                           .append_restriction_text() +
-                       ")"));
+        testing::StrEq(
+            opt_desc + " (" +
+            clapp::value::default_value_t<std::int64_t>{default_value}
+                .append_restriction_text() +
+            ", " +
+            clapp::value::min_max_value_t<std::int64_t>{min_value, max_value}
+                .append_restriction_text() +
+            ")"));
     ASSERT_THAT(descs[0].option_type,
-                testing::Eq(test_parser_t::option_type_t::scalar));
+                testing::Eq(option_test_parser_t::option_type_t::scalar));
 
     ASSERT_THAT(short_options.size(), testing::Eq(1));
     ASSERT_THAT(short_options.count(short_opt_name), testing::Eq(1));
     ASSERT_THAT(long_options.size(), testing::Eq(1));
     ASSERT_THAT(long_options.count(long_opt_name), testing::Eq(1));
 
-    test_parser_t::short_opt_variant_t short_opt_func_variant{
+    option_test_parser_t::short_opt_variant_t short_opt_func_variant{
         short_options.find(short_opt_name)->second};
-    ASSERT_THAT(std::holds_alternative<test_parser_t::short_opt_param_func_t>(
-                    short_opt_func_variant),
-                testing::Eq(true));
-    test_parser_t::short_opt_param_func_t short_opt_func{
-        std::get<test_parser_t::short_opt_param_func_t>(
+    ASSERT_THAT(
+        std::holds_alternative<option_test_parser_t::short_opt_param_func_t>(
+            short_opt_func_variant),
+        testing::Eq(true));
+    option_test_parser_t::short_opt_param_func_t short_opt_func{
+        std::get<option_test_parser_t::short_opt_param_func_t>(
             short_opt_func_variant)};
 
-    test_parser_t::long_opt_variant_t long_opt_func_variant{
+    option_test_parser_t::long_opt_variant_t long_opt_func_variant{
         long_options.find(long_opt_name)->second};
-    ASSERT_THAT(std::holds_alternative<test_parser_t::long_opt_param_func_t>(
-                    long_opt_func_variant),
-                testing::Eq(true));
-    test_parser_t::long_opt_param_func_t long_opt_func{
-        std::get<test_parser_t::long_opt_param_func_t>(long_opt_func_variant)};
+    ASSERT_THAT(
+        std::holds_alternative<option_test_parser_t::long_opt_param_func_t>(
+            long_opt_func_variant),
+        testing::Eq(true));
+    option_test_parser_t::long_opt_param_func_t long_opt_func{
+        std::get<option_test_parser_t::long_opt_param_func_t>(
+            long_opt_func_variant)};
 
     ASSERT_THAT(static_cast<bool>(opt), testing::Eq(true));
     ASSERT_THAT(opt.given(), testing::Eq(false));
     ASSERT_THAT(opt.value(), testing::Eq(default_value));
-    ASSERT_THROW(validate_funcs[0](), std::exception);
+    ASSERT_THROW(validate_funcs[0](), clapp::exception::option_exception_t);
 
     long_opt_func(long_opt_name, "123");
     ASSERT_THAT(static_cast<bool>(opt), testing::Eq(true));
     ASSERT_THAT(opt.given(), testing::Eq(true));
     ASSERT_THAT(opt.value(), testing::Eq(123));
-    ASSERT_THROW(validate_funcs[0](), std::exception);
+    ASSERT_THROW(validate_funcs[0](), clapp::exception::out_of_range_t);
 
     short_opt_func(short_opt_name, "0x2f");
     ASSERT_THAT(static_cast<bool>(opt), testing::Eq(true));
@@ -776,26 +1068,29 @@ TEST(option, vector_string_param_option_construct_with_default_throws) {
     const char short_opt_name{'s'};
     const std::string opt_desc{"string description"};
 
-    test_parser_t tp;
-    ASSERT_THROW((clapp::option::vector_string_param_option_t{
-                     tp, short_opt_name, opt_desc,
-                     clapp::value::default_value_t{"default string"}}),
-                 std::exception);
+    option_test_parser_t tp;
+    ASSERT_THROW(
+        (clapp::option::vector_string_param_option_t{
+            tp, short_opt_name, opt_desc,
+            clapp::value::default_value_t<std::string>{"default string"}}),
+        clapp::exception::option_param_exception_t);
 }
 
 TEST(option, vector_string_param_option_construct_short) {
     const char short_opt_name{'s'};
     const std::string opt_desc{"string description"};
 
-    test_parser_t tp;
+    option_test_parser_t tp;
     clapp::option::vector_string_param_option_t opt{tp, short_opt_name,
                                                     opt_desc};
-    test_parser_t::long_options_map_t long_options{tp.get_long_options()};
-    test_parser_t::short_options_map_t short_options{tp.get_short_options()};
+    option_test_parser_t::long_options_map_t long_options{
+        tp.get_long_options()};
+    option_test_parser_t::short_options_map_t short_options{
+        tp.get_short_options()};
     ASSERT_THAT(tp.get_mandatory_option_descriptions().size(), testing::Eq(0));
-    test_parser_t::option_descriptions_vec_t descs{
+    option_test_parser_t::option_descriptions_vec_t descs{
         tp.get_optional_option_descriptions()};
-    test_parser_t::validate_func_vec_t validate_funcs{
+    option_test_parser_t::validate_func_vec_t validate_funcs{
         tp.get_validate_functions()};
     ASSERT_THAT(validate_funcs.size(), testing::Eq(0));
 
@@ -805,18 +1100,19 @@ TEST(option, vector_string_param_option_construct_short) {
     ASSERT_THAT(descs[0].description,
                 testing::StrEq(opt_desc + " (vector option)"));
     ASSERT_THAT(descs[0].option_type,
-                testing::Eq(test_parser_t::option_type_t::vector));
+                testing::Eq(option_test_parser_t::option_type_t::vector));
 
     ASSERT_THAT(long_options.size(), testing::Eq(0));
     ASSERT_THAT(short_options.size(), testing::Eq(1));
     ASSERT_THAT(short_options.count(short_opt_name), testing::Eq(1));
-    test_parser_t::short_opt_variant_t short_opt_func_variant{
+    option_test_parser_t::short_opt_variant_t short_opt_func_variant{
         short_options.find(short_opt_name)->second};
-    ASSERT_THAT(std::holds_alternative<test_parser_t::short_opt_param_func_t>(
-                    short_opt_func_variant),
-                testing::Eq(true));
-    test_parser_t::short_opt_param_func_t short_opt_func{
-        std::get<test_parser_t::short_opt_param_func_t>(
+    ASSERT_THAT(
+        std::holds_alternative<option_test_parser_t::short_opt_param_func_t>(
+            short_opt_func_variant),
+        testing::Eq(true));
+    option_test_parser_t::short_opt_param_func_t short_opt_func{
+        std::get<option_test_parser_t::short_opt_param_func_t>(
             short_opt_func_variant)};
 
     ASSERT_THAT(static_cast<bool>(opt), testing::Eq(false));
@@ -843,15 +1139,18 @@ TEST(option, vector_string_param_option_construct_long_mandatory) {
     const std::string long_opt_name{"string"};
     const std::string opt_desc{"string description"};
 
-    test_parser_t tp;
+    option_test_parser_t tp;
     clapp::option::vector_string_param_option_t opt{
-        tp, long_opt_name, opt_desc, test_parser_t::purpose_t::mandatory};
-    test_parser_t::long_options_map_t long_options{tp.get_long_options()};
-    test_parser_t::short_options_map_t short_options{tp.get_short_options()};
+        tp, long_opt_name, opt_desc,
+        option_test_parser_t::purpose_t::mandatory};
+    option_test_parser_t::long_options_map_t long_options{
+        tp.get_long_options()};
+    option_test_parser_t::short_options_map_t short_options{
+        tp.get_short_options()};
     ASSERT_THAT(tp.get_optional_option_descriptions().size(), testing::Eq(0));
-    test_parser_t::option_descriptions_vec_t descs{
+    option_test_parser_t::option_descriptions_vec_t descs{
         tp.get_mandatory_option_descriptions()};
-    test_parser_t::validate_func_vec_t validate_funcs{
+    option_test_parser_t::validate_func_vec_t validate_funcs{
         tp.get_validate_functions()};
     ASSERT_THAT(validate_funcs.size(), testing::Eq(1));
 
@@ -861,23 +1160,26 @@ TEST(option, vector_string_param_option_construct_long_mandatory) {
     ASSERT_THAT(descs[0].description,
                 testing::StrEq(opt_desc + " (vector option)"));
     ASSERT_THAT(descs[0].option_type,
-                testing::Eq(test_parser_t::option_type_t::vector));
+                testing::Eq(option_test_parser_t::option_type_t::vector));
 
     ASSERT_THAT(short_options.size(), testing::Eq(0));
     ASSERT_THAT(long_options.size(), testing::Eq(1));
     ASSERT_THAT(long_options.count(long_opt_name), testing::Eq(1));
-    test_parser_t::long_opt_variant_t long_opt_func_variant{
+    option_test_parser_t::long_opt_variant_t long_opt_func_variant{
         long_options.find(long_opt_name)->second};
-    ASSERT_THAT(std::holds_alternative<test_parser_t::long_opt_param_func_t>(
-                    long_opt_func_variant),
-                testing::Eq(true));
-    test_parser_t::long_opt_param_func_t long_opt_func{
-        std::get<test_parser_t::long_opt_param_func_t>(long_opt_func_variant)};
+    ASSERT_THAT(
+        std::holds_alternative<option_test_parser_t::long_opt_param_func_t>(
+            long_opt_func_variant),
+        testing::Eq(true));
+    option_test_parser_t::long_opt_param_func_t long_opt_func{
+        std::get<option_test_parser_t::long_opt_param_func_t>(
+            long_opt_func_variant)};
 
     ASSERT_THAT(static_cast<bool>(opt), testing::Eq(false));
     ASSERT_THAT(opt.given(), testing::Eq(false));
     ASSERT_THAT(opt.value(), testing::Eq((std::vector<std::string>{})));
-    ASSERT_THROW(validate_funcs[0](), std::exception);
+    ASSERT_THROW(validate_funcs[0](),
+                 clapp::exception::option_param_exception_t);
 
     long_opt_func(long_opt_name, "string");
     ASSERT_THAT(static_cast<bool>(opt), testing::Eq(true));
@@ -904,21 +1206,63 @@ TEST(option, vector_string_param_option_construct_long_mandatory) {
     ASSERT_THAT(vec[2], testing::StrEq("string3"));
 }
 
+TEST(option, vector_string_param_option_found_func_short) {
+    const std::string long_opt_name{"long"};
+    const std::string opt_desc{"description"};
+
+    std::stringstream ss;
+    option_test_parser_t tp;
+    clapp::option::vector_string_param_option_t opt{
+        tp, long_opt_name, opt_desc, option_test_parser_t::purpose_t::mandatory,
+        clapp::value::found_func_t{
+            [&ss]() { ss << "this is another test for params"; }}};
+    option_test_parser_t::short_options_map_t short_options{
+        tp.get_short_options()};
+    option_test_parser_t::long_options_map_t long_options{
+        tp.get_long_options()};
+    ASSERT_THAT(short_options.size(), testing::Eq(0));
+    ASSERT_THAT(long_options.size(), testing::Eq(1));
+    ASSERT_THAT(long_options.count(long_opt_name), testing::Eq(1));
+    option_test_parser_t::long_opt_variant_t long_opt_func_variant{
+        long_options.find(long_opt_name)->second};
+    ASSERT_THAT(
+        std::holds_alternative<option_test_parser_t::long_opt_param_func_t>(
+            long_opt_func_variant),
+        testing::Eq(true));
+    option_test_parser_t::long_opt_param_func_t long_opt_func{
+        std::get<option_test_parser_t::long_opt_param_func_t>(
+            long_opt_func_variant)};
+
+    ASSERT_THAT(static_cast<bool>(opt), testing::Eq(false));
+    ASSERT_THAT(opt.given(), testing::Eq(false));
+    ASSERT_THAT(ss.str(), testing::StrEq(""));
+    ASSERT_THAT(opt.value().size(), testing::Eq(0));
+    long_opt_func(long_opt_name, "param");
+
+    ASSERT_THAT(ss.str(), testing::StrEq("this is another test for params"));
+    ASSERT_THAT(static_cast<bool>(opt), testing::Eq(true));
+    ASSERT_THAT(opt.given(), testing::Eq(true));
+    ASSERT_THAT(opt.value().size(), testing::Eq(1));
+    ASSERT_THAT(opt.value()[0], testing::StrEq("param"));
+}
+
 TEST(option, vector_string_param_option_construct_long_and_short_min_max) {
     const char short_opt_name{'s'};
     const std::string long_opt_name{"long"};
     const std::string opt_desc{"int description"};
-    clapp::value::min_max_value_t min_max{10, 20};
+    clapp::value::min_max_value_t<std::uint8_t> min_max{10, 20};
 
-    test_parser_t tp;
+    option_test_parser_t tp;
     clapp::option::vector_uint8_param_option_t opt{
         tp, long_opt_name, short_opt_name, opt_desc, min_max};
-    test_parser_t::long_options_map_t long_options{tp.get_long_options()};
-    test_parser_t::short_options_map_t short_options{tp.get_short_options()};
+    option_test_parser_t::long_options_map_t long_options{
+        tp.get_long_options()};
+    option_test_parser_t::short_options_map_t short_options{
+        tp.get_short_options()};
     ASSERT_THAT(tp.get_mandatory_option_descriptions().size(), testing::Eq(0));
-    test_parser_t::option_descriptions_vec_t descs{
+    option_test_parser_t::option_descriptions_vec_t descs{
         tp.get_optional_option_descriptions()};
-    test_parser_t::validate_func_vec_t validate_funcs{
+    option_test_parser_t::validate_func_vec_t validate_funcs{
         tp.get_validate_functions()};
     ASSERT_THAT(validate_funcs.size(), testing::Eq(1));
 
@@ -930,27 +1274,30 @@ TEST(option, vector_string_param_option_construct_long_and_short_min_max) {
                 testing::StrEq(opt_desc + " (vector option, " +
                                min_max.append_restriction_text() + ")"));
     ASSERT_THAT(descs[0].option_type,
-                testing::Eq(test_parser_t::option_type_t::vector));
+                testing::Eq(option_test_parser_t::option_type_t::vector));
 
     ASSERT_THAT(long_options.size(), testing::Eq(1));
     ASSERT_THAT(long_options.count(long_opt_name), testing::Eq(1));
-    test_parser_t::long_opt_variant_t long_opt_func_variant{
+    option_test_parser_t::long_opt_variant_t long_opt_func_variant{
         long_options.find(long_opt_name)->second};
-    ASSERT_THAT(std::holds_alternative<test_parser_t::long_opt_param_func_t>(
-                    long_opt_func_variant),
-                testing::Eq(true));
-    test_parser_t::long_opt_param_func_t long_opt_func{
-        std::get<test_parser_t::long_opt_param_func_t>(long_opt_func_variant)};
+    ASSERT_THAT(
+        std::holds_alternative<option_test_parser_t::long_opt_param_func_t>(
+            long_opt_func_variant),
+        testing::Eq(true));
+    option_test_parser_t::long_opt_param_func_t long_opt_func{
+        std::get<option_test_parser_t::long_opt_param_func_t>(
+            long_opt_func_variant)};
 
     ASSERT_THAT(short_options.size(), testing::Eq(1));
     ASSERT_THAT(short_options.count(short_opt_name), testing::Eq(1));
-    test_parser_t::short_opt_variant_t short_opt_func_variant{
+    option_test_parser_t::short_opt_variant_t short_opt_func_variant{
         short_options.find(short_opt_name)->second};
-    ASSERT_THAT(std::holds_alternative<test_parser_t::short_opt_param_func_t>(
-                    short_opt_func_variant),
-                testing::Eq(true));
-    test_parser_t::short_opt_param_func_t short_opt_func{
-        std::get<test_parser_t::short_opt_param_func_t>(
+    ASSERT_THAT(
+        std::holds_alternative<option_test_parser_t::short_opt_param_func_t>(
+            short_opt_func_variant),
+        testing::Eq(true));
+    option_test_parser_t::short_opt_param_func_t short_opt_func{
+        std::get<option_test_parser_t::short_opt_param_func_t>(
             short_opt_func_variant)};
 
     ASSERT_THAT(static_cast<bool>(opt), testing::Eq(false));
@@ -983,7 +1330,7 @@ TEST(option, vector_string_param_option_construct_long_and_short_min_max) {
     ASSERT_THAT(vec[0], testing::Eq(0x12));
     ASSERT_THAT(vec[1], testing::Eq(12));
     ASSERT_THAT(vec[2], testing::Eq(1));
-    ASSERT_THROW(validate_funcs[0](), std::exception);
+    ASSERT_THROW(validate_funcs[0](), clapp::exception::out_of_range_t);
 }
 
 TEST(option, invalid_long_option_construct) {
@@ -995,39 +1342,39 @@ TEST(option, invalid_long_option_construct) {
     const std::string desc{"desc1"};
     const char short_opt_name{'a'};
 
-    test_parser_t tp;
+    option_test_parser_t tp;
     ASSERT_THROW((clapp::option::bool_option_t{tp, long_opt_name1, desc}),
-                 std::exception);
+                 clapp::exception::option_exception_t);
     ASSERT_THROW((clapp::option::bool_option_t{tp, long_opt_name1,
                                                short_opt_name, desc}),
-                 std::exception);
+                 clapp::exception::option_exception_t);
 
     ASSERT_THROW((clapp::option::count_option_t{tp, long_opt_name2, desc}),
-                 std::exception);
+                 clapp::exception::option_exception_t);
     ASSERT_THROW((clapp::option::count_option_t{tp, long_opt_name2,
                                                 short_opt_name, desc}),
-                 std::exception);
+                 clapp::exception::option_exception_t);
 
     ASSERT_THROW(
         (clapp::option::string_param_option_t{tp, long_opt_name3, desc}),
-        std::exception);
+        clapp::exception::option_exception_t);
     ASSERT_THROW((clapp::option::string_param_option_t{tp, long_opt_name3,
                                                        short_opt_name, desc}),
-                 std::exception);
+                 clapp::exception::option_exception_t);
 
     ASSERT_THROW(
         (clapp::option::vector_string_param_option_t{tp, long_opt_name4, desc}),
-        std::exception);
+        clapp::exception::option_exception_t);
     ASSERT_THROW((clapp::option::vector_string_param_option_t{
                      tp, long_opt_name4, short_opt_name, desc}),
-                 std::exception);
+                 clapp::exception::option_exception_t);
 
     ASSERT_THROW(
         (clapp::option::int32_param_option_t{tp, long_opt_name5, desc}),
-        std::exception);
+        clapp::exception::option_exception_t);
     ASSERT_THROW((clapp::option::int32_param_option_t{tp, long_opt_name5,
                                                       short_opt_name, desc}),
-                 std::exception);
+                 clapp::exception::option_exception_t);
 }
 
 TEST(option, invalid_short_option_construct) {
@@ -1039,39 +1386,39 @@ TEST(option, invalid_short_option_construct) {
     const std::string desc{"desc1"};
     const std::string long_opt_name{'a'};
 
-    test_parser_t tp;
+    option_test_parser_t tp;
     ASSERT_THROW((clapp::option::bool_option_t{tp, short_opt_name1, desc}),
-                 std::exception);
+                 clapp::exception::option_exception_t);
     ASSERT_THROW((clapp::option::bool_option_t{tp, long_opt_name,
                                                short_opt_name1, desc}),
-                 std::exception);
+                 clapp::exception::option_exception_t);
 
     ASSERT_THROW((clapp::option::count_option_t{tp, short_opt_name2, desc}),
-                 std::exception);
+                 clapp::exception::option_exception_t);
     ASSERT_THROW((clapp::option::count_option_t{tp, long_opt_name,
                                                 short_opt_name2, desc}),
-                 std::exception);
+                 clapp::exception::option_exception_t);
 
     ASSERT_THROW(
         (clapp::option::string_param_option_t{tp, short_opt_name3, desc}),
-        std::exception);
+        clapp::exception::option_exception_t);
     ASSERT_THROW((clapp::option::string_param_option_t{tp, long_opt_name,
                                                        short_opt_name3, desc}),
-                 std::exception);
+                 clapp::exception::option_exception_t);
 
     ASSERT_THROW((clapp::option::vector_string_param_option_t{
                      tp, short_opt_name4, desc}),
-                 std::exception);
+                 clapp::exception::option_exception_t);
     ASSERT_THROW((clapp::option::vector_string_param_option_t{
                      tp, long_opt_name, short_opt_name4, desc}),
-                 std::exception);
+                 clapp::exception::option_exception_t);
 
     ASSERT_THROW(
         (clapp::option::int32_param_option_t{tp, short_opt_name5, desc}),
-        std::exception);
+        clapp::exception::option_exception_t);
     ASSERT_THROW((clapp::option::int32_param_option_t{tp, long_opt_name,
                                                       short_opt_name5, desc}),
-                 std::exception);
+                 clapp::exception::option_exception_t);
 }
 
 struct mock_t {
@@ -1089,29 +1436,29 @@ TEST(option, gen_opt_validate_func) {
 
     {
         mock_t mock;
-        std::optional<test_parser_t::validate_func_t> validate_func{
+        std::optional<option_test_parser_t::validate_func_t> validate_func{
             clapp::option::gen_opt_validate_func<std::int32_t,
                                                  int32_value_func_t>(
                 std::nullopt, std::nullopt,
                 [&mock]() { return mock.given_func(); },
                 std::vector<int32_validate_func_t>{}, "option string",
-                test_parser_t::purpose_t::mandatory)};
+                option_test_parser_t::purpose_t::mandatory)};
         ASSERT_THAT(validate_func.has_value(), testing::Eq(true));
         EXPECT_CALL(mock, given_func())
             .Times(1)
             .WillOnce(testing::Return(false));
-        ASSERT_THROW((*validate_func)(), std::runtime_error);
+        ASSERT_THROW((*validate_func)(), clapp::exception::option_exception_t);
     }
 
     {
         mock_t mock;
-        std::optional<test_parser_t::validate_func_t> validate_func{
+        std::optional<option_test_parser_t::validate_func_t> validate_func{
             clapp::option::gen_opt_validate_func<std::int32_t,
                                                  int32_value_func_t>(
                 std::nullopt, [&mock]() { return mock.has_value_func(); },
                 [&mock]() { return mock.given_func(); },
                 std::vector<int32_validate_func_t>{}, "option string",
-                test_parser_t::purpose_t::mandatory)};
+                option_test_parser_t::purpose_t::mandatory)};
         ASSERT_THAT(validate_func.has_value(), testing::Eq(true));
         EXPECT_CALL(mock, given_func())
             .Times(1)
@@ -1121,7 +1468,7 @@ TEST(option, gen_opt_validate_func) {
 
     {
         mock_t mock;
-        std::optional<test_parser_t::validate_func_t> validate_func{
+        std::optional<option_test_parser_t::validate_func_t> validate_func{
             clapp::option::gen_opt_validate_func<std::int32_t,
                                                  int32_value_func_t>(
                 [&mock]() { return mock.value_func(); },
@@ -1132,7 +1479,7 @@ TEST(option, gen_opt_validate_func) {
                             const std::string& option_string) {
                         mock.validate_func(value, option_string);
                     }},
-                "option string", test_parser_t::purpose_t::mandatory)};
+                "option string", option_test_parser_t::purpose_t::mandatory)};
         ASSERT_THAT(validate_func.has_value(), testing::Eq(true));
         EXPECT_CALL(mock, value_func()).Times(1).WillOnce(testing::Return(10));
         EXPECT_CALL(mock, has_value_func())
@@ -1148,7 +1495,7 @@ TEST(option, gen_opt_validate_func) {
 
     {
         mock_t mock;
-        std::optional<test_parser_t::validate_func_t> validate_func{
+        std::optional<option_test_parser_t::validate_func_t> validate_func{
             clapp::option::gen_opt_validate_func<std::int32_t,
                                                  int32_value_func_t>(
                 std::nullopt, std::nullopt,
@@ -1158,11 +1505,12 @@ TEST(option, gen_opt_validate_func) {
                             const std::string& option_string) {
                         mock.validate_func(value, option_string);
                     }},
-                "option string", test_parser_t::purpose_t::mandatory)};
+                "option string", option_test_parser_t::purpose_t::mandatory)};
         ASSERT_THAT(validate_func.has_value(), testing::Eq(true));
         EXPECT_CALL(mock, given_func())
             .Times(1)
             .WillOnce(testing::Return(true));
-        ASSERT_THROW((*validate_func)(), std::exception);
+        ASSERT_THROW((*validate_func)(),
+                     clapp::exception::option_param_exception_t);
     }
 }
diff --git a/tests/parser.cpp b/tests/parser.cpp
index 2cba267fad2ce0c87e693e89bf52045c2e7ddbf1..24bed075f888e25498a49bae851b6fd7ec2bfabd 100644
--- a/tests/parser.cpp
+++ b/tests/parser.cpp
@@ -1,93 +1,469 @@
+#include <clapp/argument.h>
 #include <clapp/main_parser.h>
 #include <clapp/option.h>
 #include <clapp/parser.h>
+#include <clapp/sub_parser.h>
 #include <gmock/gmock.h>
 
 template <class T, size_t N>
-constexpr clapp::parser::arg_t make_arg_t(T (&arg)[N]) {
-    return clapp::parser::arg_t{arg, N - 1};
+inline clapp::parser::arg_t parser_make_arg_t(T (&arg)[N]) {
+    return clapp::parser::arg_t{static_cast<const char* const*>(arg), N - 1};
 }
 
 class empty_basic_parser_t : public clapp::basic_parser_t {
    public:
     using clapp::basic_parser_t::basic_parser_t;
-    ~empty_basic_parser_t();
+    ~empty_basic_parser_t() override;
 };
 
 empty_basic_parser_t::~empty_basic_parser_t() = default;
 
-class param_option_parser_t : public clapp::basic_parser_t {
+class simple_test_parser_t : public clapp::basic_parser_t {
    public:
     using clapp::basic_parser_t::basic_parser_t;
-    ~param_option_parser_t();
+    ~simple_test_parser_t() override;
 
     clapp::option::bool_option_t bool_option{*this, "bool", 'b',
                                              "Bool option."};
+
+    clapp::option::int32_param_option_t int_option{
+        *this, "int", 'i', "Int option.",
+        clapp::value::min_max_value_t<std::int32_t>{10, 200}};
+
+    clapp::argument::string_argument_t string_arg{*this, "arg-name",
+                                                  "Arg desc"};
+
+    clapp::argument::variadic_string_argument_t variadic_string_arg{
+        *this, "variadic-arg-name", "Variadic arg desc",
+        clapp::basic_parser_t::purpose_t::optional};
+};
+
+simple_test_parser_t::~simple_test_parser_t() = default;
+
+class simple_test_parser2_t : public clapp::basic_parser_t {
+   public:
+    using clapp::basic_parser_t::basic_parser_t;
+    ~simple_test_parser2_t() override;
+
+    clapp::option::bool_option_t count_option{
+        *this, "count", 'c', "Count option.",
+        clapp::basic_parser_t::purpose_t::mandatory};
+
+    clapp::argument::string_argument_t string_arg{
+        *this, "arg-name", "Arg desc",
+        clapp::basic_parser_t::purpose_t::optional};
+};
+
+simple_test_parser2_t::~simple_test_parser2_t() = default;
+
+class simple_test_parser3_t : public clapp::basic_parser_t {
+   public:
+    using clapp::basic_parser_t::basic_parser_t;
+    ~simple_test_parser3_t() override;
+
+    clapp::option::vector_int64_param_option_t int_option{
+        *this,
+        "int",
+        'i',
+        "Int option.",
+        clapp::value::min_max_value_t<std::int64_t>{10, 200},
+        clapp::basic_parser_t::purpose_t::mandatory};
+
+    clapp::option::vector_string_param_option_t string_option{
+        *this, "str", 's', "String option.",
+        clapp::basic_parser_t::purpose_t::optional};
+
+    clapp::argument::variadic_string_argument_t variadic_string_arg{
+        *this, "variadic-arg-name", "Variadic arg desc",
+        clapp::basic_parser_t::purpose_t::mandatory};
+};
+
+simple_test_parser3_t::~simple_test_parser3_t() = default;
+
+class sub_parser_container_t : public clapp::basic_parser_t {
+   public:
+    using clapp::basic_parser_t::basic_parser_t;
+    ~sub_parser_container_t() override;
+
+    clapp::option::bool_option_t bool_option{*this, "bool", 'b',
+                                             "Bool option."};
+
+    clapp::option::bool_option_t bool_option2{*this, "second", '2',
+                                              "Second bool option."};
+
+    clapp::argument::string_argument_t string_arg{*this, "arg-name",
+                                                  "Arg desc"};
+
+    class simple_sub_parser_t : public clapp::parser::basic_sub_parser_t {
+       public:
+        ~simple_sub_parser_t() override;
+        clapp::option::bool_option_t bool_option{*this, "bool", 'b',
+                                                 "Bool option."};
+
+        using clapp::parser::basic_sub_parser_t::basic_sub_parser_t;
+        clapp::option::string_param_option_t string_option{*this, "string", 's',
+                                                           "String option."};
+
+        clapp::argument::string_argument_t string_arg{
+            *this, "sub-arg-name", "Sub arg desc",
+            clapp::basic_parser_t::purpose_t::optional};
+    };
+
+    simple_sub_parser_t sub_parser{*this, "sub-parser", "Sub parser desc"};
 };
 
-param_option_parser_t::~param_option_parser_t() = default;
+sub_parser_container_t::simple_sub_parser_t::~simple_sub_parser_t() = default;
+sub_parser_container_t::~sub_parser_container_t() = default;
 
 TEST(parser, construct_empty_basic_parser) { empty_basic_parser_t ebp; }
 
 TEST(parser, construct_empty_basic_parser_and_parse_empty_arguments) {
     constexpr const char* const argv[]{nullptr};
-    const clapp::parser::arg_t arg{make_arg_t(argv)};
+    const clapp::parser::arg_t arg{parser_make_arg_t(argv)};
+    empty_basic_parser_t ebp;
+    ASSERT_NO_THROW(ebp.parse(arg.cbegin(), arg.cend()));
+}
+
+TEST(parser,
+     construct_empty_basic_parser_and_parse_and_validate_empty_arguments) {
+    constexpr const char* const argv[]{nullptr};
+    const clapp::parser::arg_t arg{parser_make_arg_t(argv)};
     empty_basic_parser_t ebp;
-    ebp.parse(arg.cbegin(), arg.cend());
+    ASSERT_NO_THROW(ebp.parse(arg.cbegin(), arg.cend()));
+    ASSERT_NO_THROW(ebp.validate());
+}
+
+TEST(parser, construct_empty_basic_parser_and_gen_help_prefix) {
+    empty_basic_parser_t ebp;
+    ASSERT_THAT(ebp.gen_help_prefix(), testing::StrEq("Usage: \n"));
+}
+
+TEST(parser, construct_empty_basic_parser_and_gen_help_message) {
+    empty_basic_parser_t ebp;
+    ASSERT_THAT(ebp.gen_help_msg(), testing::StrEq("\n"));
+}
+
+TEST(parser, gen_func_print_help_and_exit) {
+    empty_basic_parser_t ebp;
+    clapp::value::found_func_t ff{
+        ebp.gen_func_print_help_and_exit(EXIT_SUCCESS)};
+    testing::internal::CaptureStdout();
+    ASSERT_EXIT(ff.found(), ::testing::ExitedWithCode(0), "");
+    std::string output = testing::internal::GetCapturedStdout();
+    ASSERT_THAT(output, testing::StrEq("Usage: \n \n"));
+}
+
+TEST(parser, construct_simple_test_parser_and_gen_help_message) {
+    simple_test_parser_t stp;
+    ASSERT_THAT(
+        stp.gen_help_msg(),
+        testing::StrEq(
+            "[-b|--bool] [-i|--int=<arg>] <arg-name> [<variadic-arg-name>...] "
+            "\n\nMandatory Arguments:\n  arg-name          Arg desc\n\n"
+            "Optional Arguments:\n  variadic-arg-name Variadic arg desc "
+            "(variadic argument)\n\n"
+            "Optional Options:\n  -b|--bool         Bool option.\n"
+            "  -i|--int=<arg>    Int option. (constraint: [10,200])\n"));
+}
+
+TEST(parser, construct_simple_test_parser2_and_gen_help_message) {
+    simple_test_parser2_t stp;
+    ASSERT_THAT(
+        stp.gen_help_msg(),
+        testing::StrEq("-c|--count [<arg-name>] \n\n"
+                       "Optional Arguments:\n  arg-name   Arg desc\n\n"
+                       "Mandatory Options:\n  -c|--count Count option.\n"));
+}
+
+TEST(parser, construct_simple_test_parser3_and_gen_help_message) {
+    simple_test_parser3_t stp;
+    ASSERT_THAT(
+        stp.gen_help_msg(),
+        testing::StrEq(
+            "-i|--int=<arg>... [-s|--str=<arg>...] <variadic-arg-name>... \n\n"
+            "Mandatory Arguments:\n  variadic-arg-name Variadic arg desc "
+            "(variadic argument)\n\n"
+            "Mandatory Options:\n  -i|--int=<arg>    Int option. (vector "
+            "option, constraint: [10,200])\n\n"
+            "Optional Options:\n  -s|--str=<arg>    String option. (vector "
+            "option)\n"));
+}
+
+TEST(parser, construct_sub_parser_container_and_gen_help_message) {
+    sub_parser_container_t spc;
+    ASSERT_THAT(spc.gen_help_msg(),
+                testing::StrEq(
+                    "[-b|--bool] [-2|--second] <arg-name> <sub-parser> "
+                    "[sub-parser args/opts...] \n\n"
+                    "Mandatory Arguments:\n  arg-name    Arg desc\n\n"
+                    "Available sub-parsers:\n  sub-parser  Sub parser desc\n\n"
+                    "Optional Options:\n  -b|--bool   Bool option.\n  "
+                    "-2|--second Second bool option.\n"));
+}
+
+TEST(parser, construct_sub_parser_container_and_gen_sub_parser_help_message) {
+    sub_parser_container_t spc;
+    ASSERT_THAT(spc.sub_parser.gen_help_msg(),
+                testing::StrEq(
+                    "[-b|--bool] [-s|--string=<arg>] [<sub-arg-name>] \n\n"
+                    "Optional Arguments:\n  sub-arg-name      Sub arg desc\n\n"
+                    "Optional Options:\n  -b|--bool         Bool option.\n"
+                    "  -s|--string=<arg> String option.\n"));
 }
 
 TEST(parser, construct_empty_basic_parser_and_parse_unknown_arguments_throws) {
     constexpr const char* const argv[]{"unknown-argument", nullptr};
-    const clapp::parser::arg_t arg{make_arg_t(argv)};
+    const clapp::parser::arg_t arg{parser_make_arg_t(argv)};
     empty_basic_parser_t ebp;
-    ASSERT_THROW(ebp.parse(arg.cbegin(), arg.cend()), std::exception);
+    ASSERT_THROW(ebp.parse(arg.cbegin(), arg.cend()),
+                 clapp::exception::clapp_exception_t);
 }
 
 TEST(parser,
      construct_empty_basic_parser_and_parse_unknown_long_option_throws) {
     constexpr const char* const argv[]{"--long-option", nullptr};
-    const clapp::parser::arg_t arg{make_arg_t(argv)};
+    const clapp::parser::arg_t arg{parser_make_arg_t(argv)};
     empty_basic_parser_t ebp;
-    ASSERT_THROW(ebp.parse(arg.cbegin(), arg.cend()), std::exception);
+    ASSERT_THROW(ebp.parse(arg.cbegin(), arg.cend()),
+                 clapp::exception::option_exception_t);
 }
 
 TEST(parser,
      construct_empty_basic_parser_and_parse_unknown_short_option_throws) {
     constexpr const char* const argv[]{"-s", nullptr};
-    const clapp::parser::arg_t arg{make_arg_t(argv)};
+    const clapp::parser::arg_t arg{parser_make_arg_t(argv)};
     empty_basic_parser_t ebp;
-    ASSERT_THROW(ebp.parse(arg.cbegin(), arg.cend()), std::exception);
+    ASSERT_THROW(ebp.parse(arg.cbegin(), arg.cend()),
+                 clapp::exception::option_exception_t);
 }
 
 TEST(
     parser,
-    construct_param_option_parser_and_parse_short_option_without_params_equal) {
+    construct_simple_test_parser_and_parse_long_bool_option_with_param_throws) {
     constexpr const char* const argv[]{"--bool=param", nullptr};
-    const clapp::parser::arg_t arg{make_arg_t(argv)};
-    param_option_parser_t ebp;
-    ASSERT_THROW(ebp.parse(arg.cbegin(), arg.cend()), std::exception);
+    const clapp::parser::arg_t arg{parser_make_arg_t(argv)};
+    simple_test_parser_t ebp;
+    ASSERT_THROW(ebp.parse(arg.cbegin(), arg.cend()),
+                 clapp::exception::option_param_exception_t);
 }
 
 TEST(parser,
-     construct_param_option_parser_and_parse_short_option_without_params) {
-    constexpr const char* const argv[]{"--bool", "param", nullptr};
-    const clapp::parser::arg_t arg{make_arg_t(argv)};
-    param_option_parser_t ebp;
-    ASSERT_THROW(ebp.parse(arg.cbegin(), arg.cend()), std::exception);
+     construct_simple_test_parser_and_parse_short_option_with_param_throws) {
+    constexpr const char* const argv[]{"-b=param", nullptr};
+    const clapp::parser::arg_t arg{parser_make_arg_t(argv)};
+    simple_test_parser_t ebp;
+    ASSERT_THROW(ebp.parse(arg.cbegin(), arg.cend()),
+                 clapp::exception::option_param_exception_t);
+}
+
+TEST(
+    parser,
+    construct_simple_test_parser_and_parse_long_int_option_without_param_throws) {
+    constexpr const char* const argv[]{"--int", nullptr};
+    const clapp::parser::arg_t arg{parser_make_arg_t(argv)};
+    simple_test_parser_t ebp;
+    ASSERT_THROW(ebp.parse(arg.cbegin(), arg.cend()),
+                 clapp::exception::option_param_exception_t);
+}
+
+TEST(
+    parser,
+    construct_simple_test_parser_and_parse_short_int_option_without_param_throws) {
+    constexpr const char* const argv[]{"-i", nullptr};
+    const clapp::parser::arg_t arg{parser_make_arg_t(argv)};
+    simple_test_parser_t ebp;
+    ASSERT_THROW(ebp.parse(arg.cbegin(), arg.cend()),
+                 clapp::exception::option_param_exception_t);
+}
+
+TEST(
+    parser,
+    construct_simple_test_parser_and_parse_short_int_option_without_param_throws2) {
+    constexpr const char* const argv[]{"-ib", nullptr};
+    const clapp::parser::arg_t arg{parser_make_arg_t(argv)};
+    simple_test_parser_t ebp;
+    ASSERT_THROW(ebp.parse(arg.cbegin(), arg.cend()),
+                 clapp::exception::option_param_exception_t);
+}
+
+TEST(
+    parser,
+    construct_simple_test_parser_parse_without_argument_and_validate_recursive_throws) {
+    constexpr const char* const argv[]{nullptr};
+    const clapp::parser::arg_t arg{parser_make_arg_t(argv)};
+    simple_test_parser_t ebp;
+    ASSERT_NO_THROW(ebp.parse(arg.cbegin(), arg.cend()));
+    ASSERT_THAT(ebp.get_num_processed_arguments(), testing::Eq(0));
+    ASSERT_THROW(ebp.validate_recursive(),
+                 clapp::exception::argument_exception_t);
 }
 
 TEST(parser,
-     construct_param_option_parser_and_parse_long_option_without_params_equal) {
-    constexpr const char* const argv[]{"-b=param", nullptr};
-    const clapp::parser::arg_t arg{make_arg_t(argv)};
-    param_option_parser_t ebp;
-    ASSERT_THROW(ebp.parse(arg.cbegin(), arg.cend()), std::exception);
+     construct_simple_test_parser_parse_argument_and_validate_recursive) {
+    constexpr const char* const argv[]{"argument", nullptr};
+    const clapp::parser::arg_t arg{parser_make_arg_t(argv)};
+    simple_test_parser_t ebp;
+    ASSERT_NO_THROW(ebp.parse(arg.cbegin(), arg.cend()));
+    ASSERT_THAT(static_cast<bool>(ebp.bool_option), testing::Eq(false));
+    ASSERT_THAT(static_cast<bool>(ebp.int_option), testing::Eq(false));
+    ASSERT_THAT(static_cast<bool>(ebp.string_arg), testing::Eq(true));
+    ASSERT_THAT(ebp.string_arg.value(), testing::Eq("argument"));
+    ASSERT_THAT(static_cast<bool>(ebp.variadic_string_arg), testing::Eq(false));
+    ASSERT_THAT(ebp.get_num_processed_arguments(), testing::Eq(1));
+    ASSERT_NO_THROW(ebp.validate_recursive());
+}
+
+TEST(
+    parser,
+    construct_simple_test_parser_parse_argument_and_short_option_without_params_and_validate_recursive) {
+    constexpr const char* const argv[]{"-b", "arg", nullptr};
+    const clapp::parser::arg_t arg{parser_make_arg_t(argv)};
+    simple_test_parser_t ebp;
+    ASSERT_NO_THROW(ebp.parse(arg.cbegin(), arg.cend()));
+    ASSERT_THAT(static_cast<bool>(ebp.bool_option), testing::Eq(true));
+    ASSERT_THAT(static_cast<bool>(ebp.int_option), testing::Eq(false));
+    ASSERT_THAT(static_cast<bool>(ebp.string_arg), testing::Eq(true));
+    ASSERT_THAT(ebp.string_arg.value(), testing::StrEq("arg"));
+    ASSERT_THAT(static_cast<bool>(ebp.variadic_string_arg), testing::Eq(false));
+    ASSERT_THAT(ebp.get_num_processed_arguments(), testing::Eq(1));
+    ASSERT_NO_THROW(ebp.validate_recursive());
+}
+
+TEST(
+    parser,
+    construct_simple_test_parser_parse_argument_and_long_option_with_param_and_validate) {
+    constexpr const char* const argv[]{"--int", "123", "arg", nullptr};
+    const clapp::parser::arg_t arg{parser_make_arg_t(argv)};
+    simple_test_parser_t ebp;
+    ASSERT_NO_THROW(ebp.parse(arg.cbegin(), arg.cend()));
+    ASSERT_THAT(static_cast<bool>(ebp.bool_option), testing::Eq(false));
+    ASSERT_THAT(static_cast<bool>(ebp.int_option), testing::Eq(true));
+    ASSERT_THAT(ebp.int_option.value(), testing::Eq(123));
+    ASSERT_THAT(static_cast<bool>(ebp.string_arg), testing::Eq(true));
+    ASSERT_THAT(ebp.string_arg.value(), testing::StrEq("arg"));
+    ASSERT_THAT(static_cast<bool>(ebp.variadic_string_arg), testing::Eq(false));
+    ASSERT_THAT(ebp.get_num_processed_arguments(), testing::Eq(1));
+    ASSERT_NO_THROW(ebp.validate_recursive());
+}
+
+TEST(
+    parser,
+    construct_simple_test_parser_parse_argument_variadic_argument_and_long_option_with_param_and_validate) {
+    constexpr const char* const argv[]{"--int=123", "arg", "varg0", "varg1",
+                                       nullptr};
+    const clapp::parser::arg_t arg{parser_make_arg_t(argv)};
+    simple_test_parser_t ebp;
+    ASSERT_NO_THROW(ebp.parse(arg.cbegin(), arg.cend()));
+    ASSERT_THAT(static_cast<bool>(ebp.bool_option), testing::Eq(false));
+    ASSERT_THAT(static_cast<bool>(ebp.int_option), testing::Eq(true));
+    ASSERT_THAT(ebp.int_option.value(), testing::Eq(123));
+    ASSERT_THAT(static_cast<bool>(ebp.string_arg), testing::Eq(true));
+    ASSERT_THAT(ebp.string_arg.value(), testing::StrEq("arg"));
+    ASSERT_THAT(static_cast<bool>(ebp.variadic_string_arg), testing::Eq(true));
+    ASSERT_THAT(ebp.variadic_string_arg.value().size(), testing::Eq(2));
+    ASSERT_THAT(ebp.variadic_string_arg.value()[0], testing::StrEq("varg0"));
+    ASSERT_THAT(ebp.variadic_string_arg.value()[1], testing::StrEq("varg1"));
+    ASSERT_THAT(ebp.get_num_processed_arguments(), testing::Eq(3));
+    ASSERT_NO_THROW(ebp.validate_recursive());
+}
+
+TEST(
+    parser,
+    construct_simple_test_parser_parse_argument_and_short_options_and_validate) {
+    constexpr const char* const argv[]{"-bi=123", "arg", nullptr};
+    const clapp::parser::arg_t arg{parser_make_arg_t(argv)};
+    simple_test_parser_t ebp;
+    ASSERT_NO_THROW(ebp.parse(arg.cbegin(), arg.cend()));
+    ASSERT_THAT(static_cast<bool>(ebp.bool_option), testing::Eq(true));
+    ASSERT_THAT(ebp.bool_option.value(), testing::Eq(true));
+    ASSERT_THAT(static_cast<bool>(ebp.int_option), testing::Eq(true));
+    ASSERT_THAT(ebp.int_option.value(), testing::Eq(123));
+    ASSERT_THAT(static_cast<bool>(ebp.string_arg), testing::Eq(true));
+    ASSERT_THAT(ebp.string_arg.value(), testing::StrEq("arg"));
+    ASSERT_THAT(ebp.get_num_processed_arguments(), testing::Eq(1));
+    ASSERT_NO_THROW(ebp.validate_recursive());
+}
+
+TEST(
+    parser,
+    construct_simple_test_parser2_and_parse_long_count_option_with_param_throws) {
+    constexpr const char* const argv[]{"--count=param", nullptr};
+    const clapp::parser::arg_t arg{parser_make_arg_t(argv)};
+    simple_test_parser2_t ebp;
+    ASSERT_THROW(ebp.parse(arg.cbegin(), arg.cend()),
+                 clapp::option_param_exception_t);
 }
 
 TEST(parser,
-     construct_param_option_parser_and_parse_long_option_without_params) {
-    constexpr const char* const argv[]{"-b", "param", nullptr};
-    const clapp::parser::arg_t arg{make_arg_t(argv)};
-    param_option_parser_t ebp;
-    ASSERT_THROW(ebp.parse(arg.cbegin(), arg.cend()), std::exception);
+     construct_simple_test_parser2_parse_without_mandatory_option_throws) {
+    constexpr const char* const argv[]{nullptr};
+    const clapp::parser::arg_t arg{parser_make_arg_t(argv)};
+    simple_test_parser2_t ebp;
+    ASSERT_NO_THROW(ebp.parse(arg.cbegin(), arg.cend()));
+    ASSERT_THAT(static_cast<bool>(ebp.count_option), testing::Eq(false));
+    ASSERT_THROW(ebp.validate(), clapp::option_param_exception_t);
+}
+
+TEST(parser,
+     construct_simple_test_parser2_parse_option_without_param_and_validate) {
+    constexpr const char* const argv[]{"-c", nullptr};
+    const clapp::parser::arg_t arg{parser_make_arg_t(argv)};
+    simple_test_parser2_t ebp;
+    ASSERT_NO_THROW(ebp.parse(arg.cbegin(), arg.cend()));
+    ASSERT_THAT(static_cast<bool>(ebp.count_option), testing::Eq(true));
+    ASSERT_THAT(ebp.count_option.value(), testing::Eq(1));
+    ASSERT_NO_THROW(ebp.validate());
+}
+
+TEST(
+    parser,
+    construct_simple_test_parser2_parse_option_without_param_optional_argument_and_validate) {
+    constexpr const char* const argv[]{"-c", "opt-arg", nullptr};
+    const clapp::parser::arg_t arg{parser_make_arg_t(argv)};
+    simple_test_parser2_t ebp;
+    ASSERT_NO_THROW(ebp.parse(arg.cbegin(), arg.cend()));
+    ASSERT_THAT(static_cast<bool>(ebp.count_option), testing::Eq(true));
+    ASSERT_THAT(ebp.count_option.value(), testing::Eq(1));
+    ASSERT_THAT(static_cast<bool>(ebp.string_arg), testing::Eq(true));
+    ASSERT_THAT(ebp.string_arg.value(), testing::StrEq("opt-arg"));
+    ASSERT_NO_THROW(ebp.validate());
+}
+
+TEST(parser, construct_sub_parser_container_parse_subparser_and_validate) {
+    constexpr const char* const argv[]{"string-arg", "sub-parser", "-bs=param",
+                                       nullptr};
+    const clapp::parser::arg_t arg{parser_make_arg_t(argv)};
+    sub_parser_container_t spc;
+    ASSERT_NO_THROW(spc.parse(arg.cbegin(), arg.cend()));
+    ASSERT_THAT(static_cast<bool>(spc.string_arg), testing::Eq(true));
+    ASSERT_THAT(spc.string_arg.value(), testing::StrEq("string-arg"));
+    ASSERT_THAT(static_cast<bool>(spc.bool_option), testing::Eq(false));
+    ASSERT_THAT(static_cast<bool>(spc.bool_option2), testing::Eq(false));
+    ASSERT_THAT(static_cast<bool>(spc.sub_parser.bool_option),
+                testing::Eq(true));
+    ASSERT_THAT(spc.sub_parser.bool_option.value(), testing::Eq(true));
+    ASSERT_THAT(static_cast<bool>(spc.sub_parser.string_option),
+                testing::Eq(true));
+    ASSERT_THAT(spc.sub_parser.string_option.value(), testing::StrEq("param"));
+    ASSERT_NO_THROW(spc.validate());
+}
+
+TEST(parser, construct_sub_parser_container_parse_subparser_and_validate2) {
+    constexpr const char* const argv[]{"string-arg", "-b", "sub-parser", "-s",
+                                       "param",      "-2", nullptr};
+    const clapp::parser::arg_t arg{parser_make_arg_t(argv)};
+    sub_parser_container_t spc;
+    ASSERT_NO_THROW(spc.parse(arg.cbegin(), arg.cend()));
+    ASSERT_THAT(static_cast<bool>(spc.string_arg), testing::Eq(true));
+    ASSERT_THAT(spc.string_arg.value(), testing::StrEq("string-arg"));
+    ASSERT_THAT(static_cast<bool>(spc.bool_option), testing::Eq(true));
+    ASSERT_THAT(spc.bool_option.value(), testing::Eq(true));
+    ASSERT_THAT(static_cast<bool>(spc.bool_option2), testing::Eq(true));
+    ASSERT_THAT(spc.bool_option2.value(), testing::Eq(true));
+    ASSERT_THAT(static_cast<bool>(spc.sub_parser.bool_option),
+                testing::Eq(false));
+    ASSERT_THAT(static_cast<bool>(spc.sub_parser.string_option),
+                testing::Eq(true));
+    ASSERT_THAT(spc.sub_parser.string_option.value(), testing::StrEq("param"));
+    ASSERT_NO_THROW(spc.validate());
 }
diff --git a/tests/sub_parser.cpp b/tests/sub_parser.cpp
index c94553859e65f44c4350632d845741d77987f249..d0e8a25f29bf207338bf2dabda063df8fe663080 100644
--- a/tests/sub_parser.cpp
+++ b/tests/sub_parser.cpp
@@ -1,11 +1,13 @@
-#include <clapp/sub_parser.h>
+#include <clapp/argument.h>
+#include <clapp/exception.h>
 #include <clapp/option.h>
 #include <clapp/parser.h>
+#include <clapp/sub_parser.h>
 #include <gmock/gmock.h>
 
 template <class T, size_t N>
-constexpr clapp::parser::arg_t make_arg_t(T (&arg)[N]) {
-    return clapp::parser::arg_t{arg, N - 1};
+inline clapp::parser::arg_t sub_parser_make_arg_t(T (&arg)[N]) {
+    return clapp::parser::arg_t{static_cast<const char* const*>(arg), N - 1};
 }
 
 class empty_test_parser_t : public clapp::parser::basic_parser_t {
@@ -13,40 +15,80 @@ class empty_test_parser_t : public clapp::parser::basic_parser_t {
     using clapp::parser::basic_parser_t::sub_parser_descriptions_vec_t;
     using clapp::parser::basic_parser_t::sub_parsers_map_t;
 
-    using clapp::parser::basic_parser_t::get_sub_parsers;
     using clapp::parser::basic_parser_t::get_sub_parser_descriptions;
+    using clapp::parser::basic_parser_t::get_sub_parsers;
+
+    ~empty_test_parser_t() override;
 };
 
+empty_test_parser_t::~empty_test_parser_t() = default;
+
 class empty_sub_parser_t : public clapp::parser::basic_sub_parser_t {
-    public:
+   public:
     using clapp::parser::basic_sub_parser_t::basic_sub_parser_t;
+
+    ~empty_sub_parser_t() override;
 };
 
-class simple_test_parser_t : public clapp::parser::basic_parser_t {
+empty_sub_parser_t::~empty_sub_parser_t() = default;
+
+class simple_test_sub_parser_t : public clapp::parser::basic_parser_t {
    public:
     using clapp::parser::basic_parser_t::sub_parser_descriptions_vec_t;
     using clapp::parser::basic_parser_t::sub_parsers_map_t;
 
-    using clapp::parser::basic_parser_t::get_sub_parsers;
     using clapp::parser::basic_parser_t::get_sub_parser_descriptions;
+    using clapp::parser::basic_parser_t::get_sub_parsers;
 
     clapp::option::count_option_t count_option{*this, "count", 'c',
-                                             "Count option."};
+                                               "Count option."};
+
+    ~simple_test_sub_parser_t() override;
 };
 
+simple_test_sub_parser_t::~simple_test_sub_parser_t() = default;
+
 class simple_sub_parser_t : public clapp::parser::basic_sub_parser_t {
-    public:
+   public:
     using clapp::parser::basic_sub_parser_t::basic_sub_parser_t;
     clapp::option::bool_option_t bool_option{*this, "bool", 'b',
                                              "Bool option."};
+
+    ~simple_sub_parser_t() override;
+};
+
+simple_sub_parser_t::~simple_sub_parser_t() = default;
+
+class variadic_argument_test_sub_parser_t
+    : public clapp::parser::basic_parser_t {
+   public:
+    clapp::argument::variadic_string_argument_t variadic_string_argument{
+        *this, "arg", "Arg"};
+
+    ~variadic_argument_test_sub_parser_t() override;
+};
+
+variadic_argument_test_sub_parser_t::~variadic_argument_test_sub_parser_t() =
+    default;
+
+class optional_argument_test_sub_parser_t
+    : public clapp::parser::basic_parser_t {
+   public:
+    clapp::argument::string_argument_t string_argument{
+        *this, "arg", "Arg", clapp::basic_parser_t::purpose_t::optional};
+
+    ~optional_argument_test_sub_parser_t() override;
 };
 
+optional_argument_test_sub_parser_t::~optional_argument_test_sub_parser_t() =
+    default;
+
 TEST(sub_parser, construct_empty_sub_parser_and_parse_empty_arguments) {
     const std::string sub_parser{"sub"};
     const std::string description{"sub parser"};
 
     constexpr const char* const argv[]{"sub", nullptr};
-    const clapp::parser::arg_t arg{make_arg_t(argv)};
+    const clapp::parser::arg_t arg{sub_parser_make_arg_t(argv)};
 
     empty_test_parser_t tp;
     empty_sub_parser_t sub{tp, sub_parser, description};
@@ -56,10 +98,8 @@ TEST(sub_parser, construct_empty_sub_parser_and_parse_empty_arguments) {
         tp.get_sub_parser_descriptions()};
 
     ASSERT_THAT(descs.size(), testing::Eq(1));
-    ASSERT_THAT(descs[0].sub_parser_string,
-                testing::StrEq(sub_parser));
-    ASSERT_THAT(descs[0].description,
-                testing::StrEq(description));
+    ASSERT_THAT(descs[0].sub_parser_string, testing::StrEq(sub_parser));
+    ASSERT_THAT(descs[0].description, testing::StrEq(description));
 
     ASSERT_THAT(static_cast<bool>(sub), testing::Eq(false));
     ASSERT_THAT(sub.get_sub_parser_name(), testing::StrEq(sub_parser));
@@ -72,20 +112,19 @@ TEST(sub_parser, construct_simple_sub_parser_and_parse_sub_option) {
     const std::string description{"subbb parser"};
 
     constexpr const char* const argv[]{"subbb", "--bool", nullptr};
-    const clapp::parser::arg_t arg{make_arg_t(argv)};
+    const clapp::parser::arg_t arg{sub_parser_make_arg_t(argv)};
 
-    simple_test_parser_t tp;
+    simple_test_sub_parser_t tp;
     simple_sub_parser_t sub{tp, sub_parser, description};
 
-    simple_test_parser_t::sub_parsers_map_t long_options{tp.get_sub_parsers()};
-    simple_test_parser_t::sub_parser_descriptions_vec_t descs{
+    simple_test_sub_parser_t::sub_parsers_map_t long_options{
+        tp.get_sub_parsers()};
+    simple_test_sub_parser_t::sub_parser_descriptions_vec_t descs{
         tp.get_sub_parser_descriptions()};
 
     ASSERT_THAT(descs.size(), testing::Eq(1));
-    ASSERT_THAT(descs[0].sub_parser_string,
-                testing::StrEq(sub_parser));
-    ASSERT_THAT(descs[0].description,
-                testing::StrEq(description));
+    ASSERT_THAT(descs[0].sub_parser_string, testing::StrEq(sub_parser));
+    ASSERT_THAT(descs[0].description, testing::StrEq(description));
 
     ASSERT_THAT(static_cast<bool>(sub), testing::Eq(false));
     ASSERT_THAT(sub.get_sub_parser_name(), testing::StrEq(sub_parser));
@@ -100,20 +139,19 @@ TEST(sub_parser, construct_simple_sub_parser_and_parse_base_option) {
     const std::string description{"subbb parser"};
 
     constexpr const char* const argv[]{"subbb", "--count", nullptr};
-    const clapp::parser::arg_t arg{make_arg_t(argv)};
+    const clapp::parser::arg_t arg{sub_parser_make_arg_t(argv)};
 
-    simple_test_parser_t tp;
+    simple_test_sub_parser_t tp;
     simple_sub_parser_t sub{tp, sub_parser, description};
 
-    simple_test_parser_t::sub_parsers_map_t long_options{tp.get_sub_parsers()};
-    simple_test_parser_t::sub_parser_descriptions_vec_t descs{
+    simple_test_sub_parser_t::sub_parsers_map_t long_options{
+        tp.get_sub_parsers()};
+    simple_test_sub_parser_t::sub_parser_descriptions_vec_t descs{
         tp.get_sub_parser_descriptions()};
 
     ASSERT_THAT(descs.size(), testing::Eq(1));
-    ASSERT_THAT(descs[0].sub_parser_string,
-                testing::StrEq(sub_parser));
-    ASSERT_THAT(descs[0].description,
-                testing::StrEq(description));
+    ASSERT_THAT(descs[0].sub_parser_string, testing::StrEq(sub_parser));
+    ASSERT_THAT(descs[0].description, testing::StrEq(description));
 
     ASSERT_THAT(static_cast<bool>(sub), testing::Eq(false));
     ASSERT_THAT(sub.get_sub_parser_name(), testing::StrEq(sub_parser));
@@ -128,25 +166,64 @@ TEST(sub_parser, construct_simple_sub_parser_and_ignore_main_option) {
     const std::string description{"subbb parser"};
 
     constexpr const char* const argv[]{"subbb", "--count", nullptr};
-    const clapp::parser::arg_t arg{make_arg_t(argv)};
+    const clapp::parser::arg_t arg{sub_parser_make_arg_t(argv)};
 
-    simple_test_parser_t tp;
+    simple_test_sub_parser_t tp;
     simple_sub_parser_t sub{tp, sub_parser, description, false};
 
-    simple_test_parser_t::sub_parsers_map_t long_options{tp.get_sub_parsers()};
-    simple_test_parser_t::sub_parser_descriptions_vec_t descs{
+    simple_test_sub_parser_t::sub_parsers_map_t long_options{
+        tp.get_sub_parsers()};
+    simple_test_sub_parser_t::sub_parser_descriptions_vec_t descs{
         tp.get_sub_parser_descriptions()};
 
     ASSERT_THAT(descs.size(), testing::Eq(1));
-    ASSERT_THAT(descs[0].sub_parser_string,
-                testing::StrEq(sub_parser));
-    ASSERT_THAT(descs[0].description,
-                testing::StrEq(description));
+    ASSERT_THAT(descs[0].sub_parser_string, testing::StrEq(sub_parser));
+    ASSERT_THAT(descs[0].description, testing::StrEq(description));
 
     ASSERT_THAT(static_cast<bool>(sub), testing::Eq(false));
     ASSERT_THAT(sub.get_sub_parser_name(), testing::StrEq(sub_parser));
 
     ASSERT_THAT(tp.count_option.value(), testing::Eq(0));
-    ASSERT_THROW(tp.parse(arg.cbegin(), arg.cend()), std::exception);
+    ASSERT_THROW(tp.parse(arg.cbegin(), arg.cend()),
+                 clapp::exception::option_exception_t);
     ASSERT_THAT(tp.count_option.value(), testing::Eq(0));
+    ASSERT_NO_THROW(tp.validate_recursive());
+}
+
+TEST(sub_parser, construct_sub_parser_with_same_name_throws) {
+    const std::string sub_parser{"sub"};
+    const std::string description{"sub parser"};
+
+    simple_test_sub_parser_t tp;
+    simple_sub_parser_t sub{tp, sub_parser, description, false};
+    ASSERT_THROW((simple_sub_parser_t{tp, sub_parser, description, false}),
+                 clapp::exception::sub_parser_exception_t);
+}
+
+TEST(sub_parser, construct_sub_parser_with_variadic_arguments_parser_throws) {
+    const std::string sub_parser{"sub"};
+    const std::string description{"sub parser"};
+
+    variadic_argument_test_sub_parser_t tp;
+    ASSERT_THROW((simple_sub_parser_t{tp, sub_parser, description, false}),
+                 clapp::exception::sub_parser_exception_t);
+}
+
+TEST(sub_parser, construct_sub_parser_with_optional_argument_parser_throws) {
+    const std::string sub_parser{"sub"};
+    const std::string description{"sub parser"};
+
+    optional_argument_test_sub_parser_t tp;
+    ASSERT_THROW((simple_sub_parser_t{tp, sub_parser, description, false}),
+                 clapp::exception::sub_parser_exception_t);
+}
+
+TEST(parser, construct_sub_parser_and_gen_help_prefix) {
+    const std::string sub_parser{"sub"};
+    const std::string description{"sub parser"};
+
+    empty_test_parser_t tp;
+    empty_sub_parser_t sub{tp, sub_parser, description};
+
+    ASSERT_THAT(sub.gen_help_prefix(), testing::StrEq("Usage: \n sub"));
 }
diff --git a/tests/value.cpp b/tests/value.cpp
index b8acc05f3bfd84d35af6f0e88815f79d20334615..8eb3072bfaa6fe969142ef5c636411baba351aec 100644
--- a/tests/value.cpp
+++ b/tests/value.cpp
@@ -1,3 +1,4 @@
+#include <clapp/exception.h>
 #include <clapp/value.h>
 #include <gmock/gmock.h>
 
@@ -21,7 +22,7 @@ TEST(value, convert_value_uint8_t) {
     ASSERT_THAT(clapp::value::convert_value<std::uint8_t>("255"),
                 testing::Eq(std::uint8_t{255u}));
     ASSERT_THAT(clapp::value::convert_value<std::uint8_t>("2.555"),
-                 testing::Eq(std::uint8_t{2}));
+                testing::Eq(std::uint8_t{2}));
     ASSERT_THAT(clapp::value::convert_value<std::uint8_t>("0xab"),
                 testing::Eq(std::uint8_t{0xabu}));
     ASSERT_THAT(clapp::value::convert_value<std::uint8_t>("0xff"),
@@ -29,9 +30,11 @@ TEST(value, convert_value_uint8_t) {
     ASSERT_THAT(clapp::value::convert_value<std::uint8_t>("077"),
                 testing::Eq(std::uint8_t{077}));
     ASSERT_THROW(clapp::value::convert_value<std::uint8_t>("256"),
-                 std::out_of_range);
+                 clapp::exception::out_of_range_t);
     ASSERT_THROW(clapp::value::convert_value<std::uint8_t>("-1"),
-                 std::out_of_range);
+                 clapp::exception::out_of_range_t);
+    ASSERT_THROW(clapp::value::convert_value<std::uint8_t>("zzz"),
+                 clapp::exception::invalid_value_t);
 }
 
 TEST(value, convert_value_uint16_t) {
@@ -42,7 +45,7 @@ TEST(value, convert_value_uint16_t) {
     ASSERT_THAT(clapp::value::convert_value<std::uint16_t>("65535"),
                 testing::Eq(std::uint16_t{65535u}));
     ASSERT_THAT(clapp::value::convert_value<std::uint16_t>("2222.555"),
-                 testing::Eq(std::uint16_t{2222}));
+                testing::Eq(std::uint16_t{2222}));
     ASSERT_THAT(clapp::value::convert_value<std::uint16_t>("0xffab"),
                 testing::Eq(std::uint16_t{0xffabu}));
     ASSERT_THAT(clapp::value::convert_value<std::uint16_t>("0xffff"),
@@ -50,9 +53,11 @@ TEST(value, convert_value_uint16_t) {
     ASSERT_THAT(clapp::value::convert_value<std::uint16_t>("01234"),
                 testing::Eq(std::uint16_t{01234u}));
     ASSERT_THROW(clapp::value::convert_value<std::uint16_t>("65536"),
-                 std::out_of_range);
+                 clapp::exception::out_of_range_t);
     ASSERT_THROW(clapp::value::convert_value<std::uint16_t>("-100"),
-                 std::out_of_range);
+                 clapp::exception::out_of_range_t);
+    ASSERT_THROW(clapp::value::convert_value<std::uint16_t>("z"),
+                 clapp::exception::invalid_value_t);
 }
 
 TEST(value, convert_value_uint32_t) {
@@ -69,9 +74,11 @@ TEST(value, convert_value_uint32_t) {
     ASSERT_THAT(clapp::value::convert_value<std::uint32_t>("0123456"),
                 testing::Eq(std::uint32_t{0123456u}));
     ASSERT_THROW(clapp::value::convert_value<std::uint32_t>("4294967296"),
-                 std::out_of_range);
+                 clapp::exception::out_of_range_t);
     ASSERT_THROW(clapp::value::convert_value<std::uint32_t>("-10000"),
-                 std::out_of_range);
+                 clapp::exception::out_of_range_t);
+    ASSERT_THROW(clapp::value::convert_value<std::uint32_t>("zz"),
+                 clapp::exception::invalid_value_t);
 }
 
 TEST(value, convert_value_uint64_t) {
@@ -88,10 +95,12 @@ TEST(value, convert_value_uint64_t) {
         clapp::value::convert_value<std::uint64_t>("0xffffffffffffffff"),
         testing::Eq(std::uint64_t{0xffffffffffffffffull}));
     ASSERT_THAT(clapp::value::convert_value<std::uint64_t>("012345671234"),
-            testing::Eq(std::uint64_t{012345671234ull}));
+                testing::Eq(std::uint64_t{012345671234ull}));
     ASSERT_THROW(
         clapp::value::convert_value<std::uint64_t>("18446744073709551616"),
-        std::out_of_range);
+        clapp::exception::out_of_range_t);
+    ASSERT_THROW(clapp::value::convert_value<std::uint64_t>("zzzz"),
+                 clapp::exception::invalid_value_t);
 }
 
 TEST(value, convert_value_int8_t) {
@@ -112,9 +121,11 @@ TEST(value, convert_value_int8_t) {
     ASSERT_THAT(clapp::value::convert_value<std::int8_t>("-077"),
                 testing::Eq(std::int8_t{-077}));
     ASSERT_THROW(clapp::value::convert_value<std::int8_t>("128"),
-                 std::out_of_range);
+                 clapp::exception::out_of_range_t);
     ASSERT_THROW(clapp::value::convert_value<std::int8_t>("-129"),
-                 std::out_of_range);
+                 clapp::exception::out_of_range_t);
+    ASSERT_THROW(clapp::value::convert_value<std::int8_t>("zzzz"),
+                 clapp::exception::invalid_value_t);
 }
 
 TEST(value, convert_value_int16_t) {
@@ -133,11 +144,13 @@ TEST(value, convert_value_int16_t) {
     ASSERT_THAT(clapp::value::convert_value<std::int16_t>("-0x7fff"),
                 testing::Eq(std::int16_t{-0x7fff}));
     ASSERT_THAT(clapp::value::convert_value<std::int16_t>("-01234"),
-            testing::Eq(std::int16_t{-01234ll}));
+                testing::Eq(std::int16_t{-01234ll}));
     ASSERT_THROW(clapp::value::convert_value<std::int16_t>("32768"),
-                 std::out_of_range);
+                 clapp::exception::out_of_range_t);
     ASSERT_THROW(clapp::value::convert_value<std::int16_t>("-32769"),
-                 std::out_of_range);
+                 clapp::exception::out_of_range_t);
+    ASSERT_THROW(clapp::value::convert_value<std::int16_t>("zzz"),
+                 clapp::exception::invalid_value_t);
 }
 
 TEST(value, convert_value_int32_t) {
@@ -156,11 +169,13 @@ TEST(value, convert_value_int32_t) {
     ASSERT_THAT(clapp::value::convert_value<std::int32_t>("-0x3fffffff"),
                 testing::Eq(std::int32_t{-0x3fffffff}));
     ASSERT_THAT(clapp::value::convert_value<std::int32_t>("-0123456"),
-            testing::Eq(std::int32_t{-0123456ll}));
+                testing::Eq(std::int32_t{-0123456ll}));
     ASSERT_THROW(clapp::value::convert_value<std::int32_t>("2147483648"),
-                 std::out_of_range);
+                 clapp::exception::out_of_range_t);
     ASSERT_THROW(clapp::value::convert_value<std::int32_t>("-2147483649"),
-                 std::out_of_range);
+                 clapp::exception::out_of_range_t);
+    ASSERT_THROW(clapp::value::convert_value<std::int32_t>("zz"),
+                 clapp::exception::invalid_value_t);
 }
 
 TEST(value, convert_value_int64_t) {
@@ -183,13 +198,111 @@ TEST(value, convert_value_int64_t) {
         clapp::value::convert_value<std::int64_t>("-0x3fffffffffffffff"),
         testing::Eq(std::int64_t{-0x3fffffffffffffffLL}));
     ASSERT_THAT(clapp::value::convert_value<std::int64_t>("-012345671234"),
-            testing::Eq(std::int64_t{-012345671234ll}));
+                testing::Eq(std::int64_t{-012345671234ll}));
     ASSERT_THROW(
         clapp::value::convert_value<std::int64_t>("9223372036854775808"),
-        std::out_of_range);
+        clapp::exception::out_of_range_t);
     ASSERT_THROW(
         clapp::value::convert_value<std::int64_t>("-9223372036854775809"),
-        std::out_of_range);
+        clapp::exception::out_of_range_t);
+    ASSERT_THROW(clapp::value::convert_value<std::int64_t>("z"),
+                 clapp::exception::invalid_value_t);
+}
+
+TEST(value, convert_value_double) {
+    ASSERT_THAT(clapp::value::convert_value<double>("0"),
+                testing::Eq(double{0}));
+    ASSERT_THAT(clapp::value::convert_value<double>("10000000000"),
+                testing::Eq(double{10000000000.}));
+    ASSERT_THAT(clapp::value::convert_value<double>("0.125"),
+                testing::Eq(double{0.125}));
+    ASSERT_THAT(clapp::value::convert_value<double>("9223372036854775807"),
+                testing::Eq(double{9223372036854775807.}));
+    ASSERT_THAT(clapp::value::convert_value<double>("-9223372036854775807"),
+                testing::Eq(double{-9223372036854775807.}));
+    ASSERT_THAT(clapp::value::convert_value<double>("-9223372036854775808"),
+                testing::Eq(double{-9223372036854775807. - 1.}));
+    ASSERT_THAT(clapp::value::convert_value<double>("5e-1"),
+                testing::Eq(double{.5}));
+    ASSERT_THROW(clapp::value::convert_value<double>("10e-600"),
+                 clapp::exception::out_of_range_t);
+    ASSERT_THROW(clapp::value::convert_value<double>("z"),
+                 clapp::exception::invalid_value_t);
+}
+
+TEST(value, convert_value_float) {
+    ASSERT_THAT(clapp::value::convert_value<float>("0"),
+                testing::Eq(float{0.f}));
+    ASSERT_THAT(clapp::value::convert_value<float>("10000000000"),
+                testing::Eq(float{10000000000.f}));
+    ASSERT_THAT(clapp::value::convert_value<float>("0.125"),
+                testing::Eq(float{0.125f}));
+    ASSERT_THAT(clapp::value::convert_value<float>("5e-1"),
+                testing::Eq(float{.5f}));
+    ASSERT_THROW(clapp::value::convert_value<float>("10e-600"),
+                 clapp::exception::out_of_range_t);
+    ASSERT_THROW(clapp::value::convert_value<float>("z"),
+                 clapp::exception::invalid_value_t);
+}
+
+TEST(value, convert_value_chrono_nanoseconds) {
+    ASSERT_THAT(clapp::value::convert_value<std::chrono::nanoseconds>("0"),
+                testing::Eq(std::chrono::nanoseconds{0u}));
+    ASSERT_THAT(
+        clapp::value::convert_value<std::chrono::nanoseconds>("1000000"),
+        testing::Eq(std::chrono::nanoseconds{1000000u}));
+}
+
+TEST(value, convert_value_chrono_microseconds) {
+    ASSERT_THAT(clapp::value::convert_value<std::chrono::microseconds>("0"),
+                testing::Eq(std::chrono::microseconds{0u}));
+    ASSERT_THAT(
+        clapp::value::convert_value<std::chrono::microseconds>("1000000"),
+        testing::Eq(std::chrono::microseconds{1000000u}));
+}
+
+TEST(value, convert_value_chrono_milliseconds) {
+    ASSERT_THAT(clapp::value::convert_value<std::chrono::milliseconds>("0"),
+                testing::Eq(std::chrono::milliseconds{0u}));
+    ASSERT_THAT(
+        clapp::value::convert_value<std::chrono::milliseconds>("1000000"),
+        testing::Eq(std::chrono::milliseconds{1000000u}));
+}
+
+TEST(value, convert_value_chrono_seconds) {
+    ASSERT_THAT(clapp::value::convert_value<std::chrono::seconds>("0"),
+                testing::Eq(std::chrono::seconds{0u}));
+    ASSERT_THAT(clapp::value::convert_value<std::chrono::seconds>("1000000"),
+                testing::Eq(std::chrono::seconds{1000000u}));
+}
+
+TEST(value, convert_value_chrono_minutes) {
+    ASSERT_THAT(clapp::value::convert_value<std::chrono::minutes>("0"),
+                testing::Eq(std::chrono::minutes{0u}));
+    ASSERT_THAT(clapp::value::convert_value<std::chrono::minutes>("1000000"),
+                testing::Eq(std::chrono::minutes{1000000u}));
+}
+
+TEST(value, convert_value_chrono_hours) {
+    ASSERT_THAT(clapp::value::convert_value<std::chrono::hours>("0"),
+                testing::Eq(std::chrono::hours{0u}));
+    ASSERT_THAT(clapp::value::convert_value<std::chrono::hours>("1000000"),
+                testing::Eq(std::chrono::hours{1000000u}));
+}
+
+TEST(value, get_chrono_postfix) {
+    ASSERT_THAT(clapp::value::get_chrono_postfix<std::chrono::nanoseconds>(),
+                testing::StrEq("ns"));
+    ASSERT_THAT(clapp::value::get_chrono_postfix<std::chrono::microseconds>(),
+                testing::StrEq("µs"));
+    ASSERT_THAT(clapp::value::get_chrono_postfix<std::chrono::milliseconds>(),
+                testing::StrEq("ms"));
+    ASSERT_THAT(clapp::value::get_chrono_postfix<std::chrono::seconds>(),
+                testing::StrEq("s"));
+    ASSERT_THAT(clapp::value::get_chrono_postfix<std::chrono::minutes>(),
+                testing::StrEq("m"));
+    ASSERT_THAT(clapp::value::get_chrono_postfix<std::chrono::hours>(),
+                testing::StrEq("h"));
 }
 
 TEST(value, default_value_uint8_t) {
@@ -228,6 +341,17 @@ TEST(value, default_value_string) {
     ASSERT_THAT(dv.default_value(), testing::Eq(value));
 }
 
+TEST(value, default_value_milliseconds) {
+    constexpr std::uint64_t value{30};
+    clapp::value::default_value_t<std::chrono::milliseconds> dv{
+        std::chrono::milliseconds{value}};
+    std::stringstream ss;
+    ss << "default-value: " << value << "ms";
+    ASSERT_THAT(dv.append_restriction_text(), testing::StrEq(ss.str()));
+    ASSERT_THAT(dv.default_value(),
+                testing::Eq(std::chrono::milliseconds{value}));
+}
+
 TEST(value, min_max_value_uint8_t) {
     constexpr std::uint8_t min{10};
     constexpr std::uint8_t max{50};
@@ -239,8 +363,8 @@ TEST(value, min_max_value_uint8_t) {
     EXPECT_NO_THROW(mmv.validate(min, "option"));
     EXPECT_NO_THROW(mmv.validate(max, "option"));
     EXPECT_NO_THROW(mmv.validate(30, "option"));
-    ASSERT_THROW(mmv.validate(5, "option"), std::out_of_range);
-    ASSERT_THROW(mmv.validate(55, "option"), std::out_of_range);
+    ASSERT_THROW(mmv.validate(5, "option"), clapp::exception::out_of_range_t);
+    ASSERT_THROW(mmv.validate(55, "option"), clapp::exception::out_of_range_t);
 }
 
 TEST(value, min_max_value_int32_t) {
@@ -253,8 +377,40 @@ TEST(value, min_max_value_int32_t) {
     EXPECT_NO_THROW(mmv.validate(min, "option"));
     EXPECT_NO_THROW(mmv.validate(max, "option"));
     EXPECT_NO_THROW(mmv.validate(-100, "option"));
-    ASSERT_THROW(mmv.validate(-2000, "option"), std::out_of_range);
-    ASSERT_THROW(mmv.validate(55, "option"), std::out_of_range);
+    ASSERT_THROW(mmv.validate(-2000, "option"),
+                 clapp::exception::out_of_range_t);
+    ASSERT_THROW(mmv.validate(55, "option"), clapp::exception::out_of_range_t);
+}
+
+TEST(value, min_max_value_double_t) {
+    const double min{100.};
+    const double max{200.5};
+    clapp::value::min_max_value_t<double> mmv{min, max};
+    std::stringstream ss;
+    ss << "constraint: [" << min << "," << max << "]";
+    ASSERT_THAT(mmv.append_restriction_text(), testing::StrEq(ss.str()));
+    EXPECT_NO_THROW(mmv.validate(min, "option"));
+    EXPECT_NO_THROW(mmv.validate(max, "option"));
+    EXPECT_NO_THROW(mmv.validate(130, "option"));
+    ASSERT_THROW(mmv.validate(5, "option"), clapp::exception::out_of_range_t);
+    ASSERT_THROW(mmv.validate(200.6, "option"),
+                 clapp::exception::out_of_range_t);
+}
+
+TEST(value, min_max_value_milliseconds_t) {
+    const std::chrono::milliseconds min{std::chrono::milliseconds{100}};
+    const std::chrono::milliseconds max{std::chrono::milliseconds{200}};
+    clapp::value::min_max_value_t<std::chrono::milliseconds> mmv{min, max};
+    std::stringstream ss;
+    ss << "constraint: [" << min.count() << "ms," << max.count() << "ms]";
+    ASSERT_THAT(mmv.append_restriction_text(), testing::StrEq(ss.str()));
+    EXPECT_NO_THROW(mmv.validate(min, "option"));
+    EXPECT_NO_THROW(mmv.validate(max, "option"));
+    EXPECT_NO_THROW(mmv.validate(std::chrono::milliseconds{130}, "option"));
+    ASSERT_THROW(mmv.validate(std::chrono::milliseconds{50}, "option"),
+                 clapp::exception::out_of_range_t);
+    ASSERT_THROW(mmv.validate(std::chrono::milliseconds{300}, "option"),
+                 clapp::exception::out_of_range_t);
 }
 
 TEST(value, path_exists_t) {
@@ -263,5 +419,12 @@ TEST(value, path_exists_t) {
     EXPECT_NO_THROW(pe.validate(clapp::fs::path{"/tmp"}, "option"));
     ASSERT_THROW(
         pe.validate(clapp::fs::path{"/tmp/aba/sadf/aksk/annsp"}, "arg"),
-        std::runtime_error);
+        clapp::exception::path_does_not_exist_t);
+}
+
+TEST(value, found_func_t) {
+    std::stringstream ss;
+    clapp::value::found_func_t ff{[&ss]() { ss << "called func"; }};
+    ff.found();
+    ASSERT_THAT(ss.str(), testing::StrEq("called func"));
 }