文章目录
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);
}
}
}
打印指定文件中的标记,参数file为nullptr
则打印所有文件中的标记。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
这个宏函数用于全局变量跨文件声明
小结
本文介绍了如何使用标记和如何解析命令行参数。