Home | History | Annotate | Download | only in cmdline
      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 {
     64     SaveDestination() : variant_map_(new TVariantMap()) {}
     65 
     66     // Save value to the variant map.
     67     template <typename TArg>
     68     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>
     74     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.
     88     TVariantMap&& ReleaseMap() {
     89       return std::move(*variant_map_);
     90     }
     91 
     92     // Get a read-only reference to the variant map.
     93     const TVariantMap& GetMap() {
     94       return *variant_map_;
     95     }
     96 
     97     // Clear all potential save targets.
     98     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.
    122     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.
    134     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 {}.
    141     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.
    150     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.
    162     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
    173     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.
    203     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 
    219     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 
    226     void SetNames(std::vector<const char*>&& names) {
    227       argument_info_.names_ = names;
    228     }
    229 
    230     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.
    240     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 
    261     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>
    290     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>
    296     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>
    305     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.
    311     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:
    319     void SetNames(std::vector<const char*>&& names) {
    320       names_ = std::move(names);
    321     }
    322 
    323     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>
    332     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
    341     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
    348     void InitializeTypedBuilder(void*) {}
    349 
    350     template <typename TArg>
    351     friend struct ArgumentBuilder;
    352     friend struct Builder;
    353 
    354     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 {
    363     Builder() : save_destination_(new SaveDestination()) {}
    364 
    365     // Define a single argument. The default type is Unit.
    366     UntypedArgumentBuilder Define(const char* name) {
    367       return Define({name});
    368     }
    369 
    370     // Define a single argument with multiple aliases.
    371     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.
    378     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.
    384     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.
    411     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:
    424     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 
    446   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.
    454   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).
    460   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).
    466   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.
    472   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.
    477   const TVariantMap& GetArgumentsMap() const {
    478     return save_destination_->GetMap();
    479   }
    480 
    481   // Release the arguments map that has been parsed; useful for move semantics.
    482   TVariantMap&& ReleaseArgumentsMap() {
    483     return save_destination_->ReleaseMap();
    484   }
    485 
    486   // How many arguments were defined?
    487   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.
    500   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.
    517   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>
    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>
    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