处理命令行参数args、argc


代码来自WebRTC中的rtc_base基础模块, flags.hflags.cc

支持的功能

  1. 每个Flag有一个默认的值,不指定时使用默认值,指定时使用指定的值
  2. Flag不需要通过函数去传递,允许在任意一个文件定义
  3. 选择能同时支持---,支持注释,支持打印所有的Flag
  4. 自动解析命令行参数,并能检查参数类型是否正确

如何实现

  • 定义一个union类型的FlagValue,支持bool、int、double、const char*类型
// Internal use only.
union FlagValue {
  // Note: Because in C++ non-bool values are silently converted into
  // bool values ('bool b = "false";' results in b == true!), we pass
  // and int argument to New_BOOL as this appears to be safer - sigh.
  // In particular, it prevents the (not uncommon!) bug where a bool
  // flag is defined via: DEFINE_bool(flag, "false", "some comment");.
  static FlagValue New_BOOL(int b) {
    FlagValue v;
    v.b = (b != 0);
    return v;
  }

  static FlagValue New_INT(int i) {
    FlagValue v;
    v.i = i;
    return v;
  }

  static FlagValue New_FLOAT(float f) {
    FlagValue v;
    v.f = f;
    return v;
  }

  static FlagValue New_STRING(const char* s) {
    FlagValue v;
    v.s = s;
    return v;
  }

  bool b;
  int i;
  double f;
  const char* s;
};
  • 定义Flag类,类中保存了当前Flag的定义的文件位置,名字,注释,类型,默认值,用户通过命令行设置的值,并加入到链表中
// Each flag can be accessed programmatically via a Flag object.
class Flag {
 public:
  enum Type { BOOL, INT, FLOAT, STRING };

  // Internal use only.
  Flag(const char* file, const char* name, const char* comment,
       Type type, void* variable, FlagValue default_) : file_(file), name_(name), comment_(comment), type_(type), variable_(reinterpret_cast<FlagValue*>(variable)),
      default_(default__) {  FlagList::Register(this); }

  // General flag information
  const char* file() const  { return file_; }
  const char* name() const  { return name_; }
  const char* comment() const  { return comment_; }

  // Flag type
  Type type() const  { return type_; }

  // Flag variables
  bool* bool_variable() const {  RTC_DCHECK_EQ(BOOL, type_);  return &variable_->b;  }
  int* int_variable() const {  RTC_DCHECK_EQ(INT, type_); return &variable_->i; }
  double* float_variable() const { RTC_DCHECK_EQ(FLOAT, type_); return &variable_->f; }
  const char** string_variable() const {  RTC_DCHECK_EQ(STRING, type_); return &variable_->s;  }

  // Iteration support
  Flag* next() const  { return next_; }

  // Prints flag information. The current flag value is only printed
  // if print_current_value is set.
void Print(bool print_current_value) {
  printf("  --%s (%s)  type: %s  default: ", name_, comment_, Type2String(type_));
  PrintFlagValue(type_, &default_);
  if (print_current_value) {
    printf("  current value: ");   PrintFlagValue(type_, variable_);
  }
  printf("\n");
}

 private:
  const char* file_;
  const char* name_;
  const char* comment_;
  Type type_;
  FlagValue* variable_;
  FlagValue default_;
  Flag* next_;
  friend class FlagList;  // accesses next_
};
  • 定义了数据结构之后,可以使用宏来自动设置文件名和一个固定类型的全局静态变量,变量名格式是FLAG_XXX,这个XXX就是我们自己设置的
// Internal use only.
#define DEFINE_FLAG(type, c_type, name, default, comment) \
  /* define and initialize the flag */                    \
  c_type FLAG_##name = (default);                         \
  /* register the flag */                                 \
  static rtc::Flag Flag_##name(__FILE__, #name, (comment),      \
                               rtc::Flag::type, &FLAG_##name,   \
                               rtc::FlagValue::New_##type(default))

// Use the following macros to define a new flag:
#define DEFINE_bool(name, default, comment) \
  DEFINE_FLAG(BOOL, bool, name, default, comment)
#define DEFINE_int(name, default, comment) \
  DEFINE_FLAG(INT, int, name, default, comment)
#define DEFINE_float(name, default, comment) \
  DEFINE_FLAG(FLOAT, double, name, default, comment)
#define DEFINE_string(name, default, comment) \
  DEFINE_FLAG(STRING, const char*, name, default, comment)
  • 完成了上面步骤之后我们定义的所以Flag都会存到FlagList结构的list_链表中,我们需要在main函数中把所有的argc传递给FlagList去解析,当匹配到用户设置的值时取代默认值
