arg_router  1.4.0
C++ command line argument parsing and routing
dependent.hpp
1 // Copyright (C) 2022 by Camden Mannett.
2 // Distributed under the Boost Software License, Version 1.0.
3 // (See accompanying file LICENSE or copy at https://www.boost.org/LICENSE_1_0.txt)
4 
5 #pragma once
6 
7 #include "arg_router/parsing/parse_target.hpp"
8 #include "arg_router/parsing/parsing.hpp"
9 #include "arg_router/policy/policy.hpp"
10 #include "arg_router/traits.hpp"
11 #include "arg_router/utility/compile_time_optional.hpp"
12 #include "arg_router/utility/tree_recursor.hpp"
13 
14 namespace arg_router::policy
15 {
22 template <typename... DependsPolicies>
24 {
25 public:
27  using depends_policies_type = std::tuple<std::decay_t<DependsPolicies>...>;
28 
33  constexpr explicit dependent_t([[maybe_unused]] const DependsPolicies&... policies) noexcept {}
34 
47  template <typename ProcessedTarget, typename... Parents>
49  [[maybe_unused]] parsing::dynamic_token_adapter& tokens,
51  [[maybe_unused]] parsing::parse_target& target,
52  [[maybe_unused]] const Parents&... parents) const
53  {
54  // Find the owning mode
55  using mode_type = typename nearest_mode<Parents...>::type;
56  static_assert(!std::is_void_v<mode_type>, "Cannot find parent mode");
57  static_assert(!processed_target.empty, "processed_target cannot be empty");
58 
59  // Find all the depends targets
60  using node_targets = typename depends_targets<depends_policies_type, mode_type>::type;
61  static_assert(cyclic_dependency_checker<node_targets, mode_type>::value,
62  "Cyclic dependency detected");
63 
65  utility::tuple_type_iterator<node_targets>([&](auto i) {
66  using node_target = std::tuple_element_t<i, node_targets>;
67 
68  // Skip if already failed
69  if (!result) {
70  return;
71  }
72 
73  // Try to find the matching node in the target, if it's not found then look in all the
74  // sub-targets
75  const auto target_index = utility::type_hash<node_target>();
76  if (processed_target->node_type() == target_index) {
77  return;
78  }
79 
80  for (const auto& sub_target : processed_target->sub_targets()) {
81  if (sub_target.node_type() == target_index) {
82  return;
83  }
84  }
85 
87  parsing::node_token_type<node_target>()};
88  });
89 
90  return result;
91  }
92 
93 private:
94  template <typename T>
95  struct policy_checker {
96  constexpr static auto value =
97  traits::has_long_name_method_v<T> || traits::has_short_name_method_v<T>;
98  };
99 
100  static_assert((sizeof...(DependsPolicies) > 0), "At least one name needed for dependent");
101  static_assert(policy::is_all_policies_v<depends_policies_type>,
102  "All parameters must be policies");
103  static_assert(boost::mp11::mp_all_of<depends_policies_type, policy_checker>::value,
104  "All parameters must provide a long and/or short form name");
105 
106  // Find the nearest parent with a routing policy. By definition the
107  // dependent cannot be the owner, so filter that out
108  template <typename... Parents>
109  using nearest_mode =
110  policy::nearest_mode_like<boost::mp11::mp_pop_front<std::tuple<Parents...>>>;
111 
112  // Starting from ModeType, recurse down through the tree and find all the nodes referred to in
113  // DependsPolicies
114  template <typename DependsPoliciesTuple, typename ModeType>
115  struct depends_targets {
116  template <typename Current, typename... Parents>
117  struct visitor {
118  // If current is one of the DependsPolicies and Parents is not empty, then use the first
119  // element of Parents (i.e. the owning node of the name policy). If not, then set to
120  // void
121  using type = boost::mp11::mp_eval_if_c<
122  !(boost::mp11::mp_contains<DependsPoliciesTuple, Current>::value &&
123  (sizeof...(Parents) > 0)),
124  void,
125  boost::mp11::mp_at,
126  std::tuple<Parents...>,
127  traits::integral_constant<std::size_t{0}>>;
128  };
129 
130  using type =
131  boost::mp11::mp_remove_if<utility::tree_type_recursor_collector_t<visitor, ModeType>,
132  std::is_void>;
133 
134  static_assert(std::tuple_size_v<type> == std::tuple_size_v<DependsPoliciesTuple>,
135  "Number of found modes must match depends policy count");
136  static_assert(std::tuple_size_v<boost::mp11::mp_unique<type>> == std::tuple_size_v<type>,
137  "Node dependency list must be unique, do you have short and long "
138  "names from the same node?");
139  };
140 
141  template <typename DependsNodesTuple, typename ModeType>
142  struct cyclic_dependency_checker {
143  // For each depends, find all of its depends, stop when there are no more or if you hit this
144  // policy - static_assert
145  template <std::size_t I, typename Nodes>
146  [[nodiscard]] constexpr static bool check() noexcept
147  {
148  if constexpr (I >= std::tuple_size_v<Nodes>) {
149  return true;
150  } else {
151  using depends_type = std::tuple_element_t<I, Nodes>;
152  if constexpr (algorithm::has_specialisation_v<
153  dependent_t,
154  typename depends_type::policies_type>) {
155  if constexpr (boost::mp11::mp_contains<typename depends_type::policies_type,
156  dependent_t>::value) {
157  return false;
158  } else {
159  using targets =
160  typename depends_targets<typename depends_type::depends_policies_type,
161  ModeType>::type;
162 
163  return check<I + 1, boost::mp11::mp_append<Nodes, targets>>();
164  }
165  }
166 
167  return check<I + 1, Nodes>();
168  }
169  }
170 
171  constexpr static bool value = check<0, DependsNodesTuple>();
172  };
173 };
174 
182 template <typename... DependsPolicies>
183 [[nodiscard]] constexpr auto dependent(DependsPolicies... policies) noexcept
184 {
185  return dependent_t{std::move(policies)...};
186 }
187 
188 template <typename... DependsPolicies>
189 struct is_policy<dependent_t<DependsPolicies...>> : std::true_type {
190 };
191 } // namespace arg_router::policy
constexpr dependent_t([[maybe_unused]] const DependsPolicies &... policies) noexcept
Definition: dependent.hpp:33
std::tuple< std::decay_t< DependsPolicies >... > depends_policies_type
Definition: dependent.hpp:27
parsing::pre_parse_result pre_parse_phase([[maybe_unused]] parsing::dynamic_token_adapter &tokens, utility::compile_time_optional< ProcessedTarget > processed_target, [[maybe_unused]] parsing::parse_target &target, [[maybe_unused]] const Parents &... parents) const
Definition: dependent.hpp:48
constexpr auto has_specialisation_v
Definition: algorithm.hpp:124
constexpr auto dependent(DependsPolicies... policies) noexcept
Definition: dependent.hpp:183
std::integral_constant< decltype(Value), Value > integral_constant
Definition: traits.hpp:210