最近讀別人的代碼,看到了一個有意思的東西。
主要是當我們訂閱一個消息時候,會調用一個返回函數。
例如:
ros::Subscriber scan_sub=n.subscribe<std_msgs::Int8>("/test", 10, multiprint);
這樣multiprint函數應該包含一個參數,即
void multiprint(const std_msgs::Int8::ConstPtr& msg){}
但是,如果我們想要多參數傳入的話,就需要使用boost庫中的bind函數。例如,當我們的回調函數是這樣的:
void multiprint(const std_msgs::Int8::ConstPtr& msg, int& x, int& y){}
Boost::bind
在STL中,我們經常需要使用bind1st,bind2st函數綁定器和fun_ptr,mem_fun等函數適配器,這些函數綁定器和函數適配器使用起來比較麻煩,需要根據是全局函數還是類的成員函數,是一個參數還是多個參數等做出不同的選擇,而且有些情況使用STL提供的不能滿足要求,所以如果可以我們最好使用boost提供的bind,它提供了統一的接口,提供了更多的支持,比如說它增加了shared_ptr,虛函數,類成員的綁定。
bind並不是一個單獨的類或函數,而是非常龐大的家族,依據綁定的參數的個數和要綁定的調用對象的類型,總共有數十種不同的形式,編譯器會根據具體的綁定代碼制動確定要使用的正確的形式,bind的基本形式如下:
1 template<class R,class F> bind(F f); 2 template<class R,class F,class A1> bind(F f,A1 a1); 3 namespace 4 { 5 boost::arg<1> _1; 6 boost::arg<2> _2; 7 boost::arg<3> _3; 8 ….. //其他6個占位符 9 };
bind接收的第一個參數必須是一個可調用的對象f,包括函數、函數指針、函數對象、和成員函數指針,之后bind最多接受9個參數,參數數量必須與f的參數數量相等,這些參數被傳遞給f作為入參。 綁定完成后,bind會返回一個函數對象,它內部保存了f的拷貝,具有operator(),返回值類型被自動推導為f的返回類型。在發生調用時這個函數對象將把之前存儲的參數轉發給f完成調用。例如,有一個函數func,它的形式是:
1 func(a1,a2);
那么,他將等價於一個具有無參operator()的bind函數對象調用:
1 bind(func,a1,a2)();
這是bind最簡單的形式,bind表達式存儲了func和a1、a2的拷貝,產生了一個臨時函數對象。因為func接收兩個參數,而a1和a2的拷貝傳遞給func完成真正的函數調用。
bind的真正威力在於它的占位符,它們分別定義為_1,_2,_3,一直到 _9,位於一個匿名的名字空間。占位符可以取代bind參數的位置,在發生調用時才接受真正的參數。占位符的名字表示它在調用式中的順序,而在綁定的表達式中沒有沒有順序的要求,_1不一定必須第一個出現,也不一定只出現一次,例如:
1 bind(func,_2,_1)(a1,a2);
返回一個具有兩個參數的函數對象,第一個參數將放在func的第二個位置,而第二個參數則放在第一個位置,調用時等價於:
1 func(a2,a1);
占位符的最常見的使用方法就是printf中%f,%d等等這些的使用方法。
例程
下面是一個簡單的訂閱消息的例程,在只傳入單個變量的時候如下:
#include <ros/ros.h> #include <std_msgs/Int8.h> int index1=0; int index2=0; void multiprint(const std_msgs::Int8::ConstPtr msg, int& x, int& y) { printf("%d",*msg); printf("%d \r\n",x); printf("%d \r\n",y); } void multiprint(const std_msgs::Int8::ConstPtr& msg) { printf("%d \r\n",*msg); } int main(int argc, char **argv) { ros::init(argc, argv, "multi_callback"); ros::NodeHandle n; ros::NodeHandle private_nh("~"); int rate; private_nh.param("rate",rate, 40); // ros::Subscriber scan_sub=n.subscribe<std_msgs::Int8>("/test", 10, boost::bind(&multiprint, _1, index1, index2)); ros::Subscriber scan_sub=n.subscribe<std_msgs::Int8>("test", 10, multiprint); ros::Rate r(rate); while(n.ok()) { ros::spinOnce(); r.sleep(); } return 0; }
當通過rostopic傳入消息的時候,
rostopic pub /test std_msgs/Int8 "data: 12"
subscrib函數選擇回調,
void multiprint(const std_msgs::Int8::ConstPtr& msg) { printf("%d \r\n",*msg); }
輸出的結果就應該是消息的輸入,即
12
當程序改變為:
#include <ros/ros.h> #include <std_msgs/Int8.h> int index1=0; int index2=0; void multiprint(const std_msgs::Int8::ConstPtr msg, int& x, int& y) { printf("%d",*msg); printf("%d \r\n",x); printf("%d \r\n",y); } void multiprint(const std_msgs::Int8::ConstPtr& msg) { printf("%d \r\n",*msg); } int main(int argc, char **argv) { ros::init(argc, argv, "multi_callback"); ros::NodeHandle n; ros::NodeHandle private_nh("~"); int rate; private_nh.param("rate",rate, 40); ros::Subscriber scan_sub=n.subscribe<std_msgs::Int8>("/test", 10, boost::bind(&multiprint, _1, index1, index2)); // ros::Subscriber scan_sub=n.subscribe<std_msgs::Int8>("test", 10, multiprint); ros::Rate r(rate); while(n.ok()) { ros::spinOnce(); r.sleep(); } return 0; }
注意:方程中的消息的參數類型后面一定要跟上::ConstPtr,否則會報錯。消息的前面因為是_1占位符傳入,所以回調函數里參數調用的msg前不能加&。
同樣通過rostopic傳入消息,這樣地話運行時回調用
void multiprint(const std_msgs::Int8::ConstPtr& msg, int& x, int& y) { printf("%d",*msg); printf("%d \r\n",x); printf("%d \r\n",y); }
其中
boost::bind(&multiprint, _1, index1, index2)
的_1部分,是后面的獲得的消息的傳入。
其返回結果應該是
12 0 0
這樣就可以在回調函數中傳入多個參數了。
參考:
Boost::bind工作原理 : https://www.cnblogs.com/blueoverflow/p/4740093.html
參考筆記A0220180206