个人博客,如需转载,请注明来自DDingDreams的个人博客:http://www./wordpress/。 try //encmain.cpp { if(!cTAppEncTop.parseCfg( argc, argv )) { cTAppEncTop.destroy(); return 1; } } catch (po::ParseFailure& e) { cerr << "Error parsing option \""<< e.arg <<"\" with argument \""<< e.val <<"\"." << endl; return 1; } 抛出的异常可以是系 统的标准exception,也可以像本文中的ParseFailure是自定义的异常。 class TEncTop : public TEncCfg TEncTop类中并不含有parseCfg函数,而是从TEncCfg类继承来的解析函数。 Void create (); ///< create option handling class Void destroy (); ///< destroy option handling class Bool parseCfg ( Int argc, Char* argv[] ); //< parse configuration file to fill member variables 那么对象cTAppEncTop调用的destory函数其实是类EncTop重载的destory函数。 解析函数 Bool TAppEncCfg::parseCfg( Int argc, Char* argv[] ) { Bool do_help = false; #if !H_MV string cfg_InputFile; #endif string cfg_BitstreamFile; #if !H_MV string cfg_ReconFile; #endif #if H_MV vector cfg_dimensionLength; string cfg_profiles; string cfg_levels; string cfg_tiers; #if H_3D cfg_dimensionLength.push_back( 2 ); // depth cfg_dimensionLength.push_back( 32 ); // texture #else cfg_dimensionLength.push_back( 64 ); #endif #endif string cfg_dQPFile; string cfgColumnWidth; string cfgRowHeight; string cfg_ScalingListFile; string cfg_startOfCodedInterval; string cfg_codedPivotValue; string cfg_targetPivotValue; po::Options opts; opts.addOptions() ("help", do_help, false, "this help text") ("c", po::parseConfigFile, "configuration file name") ...... ...... 注释1:H_MV & HEVC_EXT #define H_MV ( HEVC_EXT != 0) 而HEVC_EXT又是什么呢? /* HEVC_EXT might be defined by compiler/makefile options. Linux makefiles support the following settings: make -> HEVC_EXT not defined make HEVC_EXT=0 -> H_MV=0 H_3D=0 --> plain HM make HEVC_EXT=1 -> H_MV=1 H_3D=0 --> MV only make HEVC_EXT=2 -> H_MV=1 H_3D=1 --> full 3D */ #ifndef HEVC_EXT #define HEVC_EXT 2 也就是说HEVC_EXT=0的话,则为纯粹的HEVC编码,若HEVC_EXT=1,则为多视点编码,而我用的是多视点+深度(MVD)数据格式,那么应该用3D模式,所以HEVC_EXT=2; #include <vector> 还需要using namespace std;使用标准库的命名空间,下面会对命名空间有介绍。 vector c //创建一个空的vector。 vector c1(c2) //复制一个vector。 vector c(n) //创建一个vector,含有n个数据,数据均已缺省构造产生。 vector c(n, elem) //创建一个含有n个elem拷贝的vector。 vector c(beg,end) //创建一个以[beg;end)区间的vector。 c.~ vector () //销毁所有数据,释放内存。 vector vInts; vector vInts(500); //这个声明是指vInts里面可以放500个int类型的数据 vector vInts(500,int(0)); //指vInts里面可以放500个int类型的数据,并且初始化都为0; vector vIntsCopy(vInts); //创建一个拷贝; vInts.push_back(int(5)); //向vInts添加数据int类型5; int nSize = vInts.empty() ? -1 : static_cast(vInts.size()); //求vInts的size,若为空,则赋值为-1 //vector访问数据两种用法: //vector::at();像数组一样会进行边界检查 //vector::operator[];//不进行边界检查 //用例如下: vector v; v.reserve(10); //为v分配了10个int型内存空间 for(int i=0; i<7; i++) v.push_back(i); //v增加了7条数据,下标从0~6 try { int iVal1 = v[7]; // not bounds checked - will not throw int iVal2 = v.at(7); // bounds checked - will throw if out of range } catch(const exception& e) { cout << e.what(); } ////Vector成员函数 c.assign(beg,end) //将[beg; end)区间中的数据赋值给c。 c.assign(n,elem) //将n个elem的拷贝赋值给c。 c.at(idx) //传回索引idx所指的数据,如果idx越界,抛出out_of_range。 c.back() //传回最后一个数据,不检查这个数据是否存在。 c.begin() //传回迭代器重的第一个数据。 c.capacity() //返回容器中数据个数。 c.clear() //移除容器中所有数据。 c.empty() //判断容器是否为空。 c.end() //指向迭代器中的最后一个数据地址。 c.erase(pos) //删除pos位置的数据,传回下一个数据的位置。 c.erase(beg,end) //删除[beg,end)区间的数据,传回下一个数据的位置。 c.front() //传回第一个数据。 get_allocator //使用构造函数返回一个拷贝。 c.insert(pos,elem) //在pos位置插入一个elem拷贝,传回新数据位置。 c.insert(pos,n,elem) //在pos位置插入n个elem数据。无返回值。 c.insert(pos,beg,end) //在pos位置插入在[beg,end)区间的数据。无返回值。 c.max_size() //返回容器中最大数据的数量。 c.pop_back() //删除最后一个数据。 c.push_back(elem) //在尾部加入一个数据。 c.rbegin() //传回一个逆向队列的第一个数据。 c.rend() //传回一个逆向队列的最后一个数据的下一个位置。 c.resize(num) //重新指定队列的长度。 c.reserve() //保留适当的容量。 c.size() //返回容器中实际数据的个数。 c1.swap(c2) //将c1和c2元素互换。 swap(c1,c2) //将c1和c2元素互换。 注释3:namespace using namespace std; namespace po = df::program_options_lite; 这段代码来自3D-HEVC,using namespace std;表示使用C++标准库命名空间。 使用命名空间的目的是对标识符的名称进行本地化,以避免命名冲突。在C++中,变量、函数和类都是大量存在的。如果没有命名空间,这些变量、函数、类的名称将都存在于全局命名空间中,会导致很多冲突。比如,如果我们在自己的程序中定义了一个函数toupper(),这将重写标准库中的toupper()函 数,这是因为这两个函数都是位于全局命名空间中的。命名冲突还会发生在一个程序中使用两个或者更多的第三方库的情况中。此时,很有可能,其中一个库中的名 称和另外一个库中的名称是相同的,这样就冲突了。这种情况会经常发生在类的名称上。比如,我们在自己的程序中定义了一个Stack类,而我们程序中使用的某个库中也可能定义了一个同名的类,此时名称就冲突了。 Namespace 关键字的出现就是针对这种问题的。由于这种机制对于声明于其中的名称都进行了本地化,就使得相同的名称可以在不同的上下文中使用,而不会引起名称的冲突。或许命名空间最大的受益者就是C++中的标准库了。在命名空间出现之前,整个C++库都是定义在全局命名空间中的(这当然也是唯一的命名空间)。引入命名空间后,C++库就被定义到自己的名称空间中了,称之为std。这样就减少了名称冲突的可能性。我们也可以在自己的程序中创建自己的命名空间,这样可以对我们认为可能导致冲突的名称进行本地化。这点在我们创建类或者是函数库的时候是特别重要的。 namespace CounterNameSpace { int upperbound; int lowerbound; class couter { } } 然而,既然命名空间定义了一个范围,那么我们在命名空间之外就需要使用范围解析运算符来引用命名空间中的对象。例如,在命名空间CounterNameSpace定义的范围之外给upperbound赋值为10,就必须这样写: CounterNameSpace::upperbound = 10; 或者在CounterNameSpace定义的范围之外想要声明一个counter类的对象就必须这样写: CounterNameSpace::counter obj; 一般来讲,在命名空间之外想要访问命名空间内部的成员需要在成员前面加上命名空间和范围解析运算符。 //访问命名空间或者成员: using namespace 命名空间名称; using 命名空间名称::成员; 第一种形式中的命名空间名称就是我们要访问的命名空间。该命名空间中的所有成员都会被引入到当前范围中。也就是说,他们都变成当前命名空间的一部分了,使用的时候不再需要使用范围限定符了。 using CounterNameSpace::lowerbound; //只有lowerbound当前是可见的 lowerbound = 10; //这样写是合法的,因为lowerbound成员当前是可见的 using CounterNameSpace; //所有CounterNameSpace空间的成员当前都是可见的 upperbound = 100; //这样写是合法的,因为所有的CounterNameSpace成员目前都是可见的 相同的空间名称是可以被多次声明的,这种声明向相互补充的。这就使得命名空间可以被分割到几个文件中甚至是同一个文件的不同地方中。 struct Options po::Options opts; opts.addOptions() ("help", do_help, false, "this help text") ("c", po::parseConfigFile, "configuration file name") ...... ...... “po”其实就是命名空间,如下的代码中可见: namespace df { namespace program_options_lite { struct Options;//Options结构体其实就是在df嵌套program_options_lite两层命名空间中的。 ...... ...... using namespace std; namespace po = df::program_options_lite;//那么其实,po就是命名空间df嵌套下的program_options_lite,那么通过po就可以直接调用Options结构体了。 po::Options opts; //声明了一个Options结构体对象opts。 注释1:addOptions函数 //addOptions()函数: OptionSpecific Options::addOptions() //上述的addOpitons函数,只是返回了OptionSpecific指针。 { return OptionSpecific(*this); } 首先来看一下po命名空间下Options结构,其中包括addOptions函数和Names结构体。 class OptionSpecific; struct Options { ~Options(); OptionSpecific addOptions(); //这个addOptions函数返回的类型为OptionSpecific; struct Names { Names() : opt(0) {};//冒号后面其实是对成员opt的初始化 ~Names() { if (opt) delete opt; } std::list opt_long;//这两个都为string数据类型的链表 std::list opt_short; OptionBase* opt; }; void addOption(OptionBase *opt); typedef std::list<Names*> NamesPtrList;//这是Names数据机构的链表宏定义 NamesPtrList opt_list; //声明一个Names结构链表对象 typedef std::map<std::string, NamesPtrList> NamesMap;//map以string数据类型为key值,上面Names数据机构的链表为value。 NamesMap opt_long_map; //这里声明了两个map对象 NamesMap opt_short_map; }; 函数调用addOptions方法,实质函数是通过返回下面的OptionSpecific类指针,来调用了options结构体中的addOption(OptionBase *opt);方法,先来看OptionSpecific类: /* Class with templated overloaded operator(), for use by Options::addOptions() */ class OptionSpecific { public: OptionSpecific(Options& parent_) : parent(parent_) {}//OptionSpecific的含参数构造函数,其中其参数通过冒号后面的常量赋值方法来赋值,即参数传入的parent_赋给了OptionSpecific的参数parent,此段代码的最后可见,parent是OptionSpecific私有变量,类型为Options结构。 /** * Add option described by name to the parent Options list, * with storage for the option's value * with default_val as the default value * with desc as an optional help description */ //下面这段其实是重载运算符(),这()内的四个参数传给了parent.addOption,根据()内的参数类型不同,采用了模板数据类型,以及重载了三个不同的()运算方法。 template OptionSpecific& operator()(const std::string& name, T& storage, T default_val, const std::string& desc = "") { parent.addOption(new Option(name, storage, default_val, desc)); return *this; } #if H_MV template OptionSpecific& operator()(const std::string& name, std::vector& storage, T default_val, unsigned uiMaxNum, const std::string& desc = "" ) { std::string cNameBuffer; std::string cDescBuffer; storage.resize(uiMaxNum); for ( unsigned int uiK = 0; uiK < uiMaxNum; uiK++ ) { cNameBuffer .resize( name.size() + 10 ); cDescBuffer.resize( desc.size() + 10 ); Bool duplicate = (uiK != 0); // isn't there are sprintf function for string?? sprintf((char*) cNameBuffer.c_str() ,name.c_str(),uiK,uiK); if ( !duplicate ) { sprintf((char*) cDescBuffer.c_str(),desc.c_str(),uiK,uiK); } cNameBuffer.resize( std::strlen(cNameBuffer.c_str()) ); cDescBuffer.resize( std::strlen(cDescBuffer.c_str()) ); parent.addOption(new Option( cNameBuffer, (storage[uiK]), default_val, cDescBuffer, duplicate )); } return *this; } #endif /** * Add option described by name to the parent Options list, * with desc as an optional help description * instead of storing the value somewhere, a function of type * OptionFunc::Func is called. It is upto this function to correctly * handle evaluating the option's value. */ OptionSpecific& operator()(const std::string& name, OptionFunc::Func *func, const std::string& desc = "") { parent.addOption(new OptionFunc(name, parent, func, desc)); return *this; } private: Options& parent; }; }; /* namespace: program_options_lite */ }; /* namespace: df */ 我们来看一下addOptions具体做了些什么工作,以参数(“help”, do_help, false, “this help text”)为例: template OptionSpecific& operator()(const std::string& name, T& storage, T default_val, const std::string& desc = "") { parent.addOption(new Option(name, storage, default_val, desc)); return *this; } //name传入了“help”,storage传入了"Bool do_help = false;"这个BOOL变量,default_val=false,desc传入的是"this help text"; 利用小括号运算符重载,将()内的四个参数传入,接着调用addOptions方法调用OptionBase和OptionSpecifc的构造函数。将四个参数存储在以Names结构体为数据类型的双向链表opt_list中。 void Options::addOption(OptionBase *opt)//我们看到上面调用addOption的时候都是传入的option对象或者OptionFunc对象,而这里传入的对象为OptionBase.查看option和OptionFunc的定义可知,他们都继承自OptionBase。 //当四个参数传进来,先进入OptionBase的构造函数,然后再进行option或者OptionFunc的构造函数。 { //opt对象的参数opt_string为“help”,opt_desc为“this help text”; Names* names = new Names(); //Names包含一个OptionBase类,即string类型的name和desc,和一个长链表,一个短链表。 names->opt = opt;//将传入的OptionBase参数赋值给names的成员opt。 string& opt_string = opt->opt_string;//“help” ,取第一个参数,即OptionBase的name参数 size_t opt_start = 0; for (size_t opt_end = 0; opt_end != string::npos;)// { opt_end = opt_string.find_first_of(',', opt_start);//找opt_string字符串中第一个“,”的位置,如果没找到“,”,返回一个特别的标志c++中用npos表示 bool force_short = 0; if (opt_string[opt_start] == '-') //若起始的字符为“-”,则字符的起始位置+1,force_short=1,是用来强制字符“-”开始的字符串长度只为“1”: { opt_start++; force_short = 1; } string opt_name = opt_string.substr(opt_start, opt_end - opt_start); //取opt_string子串 if (force_short || opt_name.size() == 1) //若是短字符串,存入names中的短链表,并在Options的短map中存入。 { names->opt_short.push_back(opt_name); opt_short_map[opt_name].push_back(names); } else //若是长字符串,存入names中的长链表,并在Options的长map中存入 { names->opt_long.push_back(opt_name); opt_long_map[opt_name].push_back(names); } opt_start += opt_end + 1; //取完第一个逗号前面的字符串,opt_start向后挪去取第二个字符串 } opt_list.push_back(names); //将字符串构建的names结构存入opt_list,这是以Names结构为数据类型的链表。 } void Options::addOption(OptionBase *opt)//这个方法只是解析了name字符串,即传入的第一个参数。 struct OptionBase//其只有三个参数,opt_string,opt_desc,opt_duplicate { #if H_MV OptionBase(const std::string& name, const std::string& desc, bool duplicate = false) : opt_string(name), opt_desc(desc), opt_duplicate(duplicate)//这里是OptionBase的三参数构造函数,同时给三个参数初始化; #else OptionBase(const std::string& name, const std::string& desc) : opt_string(name), opt_desc(desc)//两参数构造函数 #endif {}; virtual ~OptionBase() {} /* parse argument arg, to obtain a value for the option */ virtual void parse(const std::string& arg) = 0; /* set the argument to the default value */ virtual void setDefault() = 0; std::string opt_string; std::string opt_desc; #if H_MV bool opt_duplicate; #endif }; /** Type specific option storage */ template struct Option : public OptionBase//OptionBase的子类 { #if H_MV Option(const std::string& name, T& storage, T default_val, const std::string& desc, bool duplicate = false) : OptionBase(name, desc, duplicate), opt_storage(storage), opt_default_val(default_val) //其构造函数先调用父类OptionBase的构造函数OptionBase(name, desc, duplicate), 然后再调用子类的构造函数,同时给自己的两个变量赋值。Option的构造函数的参数顺序,第一和最后两个给父类赋值。第二和第三个参数给自己的变量赋值。 #else Option(const std::string& name, T& storage, T default_val, const std::string& desc) : OptionBase(name, desc), opt_storage(storage), opt_default_val(default_val) #endif {} void parse(const std::string& arg);//解析函数 void setDefault() //设置默认值。 { opt_storage = opt_default_val; } T& opt_storage; T opt_default_val; }; 也就是说,当这段addOption运行后,Options结构的对象中,opt_list存储情况如下: 小括号运算符重载,五个参数 //addOptions中一句代码如下,五个参数的小括号重载调用: ("InputFile_%d,i_%d", m_pchInputFileList, (char *) 0 , MAX_NUM_LAYER_IDS , "original Yuv input file name %d") 其调用的函数是如下: #if H_MV//多视点序列输入,传入MAX_NUM_LAYER_IDS=63,也就是可以输入63个序列 template 呵呵 老大的呵呵一笑很倾城啊! |
|