ROSソース読解(2):ROSプログラムの初期化--ros:init()から
19864 ワード
ros::init()から
次のブログのROSソースコードを読みます(1):切り込み点を探して、ros::init()関数から、ROSソースコードの探索を始めました.ros::init()関数の宣言は、ROSコードの
./src/ros_comm/roscpp/include/ros/init.h
ファイルにあります.リロードは次の3つの形式です.//./src/ros_comm/roscpp/include/ros/init.h
void 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.cpp
void 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()は主に以下のいくつかの関数を呼び出して初期化を完了していることがわかります.
1. network::init()
名前から見ると、この関数はネットワークの初期化に使用されます.実は今
./src/ros_comm/roscpp/src/libros/network.cpp
中です.実装コードは次のとおりです.//./src/ros_comm/roscpp/src/libros/network.cpp
void 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.h
namespace 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_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;
この関数の2番目の機能モジュールと対応するコメントは次のとおりです.
// "__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_キャストは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;
3番目のモジュールのコードは次のとおりです.
// 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つの変数の付与.
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:
" \
"export ROS_MASTER_URI=http://localhost:11311
" \
"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();//
}
}
ここで、2つの未知の関数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
\tfile = %s
\tline=%d
", __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()を簡潔に解析した.この関数は主に以下の関数を呼び出して初期化を完了していることがわかりました.
次に,その中の最初の2つの関数の簡単な解析を行った.