ROS源代码阅读(2):ROS程序的初始化——从ros:init()出发
来源:互联网 发布:淘宝店家怎么代销 编辑:程序博客网 时间:2024/05/18 01:35
从ros::init()出发
接着上一篇博客ROS源代码阅读(1):找切入点,我们从ros::init()函数出发,开始探索ROS源代码。
ros::init()函数的声明在ROS代码中的./src/ros_comm/roscpp/include/ros/init.h
文件中。
重载为以下三种形式:
//./src/ros_comm/roscpp/include/ros/init.hvoid init(int &argc, char **argv, const std::string& name, uint32_t options = 0);void init(const M_string& remappings, const std::string& name, uint32_t options = 0);void init(const VP_string& remapping_args, const std::string& name, uint32_t options = 0);
该函数的具体实现在./src/ros_comm/roscpp/src/libros/init.cpp
文件中。我们先选择其中最简单的一个形式的实现进行分析:
//./src/ros_comm/roscpp/src/libros/init.cppvoid init(const M_string& remappings, const std::string& name, uint32_t options){ if (!g_atexit_registered) { g_atexit_registered = true; atexit(atexitCallback); } if (!g_global_queue) { g_global_queue.reset(new CallbackQueue); } //上面做了一些预处理,主要部分在下面: if (!g_initialized) { g_init_options = options; g_ok = true; ROSCONSOLE_AUTOINIT; //在console.h中的一段宏定义:Initializes the rosconsole library. // Disable SIGPIPE#ifndef WIN32 signal(SIGPIPE, SIG_IGN);#endif network::init(remappings);//初始化网络,实现在network.cpp中 master::init(remappings); //初始化master // names:: namespace is initialized by this_node this_node::init(name, remappings, options); //初始化当前节点 file_log::init(remappings); param::init(remappings); g_initialized = true;//置上初始化标记 }}
可以看出,ROS程序的初始化函数ros::init()主要调用了以下几个函数完成初始化:
- network::init(remappings);
- master::init(remappings);
- this_node::init(name, remappings, options);
- file_log::init(remappings);
- param::init(remappings);
其中,前两个函数(network::init和master::init)比较简单,在此文档中先介绍这两个函数。
1. network::init()
从名字上看,该函数用于对网络的初始化。其实现在./src/ros_comm/roscpp/src/libros/network.cpp
中。实现代码如下:
//./src/ros_comm/roscpp/src/libros/network.cppvoid init(const M_string& remappings) //该函数在init.cpp中被调用{ //模块1: M_string::const_iterator it = remappings.find("__hostname"); if (it != remappings.end()) { g_host = it->second; } else { it = remappings.find("__ip"); if (it != remappings.end()) { g_host = it->second; } } //模块2 it = remappings.find("__tcpros_server_port"); if (it != remappings.end()) { try { g_tcpros_server_port = boost::lexical_cast<uint16_t>(it->second); } catch (boost::bad_lexical_cast&) { throw ros::InvalidPortException("__tcpros_server_port [" + it->second + "] was not specified as a number within the 0-65535 range"); } } //模块3 if (g_host.empty()) { g_host = determineHost(); }}} // namespace network} // namespace ros
根据上述代码,在正式解析该函数之前,我们关注到输入参数的数据类型是M_string的一个引用,那么我们先来看看M_string是一个什么数据类型:
插曲1:关于数据类型M_string
该函数的输入参数是const M_string& remappings
。数据类型M_string的定义./src/roscpp_core/cpp_common/include/ros/datatypes.h
中,该文件定义了ROS实现用到的若干种数据类型。代码如下:
//./src/roscpp_core/cpp_common/include/ros/datatypes.hnamespace ros {typedef std::vector<std::pair<std::string, std::string> > VP_string;typedef std::vector<std::string> V_string;typedef std::set<std::string> S_string;typedef std::map<std::string, std::string> M_string;typedef std::pair<std::string, std::string> StringPair;typedef boost::shared_ptr<M_string> M_stringPtr;}
可以看到,M_string为一个键/值的数据类型都是std::string的map容器。关于与此处map容器相关的内容见我的博文std:map与迭代器简析。
解决了数据类型M_string的问题,我们细看代码。代码的第一个功能模块如下,该模块主要对变量g_host进行赋值,见代码和注释。
//查找remappings中键为“__hostname”的元素M_string::const_iterator it = remappings.find("__hostname"); //如果找到了,则将该元素的值赋值给“g_host” if (it != remappings.end()) { g_host = it->second; } //如果没找到,则: else { //查找键为“__ip”的元素 it = remappings.find("__ip"); //如果找到了,则将该元素的值赋值给“g_host” if (it != remappings.end()) { g_host = it->second; } }
其中,g_host是一个std::string类型的变量,在network.cpp文件的一开头就已定义。
std::string g_host;
该函数的第二个功能模块和相应的注释如下:
//查找键为"__tcpros_server_port"的元素it = remappings.find("__tcpros_server_port"); //如果找到了 if (it != remappings.end()) { try//尝试将对应元素的值(std::string)转化成uint16_t类型 { g_tcpros_server_port = boost::lexical_cast<uint16_t>(it->second); } catch (boost::bad_lexical_cast&)//如果上述类型转化发生异常 { throw ros::InvalidPortException("__tcpros_server_port [" + it->second + "] was not specified as a number within the 0-65535 range"); } }
其中又涉及到C++的类型转换器boost::lexical_cast。
插曲2:关于类型转换器boost::lexical_cast
boost::lexical_cast为数值之间的转换(conversion)提供了一揽子方案,比如:将一个字符串”712”转换成整数712,代码如下:
string s = "712"; int a = lexical_cast<int>(s);
这种方法的好处是:如果转换发生了意外,lexical_cast会抛出一个bad_lexical_cast异常,可以在程序中进行捕捉。
以上模块就是用boost::lexical_cast进行转换(该函数包含在头文件boost/lexical_cast.hpp
中),然后捕捉bad_lexical_cast异常。从上述代码中看出,该模块的作用是为变量g_tcpros_server_port赋值,该变量的定义在network.cpp的开头,且默认值为0:
uint16_t g_tcpros_server_port = 0;
第三个模块的代码如下:
//当g_host为空的时候,调用函数determineHost()为其赋值。if (g_host.empty()) { g_host = determineHost(); }
这段代码所调用的determineHost()也定义在./src/ros_comm/roscpp/src/libros/network.cpp
文件中。对于该函数我们在此暂不深入。这段代码的作用就是:当g_host为空(在模块1中暂未被赋值)时,调用determineHost对其进行赋值。
综上所述,network:init()函数主要完成了g_host和g_tcpros_server_port两个变量的赋值。
2. master::init()
master::init()函数定义在./src/ros_comm/roscpp/src/libros/master.cpp
文件中。具体实现代码如下。关于输入参数类型M_string,我已在上文中介绍。关于这段代码的实现细节network::init类似,也是根据输入参数对若干变量赋值,我直接将代码注释附上,不再分模块详述。
void init(const M_string& remappings){ //构建迭代器,查找remappings中键为"__master"的节点。 M_string::const_iterator it = remappings.find("__master"); //如果找到了,则将该节点对应的值赋值给g_uri if (it != remappings.end()) { g_uri = it->second; } //如果g_uri没有被赋值(即刚刚没找到相应节点) if (g_uri.empty()) { char *master_uri_env = NULL; //设法给master_uri_env赋值 #ifdef _MSC_VER _dupenv_s(&master_uri_env, NULL, "ROS_MASTER_URI"); #else master_uri_env = getenv("ROS_MASTER_URI"); #endif if (!master_uri_env)//如果master_uri_env没有被赋值 { ROS_FATAL( "ROS_MASTER_URI is not defined in the environment. Either " \ "type the following or (preferrably) add this to your " \ "~/.bashrc file in order set up your " \ "local machine as a ROS master:\n\n" \ "export ROS_MASTER_URI=http://localhost:11311\n\n" \ "then, type 'roscore' in another shell to actually launch " \ "the master program."); ROS_BREAK(); } g_uri = master_uri_env;#ifdef _MSC_VER // http://msdn.microsoft.com/en-us/library/ms175774(v=vs.80).aspx free(master_uri_env);#endif }//if(g_uri.empty()) //对g_uri进行解析,把g_uri中去掉协议部分赋值给g_host,并将端口赋值给g_port。 if (!network::splitURI(g_uri, g_host, g_port)) { ROS_FATAL( "Couldn't parse the master URI [%s] into a host:port pair.", g_uri.c_str()); ROS_BREAK();// }}
其中,涉及到两个未知的函数ROS_BREAK()和network::splitURI(g_uri, g_host, g_port)。
插曲2.1 ROS_BREAK()函数
ROS_BREAK()函数的定义在./src/ros_comm/rosconsole/include/ros/assert.h
文件中,具体如下:
#define ROS_BREAK() \ do { \ ROS_FATAL("BREAKPOINT HIT\n\tfile = %s\n\tline=%d\n", __FILE__, __LINE__); \ ROS_ISSUE_BREAK() \ } while (0)
该函数用于中断ROS程序的执行,并显示在哪个文件哪行发生了中断。该函数的具体细节在此先不详述。
插曲2.2 network::splitURI()
该函数定义在./src/ros_comm/roscpp/src/libros/network.cpp
文件中,具体实现如下,我同时将注释附上。
bool splitURI(const std::string& uri, std::string& host, uint32_t& port){ // 提取uri中去掉协议的部分,赋值给host if (uri.substr(0, 7) == std::string("http://")) host = uri.substr(7); else if (uri.substr(0, 9) == std::string("rosrpc://")) host = uri.substr(9); // 将uri中的端口号(:后面部分)抽取,赋值给port_str std::string::size_type colon_pos = host.find_first_of(":"); if (colon_pos == std::string::npos) return false; //如果uri中没有端口号,则该函数返回失败 std::string port_str = host.substr(colon_pos+1); //删除端口号中第一个“/” std::string::size_type slash_pos = port_str.find_first_of("/"); if (slash_pos != std::string::npos) port_str = port_str.erase(slash_pos); port = atoi(port_str.c_str());//将字符串转化为整形 host = host.erase(colon_pos);//从host变量中删除第一个“:” return true;}
即该函数的作用是对uri进行解析,将去掉协议部分赋值给host,将端口号赋值给port。
综上所述,master::init()的作用是在参数remappings中提取出变量g_uri的值,然后再将g_uri解析成g_host和g_port。
总结
在本文中,我们简析了ROS的初始化函数ros::init()。发现该函数主要调用了以下几个函数完成初始化:
- network::init(remappings);
- master::init(remappings);
- this_node::init(name, remappings, options);
- file_log::init(remappings);
- param::init(remappings);
然后,我们对其中的前两个函数进行的简单的分析。
后续将从以下几个角度深入:
- 继续分析剩下的三个初始化函数:this_node::init,file_log::init和param::init
- 在分析函数 network::init()时我们留了个尾巴,即determineHost()函数。我们后续将对determineHost()进行深入分析
- 完成上述分析后,从整体的角度,对ros:init()完成的工作做一个完整的总结。
- ROS源代码阅读(2):ROS程序的初始化——从ros:init()出发
- ROS源代码阅读(5):ROS程序的初始化——对ros:init()的总结
- ROS源代码阅读(4):ROS程序的初始化——file_log::init()和param::init()
- ROS源代码阅读(3):ROS程序的初始化——this_node::init()
- ros 初始化和关闭 (ros::init(); ros::shutdown())
- ROS常见问题2——用KDevelop编译ROS程序
- ROS源代码阅读(1):找切入点
- ros:(2)ros节点
- ROS
- ros
- Ros
- ROS
- ros
- ROS
- ROS
- 【ROS】
- ros
- ROS
- 进程暂停
- 【数据库】模糊查询
- 深入理解 Spring 事务原理
- HDU 6208:The Dominator of Strings(字符串匹配)
- Jenkins修改默认端口号8080
- ROS源代码阅读(2):ROS程序的初始化——从ros:init()出发
- 《MISRA-C 2004 工业标准的C编程规范》-初始化规则
- 第三周项目1---顺序表的基本运算
- Java基础部分第八节
- 枚举
- 泛泛谈SSM和SSH
- Java进阶:垃圾回收机制GC介绍,常见的垃圾回收算法以及垃圾回收与内存分配的关系
- 欢迎使用CSDN-markdown编辑器
- javascript实现web版拼图游戏