LArSoft  v09_90_00
Liquid Argon Software toolkit - https://larsoft.org/
fhicl_get_impl.cc
Go to the documentation of this file.
1 #include "tools/fhicl_get_impl.h"
2 #include "cetlib/filepath_maker.h"
3 #include "cetlib/parsed_program_options.h"
5 #include "fhiclcpp/exception.h"
6 #include "tools/Printer.h"
7 
8 #include <cassert>
9 #include <iostream>
10 #include <string>
11 #include <variant>
12 #include <vector>
13 
14 namespace {
15  std::string const fhicl_env_var{"FHICL_FILE_PATH"};
16 
17  std::string const help{"Help"};
18  std::string const processing{"Processing"};
19  std::string const config{"Configuration"};
20  std::string const usage{"Usage error"};
21 
22  struct Help {
23  std::string msg;
24  };
25 
26  struct Options {
27  std::string input_filename;
28  std::string atom_as{};
29  std::string sequence_of{};
30  bool names_in{false};
31  bool allow_missing{false};
32  std::string parameter_key{};
33  std::unique_ptr<cet::filepath_maker> policy;
34  std::string lookup_path;
35  };
36 
37  using supported_types_t =
39  using strings_t = std::vector<std::string>;
40 
41  std::variant<Options, Help>
42  process_arguments(int argc,
43  char** argv,
44  supported_types_t const& supported_types)
45  {
46  namespace bpo = boost::program_options;
47 
48  Options opts;
49 
50  bpo::options_description desc(
51  "Usage: fhicl-get [options] [key] <file>\n\n"
52  "Required parameters:\n"
53  " [key] A fully-qualified parameter key of the form 'a.b.c.d'.\n"
54  " When used with the --names-in program option, one may "
55  "omit\n"
56  " this option to print all top-level names in the file.\n"
57  " <file> A valid FHiCL document that contains the parameter with\n"
58  " the name as specified for <key>\n\n"
59  "Supported options");
60  // clang-format off
61  desc.add_options()
62  ("help,h", "Produce this help message")
63  ("atom-as", bpo::value<std::string>(&opts.atom_as),
64  "Return value for the supplied key as an atom with the provided C++ type.")
65  ("sequence-of", bpo::value<std::string>(&opts.sequence_of),
66  "Return value for the supplied key as a sequence of the provided C++ type.")
67  ("names-in", "Print the top-level names of the supplied key, which "
68  "must correspond to a FHiCL table.")
69  ("allow-missing",
70  "Return to the command line if the supplied key does not exist when using "
71  "the --names-in option.")
72  ("lookup-policy",
73  bpo::value<std::string>()->default_value("permissive"), "see --supported-policies")
74  ("lookup-path",
75  bpo::value<std::string>(&opts.lookup_path)->default_value(fhicl_env_var),
76  "path or environment variable to be used by lookup-policy")
77  ("supported-types", "list the C++ types supported for by the --atom-as and --sequence-of options.")
78  ("supported-policies", "list the supported file lookup policies");
79  // clang-format on
80 
81  bpo::options_description positional_desc;
82  auto split_args = [&opts](auto const& args) {
83  auto const n_args = size(args);
84  assert(n_args < 3ull);
85  if (n_args == 1ull) {
86  opts.input_filename = args[0];
87  return;
88  }
89  if (n_args == 2ull) {
90  opts.parameter_key = args[0];
91  opts.input_filename = args[1];
92  }
93  };
94 
95  // clang-format off
96  positional_desc.add_options()
97  ("key-conf", bpo::value<strings_t>()->composing()->notifier(split_args));
98  // clang-format on
99 
100  bpo::positional_options_description p;
101  p.add("key-conf", 2);
102 
103  bpo::options_description all;
104  all.add(desc).add(positional_desc);
105 
106  auto const vm = cet::parsed_program_options(argc, argv, all, p);
107 
108  if (vm.count("help")) {
109  std::ostringstream oss;
110  oss << '\n' << desc << '\n';
111  return Help{oss.str()};
112  }
113 
114  if (vm.count("supported-types")) {
115  std::string result{R"(
116 For the following command line:
117 
118  fhicl-get --atom-as=T <key> <file>
119 
120 the <file> is queried for the <key>, and an attempt is made to
121 interpret the corresponding value as an object of type T.
122 
123 If instead the command line were specified as:
124 
125  fhicl-get --sequence-of=T <key> <file>
126 
127 then the value corresponding to <key> would be interpreted as an
128 std::vector<T> object.
129 
130 For either the --atom-as or --sequence-of program options, an
131 exception will be thrown if the <key> parameter does not exist in the
132 <file>, or if the parameter does not correspond to a value that can be
133 interpreted according to the user-specified command-line.
134 
135 )"};
136  result += supported_types.help_message();
137  result += '\n';
138  return Help{result};
139  }
140 
141  cet::lookup_policy_selector const supported_policies{};
142  if (vm.count("supported-policies")) {
143  return Help{supported_policies.help_message()};
144  }
145 
146  if (empty(opts.input_filename)) {
147  std::ostringstream err_stream;
148  err_stream << "\nMissing input configuration file.\n\n" << desc << '\n';
149  throw cet::exception(config) << err_stream.str();
150  }
151 
152  opts.names_in = vm.count("names-in") == 1;
153  auto const options_count = vm.count("atom-as") + vm.count("sequence-of") +
154  static_cast<unsigned>(opts.names_in);
155 
156  if (options_count == 0) {
157  throw cet::exception(usage)
158  << "One of the 'atom-as', 'sequence-of', and 'names-in' program "
159  "options must be specified.\n";
160  }
161  if (options_count > 1) {
162  throw cet::exception(usage)
163  << "The 'atom-as', 'sequence-of', and 'names-in' program options "
164  "are mutually exclusive and may only appear once in the command "
165  "line.\n";
166  }
167 
168  if (not opts.names_in and empty(opts.parameter_key)) {
169  throw cet::exception(usage)
170  << "A key must be specified unless the '--names-in' option is used.\n";
171  }
172 
173  opts.allow_missing = vm.count("allow-missing");
174  if (opts.allow_missing) {
175  if (not opts.names_in) {
176  throw cet::exception(usage)
177  << "The 'allow-missing' program option may be used only with the "
178  "'names-in' option.\n";
179  }
180  if (empty(opts.parameter_key)) {
181  throw cet::exception(usage)
182  << "The 'allow-missing' program option may be used only when a "
183  "fully-qualified key is also specified.\n";
184  }
185  }
186  opts.policy =
187  supported_policies.select(vm["lookup-policy"].as<std::string>(),
188  vm["lookup-path"].as<std::string>());
189  return opts;
190  }
191 
192  void
193  print_names(fhicl::ParameterSet const& pset)
194  {
195  auto const names = pset.get_names();
196  for (auto const& name : names) {
197  std::cout << name << '\n';
198  }
199  }
200 
201  void
202  print_table_names(fhicl::ParameterSet const& pset,
203  std::string const& key,
204  bool const allow_missing)
205  {
206  if (empty(key)) {
207  // Top-level table
208  print_names(pset);
209  return;
210  }
211 
212  if (not pset.has_key(key)) {
213  if (allow_missing) {
214  return;
215  }
216  throw cet::exception{config}
217  << "A parameter with the fully-qualified key '" << key
218  << "' does not exist.";
219  }
220 
221  if (not pset.is_key_to_table(key)) {
222  throw cet::exception{config} << "The parameter named '" << key
223  << "' does not have a table value.";
224  }
225 
226  print_names(pset.get<fhicl::ParameterSet>(key));
227  }
228 }
229 
230 int
231 fhicl::detail::fhicl_get_impl(int argc, char** argv)
232 try {
233  supported_types_t const printer_for_types{};
234  auto const maybe_opts = process_arguments(argc, argv, printer_for_types);
235 
236  if (std::holds_alternative<Help>(maybe_opts)) {
237  std::cout << std::get<Help>(maybe_opts).msg;
238  return 0;
239  }
240 
241  auto const& opts = std::get<Options>(maybe_opts);
242 
243  auto const pset =
244  fhicl::ParameterSet::make(opts.input_filename, *opts.policy);
245 
246  auto const& key = opts.parameter_key;
247 
248  if (opts.names_in) {
249  print_table_names(pset, key, opts.allow_missing);
250  return 0;
251  }
252 
253  if (not pset.has_key(key)) {
254  throw cet::exception{config} << "A parameter with the fully-qualified key '"
255  << key << "' does not exist.";
256  }
257 
258  if (not empty(opts.atom_as)) {
259  printer_for_types.value_for_atom(pset, opts.atom_as, key);
260  return 0;
261  }
262 
263  if (not empty(opts.sequence_of)) {
264  printer_for_types.value_for_sequence(pset, opts.sequence_of, key);
265  return 0;
266  }
267 
268  return 0;
269 }
270 catch (std::exception const& e) {
271  std::cerr << e.what();
272  return 1;
273 }
static ParameterSet make(intermediate_table const &tbl)
Definition: ParameterSet.cc:68
decltype(auto) constexpr size(T &&obj)
ADL-aware version of std::size.
Definition: StdUtils.h:101
bool is_key_to_table(std::string const &key) const
Definition: ParameterSet.h:208
T get(std::string const &key) const
Definition: ParameterSet.h:314
int fhicl_get_impl(int argc, char **argv)
bool has_key(std::string const &key) const
std::vector< std::string > get_names() const
void usage()
Definition: genwindef.cc:350
Float_t e
Definition: plot.C:35
cet::coded_exception< error, detail::translate > exception
Definition: exception.h:33
decltype(auto) constexpr empty(T &&obj)
ADL-aware version of std::empty.
Definition: StdUtils.h:109