Skip to content

Commit

Permalink
#317 Enable/disable nodes at runtime
Browse files Browse the repository at this point in the history
Fix #317
  • Loading branch information
cmannett85 committed Jun 11, 2023
1 parent 1c9583e commit c5231c0
Show file tree
Hide file tree
Showing 48 changed files with 2,189 additions and 117 deletions.
3 changes: 3 additions & 0 deletions .clang-format
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ ConstructorInitializerAllOnOneLineOrOnePerLine: false
IndentCaseLabels: false
IndentPPDirectives: AfterHash
IndentWidth: 4
PointerAlignment: Left
QualifierAlignment: Left
ReferenceAlignment: Pointer
ReflowComments: true
SortUsingDeclarations: false
SpacesInContainerLiterals: false
Expand Down
2 changes: 2 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@ path_prefixer(HEADERS
include/arg_router/policy/program_version.hpp
include/arg_router/policy/required.hpp
include/arg_router/policy/router.hpp
include/arg_router/policy/runtime_enable.hpp
include/arg_router/policy/short_form_expander.hpp
include/arg_router/policy/short_name.hpp
include/arg_router/policy/token_end_marker.hpp
Expand All @@ -123,6 +124,7 @@ path_prefixer(HEADERS
include/arg_router/tree_node_fwd.hpp
include/arg_router/utility/compile_time_string.hpp
include/arg_router/utility/compile_time_optional.hpp
include/arg_router/utility/dynamic_string_view.hpp
include/arg_router/utility/result.hpp
include/arg_router/utility/string_to_policy.hpp
include/arg_router/utility/string_view_ops.hpp
Expand Down
63 changes: 63 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -505,6 +505,69 @@ ar::root(
```
`ar::list` is a simple `arg` and `flag` container that `mode` and `root` instances detect and add the contents to their child/policy lists. Also don't be afraid of the copies, the majority of `arg_router` types hold no data (the advantage of compile-time!) and those that do (e.g. `default_value`) generally have small types like primitives or `std::string_view`.
## Enabling/Disabling Nodes at Runtime
Sometimes features or parameters only make sense within certain environments or scenarios that can only be detected at runtime. You can use `policy::runtime_enable` to dynamically make a node 'disappear' from the parsing process and help output by the value set at runtime in the policy's constructor. A trivial example is given by the [runtime_node_enable example](https://cmannett85.github.io/arg_router/c_09_0920_2runtime_node_enable_2main_8cpp-example.html):
```
const auto advanced = std::getenv(license_env_var) != nullptr;
ar::root(
arp::validation::default_validator,
ar::help("help"_S,
"h"_S,
"Display this help and exit"_S,
arp::flatten_help,
arp::program_name_t{"runtime_node_enable"_S},
arp::program_version<ar::str<version>>,
arp::program_addendum_t{"An example program for arg_router."_S}),
ar::flag("version"_S, "Output version information and exit"_S, arp::router{[](bool) { ... }}),
ar::mode("advanced"_S,
"Advanced features"_S,
ar::flag("feature1"_S, "First feature"_S),
ar::arg<int>("feature2"_S, "Second feature"_S, arp::default_value{42}),
arp::router{[](bool f1, int f2) { ... }},
arp::runtime_enable{advanced}),
ar::mode(
ar::flag("foo"_S, "Foo flag"_S, "f"_S),
ar::flag("bar"_S, "Bar flag"_S, "b"_S),
ar::arg<std::string_view>("advance-foo"_S,
"Licensed foo"_S,
arp::runtime_enable_required<std::string_view>{advanced}),
arp::router{[](bool f, bool b, std::string_view advance_foo) { ... }}))
.parse(argc, argv);
```
Here an environment variable is used to detect if a license is installed (do not use this method in production...) and if it
is, unlocks 'advanced' features. If the license is available then the entire `advanced` mode is available and so is the `--advance-foo` `std::string_view` `arg` in the default mode.
The help output adjusts to match:
```
$ ./example_runtime_node_enable_cpp20 --help
runtime_node_enable v3.14

--help,-h Display this help and exit
--version Output version information and exit
--foo,-f Foo flag
--bar,-b Bar flag

An example program for arg_router.
```
And with the license:
```
$ AR_EXAMPLE_LICENSE=1 ./example_runtime_node_enable_cpp20 --help
runtime_node_enable v3.14

--help,-h Display this help and exit
--version Output version information and exit
advanced Advanced features
--feature1 First feature
--feature2 <Value> Second feature
--foo,-f Foo flag
--bar,-b Bar flag
--advance-foo <Value> Licensed foo

An example program for arg_router.
```
## Help Output
As shown in prior sections, a `help` node can be a child of the `root` (and only the `root`!), which acts like an `arg`, and generates the help output when requested by the user. This node is optional, without it there is no help command line argument. As the node is `arg`-like, it requires a long and/or short name.
Expand Down
6 changes: 4 additions & 2 deletions cmake/build_types/unit_test.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ path_prefixer(TEST_SRCS
policy/min_max_value_t_test.cpp
policy/required_test.cpp
policy/router_test.cpp
policy/runtime_enable_test.cpp
policy/short_form_expander_test.cpp
policy/short_name_test.cpp
policy/token_end_marker_test.cpp
Expand All @@ -72,6 +73,7 @@ path_prefixer(TEST_SRCS
tree_node_test.cpp
utility/compile_time_string_test.cpp
utility/compile_time_optional_test.cpp
utility/dynamic_string_view_test.cpp
utility/result_test.cpp
utility/string_to_policy_test.cpp
utility/string_view_ops_test.cpp
Expand Down Expand Up @@ -114,8 +116,8 @@ function(configure_test_build TARGET)
set_property(TARGET ${TARGET} PROPERTY
MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>")

if (CMAKE_CXX_COMPILER_FRONTEND_VARIANT STREQUAL "MSVC")
set(EXTRA_FLAGS /clang:-fconstexpr-steps=10000000)
if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
set(EXTRA_FLAGS ${EXTRA_FLAGS} /clang:-fconstexpr-steps=10000000)
endif()
else()
set(EXTRA_FLAGS -Werror -Wall -Wextra -ftemplate-backtrace-limit=0 -fno-rtti
Expand Down
2 changes: 2 additions & 0 deletions docs/related_pages/examples.doxy
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,15 @@
* @example c++17/custom_policy_and_node/main.cpp
* @example c++17/just_cats/main.cpp
* @example c++17/launcher/main.cpp
* @example c++17/runtime_node_enable/main.cpp
* @example c++17/simple/main.cpp
* @example c++17/simple_ml/main.cpp
*
* @example c++20/basic_cat/main.cpp
* @example c++20/custom_policy_and_node/main.cpp
* @example c++20/just_cats/main.cpp
* @example c++20/launcher/main.cpp
* @example c++20/runtime_node_enable/main.cpp
* @example c++20/simple/main.cpp
* @example c++20/simple_ml/main.cpp
*/
34 changes: 27 additions & 7 deletions docs/related_pages/help.doxy
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,31 @@ namespace arg_router
*
* @section help_hdt help_data_type
* Help output is crucial for most application's command line interface, in arg_router this is
* implemented by defining a public type called <TT>help_data_type</TT> that is struct with a
* boolean NTTP providing up to three utility::compile_time_string type aliases:
* -# <TT>label</TT> which provides the node name(s)
* -# <TT>description</TT> which provides the help blurb
* implemented by defining a public type called <TT>help_data_type</TT> that is equivalent to:
* @code
* template <bool Flatten>
* struct help_data_type {
* using label = ...; // Compile-time string
* using description = ...; // Compile-time string
* using children = std::tuple<help_data_type<...>, ...>;
*
* // New in v1.4!
* template <typename OwnerNode, typename FilterFn>
* [[nodiscard]] static vector<runtime_help_data> runtime_children(const OwnerNode& owner,
* FilterFn&& f)
* {
* return ...;
* }
* }
* @endcode
* -# <TT>label</TT> which provides the node name, and may be empty
* -# <TT>description</TT> which provides the help blurb, and may be empty
* -# <TT>children</TT> which provides a tuple of other <TT>help_data_type</TT> compatible structure
* types defining child help data
* -# <TT>runtime_children(..)</TT> is an optional method that is new for v1.4. It allows for
* runtime modification of the help output (initially to support policy::runtime_enable), via
* skipping child output if the filter parameter returns false for a given child - see
* runtime_help_data and tree_node::default_leaf_help_data_type for more information
*
* Help data is all generated at compile-time and the result held in program static read-only
* memory. For parse tree leaf-level nodes (e.g. flag_t), the library provides a default alias that
Expand All @@ -27,8 +46,9 @@ namespace arg_router
* defined by a mode-like type in which case <TT>children</TT> will be a tuple of the child parse
* tree node's <TT>help_data_type</TT>s.
*
* An outlier to the above is dependency::one_of_t, where the <TT>help_data_type::children</TT>
* tuple elements are the still the node's children, but it will ignore the flattening NTTP.
* An outlier to the above is dependency::one_of_t and dependency::alias_group_t, where the
* <TT>help_data_type::children</TT> tuple elements are the still the node's children, but it will
* ignore the flattening NTTP.
*
* @subsection help_hdt_flatten Flattening
* The boolean NTTP the <TT>help_data_type</TT> accepts is for 'flattening'. 'Flattened' help
Expand Down Expand Up @@ -78,4 +98,4 @@ namespace arg_router
* basic, so a colour version is provided in the library too:
* policy::help_formatter_component::colour_line_formatter.
*/
}
} // namespace arg_router
1 change: 1 addition & 0 deletions examples/c++17/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,6 @@ add_subdirectory(basic_cat)
add_subdirectory(custom_policy_and_node)
add_subdirectory(just_cats)
add_subdirectory(launcher)
add_subdirectory(runtime_node_enable)
add_subdirectory(simple)
add_subdirectory(simple_ml)
22 changes: 22 additions & 0 deletions examples/c++17/runtime_node_enable/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
### Copyright (C) 2023 by Camden Mannett.
### Distributed under the Boost Software License, Version 1.0.
### (See accompanying file LICENSE or copy at https://www.boost.org/LICENSE_1_0.txt)

create_clangformat_target(
NAME clangformat_example_runtime_node_enable
SOURCES "${CMAKE_CURRENT_SOURCE_DIR}/main.cpp"
)

add_executable(example_runtime_node_enable "${CMAKE_CURRENT_SOURCE_DIR}/main.cpp")
add_dependencies(example_runtime_node_enable clangformat_example_runtime_node_enable)

target_compile_features(example_runtime_node_enable PUBLIC cxx_std_17)
set_target_properties(example_runtime_node_enable PROPERTIES CXX_EXTENSIONS OFF)
target_link_libraries(example_runtime_node_enable
PRIVATE arg_router
)

configure_example_build(example_runtime_node_enable)
add_clangtidy_to_target(example_runtime_node_enable)

add_dependencies(cpp17_examples example_runtime_node_enable)
59 changes: 59 additions & 0 deletions examples/c++17/runtime_node_enable/main.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
// Copyright (C) 2023 by Camden Mannett.
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE or copy at https://www.boost.org/LICENSE_1_0.txt)

#include <arg_router/arg_router.hpp>

namespace ar = arg_router;
namespace arp = ar::policy;

using namespace ar::literals;
using namespace std::string_view_literals;

namespace
{
constexpr auto version = "v3.14"sv;
constexpr auto license_env_var = "AR_EXAMPLE_LICENSE";
} // namespace

int main(int argc, char* argv[])
{
const auto advanced = std::getenv(license_env_var) != nullptr;
ar::root(
arp::validation::default_validator,
ar::help(S_("help"){},
S_('h'){},
S_("Display this help and exit"){},
arp::flatten_help,
arp::program_name<S_("runtime_node_enable")>,
arp::program_version<S_(version)>,
arp::program_addendum<S_("An example program for arg_router.")>),
ar::flag(S_("version"){},
S_("Output version information and exit"){},
arp::router{[](bool) {
std::cout << version << std::endl;
std::exit(EXIT_SUCCESS);
}}),
ar::mode(S_("advanced"){},
S_("Advanced features"){},
ar::flag(S_("feature1"){}, S_("First feature"){}),
// NOLINTNEXTLINE(readability-magic-numbers)
ar::arg<int>(S_("feature2"){}, S_("Second feature"){}, arp::default_value{42}),
arp::router{[](bool f1, int f2) {
std::cout << "F1: " << std::boolalpha << f1 << ", F2: " << f2 << std::endl;
}},
arp::runtime_enable{advanced}),
ar::mode(
ar::flag(S_("foo"){}, S_("Foo flag"){}, S_('f'){}),
ar::flag(S_("bar"){}, S_("Bar flag"){}, S_('b'){}),
ar::arg<std::string_view>(S_("advance-foo"){},
S_("Licensed foo"){},
arp::runtime_enable_required<std::string_view>{advanced}),
arp::router{[](bool f, bool b, std::string_view advance_foo) {
std::cout << "F: " << std::boolalpha << f << ", B: " << b
<< ", Advance-foo: " << advance_foo << std::endl;
}}))
.parse(argc, argv);

return EXIT_SUCCESS;
}
1 change: 1 addition & 0 deletions examples/c++20/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,6 @@ add_subdirectory(basic_cat)
add_subdirectory(custom_policy_and_node)
add_subdirectory(just_cats)
add_subdirectory(launcher)
add_subdirectory(runtime_node_enable)
add_subdirectory(simple)
add_subdirectory(simple_ml)
22 changes: 22 additions & 0 deletions examples/c++20/runtime_node_enable/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
### Copyright (C) 2023 by Camden Mannett.
### Distributed under the Boost Software License, Version 1.0.
### (See accompanying file LICENSE or copy at https://www.boost.org/LICENSE_1_0.txt)

create_clangformat_target(
NAME clangformat_example_runtime_node_enable_cpp20
SOURCES "${CMAKE_CURRENT_SOURCE_DIR}/main.cpp"
)

add_executable(example_runtime_node_enable_cpp20 "${CMAKE_CURRENT_SOURCE_DIR}/main.cpp")
add_dependencies(example_runtime_node_enable_cpp20 clangformat_example_runtime_node_enable_cpp20)

target_compile_features(example_runtime_node_enable_cpp20 PUBLIC cxx_std_20)
set_target_properties(example_runtime_node_enable_cpp20 PROPERTIES CXX_EXTENSIONS OFF)
target_link_libraries(example_runtime_node_enable_cpp20
PRIVATE arg_router
)

configure_example_build(example_runtime_node_enable_cpp20)
add_clangtidy_to_target(example_runtime_node_enable_cpp20)

add_dependencies(cpp20_examples example_runtime_node_enable_cpp20)
58 changes: 58 additions & 0 deletions examples/c++20/runtime_node_enable/main.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
// Copyright (C) 2023 by Camden Mannett.
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE or copy at https://www.boost.org/LICENSE_1_0.txt)

#include <arg_router/arg_router.hpp>

namespace ar = arg_router;
namespace arp = ar::policy;

using namespace ar::literals;

namespace
{
// NOLINTNEXTLINE(*-avoid-c-arrays)
constexpr char version[] = "v3.14";

constexpr auto license_env_var = "AR_EXAMPLE_LICENSE";
} // namespace

int main(int argc, char* argv[])
{
const auto advanced = std::getenv(license_env_var) != nullptr;
ar::root(
arp::validation::default_validator,
ar::help("help"_S,
"h"_S,
"Display this help and exit"_S,
arp::flatten_help,
arp::program_name_t{"runtime_node_enable"_S},
arp::program_version<ar::str<version>>,
arp::program_addendum_t{"An example program for arg_router."_S}),
ar::flag("version"_S, "Output version information and exit"_S, arp::router{[](bool) {
std::cout << &version[0] << std::endl;
std::exit(EXIT_SUCCESS);
}}),
ar::mode("advanced"_S,
"Advanced features"_S,
ar::flag("feature1"_S, "First feature"_S),
// NOLINTNEXTLINE(readability-magic-numbers)
ar::arg<int>("feature2"_S, "Second feature"_S, arp::default_value{42}),
arp::router{[](bool f1, int f2) {
std::cout << "F1: " << std::boolalpha << f1 << ", F2: " << f2 << std::endl;
}},
arp::runtime_enable{advanced}),
ar::mode(
ar::flag("foo"_S, "Foo flag"_S, "f"_S),
ar::flag("bar"_S, "Bar flag"_S, "b"_S),
ar::arg<std::string_view>("advance-foo"_S,
"Licensed foo"_S,
arp::runtime_enable_required<std::string_view>{advanced}),
arp::router{[](bool f, bool b, std::string_view advance_foo) {
std::cout << "F: " << std::boolalpha << f << ", B: " << b
<< ", Advance-foo: " << advance_foo << std::endl;
}}))
.parse(argc, argv);

return EXIT_SUCCESS;
}
2 changes: 1 addition & 1 deletion external/vcpkg
Submodule vcpkg updated 3325 files
1 change: 0 additions & 1 deletion include/arg_router/arg_router.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
#include "arg_router/multi_lang/string_selector.hpp"
#include "arg_router/policy/colour_help_formatter.hpp"
#include "arg_router/policy/custom_parser.hpp"
#include "arg_router/policy/dependent.hpp"
#include "arg_router/policy/description.hpp"
#include "arg_router/policy/min_max_value.hpp"
#include "arg_router/policy/token_end_marker.hpp"
Expand Down
7 changes: 7 additions & 0 deletions include/arg_router/config.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -72,4 +72,11 @@ using allocator = AR_ALLOCATOR<T>;
#if (__cplusplus >= 202002L) && !AR_DISABLE_CPP20_STRINGS
# define AR_ENABLE_CPP20_STRINGS
#endif

/** There's a bizarre issue that appeared in MSVC 1936 where quoted metafunctions appeared to stop
* working due to the <TT>Q::template fn</TT> definition not being accepted.
*/
#if (!defined(__clang__) && defined(_MSC_VER) && (_MSC_VER >= 1936))
# define MSVC_1936_WORKAROUND 1
#endif
} // namespace arg_router::config
Loading

0 comments on commit c5231c0

Please sign in to comment.