WebRTC源码分析之配置信息-FlagList


FlagList是一个全局链表,用于保存所有的标记,这些标记一般都是WebRTC的配置信息。标记都有两个值,一个是默认值,另一个是可变值。默认值是定义时赋给的,而可变值是通过命令行解析的。 FlagList::SetFlagsFromCommandLine用于解析命令行中与标记相关的参数,方便了命令行参数的处理。

FlagList使用示例
工程

示例工程:https://pan.baidu.com/s/1rbI2hwXpMA-Pb-i-zCdVWA 提取码:cenz

示例-定义标记
#define WEBRTC_POSIX

#include <iostream>
#include "flags.h"

using namespace std;

WEBRTC_DEFINE_string(name, "zhangsan", "this is name");
WEBRTC_DEFINE_bool(sex, true, "zhangsan is man");
WEBRTC_DEFINE_int(id, 1001, "zhangsan's id is 1001");
WEBRTC_DEFINE_float(score, 99.0, "I got a hundred in the maths examination");

int main()
{
	cout << "name  = " << FLAG_name << endl;
	cout << "sex   = " << (FLAG_sex ? "man" : "woman") << endl;
	cout << "id    = " << FLAG_id << endl;
	cout << "score = " << FLAG_score << endl;
	
    return 0;
}

在这里插入图片描述
通过定义标记,使用标记。注意标记需要定义在全局。

示例-标记的额外信息
#define WEBRTC_POSIX

#include <iostream>
#include "flags.h"

using namespace std;

WEBRTC_DEFINE_string(name, "zhangsan", "this is name");
WEBRTC_DEFINE_bool(sex, true, "zhangsan is man");
WEBRTC_DEFINE_int(id, 1001, "zhangsan's id is 1001");
WEBRTC_DEFINE_float(score, 100.0, "I got a hundred in the maths examination");

int main()
{
	cout << "name  = " << FLAG_name << endl;
	
	cout <<"current value = "<< *Flag_name.string_variable() << endl;
	cout <<"default value = "<< Flag_name.string_default() << endl;
	cout <<"comment = "<< Flag_name.comment() << endl;
	cout <<"file    = "<< Flag_name.file() << endl;
	
    return 0;
}

在这里插入图片描述
在定义FLAG_name时,同时会定义Flag_name对象。Flag_name对象中包含更多的信息,解析命令行中name参数后,会把结果放在Flag_name对象中。

示例-显示所有的标记

flag_defs.h

#ifndef __FLAG_DEFS_H__
#define __FLAG_DEFS_H__

WEBRTC_DEFINE_float(salary,10000,"zhangsan's salary is 10000");
WEBRTC_DEFINE_string(title,"teacher","zhangsan is a teacher");

#endif

main.cc

#define WEBRTC_POSIX

#include <iostream>
#include "flags.h"
#include "flag_defs.h"

using namespace std;
using namespace rtc;

WEBRTC_DEFINE_string(name, "zhangsan", "this is name");
WEBRTC_DEFINE_bool(sex, true, "zhangsan is man");
WEBRTC_DEFINE_int(id, 1001, "zhangsan's id is 1001");
WEBRTC_DEFINE_float(score, 100.0, "I got a hundred in the maths examination");

int main()
{
	FlagList::Print(nullptr,true); 
	
	return 0;
}

在这里插入图片描述
将所有文件中定义的标记全部打印出来,打印标记时,会按照文件分类打印。

示例-显示指定文件中的标记
#define WEBRTC_POSIX

#include <iostream>
#include "flags.h"
#include "flag_defs.h"

using namespace std;
using namespace rtc;

WEBRTC_DEFINE_string(name, "zhangsan", "this is name");
WEBRTC_DEFINE_bool(sex, true, "zhangsan is man");
WEBRTC_DEFINE_int(id, 1001, "zhangsan's id is 1001");
WEBRTC_DEFINE_float(score, 100.0, "I got a hundred in the maths examination");

int main()
{
	FlagList::Print("flag_defs.h",false); 
	
	return 0;
}

在这里插入图片描述
打印指定文件中的标记,传入false表示只打印标记的默认值。

示例-解析命令行参数
#define WEBRTC_POSIX

