第十章g2o_custombundle中的common文件夹中的flags文件夹下的command_args.cpp

来源:互联网 发布:原版优化9 编辑:程序博客网 时间:2024/06/09 19:10

这篇为上篇的下半部分,也就是.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;        }        //上面过滤掉了没有'-'和‘--’的情况,那么剩下的就是‘-’和‘----’的情况,找到第一个不是'-'的地方,然后将前面的切掉。重新赋值name        string::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置为true                        it->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是描述块所描述的参数变量的地址,将这个地址赋值给s        string* 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 table        vector<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 + ")"));                else                    tableStrings.push_back(make_pair(_args[i].name + " " + type2str(_args[i].type), _args[i].description));            } else                tableStrings.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参数的变量aux            float 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/21447431        case 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 s    if(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);        } else            hasNextValue = 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,说明参数列表中压根没找到这个参数,也是返回false    return false;}
阅读全文
0 0