Flag* FlagList::Lookup(const char* name) {
  Flag* f = list_;
  while (f != nullptr && strcmp(name, f->name()) != 0)
    f = f->next();
  return f;
}

void FlagList::SplitArgument(const char* arg, char* buffer, int buffer_size, const char** name, const char** value, bool* is_bool) {
  *name = nullptr;
  *value = nullptr;
  *is_bool = false;

  if (*arg == '-') {
    // find the begin of the flag name
    arg++;  // remove 1st '-'
    if (*arg == '-')
      arg++;  // remove 2nd '-'
    if (arg[0] == 'n' && arg[1] == 'o' && Lookup(arg + 2)) {
      arg += 2;  // remove "no"
      *is_bool = true;
    }
    *name = arg;

    // find the end of the flag name
    while (*arg != '\0' && *arg != '=')
      arg++;

    // get the value if any
    if (*arg == '=') {
      // make a copy so we can NUL-terminate flag name
      int n = static_cast<int>(arg - *name);
      RTC_CHECK_LT(n, buffer_size);
      memcpy(buffer, *name, n * sizeof(char));
      buffer[n] = '\0';
      *name = buffer;
      // get the value
      *value = arg + 1;
    }
  }
}


int FlagList::SetFlagsFromCommandLine(int* argc, const char** argv, bool remove_flags) {
  // parse arguments
  for (int i = 1; i < *argc; /* see below */) {
    int j = i;  // j > 0
    const char* arg = argv[i++];

    // split arg into flag components
    char buffer[1024];
    const char* name;
    const char* value;
    bool is_bool;
    SplitArgument(arg, buffer, sizeof buffer, &name, &value, &is_bool);

    if (name != nullptr) {
      // lookup the flag
      Flag* flag = Lookup(name);
      if (flag == nullptr) {
        fprintf(stderr, "Error: unrecognized flag %s\n", arg);
        return j;
      }

      // if we still need a flag value, use the next argument if available
      if (flag->type() != Flag::BOOL && value == nullptr) {
        if (i < *argc) {
          value = argv[i++];
        } else {
          fprintf(stderr, "Error: missing value for flag %s of type %s\n",
            arg, Type2String(flag->type()));
          return j;
        }
      }

      // set the flag
      char empty[] = { '\0' };
      char* endp = empty;
      switch (flag->type()) {
        case Flag::BOOL:
          *flag->bool_variable() = !is_bool;
          break;
        case Flag::INT:
          *flag->int_variable() = strtol(value, &endp, 10);
          break;
        case Flag::FLOAT:
          *flag->float_variable() = strtod(value, &endp);
          break;
        case Flag::STRING:
          *flag->string_variable() = value;
          break;
      }

      // handle errors
      if ((flag->type() == Flag::BOOL && value != nullptr) ||
          (flag->type() != Flag::BOOL && is_bool) || *endp != '\0') {
        fprintf(stderr, "Error: illegal value for flag %s of type %s\n",
          arg, Type2String(flag->type()));
        return j;
      }

      // remove the flag & value from the command
      if (remove_flags)
        while (j < i)
          argv[j++] = nullptr;
    }
  }

  // shrink the argument list
  if (remove_flags) {
    int j = 1;
    for (int i = 1; i < *argc; i++) {
      if (argv[i] != nullptr)
        argv[j++] = argv[i];
    }
    *argc = j;
  }

  // parsed all flags successfully
  return 0;
}

void FlagList::Register(Flag* flag) {
  RTC_DCHECK(flag);
  RTC_DCHECK_GT(strlen(flag->name()), 0);
  // NOTE: Don't call Lookup() within Register because it accesses the name_
  // of other flags in list_, and if the flags are coming from two different
  // compilation units, the initialization order between them is undefined, and
  // this will trigger an asan initialization-order-fiasco error.
  flag->next_ = list_;
  list_ = flag;
}

如何使用

// 定义Flag
DEFINE_bool(h, false, "Prints this message.");
DEFINE_bool(help, false, "Prints this message.");
DEFINE_bool(log, false, "Enable log output to stdout.");
DEFINE_string(logfile, "log.txt", "Set log storage file.");
DEFINE_string(ip, "0.0.0.0", "Set media service IP.");
DEFINE_int(port, 8800, "Set media service port.");

// 读取命令行参数
通过FLAG_xxx去使用我们定义的flag,例如FLAG_h、FLAG_help
int32_t main(int32_t argc, char* argv[])
{
    if (FlagList::SetFlagsFromCommandLine(&argc, argv, true) || FLAG_help || FLAG_h) {
        if (FLAG_help || FLAG_h) {
            rtc::FlagList::Print(nullptr, false);
            return 0;
        }
        return 1;
    }

    // TODO
   return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值