Fast DDS 入门
本篇是搬运自官方文档:
1. Getting Started — Fast DDS 2.6.0 documentation (eprosima.com)
以及加入了自己的实践,会比较偏实战,减去很多理论的东西,很多理论的东西翻译不过来,见谅,想要深入理解原理的话,可以进入链接自行查看,
什么是DDS?DDS:Data Distribution Service(数据分发服务),是由对象管理组织(OMG®)发布和维护,是一个中间件协议和API标准,采用发布/订阅体系架构,强调以数据为中心,提供丰富的QoS服务质量策略,以保障数据进行实时、高效、灵活的分发,可满足各种分布式实时通信应用需求。
而我们这里的Fast DDS是DDS规范的C++实现,Fast DDS的前称是Fast RTPS,目前ROS2将Fast DDS作为默认的DDS中间件实现。
DDS标准和相关版本信息可以参考下面链接:
DDS 介绍 (qq.com)
DDS APIDDS采用的通信模型是一种多对多单项数据交换,其中产生数据的应用程序将数据发布到属于使用数据的应用程序的订阅者的本地缓存。信息流由负责数据交换的尸体之间简历的 ...
Tip of the Week 88 初始化:=,()和{}
Tip of the Week#88: 初始化:=,()和{}C++11提供了一种称为”统一初始化语法”的新语法,它被认为统一所有不同风格的初始化,避免最麻烦人的解析,并避免窄化转换。这种新机制意味着我们现在有另一种初始化语法,它有自己的权衡。
C++11括号初始化一些统一初始化语法的支持者会建议我们使用{}和直接初始化(不适用’=’,尽管在多数情况下两种形式调用相同的构造函数)来初始化所有类型:
123int x{2};std::string foo{"Hello World"};std::vector<int> v{1, 2, 3};
对比(例如):
123int x = 2;std::string foo = "Hello World";std::vector<int> v = {1, 2, 3};
这种方法有两个特点。首先,“统一”是一个延伸概念:在某些情况下,在调用什么以及如何调用这方面依然存在歧义(针对普通读者,而不是编译器)。 ...
Tip of the Week 93 使用absl::Span
Tip of the Week#93: 使用absl::Span在google,当我们想处理没有owner的字符串时,通常会使用absl::string_view作为函数的参数和返回值。它能够使API更加灵活,并且它能够通过避免对std::string进行不必要的转换来提升性能。
absl::string_view有一个更加通用的表亲,被称为absl::Span。absl::Span对std::vector就像absl::string_view对std::string一样。它为vector的元素提供只读接口,但是它也可以从由非vector(如数组和初始化列表)来构造,并且不会产生拷贝元素的消耗。
const可以被删除,因此absl::Span是一个元素不能改变的数组的视图,absl::Span允许对元素进行非常量访问。然而,与const的跨度不同,这些需要显式构造。
关于std::span/gsl::span的注释需要重点注意的是,虽然absl::Span在设计和目的上与std::span方案(以及现存的gsl::span引用实现)相似,但是absl::Span目前并不保证是一个对最终 ...
FastDDS初体验
写在前面目前正在往自动驾驶的方向上走,目前公司用的底层通信框架还比较粗暴、稚嫩,这里参考一些开源的框架来学习,看能否对改造现有方案起到作用,也是为了记录自己的一些学习过程,以免后面想要回过头来做参考。
简单介绍FastDDS也是目前比较好的一个框架,FastDDS是由位于西班牙马德里的eProsima公司推出的免费开源DDS中间件解决方案,并提供支付技术支持服务。它的源码基于C++,规范基于OMG DDS 1.4 and the OMG RTPS 2.2。
这里先介绍一下依照官方如何安装FastDDS到系统,并进行简单的测试。
官方文档链接:https://fast-dds.docs.eprosima.com/en/latest/,这里出了安装步骤,还有很多原理和例程介绍。
这里是使用的操作系统是ubuntu 1804进行安装。
FastDDS的安装内容主要有三部分:
FastDDS library
foonathan_memory_vendor: C++内存分配库
fastdcdr: CDR序列化(serialization)机制
fastrtps: FastDDS核心库
Fa ...
C++线程同步之条件变量
条件变量是C++11提供的另外一种用于等待的同步机制,它能阻塞一个或多个线程,直到收到另外一个线程发出的通知或者超时,才会环形当前阻塞的线程。条件变量需要和互斥锁配合起来使用,C++11提供了两种条件变量:
**condition_variable:**需要配合std::unique_lock<std::mutex>进行wait操作,也就是阻塞线程的操作。
**condition_variable_any**: 可以和任意带有lock()、unkock()语义的mutex搭配使用,也就是说有四种:
**std::mutex**: 独占的非递归互斥锁
std::timed_mutex: 带超时的独占非递归互斥锁
std::recursive_mutex: 不带超时功能的递归互斥锁
std::recursive_timed_mutex: 带超时的递归互斥锁
条件变量通常用于生产者和消费者模型,大致使用过程如下:
拥有条件变量的线程获取互斥量
循环检查某个条件,如果条件不满足阻塞当前条件,否则线程继续向下执行
产品的数量达到上限,生产者阻塞,否则生产者一直生成。。。
产 ...
Tip of the Week 77 临时变量,移动,复制
我们正在尝试给语言专家意外的人解释清楚C++11会怎样改变代码世界,因此在“什么时候发生复制?”系列中又增加了一篇温江,这也是更广泛的尝试之中的一部分,试图用一套更简单的规则简化C++中的变量复制规则。
你会数到2吗?你会?太好了。记住“名字规则”意味着你能为一个资源指定的所有名字,会影响该对象有多少份拷贝。
简单解释数名字如果你在担心发生了变量复制,那我们驾驶你在担心几行特定的代码。在你认为被复制的数据上,有几个名字存在?只有三种情况需要考虑:
1. 两个名字:复制这个容易:如果你在给同一份数据第二个名字,那就是复制。
1234567std::vector<int> foo;FillAVectorOfIntsByOutputParameterSoNobodyThinksAboutCopies(&foo);std::vector<int> bar = foo; // 没错,这就是复制。std::map<int, string> my_map;string forty_two = "42";my_map[5] = f ...
Tip of the Week 24 复制简明判定法
“模仿别人是必要的,但重复自己是可悲的。” ——巴勃罗·毕加索
一个名字,没有复制;两个名字,两个副本在任何作用域内(包括触发RVO的情况),要知道发生了多少次复制,只需要检查你的数据有多少个名字。
在任意时刻,如果两份数据有两个有效的名字,那这些数据就有两个副本。作为一个很好的近似,
在除此以外的情况下,编译器会(而且尝尝是必须)避免复制。
因此,如果你的代码中,一份数据在某个时间点有两个名字,那么你应该期待有一次复制。如果你避免了指向一份数据的多余的名字,那么你在帮助编译器移除不必要的复制。
示例通过几个例子,我们来看看实践中是怎么使用这个定则的:
123456789101112131415std::string build();std::string foo(std::string arg) { return arg; // 没有复制,数据`arg`只有一个名字。}void bar() { std::string local = build(); // 只有一个实例,只有一个名字 // 没有复制,引用不会触发复制 std::string& ...
Tip of the Week 11 返回策略
注:这条贴士—虽然仍然有效—但是写它的时候还没有C++11的移动语义。同时可以参阅Totw #77。
很多老的C__代码库使用了“害怕复制对象”的范式。幸运的是,多亏了“返回值优化(RVO)”,我们可以“复制”但不真的复制。
RVO特性存在已久,几乎所有的C++编译器都实现了它。考虑如下的C++98代码,有一个复制构造函数和一个复制运算符。这些函数代价都很大,开发者让它们在每次调用时都打印一条消息:
123456789101112131415class SomeBigObject { public: SomeBigObject() { ... } SomeBigObject(const SomeBigObject& s) { printf("死贵的复制…\n", …); … } SomeBigObject& operator=(const SomeBigObject& s) { printf("死贵的赋值…\n", …); … ...
Tip of the Week 10 分割字符串,不必拘小节
将字符串分割为子串是任何通用编程语言的常见操作,C++也不例外。这个需求出现在Google的时候,很多工程师发现他(她)们掉进了一堆(且不断增加的)“分割函数”填成的泥沼。你必须搜索那个输入参数、输出参数和语义都满足你要求的函数。在考察过600多行的头文件理的里的50多个函数之后,你也许会最终决定使用一个命名拐弯抹角的函数:
SplitStringViewToDequeueIfStringAllowEmpty()。
为了解决这个麻烦,C++库团队实现了一个新的API来分割字符串,放在了absl/strings/str_split.h里。
这个新的API用一个absl::StrSplit()函数取代了所有类似的分割函数。absl::StrSplit()接受一个准备被分割的字符串和一个分割符参数。其返回的子串集合可以自动适配为调用者接受的容器类型。absl::StrSplit()的内部实现使用absl::string_view,因此非常高效。除非调用者显式要求将结果存储为字符串对象集合,否则字符串内容不会被复制。
来看下下面的例子,比较直截了当:
123456789101112131415 ...
Tip of the Week 5 消逝的演出
有时候,为了正确的运用C++的库,你既需要理解库本身,又需要理解这门语言。那么……下面的代码中的问题是什么?
12345// 别这么干std::string s1, s2;...const char* p1 = (s1 + s2).c_str(); // 别!const char* p2 = absl::StrCat(s1, s2).c_str(); // 别!
s1+s2和absl::StrCat(s1, s2)都创建了临时对象(这里都是字符串对象,但同样的规则使用于任意对象)。成员函数c_str()返回指向底层数据的指针,而底层数据与临时对象生命周期一致。临时对象能活多长?根据C++17标准中的[class temporary],“在临时对象创建点所在的完整表达式中,临时变量的销毁时表达式的最后一步。”,那么也就是说,当赋值运算符右边的表达式结束的时候,临时变量就被销毁了,c_str()的返回值就成了“野”指针。那么如何避免这类问题呢?
方法一,在完整表达式结束前用完临时对象:123// 安全(虽然弱鸡了一点)size_t len1 = strlen(( ...