#include <iostream>
#include "flags.h"

using namespace std;
using namespace rtc;

WEBRTC_DEFINE_string(name, "zhangsan", "this is name");
WEBRTC_DEFINE_bool(sex, true, "zhangsan is man");
WEBRTC_DEFINE_int(id, 1001, "zhangsan's id is 1001");
WEBRTC_DEFINE_float(score, 100.0, "I got a hundred in the maths examination");

int main(int argc,char ** argv)
{
	/*解析命令行*/
	FlagList::SetFlagsFromCommandLine(&argc,argv,true);	
	
	FlagList::Print(nullptr,true);
    
    /*获取命令行中的值*/
    FLAG_name = *Flag_name.string_variable();
		
	cout<<endl<<"name = "<<FLAG_name<<endl;
		
	return 0;
}

在这里插入图片描述
解析命令行参数,更新标记的值。

示例-解析命令行参数并删除命令行中的标记
#define WEBRTC_POSIX

#include <iostream>
#include "flags.h"

using namespace std;
using namespace rtc;

WEBRTC_DEFINE_string(name, "zhangsan", "this is name");
WEBRTC_DEFINE_bool(sex, true, "zhangsan is man");
WEBRTC_DEFINE_int(id, 1001, "zhangsan's id is 1001");
WEBRTC_DEFINE_float(score, 100.0, "I got a hundred in the maths examination");

int main(int argc,char ** argv)
{
	int i = 0;

    /*打印命令行参数*/
	while(i<argc)
    {		
    	cout<<"argv["<<i<<"] = "<<argv[i]<<endl;
		i++;
    }

	cout<<endl;

	/*true:表示删除命令行中已解析的参数*/
	FlagList::SetFlagsFromCommandLine(&argc,argv,true);	
	
	FlagList::Print(nullptr,true);
   
	cout<<endl;
 
	i = 0;
    
     /*打印命令行参数*/
	while(i<argc)
    {		
    	cout<<"argv["<<i<<"] = "<<argv[i]<<endl;
		i++;
    }

	return 0;
}

在这里插入图片描述

SetFlagsFromCommandLine()函数传入true,表示删除命令行中已经解析的参数。

FlagList源码分析

所有的源码都在:src\rtc_base\flags.h flags.cc

没定义一个标记,都会生成一个Flag对象,其值存储在内部的FlagValue内,所有的Flag对象由一个全局的链表FlagList管理。

FlagValue类
union FlagValue 
{
  /*静态的用于生成存储bool类型的对象*/
  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;
};

FlagValue是个联合体,只能存放bool、int、float、char*类型,这也决定了标记也只能定义成这四种类型。每种类型都提供了对应的静态成员函数,用于生成对应类型的联合体。

Flag类
const char* file_;       /*标记所在的文件名*/
const char* name_;       /*标记的文件名*/
const char* comment_;    /*标记的注释*/

Type type_;              /*标记的类型*/
FlagValue* variable_;    /*标记的实际值*/
FlagValue default_;      /*标记的默认值*/

Flag* next_;     /*指向链表中下一个标记*/

所有的标记会组成一条链表,每个标记都是链表中的一个结点,所以需要有指针指向下一个结点。

Flag::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);   /*添加到链表上*/
}

定义标记时,会生成Flag对象,标记的很多额外的信息都被存储在这里,包括标记被定义的文件名标记的名称注释实际值默认值

在生成Flag对象时,会将本对象添加到全局的链表上。

const char* file() const { return file_; }
const char* name() const { return name_; }
const char* comment() const { return comment_; }
Type type() const { return type_; }    /*返回标记的类型*/
Flag* next() const { return next_; }

用于返回相应的信息

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;
}

用于返回标记的实际值。返回底层对象指针的原因是,既可以获取实际值,也可以更改实际值,这一点和默认值不同。

bool bool_default() const 
{
	RTC_DCHECK_EQ(BOOL, type_);
	return default_.b;
}
int int_default() const 
{
	RTC_DCHECK_EQ(INT, type_);
	return default_.i;
}
double float_default() const 
{
	RTC_DCHECK_EQ(FLOAT, type_);
	return default_.f;
}
const char* string_default() const
{
	RTC_DCHECK_EQ(STRING, type_);
	return default_.s;
}

