1.坑描述:不用临时变量接收函数node->CreateService返回值导致服务创建失败
2.前戏:cyber作为ROS的替代品提供了主要两大功能:调度功能和通讯功能.通讯功能包括订阅发布模式和一问一答的Service模式.本篇主要讲的是Service模式下的一个坑.Service的主要实现代码在/cyber/service下面,仅4个文件(两个头文件两个实现文件).可谓相当的精巧.怎么使用呢?源代码里面/cyber/examples/service.cc.50多行代码展示了如何使用他的Service功能.下面展示如何运行这个例子.首先启动控制台进入docker里面.输入命令:
export GLOG_alsologtostderr=1
输入这个是为了让cyber用AINFO等宏产生日志可以直接在控制台看见.接着就可以输入运行命令:
bazel run //cyber/examples:service
此后就可以看到运行输出效果.
3.坑的复现:代码/cyber/examples/service.cc里的第27行写的是
auto server = node->CreateService<Driver, Driver>(....
而后产生的server变量后面没有再被引用.我们将上述代码"auto server = "去掉,重新运行,发现服务启动失败了,客户端始终连接不上去.
4.分析原因:直观的来看很难理解,你这个返回值后来也没用到,我拿不拿变量接收你有什么区别吗?写代码的时候为了简洁起见,反正你后面用不到我就不用拿变量接收你了(我正是干了这种事儿,事后追查了几个小时原因),有什么问题吗?测试的时候就发现客户端始终连接不上,他这这种还不是普通的socket,是高度封装的,正常的socket可以通过工具查看端口的监听情况,以此推断是哪个地方出了问题.后来通过反复的对比发现是忘记了用一个临时变量接收返回值,加上就好了.经过阅读源代码发现是没有用临时变量接受返回值导致智能指针认为自己没有人用了提前释放了.下面举个例子来说明这种情况
#include <iostream>
#include <memory>
class Test
{
public:
Test() = default;
virtual ~Test()
{
std::cout << "~Test()" << std::endl;
}
};
auto GetOneTest()
{
auto t = std::make_shared<Test>();
return t;
}
int main(int argc, char **argv)
{
auto t = GetOneTest();
std::cout << "main end" << std::endl;
return 0;
}
编译运行上面的代码,我们看到结果是主函数先结束之后再进行Test的析构.倘若我们将主函数里面的第一行"auto t ="去掉运行结果是先析构Test之后main结束. 言简意赅的总结原因就是:智能指针为自己没有人用了提前释放了,Test实例随之析构.
5.遗留问题:cyber有个插件就做cyber_service, cyber_service list命令可以查看服务列表,令人奇怪的是不用临时变量接收返回值Service提前被析构的情况下该命令仍然可以看到创建的服务.这令我很不理解,误认为服务端没啥问题浪费时间去研究客户端问题.操蛋,有时间再去研究这个事情吧