• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2015 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #ifndef ART_CMDLINE_CMDLINE_PARSER_H_
18 #define ART_CMDLINE_CMDLINE_PARSER_H_
19 
20 #define CMDLINE_NDEBUG 1  // Do not output any debugging information for parsing.
21 
22 #include "cmdline/detail/cmdline_parser_detail.h"
23 #include "cmdline/detail/cmdline_parse_argument_detail.h"
24 #include "cmdline/detail/cmdline_debug_detail.h"
25 
26 #include "cmdline_type_parser.h"
27 #include "token_range.h"
28 #include "cmdline_types.h"
29 #include "cmdline_result.h"
30 #include "cmdline_parse_result.h"
31 
32 #include "runtime/base/variant_map.h"
33 
34 #include <vector>
35 #include <memory>
36 
37 namespace art {
38 // Build a parser for command line arguments with a small domain specific language.
39 // Each parsed type must have a specialized CmdlineType<T> in order to do the string->T parsing.
40 // Each argument must also have a VariantMap::Key<T> in order to do the T storage.
41 template <typename TVariantMap,
42           template <typename TKeyValue> class TVariantMapKey>
43 struct CmdlineParser {
44   template <typename TArg>
45   struct ArgumentBuilder;
46 
47   struct Builder;  // Build the parser.
48   struct UntypedArgumentBuilder;  // Build arguments which weren't yet given a type.
49 
50  private:
51   // Forward declare some functions that we need to use before fully-defining structs.
52   template <typename TArg>
53   static ArgumentBuilder<TArg> CreateArgumentBuilder(Builder& parent);
54   static void AppendCompletedArgument(Builder& builder, detail::CmdlineParseArgumentAny* arg);
55 
56   // Allow argument definitions to save their values when they are parsed,
57   // without having a dependency on CmdlineParser or any of the builders.
58   //
59   // A shared pointer to the save destination is saved into the load/save argument callbacks.
60   //
61   // This also allows the underlying storage (i.e. a variant map) to be released
62   // to the user, without having to recreate all of the callbacks.
63   struct SaveDestination {
SaveDestinationCmdlineParser::SaveDestination64     SaveDestination() : variant_map_(new TVariantMap()) {}
65 
66     // Save value to the variant map.
67     template <typename TArg>
SaveToMapCmdlineParser::SaveDestination68     void SaveToMap(const TVariantMapKey<TArg>& key, TArg& value) {
69       variant_map_->Set(key, value);
70     }
71 
72     // Get the existing value from a map, creating the value if it did not already exist.
73     template <typename TArg>
GetOrCreateFromMapCmdlineParser::SaveDestination74     TArg& GetOrCreateFromMap(const TVariantMapKey<TArg>& key) {
75       auto* ptr = variant_map_->Get(key);
76       if (ptr == nullptr) {
77         variant_map_->Set(key, TArg());
78         ptr = variant_map_->Get(key);
79         assert(ptr != nullptr);
80       }
81 
82       return *ptr;
83     }
84 
85    protected:
86     // Release the map, clearing it as a side-effect.
87     // Future saves will be distinct from previous saves.
ReleaseMapCmdlineParser::SaveDestination88     TVariantMap&& ReleaseMap() {
89       return std::move(*variant_map_);
90     }
91 
92     // Get a read-only reference to the variant map.
GetMapCmdlineParser::SaveDestination93     const TVariantMap& GetMap() {
94       return *variant_map_;
95     }
96 
97     // Clear all potential save targets.
ClearCmdlineParser::SaveDestination98     void Clear() {
99       variant_map_->Clear();
100     }
101 
102    private:
103     // Don't try to copy or move this. Just don't.
104     SaveDestination(const SaveDestination&) = delete;
105     SaveDestination(SaveDestination&&) = delete;
106     SaveDestination& operator=(const SaveDestination&) = delete;
107     SaveDestination& operator=(SaveDestination&&) = delete;
108 
109     std::shared_ptr<TVariantMap> variant_map_;
110 
111     // Allow the parser to change the underlying pointers when we release the underlying storage.
112     friend struct CmdlineParser;
113   };
114 
115  public:
116   // Builder for the argument definition of type TArg. Do not use this type directly,
117   // it is only a separate type to provide compile-time enforcement against doing
118   // illegal builds.
119   template <typename TArg>
120   struct ArgumentBuilder {
121     // Add a range check to this argument.
WithRangeCmdlineParser::ArgumentBuilder122     ArgumentBuilder<TArg>& WithRange(const TArg& min, const TArg& max) {
123       argument_info_.has_range_ = true;
124       argument_info_.min_ = min;
125       argument_info_.max_ = max;
126 
127       return *this;
128     }
129 
130     // Map the list of names into the list of values. List of names must not have
131     // any wildcards '_' in it.
132     //
133     // Do not use if a value map has already been set.
WithValuesCmdlineParser::ArgumentBuilder134     ArgumentBuilder<TArg>& WithValues(std::initializer_list<TArg> value_list) {
135       SetValuesInternal(value_list);
136       return *this;
137     }
138 
139     // When used with a single alias, map the alias into this value.
140     // Same as 'WithValues({value})' , but allows the omission of the curly braces {}.
WithValueCmdlineParser::ArgumentBuilder141     ArgumentBuilder<TArg> WithValue(const TArg& value) {
142       return WithValues({ value });
143     }
144 
145     // Map the parsed string values (from _) onto a concrete value. If no wildcard
146     // has been specified, then map the value directly from the arg name (i.e.
147     // if there are multiple aliases, then use the alias to do the mapping).
148     //
149     // Do not use if a values list has already been set.
WithValueMapCmdlineParser::ArgumentBuilder150     ArgumentBuilder<TArg>& WithValueMap(
151         std::initializer_list<std::pair<const char*, TArg>> key_value_list) {
152       assert(!argument_info_.has_value_list_);
153 
154       argument_info_.has_value_map_ = true;
155       argument_info_.value_map_ = key_value_list;
156 
157       return *this;
158     }
159 
160     // If this argument is seen multiple times, successive arguments mutate the same value
161     // instead of replacing it with a new value.
AppendValuesCmdlineParser::ArgumentBuilder162     ArgumentBuilder<TArg>& AppendValues() {
163       argument_info_.appending_values_ = true;
164 
165       return *this;
166     }
167 
168     // Convenience type alias for the variant map key type definition.
169     using MapKey = TVariantMapKey<TArg>;
170 
171     // Write the results of this argument into the key.
172     // To look up the parsed arguments, get the map and then use this key with VariantMap::Get
IntoKeyCmdlineParser::ArgumentBuilder173     CmdlineParser::Builder& IntoKey(const MapKey& key) {
174       // Only capture save destination as a pointer.
175       // This allows the parser to later on change the specific save targets.
176       auto save_destination = save_destination_;
177       save_value_ = [save_destination, &key](TArg& value) {
178         save_destination->SaveToMap(key, value);
179         CMDLINE_DEBUG_LOG << "Saved value into map '"
180             << detail::ToStringAny(value) << "'" << std::endl;
181       };
182 
183       load_value_ = [save_destination, &key]() -> TArg& {
184         TArg& value = save_destination->GetOrCreateFromMap(key);
185         CMDLINE_DEBUG_LOG << "Loaded value from map '" << detail::ToStringAny(value) << "'"
186             << std::endl;
187 
188         return value;
189       };
190 
191       save_value_specified_ = true;
192       load_value_specified_ = true;
193 
194       CompleteArgument();
195       return parent_;
196     }
197 
198     // Ensure we always move this when returning a new builder.
199     ArgumentBuilder(ArgumentBuilder&&) = default;
200 
201    protected:
202     // Used by builder to internally ignore arguments by dropping them on the floor after parsing.
IntoIgnoreCmdlineParser::ArgumentBuilder203     CmdlineParser::Builder& IntoIgnore() {
204       save_value_ = [](TArg& value) {
205         CMDLINE_DEBUG_LOG << "Ignored value '" << detail::ToStringAny(value) << "'" << std::endl;
206       };
207       load_value_ = []() -> TArg& {
208         assert(false && "Should not be appending values to ignored arguments");
209         return *reinterpret_cast<TArg*>(0);  // Blow up.
210       };
211 
212       save_value_specified_ = true;
213       load_value_specified_ = true;
214 
215       CompleteArgument();
216       return parent_;
217     }
218 
SetValuesInternalCmdlineParser::ArgumentBuilder219     void SetValuesInternal(const std::vector<TArg>&& value_list) {
220       assert(!argument_info_.has_value_map_);
221 
222       argument_info_.has_value_list_ = true;
223       argument_info_.value_list_ = value_list;
224     }
225 
SetNamesCmdlineParser::ArgumentBuilder226     void SetNames(std::vector<const char*>&& names) {
227       argument_info_.names_ = names;
228     }
229 
SetNamesCmdlineParser::ArgumentBuilder230     void SetNames(std::initializer_list<const char*> names) {
231       argument_info_.names_ = names;
232     }
233 
234    private:
235     // Copying is bad. Move only.
236     ArgumentBuilder(const ArgumentBuilder&) = delete;
237 
238     // Called by any function that doesn't chain back into this builder.
239     // Completes the argument builder and save the information into the main builder.
CompleteArgumentCmdlineParser::ArgumentBuilder240     void CompleteArgument() {
241       assert(save_value_specified_ &&
242              "No Into... function called, nowhere to save parsed values to");
243       assert(load_value_specified_ &&
244              "No Into... function called, nowhere to load parsed values from");
245 
246       argument_info_.CompleteArgument();
247 
248       // Appending the completed argument is destructive. The object is no longer
249       // usable since all the useful information got moved out of it.
250       AppendCompletedArgument(parent_,
251                               new detail::CmdlineParseArgument<TArg>(
252                                   std::move(argument_info_),
253                                   std::move(save_value_),
254                                   std::move(load_value_)));
255     }
256 
257     friend struct CmdlineParser;
258     friend struct CmdlineParser::Builder;
259     friend struct CmdlineParser::UntypedArgumentBuilder;
260 
ArgumentBuilderCmdlineParser::ArgumentBuilder261     ArgumentBuilder(CmdlineParser::Builder& parser,
262                     std::shared_ptr<SaveDestination> save_destination)
263         : parent_(parser),
264           save_value_specified_(false),
265           load_value_specified_(false),
266           save_destination_(save_destination) {
267       save_value_ = [](TArg&) {
268         assert(false && "No save value function defined");
269       };
270 
271       load_value_ = []() -> TArg& {
272         assert(false && "No load value function defined");
273         return *reinterpret_cast<TArg*>(0);  // Blow up.
274       };
275     }
276 
277     CmdlineParser::Builder& parent_;
278     std::function<void(TArg&)> save_value_;
279     std::function<TArg&(void)> load_value_;
280     bool save_value_specified_;
281     bool load_value_specified_;
282     detail::CmdlineParserArgumentInfo<TArg> argument_info_;
283 
284     std::shared_ptr<SaveDestination> save_destination_;
285   };
286 
287   struct UntypedArgumentBuilder {
288     // Set a type for this argument. The specific subcommand parser is looked up by the type.
289     template <typename TArg>
WithTypeCmdlineParser::UntypedArgumentBuilder290     ArgumentBuilder<TArg> WithType() {
291       return CreateTypedBuilder<TArg>();
292     }
293 
294     // When used with multiple aliases, map the position of the alias to the value position.
295     template <typename TArg>
WithValuesCmdlineParser::UntypedArgumentBuilder296     ArgumentBuilder<TArg> WithValues(std::initializer_list<TArg> values) {
297       auto&& a = CreateTypedBuilder<TArg>();
298       a.WithValues(values);
299       return std::move(a);
300     }
301 
302     // When used with a single alias, map the alias into this value.
303     // Same as 'WithValues({value})' , but allows the omission of the curly braces {}.
304     template <typename TArg>
WithValueCmdlineParser::UntypedArgumentBuilder305     ArgumentBuilder<TArg> WithValue(const TArg& value) {
306       return WithValues({ value });
307     }
308 
309     // Set the current building argument to target this key.
310     // When this command line argument is parsed, it can be fetched with this key.
IntoKeyCmdlineParser::UntypedArgumentBuilder311     Builder& IntoKey(const TVariantMapKey<Unit>& key) {
312       return CreateTypedBuilder<Unit>().IntoKey(key);
313     }
314 
315     // Ensure we always move this when returning a new builder.
316     UntypedArgumentBuilder(UntypedArgumentBuilder&&) = default;
317 
318    protected:
SetNamesCmdlineParser::UntypedArgumentBuilder319     void SetNames(std::vector<const char*>&& names) {
320       names_ = std::move(names);
321     }
322 
SetNamesCmdlineParser::UntypedArgumentBuilder323     void SetNames(std::initializer_list<const char*> names) {
324       names_ = names;
325     }
326 
327    private:
328     // No copying. Move instead.
329     UntypedArgumentBuilder(const UntypedArgumentBuilder&) = delete;
330 
331     template <typename TArg>
CreateTypedBuilderCmdlineParser::UntypedArgumentBuilder332     ArgumentBuilder<TArg> CreateTypedBuilder() {
333       auto&& b = CreateArgumentBuilder<TArg>(parent_);
334       InitializeTypedBuilder(&b);  // Type-specific initialization
335       b.SetNames(std::move(names_));
336       return std::move(b);
337     }
338 
339     template <typename TArg = Unit>
340     typename std::enable_if<std::is_same<TArg, Unit>::value>::type
InitializeTypedBuilderCmdlineParser::UntypedArgumentBuilder341     InitializeTypedBuilder(ArgumentBuilder<TArg>* arg_builder) {
342       // Every Unit argument implicitly maps to a runtime value of Unit{}
343       std::vector<Unit> values(names_.size(), Unit{});  // NOLINT [whitespace/braces] [5]
344       arg_builder->SetValuesInternal(std::move(values));
345     }
346 
347     // No extra work for all other types
InitializeTypedBuilderCmdlineParser::UntypedArgumentBuilder348     void InitializeTypedBuilder(void*) {}
349 
350     template <typename TArg>
351     friend struct ArgumentBuilder;
352     friend struct Builder;
353 
UntypedArgumentBuilderCmdlineParser::UntypedArgumentBuilder354     explicit UntypedArgumentBuilder(CmdlineParser::Builder& parent) : parent_(parent) {}
355     // UntypedArgumentBuilder(UntypedArgumentBuilder&& other) = default;
356 
357     CmdlineParser::Builder& parent_;
358     std::vector<const char*> names_;
359   };
360 
361   // Build a new parser given a chain of calls to define arguments.
362   struct Builder {
BuilderCmdlineParser::Builder363     Builder() : save_destination_(new SaveDestination()) {}
364 
365     // Define a single argument. The default type is Unit.
DefineCmdlineParser::Builder366     UntypedArgumentBuilder Define(const char* name) {
367       return Define({name});
368     }
369 
370     // Define a single argument with multiple aliases.
DefineCmdlineParser::Builder371     UntypedArgumentBuilder Define(std::initializer_list<const char*> names) {
372       auto&& b = UntypedArgumentBuilder(*this);
373       b.SetNames(names);
374       return std::move(b);
375     }
376 
377     // Whether the parser should give up on unrecognized arguments. Not recommended.
IgnoreUnrecognizedCmdlineParser::Builder378     Builder& IgnoreUnrecognized(bool ignore_unrecognized) {
379       ignore_unrecognized_ = ignore_unrecognized;
380       return *this;
381     }
382 
383     // Provide a list of arguments to ignore for backwards compatibility.
IgnoreCmdlineParser::Builder384     Builder& Ignore(std::initializer_list<const char*> ignore_list) {
385       for (auto&& ignore_name : ignore_list) {
386         std::string ign = ignore_name;
387 
388         // Ignored arguments are just like a regular definition which have very
389         // liberal parsing requirements (no range checks, no value checks).
390         // Unlike regular argument definitions, when a value gets parsed into its
391         // stronger type, we just throw it away.
392 
393         if (ign.find("_") != std::string::npos) {  // Does the arg-def have a wildcard?
394           // pretend this is a string, e.g. -Xjitconfig:<anythinggoeshere>
395           auto&& builder = Define(ignore_name).template WithType<std::string>().IntoIgnore();
396           assert(&builder == this);
397           (void)builder;  // Ignore pointless unused warning, it's used in the assert.
398         } else {
399           // pretend this is a unit, e.g. -Xjitblocking
400           auto&& builder = Define(ignore_name).template WithType<Unit>().IntoIgnore();
401           assert(&builder == this);
402           (void)builder;  // Ignore pointless unused warning, it's used in the assert.
403         }
404       }
405       ignore_list_ = ignore_list;
406       return *this;
407     }
408 
409     // Finish building the parser; performs sanity checks. Return value is moved, not copied.
410     // Do not call this more than once.
BuildCmdlineParser::Builder411     CmdlineParser Build() {
412       assert(!built_);
413       built_ = true;
414 
415       auto&& p = CmdlineParser(ignore_unrecognized_,
416                                std::move(ignore_list_),
417                                save_destination_,
418                                std::move(completed_arguments_));
419 
420       return std::move(p);
421     }
422 
423    protected:
AppendCompletedArgumentCmdlineParser::Builder424     void AppendCompletedArgument(detail::CmdlineParseArgumentAny* arg) {
425       auto smart_ptr = std::unique_ptr<detail::CmdlineParseArgumentAny>(arg);
426       completed_arguments_.push_back(std::move(smart_ptr));
427     }
428 
429    private:
430     // No copying now!
431     Builder(const Builder& other) = delete;
432 
433     template <typename TArg>
434     friend struct ArgumentBuilder;
435     friend struct UntypedArgumentBuilder;
436     friend struct CmdlineParser;
437 
438     bool built_ = false;
439     bool ignore_unrecognized_ = false;
440     std::vector<const char*> ignore_list_;
441     std::shared_ptr<SaveDestination> save_destination_;
442 
443     std::vector<std::unique_ptr<detail::CmdlineParseArgumentAny>> completed_arguments_;
444   };
445 
ParseCmdlineParser446   CmdlineResult Parse(const std::string& argv) {
447     std::vector<std::string> tokenized;
448     Split(argv, ' ', &tokenized);
449 
450     return Parse(TokenRange(std::move(tokenized)));
451   }
452 
453   // Parse the arguments; storing results into the arguments map. Returns success value.
ParseCmdlineParser454   CmdlineResult Parse(const char* argv) {
455     return Parse(std::string(argv));
456   }
457 
458   // Parse the arguments; storing the results into the arguments map. Returns success value.
459   // Assumes that argv[0] is a valid argument (i.e. not the program name).
ParseCmdlineParser460   CmdlineResult Parse(const std::vector<const char*>& argv) {
461     return Parse(TokenRange(argv.begin(), argv.end()));
462   }
463 
464   // Parse the arguments; storing the results into the arguments map. Returns success value.
465   // Assumes that argv[0] is a valid argument (i.e. not the program name).
ParseCmdlineParser466   CmdlineResult Parse(const std::vector<std::string>& argv) {
467     return Parse(TokenRange(argv.begin(), argv.end()));
468   }
469 
470   // Parse the arguments (directly from an int main(argv,argc)). Returns success value.
471   // Assumes that argv[0] is the program name, and ignores it.
ParseCmdlineParser472   CmdlineResult Parse(const char* argv[], int argc) {
473     return Parse(TokenRange(&argv[1], argc - 1));  // ignore argv[0] because it's the program name
474   }
475 
476   // Look up the arguments that have been parsed; use the target keys to lookup individual args.
GetArgumentsMapCmdlineParser477   const TVariantMap& GetArgumentsMap() const {
478     return save_destination_->GetMap();
479   }
480 
481   // Release the arguments map that has been parsed; useful for move semantics.
ReleaseArgumentsMapCmdlineParser482   TVariantMap&& ReleaseArgumentsMap() {
483     return save_destination_->ReleaseMap();
484   }
485 
486   // How many arguments were defined?
CountDefinedArgumentsCmdlineParser487   size_t CountDefinedArguments() const {
488     return completed_arguments_.size();
489   }
490 
491   // Ensure we have a default move constructor.
492   CmdlineParser(CmdlineParser&&) = default;
493   // Ensure we have a default move assignment operator.
494   CmdlineParser& operator=(CmdlineParser&&) = default;
495 
496  private:
497   friend struct Builder;
498 
499   // Construct a new parser from the builder. Move all the arguments.
CmdlineParserCmdlineParser500   CmdlineParser(bool ignore_unrecognized,
501                 std::vector<const char*>&& ignore_list,
502                 std::shared_ptr<SaveDestination> save_destination,
503                 std::vector<std::unique_ptr<detail::CmdlineParseArgumentAny>>&& completed_arguments)
504     : ignore_unrecognized_(ignore_unrecognized),
505       ignore_list_(std::move(ignore_list)),
506       save_destination_(save_destination),
507       completed_arguments_(std::move(completed_arguments)) {
508     assert(save_destination != nullptr);
509   }
510 
511   // Parse the arguments; storing results into the arguments map. Returns success value.
512   // The parsing will fail on the first non-success parse result and return that error.
513   //
514   // All previously-parsed arguments are cleared out.
515   // Otherwise, all parsed arguments will be stored into SaveDestination as a side-effect.
516   // A partial parse will result only in a partial save of the arguments.
ParseCmdlineParser517   CmdlineResult Parse(TokenRange&& arguments_list) {
518     save_destination_->Clear();
519 
520     for (size_t i = 0; i < arguments_list.Size(); ) {
521       TokenRange possible_name = arguments_list.Slice(i);
522 
523       size_t best_match_size = 0;  // How many tokens were matched in the best case.
524       size_t best_match_arg_idx = 0;
525       bool matched = false;  // At least one argument definition has been matched?
526 
527       // Find the closest argument definition for the remaining token range.
528       size_t arg_idx = 0;
529       for (auto&& arg : completed_arguments_) {
530         size_t local_match = arg->MaybeMatches(possible_name);
531 
532         if (local_match > best_match_size) {
533           best_match_size = local_match;
534           best_match_arg_idx = arg_idx;
535           matched = true;
536         }
537         arg_idx++;
538       }
539 
540       // Saw some kind of unknown argument
541       if (matched == false) {
542         if (UNLIKELY(ignore_unrecognized_)) {  // This is usually off, we only need it for JNI.
543           // Consume 1 token and keep going, hopefully the next token is a good one.
544           ++i;
545           continue;
546         }
547         // Common case:
548         // Bail out on the first unknown argument with an error.
549         return CmdlineResult(CmdlineResult::kUnknown,
550                              std::string("Unknown argument: ") + possible_name[0]);
551       }
552 
553       // Look at the best-matched argument definition and try to parse against that.
554       auto&& arg = completed_arguments_[best_match_arg_idx];
555 
556       assert(arg->MaybeMatches(possible_name) == best_match_size);
557 
558       // Try to parse the argument now, if we have enough tokens.
559       std::pair<size_t, size_t> num_tokens = arg->GetNumTokens();
560       size_t min_tokens;
561       size_t max_tokens;
562 
563       std::tie(min_tokens, max_tokens) = num_tokens;
564 
565       if ((i + min_tokens) > arguments_list.Size()) {
566         // expected longer command line but it was too short
567         // e.g. if the argv was only "-Xms" without specifying a memory option
568         CMDLINE_DEBUG_LOG << "Parse failure, i = " << i << ", arg list " << arguments_list.Size() <<
569             " num tokens in arg_def: " << min_tokens << "," << max_tokens << std::endl;
570         return CmdlineResult(CmdlineResult::kFailure,
571                              std::string("Argument ") +
572                              possible_name[0] + ": incomplete command line arguments, expected "
573                              + std::to_string(size_t(i + min_tokens) - arguments_list.Size()) +
574                              " more tokens");
575       }
576 
577       if (best_match_size > max_tokens || best_match_size < min_tokens) {
578         // Even our best match was out of range, so parsing would fail instantly.
579         return CmdlineResult(CmdlineResult::kFailure,
580                              std::string("Argument ") + possible_name[0] + ": too few tokens "
581                              "matched " + std::to_string(best_match_size)
582                              + " but wanted " + std::to_string(num_tokens.first));
583       }
584 
585       // We have enough tokens to begin exact parsing.
586       TokenRange exact_range = possible_name.Slice(0, max_tokens);
587 
588       size_t consumed_tokens = 1;  // At least 1 if we ever want to try to resume parsing on error
589       CmdlineResult parse_attempt = arg->ParseArgument(exact_range, &consumed_tokens);
590 
591       if (parse_attempt.IsError()) {
592         // We may also want to continue parsing the other tokens to gather more errors.
593         return parse_attempt;
594       }  // else the value has been successfully stored into the map
595 
596       assert(consumed_tokens > 0);  // Don't hang in an infinite loop trying to parse
597       i += consumed_tokens;
598 
599       // TODO: also handle ignoring arguments for backwards compatibility
600     }  // for
601 
602     return CmdlineResult(CmdlineResult::kSuccess);
603   }
604 
605   bool ignore_unrecognized_ = false;
606   std::vector<const char*> ignore_list_;
607   std::shared_ptr<SaveDestination> save_destination_;
608   std::vector<std::unique_ptr<detail::CmdlineParseArgumentAny>> completed_arguments_;
609 };
610 
611 // This has to be defined after everything else, since we want the builders to call this.
612 template <typename TVariantMap,
613           template <typename TKeyValue> class TVariantMapKey>
614 template <typename TArg>
615 CmdlineParser<TVariantMap, TVariantMapKey>::ArgumentBuilder<TArg>
CreateArgumentBuilder(CmdlineParser<TVariantMap,TVariantMapKey>::Builder & parent)616 CmdlineParser<TVariantMap, TVariantMapKey>::CreateArgumentBuilder(
617     CmdlineParser<TVariantMap, TVariantMapKey>::Builder& parent) {
618   return CmdlineParser<TVariantMap, TVariantMapKey>::ArgumentBuilder<TArg>(
619       parent, parent.save_destination_);
620 }
621 
622 // This has to be defined after everything else, since we want the builders to call this.
623 template <typename TVariantMap,
624           template <typename TKeyValue> class TVariantMapKey>
AppendCompletedArgument(CmdlineParser<TVariantMap,TVariantMapKey>::Builder & builder,detail::CmdlineParseArgumentAny * arg)625 void CmdlineParser<TVariantMap, TVariantMapKey>::AppendCompletedArgument(
626     CmdlineParser<TVariantMap, TVariantMapKey>::Builder& builder,
627     detail::CmdlineParseArgumentAny* arg) {
628   builder.AppendCompletedArgument(arg);
629 }
630 
631 }  // namespace art
632 
633 #endif  // ART_CMDLINE_CMDLINE_PARSER_H_
634