用于返回标记的默认值

void Flag::SetToDefault() 
{
  switch (type_) 
  {
    case Flag::BOOL:
      variable_->b = default_.b;
      return;
    case Flag::INT:
      variable_->i = default_.i;
      return;
    case Flag::FLOAT:
      variable_->f = default_.f;
      return;
    case Flag::STRING:
      variable_->s = default_.s;
      return;
  }

  FATAL() << "unreachable code";      /*断言失败,结束进行。*/
}

将标记的实际值设置成默认值

/*类型字符串化*/
static const char* Type2String(Flag::Type type) 
{
  switch (type) 
  {
    case Flag::BOOL:
      return "bool";
    case Flag::INT:
      return "int";
    case Flag::FLOAT:
      return "float";
    case Flag::STRING:
      return "string";
  }

  FATAL() << "unreachable code";
}

/*打印标记值*/
static void PrintFlagValue(Flag::Type type, FlagValue* p) 
{
  switch (type) 
  {
    case Flag::BOOL:
      printf("%s", (p->b ? "true" : "false"));
      return;
    case Flag::INT:
      printf("%d", p->i);
      return;
    case Flag::FLOAT:
      printf("%f", p->f);
      return;
    case Flag::STRING:
      printf("%s", p->s);
      return;
  }

  FATAL() << "unreachable code";
}

/*打印标记*/
void Flag::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");
}

打印标记的值,传入的参数决定是否打印标记的实际值。

FlagList类
class FlagList 
{
 public:
	FlagList();
	static Flag* list() { return list_; }
	static void Print(const char* file, bool print_current_value);
	static Flag* Lookup(const char* name);
    
	static void SplitArgument(const char* arg,char* buffer,int buffer_size,const char** name,const char** value,bool* is_bool);
	static int SetFlagsFromCommandLine(int* argc,const char** argv,bool remove_flags);
    
	static inline int SetFlagsFromCommandLine(int* argc,char** argv,bool remove_flags) 
	{
		return SetFlagsFromCommandLine(argc, const_cast<const char**>(argv),remove_flags);   /*参数发生强转*/
	}
    
	static void Register(Flag* flag);

 private:
  static Flag* list_;
};   

FlagList类的成员是静态的,相当于一个全局变量。特别是list_也是静态数据成员,相当于一个全局的链表。

void FlagList::Print(const char* file, bool print_current_value) 
{
  const char* current = nullptr;

  /*遍历链表*/
  for (Flag* f = list_; f != nullptr; f = f->next()) 
  {
    if (file == nullptr || file == f->file()) 
    {
      /*只打印一次*/
      if (current != f->file()) 
      {
        printf("Flags from %s:\n", f->file());
        current = f->file();
      }

      f->Print(print_current_value);
    }
  }
}

打印指定文件中的标记,参数filenullptr则打印所有文件中的标记。print_current_value用于指示是否打印标记的实际值。

/*判断两个字符串是否相等*/
bool FlagEq(const char* arg, const char* flag) 
{
  /*两个字符串中,-字符和_字符是相等的。*/
  while (*arg != '\0' && (*arg == *flag || (*arg == '-' && *flag == '_'))) 
  {
    ++arg;
    ++flag;
  }

  return *arg == '\0' && *flag == '\0';
}

Flag* FlagList::Lookup(const char* name) 
{
  Flag* f = list_;
  
  /*遍历链表,查找指定节点。*/
  while (f != nullptr && !FlagEq(name, f->name()))
    f = f->next();

  return f;
}

根据标记的名称查找链表中结点。

void FlagList::Register(Flag* flag) 
{
  RTC_DCHECK(flag);
  RTC_DCHECK_GT(strlen(flag->name()), 0);

  flag->next_ = list_;   /*链表的头插法*/
  list_ = flag;
}

采用头插法,将标记结点插入到链表中。

