本文主要是介绍第十章g2o_custombundle中的common文件夹中的flags文件夹下的command_args.cpp,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
这篇为上篇的下半部分,也就是.cpp文件的解读。这个文件中主要是对类成员函数的一些定义。一些操作方法还是很常用的。
还是有没细化的地方,后续慢慢搞了。
贴代码:
#include "command_args.h"#include <cstdlib>
#include <cstring>
#include <fstream>
#include <algorithm>
#include <functional>using namespace std;// forward declarations
//四个重载,用于输入输出int型数组和double型数组,这个纯粹就是功能,跟BA程序没关系。
std::istream& operator>>(std::istream& is, std::vector<int>& v);
std::ostream& operator<<(std::ostream& os, const std::vector<int>& v);
std::istream& operator>>(std::istream& is, std::vector<double>& v);
std::ostream& operator<<(std::ostream& os, const std::vector<double>& v);/*** 重载输入符>>,让其能够给int类型数组进行输入。* 参数很明显就是两个了,输入流对象引用和要被输入的数组*/
std::istream& operator>>(std::istream& is, std::vector<int>& v)
{//创建一个字符串s,用于承接输入流is中的数据string s;//如果输入失败,则直接return了。这里主要用处还是防呆。// 为什么这里可以用>>呢,因为输入流到字符串是已经定义好的,流中的数据本来就是一律视为字符的,所以直接输入到字符串是没毛病的。if (! (is >> s) )return is;/*** 如果能走到这里,说明没有在上一步return,说明is >> s成功了,说明输入流中的数据已经存进了字符串s中。* 那么下面就是将字符串s中的数字解析出来,存进int型数组v中。*///.c_str()函数返回字符串的首字符地址。返回类型为const char*,所以不能直接赋值给char*类型const char* c = s.c_str();//这一步用了一下类型转换,将const char*类型的c,转换成char*类型,并赋值给char*类型的caux。//最后要的结果就是: caux 指向了字符串s的首字符的地址。char* caux = const_cast<char*>(c);//首先将数组v清空。v.clear();//定义一个循环遍历控制标识符,bool型bool hasNextValue=true;//通过hasNextValue控制循环解析while(hasNextValue){/*** string to long int 函数说明* 函数定义: long int strtol(const char *nptr, char **endptr, int base)* 函数说明: 将nptr字符串转换成长整型数* 参数说明:* const char *nptr,要被转换的字符串* char **endptr,转换到哪个字符不符合条件结束了,将指向这个字符的指针的地址反馈给endptr。这里发现endptr是char**类型的。* base,指转换成几进制的数。base范围从2至36,或0。* 如base值为10则采用10进制(字符串以10进制表示),若base值为16则采用16进制(字符串以16进制表示)。* 当base值为0时则是采用10进制做转换,但遇到如''0x''前置字符则会使用16进制做转换。*//*** 由这里发现门路了。指针c用于指向解析的开头处,指针caux用于指向解析的结尾处,*///这里开始解析转换:// 从c指向的地方开始解析,// 将一次解析结束的位置,赋值给caux,// 按照10进制解析// 解析出来的返回类型为long int,所以进行下类型转换,转换成int型,并赋值给i。// 这句完之后,一次解析就完成了,解析出来的数字存在了i中。c指向开头处,caux指向解析掉i后的位置。int i = static_cast<int>(strtol(c,&caux,10));//下面这里判断一下指向开头的c和指向结束的caux是否指向了同一个位置,如果指向同一个位置,说明已经解析完了。//因为如果解析完了的话,c开头解析,没解析就停止了,导致caux指向的就是c开头的位置,说明解析完了。//如果没解析完,进行一些正常的操作:if (c!=caux){//将末尾指针caux的值赋值给开头指针c,c=caux;//跳出的位置肯定不是数字了,所以从下一个字符开始c++;//然后就是最重要的,将解析出来的数字,push进数组v中。v.push_back(i);}//else说明解析完了,将循环判断标志置为false,然后循环就跳出了。else hasNextValue = false;}//当然最后,为了能够链接式的使用>>,最后肯定要返回输入流对象引用,同理上面判断失败直接return,也是返回的is。return is;
}/*** 重载输出符<<,让其能够对int类型数组进行输出。* @param os 输出流对象引用* @param v 要被输出的int数组* @return 因为要保证链式使用,所以返回输出流对象引用*/
std::ostream& operator<<(std::ostream& os, const std::vector<int>& v){//将第一个元素单独输出,因为后面的循环看出来,元素前面有个"," 而第一个是不需要的,所以单独输出。//同时判断一下,v是否为空。if (v.size()){os << v[0];}//剩下的就是for循环进行输出了,从第二个元素开始,直到最后一个。for (size_t i=1; i<v.size(); i++){os << "," << v[i];}//最后返回输出流对象引用return os;
}/*** convert a string into an other type.* 将字符串转化成其他类型,一般是纯数字字符串转化成int、double、float值等这种用法。明显要用模板。** 原理还是利用字符串流,先构造字符串输入流i,再将其作为输入,输入到x中。* 返回值返回转化成功与否*/
template<typename T>
bool convertString(const std::string& s, T& x)
{//这里用s字符串构造了一个输入流对象i,std::istringstream i(s);//将流输入进x,同时判断输入是否成功。if (! (i >> x))return false;return true;
}/** Helper class to sort pair based on first elem */
//??啥玩意?干啥的?
template<class T1, class T2, class Pred = std::less<T1> >
struct CmpPairFirst {bool operator()(const std::pair<T1,T2>& left, const std::pair<T1,T2>& right) {return Pred()(left.first, right.first);}
};//这里就是参数类型的枚举,用不同的int值来表征类型,也就是为什么commad_args中的type属性的类型是int型的了。
enum CommandArgumentType
{CAT_DOUBLE, CAT_FLOAT, CAT_INT, CAT_STRING, CAT_BOOL, CAT_VECTOR_INT, CAT_VECTOR_DOUBLE
};//class CommandArgs的默认构造函数
CommandArgs::CommandArgs() {}//class CommandArgs的默认析构函数
CommandArgs::~CommandArgs() {}/*** 整个的命令行用法应该是:* ./progname -parameter1 parameter1_value -parameter2 parameter2_value -- parameterleftover...** 整体的逻辑是程序名后面接一个-参数名,参数名后面接参数值,成对出现,在后面会有--或者直接是参数名的,这些是放到_leftOvers[]中的。* 对于-参数,就去_args[]数组中去寻找,找到后将自定义的值覆盖进去。没找到肯定报错。没找到说明输入了不相关的参数。* 对于_leftOvers[]中的参数,就依次读进去值,在_leftOvers[]读满了之后,将后面的放在_leftOversOptional[]中。*/bool CommandArgs::parseArgs(int argc, char** argv, bool exitOnError)
{//第一个命令行参数为程序名称,将其保存在_progName_progName = argv[0];int i;//用argc控制循环遍历命令行参数数组。for (i = 1; i < argc; i++){//这里将第i个参数赋值给name字符串。string name = argv[i];//每个命令参数的参数名必须以至少一个‘-’字符开头,多个也没问题,不是的话直接break到后面对于_leftOvers[]的输入if (name[0] != '-') { // each param has to start with at least one dash//cerr << "Error: expecting parameter, got " << name << endl;//printHelp(cerr);//if (exitOnError)//exit(1);//return false;break;}/* first check whether it's -- and we should not continue parsing *///首先检查是否是'--',表征人为自定义值输入结束,也是break到后面的_leftOvers[]的输入if (name == "--") {//这里++i是因为本字符已经被检查出是"--"了,再读取就没什么意思了,所以从后一开始。//对比上面的break,上面的只是检测出来一个命令行参数不是以'-'开头,所以有可能就是_leftOvers[]的输入了,所以不++i++i;break;}//上面过滤掉了没有'-'和‘--’的情况,那么剩下的就是‘-’和‘----’的情况,找到第一个不是'-'的地方,然后将前面的切掉。重新赋值namestring::size_type dashPos = name.find_first_not_of('-');//判断一下,防止只有横杠的情况。if (dashPos != string::npos)//剪切掉dashPos之前的东西,只留后面的,重新赋值name,此时的name就是干净的参数名了。name = name.substr(dashPos);//判断剩下的name字符串是否是”help“或者”h“,是的话就打印帮助然后,exit()if (name == "help" || name == "h"){printHelp(cout);exit(0);}else {// command line argument parsing//排除掉上面一切其他情况,这里开始正常的参数赋值。//这里创建了一个迭代器:std::vector<CommandArgument>::iterator类型的it,指向_args的开始处。std::vector<CommandArgument>::iterator it = _args.begin();//迭代器开始在_args中找相应的参数。for ( ; it != _args.end(); ++it){//如果_args数组中找到了命令行中对应的参数名if (it->name == name){//找到有此参数之后,看类型是否是bool型的,如果是,操作上就是直接翻转if (it->type == CAT_BOOL){//判断一下是否没有读取,没读取执行下面的操作:对于读取过了的情况发现就直接不管了。不进行任何操作。if (!it->parsed){//同样的套路,将参数取出进行赋值就好了,由于这里是bool类型,所以直接拉出来翻转即可。bool* data = static_cast<bool*>(it->data);*data = !(*data);}//操作完之后,将其parsed属性置为true。表征命令行读入参数值成功it->parsed = true;}//else的情况,即为不是bool类型的参数。else{/*** 这一步的意义是检查一下命令行是否只给了参数名,而后面结束了,并没有给参数的值。* 说一下原理:整个程序有两处对i的值进行操作,一处就是for循环中的i++,这里记为第一个i++* 另外一处就是在str2arg(argv[i], *it);读入参数值之前的一句i++,这里记为第二个i++* 也就是说,从第一个i++之后到第二个i++之前,argv[i]都是参数名。* 第二个i++之后到第一个i++之前,argv[i]都是参数值。* 很明显,这一句的位置在第一个i++与第二个i++之间。argv[i]应该是一个参数名。* 而i >= argc-1是什么意思呢?* 先看i = argc-1的情况:i在这里的作用就是用于argv[i],用于遍历命令行的各个命令的。* 那么argv[argc-1]是哪个命令呢,由于argv的索引从0开始,而argc为个数计数,是从1开始的,所以argv[argc-1]就是只的是最后一个命令行参数* 问题来了,此处的argv[i]是一个参数名,后面是需要有参数值的,而这里判断这个参数已经为最后一个命令了,后面没有命令提供参数值了。* 所以面的一系列报警输出,exit()等就好理解了。* 至于i>argc-1的情况就更好理解了。* 举个例子,假如命令行就提供了一个命令参数,此时argc=2,argv也就只有argv[0]和argv[1],* 你非得去读取argv[2]或者argv[3]等等越界的地方,肯定结果也是输出报错。*/if(i >= argc-1){cerr << "Argument " << name << "needs value.\n";printHelp(cerr);if (exitOnError)exit(1);return false;}//紧接着,跟在读参数名后面的参数值,所以i++,让argv[i]对应参数值。i++;// 由于命令行类型一律是字符串,所以用到了定义的字符串转参数的函数,// 看函数定义发现其实就是用字符串格式的参数值给参数赋值。str2arg(argv[i], *it);//解析完成,将parsed置为trueit->parsed = true;}/*** 这个break想了好久。。还是程序结构没看懂。* for ( ; it != _args.end(); ++it){}中是在拿着命令行的参数名(这里的name)去_args[]中去寻找对应的参数,找到了将值更新。* 这个for{}中只有一个if (it->name == name){},也就是说只定义了找到的情况,找到后自然就是赋值,parsed置为true等操作* 问题就在这,赋值完了,parsed也置为true了之后呢,之后程序怎么走?* 仔细观察你就发现会自然的会走到这一句break,跳出这个for ( ; it != _args.end(); ++it){}遍历_args[]的循环。* 跳出之后, 如果下面的if (it == _args.end()){}不执行,那就正常跑到了for (i = 1; i < argc; i++){}的一次循环的末尾,* 也就是// for argv[i]这一句的那个括号。去之后就是再读一个argv[i],再去遍历_args[],找到赋值,找不到报警。如此循环。**/break;}}//如果it被++到了_args末尾后一个,说明命令行中输入的参数名,并没有在_args[]数组中找到,也就是说命令行中输入了一个不相干的参数名,//所以也是输出报警信息,并退出,返回false等一些列操作。/*** 这一句也是因上面的break才可以这么写。上面找到对应的变量并赋值成功后会break,说明it会在_args[]中间的一个位置。* 所以成功的话这个if永远不会成立。* 成立的情况是什么样的呢?it在遍历 _args[]时,遍历到末尾遍历完了也没找到。* 也就是说上方的for ( ; it != _args.end(); ++it){}都完整运行完了,也没找到。* 那就说明你命令行输入的参数名我这个程序并不认识。输入了一个不相干的参数。所以报警输出为unknown*/if (it == _args.end()){cerr << "Error: Unknown Option '" << name << "' (use -help to get list of options).\n";if (exitOnError)exit(1);return false;}}} // for argv[i]/*** 这里是承接上面判断命令行的参数开头没有-或者为--的。两个break到了这里。* ‘--’有i++,argv[i]跳到了下一个字符串,而没有-的也就没有i++,说明argv[i]就在brake处的当前字符串。* 这里的判断是说剩余的命令行参数够不够填满_leftOvers[]了,而这里面的参数必须要用户定义值,如果不够了,说明没有提供足够的必要参数值。* 所以这里就cerr报警说缺参数值,打印帮助,程序退出,返回false等一系列的操作。* 如果这一步不满足,说明剩下的命令行参数还够填满_leftOvers[]数组,填满了多的就继续放在_leftOversOptional[]中*/if ((int)_leftOvers.size() > argc - i) {cerr << "Error: program requires parameters" << endl;printHelp(cerr);if (exitOnError)exit(1);return false;}//这里其他参数的命令行赋值,就必须要对其顺序了。一般都会先将必须要有的_leftOvers[]赋全,然后触发j < _leftOvers.size()不满足,循环结束//因为不检查名字,按顺序赋值给_leftOvers[j],所以顺序一定要对齐。//这里说一下这两个循环控制符i和j,由于上方的if已经判定了,所以一定是j < _leftOvers.size()先触发完毕,也就是赋全了_leftOvers[].for (size_t j = 0; (i < argc && j < _leftOvers.size()); i++, j++) {//这两步捯饬还是将命令行中的值,赋值给内部的参数。// 一步一步来看,_leftOvers[j].data是描述块所描述的参数变量的地址,将这个地址赋值给sstring* s = static_cast<string*>(_leftOvers[j].data);//将s所指地址上的数据,赋值为argv[i],s就是指的描述块所描述的参数变量。所以参数变量的值被修改*s = argv[i];//这里来看,还是一步比较容易理解:将.data所指的地方的值,更新为argv[i]//*(static_cast<string*>(_leftOvers[j].data))=argv[i];}// the optional leftOvers//然后就是这里了,将填满_leftOvers后剩下的命令行参数值放到_leftOversOptional中。由于不检查名字,也是按顺序直接赋值,所以顺序一定要对齐。//这里的i,j判定条件谁先达到就不知道了。i先到头,说明_leftOversOptional[]没有赋全,可选,没关系。// j先到头,说明_leftOversOptional[]也赋全了,所有的参数都已经被自定义了值,后面剩下的那些命令行参数就扔了,不要了。for (size_t j = 0; (i < argc && j < _leftOversOptional.size()); i++, j++) {string* s = static_cast<string*>(_leftOversOptional[j].data);*s = argv[i];}//能走到这一步,说明正确解析完成,没有在中间报错退出,所以可以返回true了。return true;
}/*** 下面一堆重载的param()函数,用于往_args[]数组中添加参数描述块。* @param name 参数名称* @param p 参数快链接的参数。这个就是BA要用的内部参数。* @param defValue 参数值* @param desc 参数描述*/
void CommandArgs::param(const std::string& name, bool& p, bool defValue, const std::string& desc)
{CommandArgument ca;ca.name = name;ca.description = desc;ca.type = CAT_BOOL;//将字符串引用的地址传到.data中去,也就是这里存储数据是存储的数据指针ca.data = static_cast<void*>(&p);ca.parsed = false;//这一步其实是跟ca没啥关系的,只是将p的值赋值为传入的值。p = defValue;//将ca压入_args数组中。_args.push_back(ca);
}/**** 这里继续总结一下class CommandArgs类中的struct CommandArgument结构体* 这个结构体是_args数组中的基本元素类型,添加元素的话只能通过调用param()函数,因为只有这个函数中有_args.push_back()操作。* 再来观察一下结构体的各个元素: .name .description .type .parsed 这些都好理解,都是参数的一些描述,* 来看最核心的.data。这个成员的类型是void*类型的,也就是个指针,这个指针指向哪里呢?指向param()函数中的第二个参数,也就是int& p这个。* 这里也就发现眉目了。在_args[]数组中存的各个ca,其实并不是参数本身(至少在ca个各个属性中,没有参数值一说,全是参数的描述),而是一个具体参数后面的一些描述(name,type...),* 通过.data指向具体的参数,也就是说ca不能单独存在,必须有外部真实的参数变量进行挂靠。* 这点从param()中必须要传入一个int& p实打实的int类型引用 和 BundleParams.h中的struct BundleParams结构体中定义的那一堆参数变量可见* 终其原因就是_args[]数组中的CommandArgument类型的元素并不会真正的存储参数和参数的值,它只是通过一个.data指针指向一个外部存在的参数。* 跟这个参数对应之后,CommandArgument只是用来存储这个参数的一些信息。* 至于为什么这么搞,猜测还是为了-help。***/void CommandArgs::param(const std::string& name, int& p, int defValue, const std::string& desc)
{CommandArgument ca;ca.name = name;ca.description = desc;ca.type = CAT_INT;ca.data = static_cast<void*>(&p);ca.parsed = false;p = defValue;_args.push_back(ca);
}void CommandArgs::param(const std::string& name, float& p, float defValue, const std::string& desc)
{CommandArgument ca;ca.name = name;ca.description = desc;ca.type = CAT_FLOAT;ca.data = static_cast<void*>(&p);ca.parsed = false;p = defValue;_args.push_back(ca);
}void CommandArgs::param(const std::string& name, double& p, double defValue, const std::string& desc)
{CommandArgument ca;ca.name = name;ca.description = desc;ca.type = CAT_DOUBLE;ca.data = static_cast<void*>(&p);ca.parsed = false;p = defValue;_args.push_back(ca);
}void CommandArgs::param(const std::string& name, std::string& p, const std::string& defValue, const std::string& desc)
{CommandArgument ca;ca.name = name;ca.description = desc;ca.type = CAT_STRING;//将字符串引用的地址传到.data中去,也就是这里存储数据是存储的数据指针ca.data = static_cast<void*>(&p);ca.parsed = false;//这一步其实是跟ca没啥关系的,只是将p的值赋值为传入的值。p = defValue;//将ca压入_args数组中。_args.push_back(ca);
}void CommandArgs::param(const std::string& name, std::vector<int>& p, const std::vector<int>& defValue, const std::string& desc){CommandArgument ca;ca.name = name;ca.description = desc;ca.type = CAT_VECTOR_INT;ca.data = static_cast<void*>(&p);ca.parsed = false;p = defValue;_args.push_back(ca);
}void CommandArgs::param(const std::string& name, std::vector<double>& p, const std::vector<double>& defValue, const std::string& desc)
{CommandArgument ca;ca.name = name;ca.description = desc;ca.type = CAT_VECTOR_DOUBLE;ca.data = static_cast<void*>(&p);ca.parsed = false;p = defValue;_args.push_back(ca);
}//这里这个打印help函数瞅着就蛋疼。。。不看了
void CommandArgs::printHelp(std::ostream& os)
{if (_banner.size())os << _banner << endl;os << "Usage: " << _progName << (_args.size()>0?" [options] ":" ");if (_leftOvers.size() > 0) {for (size_t i = 0; i < _leftOvers.size(); ++i) {if (i > 0)os << " ";os << _leftOvers[i].name;}}if (_leftOversOptional.size() > 0) {if (_leftOvers.size() > 0)os << " ";for (size_t i = 0; i < _leftOversOptional.size(); ++i) {if (i > 0)os << " ";os << "[" << _leftOversOptional[i].name << "]";}}os << endl << endl;os << "General options:" << endl;os << "-------------------------------------------" << endl;os << "-help / -h Displays this help." << endl << endl;if (_args.size() > 0) {os << "Program Options:" << endl;os << "-------------------------------------------" << endl;// build up option string to print as tablevector<pair<string, string> > tableStrings;tableStrings.reserve(_args.size());size_t maxArgLen = 0;for (size_t i = 0; i < _args.size(); ++i) {if (_args[i].type != CAT_BOOL) {string defaultValueStr = arg2str(_args[i]);if (! defaultValueStr.empty())tableStrings.push_back(make_pair(_args[i].name + " " + type2str(_args[i].type), _args[i].description + " (default: " + defaultValueStr + ")"));elsetableStrings.push_back(make_pair(_args[i].name + " " + type2str(_args[i].type), _args[i].description));} elsetableStrings.push_back(make_pair(_args[i].name, _args[i].description));maxArgLen = (std::max)(maxArgLen, tableStrings.back().first.size());}sort(tableStrings.begin(), tableStrings.end(), CmpPairFirst<string,string>());maxArgLen += 3;for (size_t i = 0; i < tableStrings.size(); ++i) {os << "-" << tableStrings[i].first;for (size_t l = tableStrings[i].first.size(); l < maxArgLen; ++l)os << " ";os << tableStrings[i].second << endl;}// TODO should output description for leftOver params?}
}//设置banner,直接赋值。
void CommandArgs::setBanner(const std::string& banner)
{_banner = banner;
}//这个是用于设定其他参数的函数。并根据是否可选,分别存进_leftOversOptional和_leftOvers中
void CommandArgs::paramLeftOver(const std::string& name, std::string& p, const std::string& defValue, const std::string& desc, bool optional)
{CommandArgument ca;ca.name = name;ca.description = desc;ca.type = CAT_STRING;ca.data = static_cast<void*>(&p);ca.parsed = false;ca.optional = optional;p = defValue;//这里根据是否可选分别存进不同的数组。if (optional)_leftOversOptional.push_back(ca);else_leftOvers.push_back(ca);
}//将类型转换成字符串,貌似是用于类型的输出?程序很简单,switch分列就好了。返回字符串。
const char* CommandArgs::type2str(int t) const
{switch (t) {case CAT_DOUBLE:return "<double>";case CAT_FLOAT:return "<float>";case CAT_INT:return "<int>";case CAT_STRING:return "<string>";case CAT_BOOL:return "<bool>";case CAT_VECTOR_INT:return "<vector_int>";case CAT_VECTOR_DOUBLE:return "<vector_double>";}return "";
}/*** 字符串转参数。string to argument,* 其实其实功能并不是名字所表示的那样,功能就是将字符串类型的参数值,赋值给.data所指的参数。* @param input 输入的字符串* @param ca 要赋值的参数。 CommandArgument& 类型的引用。*/
void CommandArgs::str2arg(const std::string& input, CommandArgument& ca) const
{//这里要根据ca的类型进行转化,用switch进行分列。switch (ca.type) {//以folat为例进行说明//流程就是将string转成float然后赋值给.data所指的参数。case CAT_FLOAT:{//定义一个承接float参数的变量auxfloat aux;//这个函数在上方定义了。将input字符串转化成float类型的aux,返回是否转化成功。bool convertStatus = convertString(input, aux);//如果成功,将ca.data指针转换成float*类型,并赋值为aux。if (convertStatus) {//其实可以这样一句://这里注意一下,.data是的类型这里是void*,使用时要转换成其他基本类型指针。//可以发现还是赋值,其他并没有什么操作。*(static_cast<float*>(ca.data)) = aux;//*(ca.data)=aux;这样是不行的,必须显式的表明类型转换。//float* data = static_cast<float*>(ca.data);//*data = aux;}}break;//同理case CAT_DOUBLE:{double aux;bool convertStatus = convertString(input, aux);if (convertStatus) {double* data = static_cast<double*>(ca.data);*data = aux;}}break;case CAT_INT:{int aux;bool convertStatus = convertString(input, aux);if (convertStatus) {int* data = static_cast<int*>(ca.data);*data = aux;}}break;case CAT_BOOL:{bool aux;bool convertStatus = convertString(input, aux);if (convertStatus) {bool* data = static_cast<bool*>(ca.data);*data = aux;}}break;case CAT_STRING:{//string这里就比较简单了,直接赋值就好了,不用转换。string* data = static_cast<string*>(ca.data);*data = input;}break;case CAT_VECTOR_INT:{//同理,这里都是一眼的,先将字符串转化成int类型数组。std::vector<int> aux;bool convertStatus = convertString(input, aux);//转化成功后进行参数赋值。if (convertStatus) {std::vector<int>* data = static_cast< std::vector<int>* >(ca.data);*data = aux;}}break;case CAT_VECTOR_DOUBLE:{std::vector<double> aux;bool convertStatus = convertString(input, aux);if (convertStatus) {std::vector<double>* data = static_cast< std::vector<double>* >(ca.data);*data = aux;}}break;}
}/*** 将参数值转化成字符串。同样也是用switch进行分列。* @param ca 需要转化哪个参数的值* @return 返回当然是字符串了。*/
std::string CommandArgs::arg2str(const CommandArgument& ca) const
{switch (ca.type) {//以float类型参数为例。这里涉及到字符串流sstream的用法,字符串流长用于类型转化,参看://http://blog.csdn.net/xiaogugood/article/details/21447431case CAT_FLOAT:{//将ca.data取出,赋值给data,后面都是操作这个data。float* data = static_cast<float*>(ca.data);//创建一个字符串流对象stringstream auxStream;//将data所指的参数值传入到字符串流中,// 细聊一句,为什么这里是参数值,因为如果data指向一个int,那么*data就是个int值。所以<<输入就是int值了。auxStream << *data;//用.str()方法返回字符串流中的字符,同时return。return auxStream.str();}case CAT_DOUBLE:{double* data = static_cast<double*>(ca.data);stringstream auxStream;auxStream << *data;return auxStream.str();}case CAT_INT:{int* data = static_cast<int*>(ca.data);stringstream auxStream;auxStream << *data;return auxStream.str();}case CAT_BOOL:{bool* data = static_cast<bool*>(ca.data);stringstream auxStream;auxStream << *data;return auxStream.str();}case CAT_STRING:{string* data = static_cast<string*>(ca.data);return *data;}case CAT_VECTOR_INT:{std::vector<int> * data = static_cast< std::vector<int> * >(ca.data);stringstream auxStream;auxStream << (*data);return auxStream.str();}case CAT_VECTOR_DOUBLE:{std::vector<double> * data = static_cast< std::vector<double> * >(ca.data);stringstream auxStream;auxStream << (*data);return auxStream.str();}}return "";
}//从这个定义中看,貌似是将字符串中的纯字符提取出来,把前后的空格,缩进,换行都给剔除掉。
std::string CommandArgs::trim(const std::string& s) const
{//长度为0,说明是空字符串,直接return sif(s.length() == 0)return s;//find_first_not_of 从前往后查找,查找到不是子串中的任意一个字符,返回第一个的位置。这里相当于把前面的空格缩进和换行全部跳过。string::size_type b = s.find_first_not_of(" \t\n");//find_last_not_of 从后往前查找,查找到不是字串中的任意一个字符,返回最后一个位置。这里相当于把后面的空格缩进换行全都跳过。// 注意!从功能上看,这里并不能跳过字符串中间的空格缩进和换行,因为只是跳过了最前面的和最后面的。string::size_type e = s.find_last_not_of(" \t\n");//如果b的值为npos,npos是啥百度很多。这个判断的意思就是如果//上一句是找s中第一个不是空格缩进换行的字符位置。假如结果没找到的话,就会返回string::npos。//没找到说明什么意思呢?上面已经判定了字符串s不是空,但是又找不到不是空格缩进换行的字符,// 那就说明s是仅由空格缩进换行构成。所以后面只能返回空字符串""if(b == string::npos)return "";//排除了本身为空和全是空格缩进换行两种情况,剩下的肯定就是比较正常的了。掐头去尾留中间,构造一个string,然后return//这里说一下b ,e 这俩都是位置,也就是偏移量,起点都是字符串的首字符。//这里用到了string类的构造函数:String(char[] str,int index,int length)//在str字符串中的index位置开始,往后取一个length长度的字符串。//这样也就明白了第三个参数为什么是e-b+1了。其实是e-(b-1),从e位置,减掉b前一个位置,最后就是从b到e的长度了。return std::string(s, b, e - b + 1);
}/*** 这个double类型数组输入,套路跟前面的int类型的一样。唯一不同的是strtod(c,&caux)这个函数的调用。* string to double函数,用法原理一样,就是没有了后面的进制参数。所以这里不细说了。* @param is* @param v* @return*/
std::istream& operator>>(std::istream& is, std::vector<double>& v){string s;if (! (is >> s) )return is;const char* c = s.c_str();char* caux = const_cast<char*>(c);v.clear();bool hasNextValue=true;while(hasNextValue){double i=strtod(c,&caux);if (c!=caux){c=caux;c++;v.push_back(i);} elsehasNextValue = false;}return is;
}/*** double类型数组输出也是一样,参看上方int类型。* @param os* @param v* @return*/
std::ostream& operator<<(std::ostream& os, const std::vector<double>& v)
{if (v.size())os << v[0];for (size_t i=1; i<v.size(); i++)os << ";" << v[i];return os;
}//查看参数的parsed属性。看看是否由命令行正确读入了进来。也就是一个参数是否被用户自定了。
bool CommandArgs::parsedParam(const std::string& param) const
{//std::vector<CommandArgument>::const_iterator it = _args.begin();auto it = _args.begin();//遍历参数数组for ( ; it != _args.end(); ++it){//如果找到名称一样的,直接return返回它的parsed属性。if (it->name == param) {return it->parsed;}}//如果遍历完了都没有,则返回false,说明参数列表中压根没找到这个参数,也是返回falsereturn false;
}
这篇关于第十章g2o_custombundle中的common文件夹中的flags文件夹下的command_args.cpp的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!