/*分割参数*/
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 == '-')   
  {
    arg++;                    /*去掉第一个-*/
    if (*arg == '-')          /*若有第二个-,也去掉。*/
      arg++;

    /*参数以no开头,且参数在FlagList中可以查找到。*/
    if (arg[0] == 'n' && arg[1] == 'o' && Lookup(arg + 2)) 
    {
      arg += 2;  /*去掉no*/
      *is_bool = true;
    }

    *name = arg;  /*指向参数*/

    while (*arg != '\0' && *arg != '=')
      arg++;
      
    if (*arg == '=') 
    {
      int n = static_cast<int>(arg - *name);
      RTC_CHECK_LT(n, buffer_size);
      memcpy(buffer, *name, n * sizeof(char));
      buffer[n] = '\0';
      *name = buffer;
      *value = arg + 1;
    }
  }
}

/*解析命令行参数*/
int FlagList::SetFlagsFromCommandLine(int* argc,const char** argv,bool remove_flags) 
{
  for (int i = 1; i < *argc; /* see below */) 
  {
    int j = i; 
    const char* arg = argv[i++];

    char buffer[1024];
    const char* name;
    const char* value;
    bool is_bool;
    SplitArgument(arg, buffer, sizeof buffer, &name, &value, &is_bool);

    if (name != nullptr)     /*有参数时*/
    {
      Flag* flag = Lookup(name);   /*查找参数是否在FlagList中*/
      if (flag == nullptr) 
      {
        fprintf(stderr, "Error: unrecognized flag %s\n", arg);
        return j;   /*返回第几个参数错误*/
      }

      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;
        }
      }

      char empty[] = {'\0'};
      char* endp = empty;
      switch (flag->type())     /*更新配置的值*/
      {
        case Flag::BOOL:
          /*取值正好相反,no时is_bool为true。*/
          *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;
      }

      /* 处理错误*/
      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;
      }

      if (remove_flags)           /*删除命令行参数*/
        while (j < i)
          argv[j++] = nullptr;    /*将处理过的参数置为NULL*/
    }
  }

  if (remove_flags) 
  {
    int j = 1;
    for (int i = 1; i < *argc; i++) 
    {
      if (argv[i] != nullptr)   /*删除为NULL的参数*/
        argv[j++] = argv[i];    /*将后面的参数前移*/
    }

    *argc = j;  /*更新参数的个数*/
  }

  return 0;
}

解析命令行参数,若参数中有和标记值相同的,则从命令行中取值更新标记。参数remove_flags为true时,命令行中解析过的参数会从命令行参数中删除。

WEBRTC_DEFINE_string宏函数
宏函数原型
#define WEBRTC_DEFINE_string(name, default, comment) \
  WEBRTC_DEFINE_FLAG(STRING, const char*, name, default, comment)

#define WEBRTC_DEFINE_FLAG(type, c_type, name, default, comment)     \
  c_type FLAG_##name = (default);                                    \
  static rtc::Flag Flag_##name(__FILE__, #name, (comment),           \
  rtc::Flag::type,&FLAG_##name,                                      \
  rtc::FlagValue::New_##type(default))
宏函数的使用
WEBRTC_DEFINE_string(title, "teacher", "zhangsan is a teacher");

将宏展开后:

const char* FLAG_title = ("teacher");
static rtc::Flag Flag_title(__FILE__,title, ("zhangsan is a teacher"),rtc::Flag::STRING,&FLAG_title,rtc::FlagValue::New_STRING("teacher"));

去掉命名空间,简化后:

const char* FLAG_title = ("teacher");
static Flag Flag_title(__FILE__,title, ("zhangsan is a teacher"),Flag::STRING,&FLAG_title,FlagValue::New_STRING("teacher"));

这个宏函数定义了两个变量,这个宏一般在全局使用,定义的变量也是全局变量,其中Flag_title是静态的,所以其作用域是文件内部。

WEBRTC_DECLARE_string宏函数
宏函数原型
#define WEBRTC_DECLARE_string(name)       \
  WEBRTC_DECLARE_FLAG(const char*, name)

#define WEBRTC_DECLARE_FLAG(c_type, name) \
  extern c_type FLAG_##name
宏函数的使用
WEBRTC_DECLARE_string(title)

将宏展开后:

extern const char* FLAG_title

这个宏函数用于全局变量跨文件声明

小结

本文介绍了如何使用标记和如何解析命令行参数。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值