Tip of the Week 112 emplace与push_back
“我们越少使用我们的力量,它就会越强大。” —— 托马斯 杰克逊
正如你所知(如果不知道,参见TotW 65)
正如你所知道的,C++11引入了一种强大的新方法来讲条目插入到容器中:emplace方法。这些可以让使用任何对象的构建函数在一个容器内原地构建一个对象。这包含移动和拷贝构造函数,因此在你可以使用push或者insert方法时,你可以使用emplace方法代替,而不需要其他修改。
123456std::vector<string> my_vec;my_vec.push_back("foo"); // 这是可行的,因此...my_vec.emplace_back("foo"); // 这也是可行的,并且有相同的结果std::set<string> my_set;my_set.insert("foo"); // 此处相同:任何调用imsert可以被emplace重写my_set.emplace("foo");
这产生一个明显的问题:你应该用哪一个好? ...
Tip of the Week 55 名字计数与unique_ptr
“尽管我们可能以一千种名字来认识他,但是他于我们所有人而言是同一个人。”——圣雄甘地
通俗来讲,值的“名字”是在任意作用域内保持特定数值的任意值类型变量(不是指针也不是引用)。(对于规范准则而言,如果我们说“名字”,实质上我们谈论的是左值。)因为std::unique_ptr的特定行为的要求,我们需要确保在std::unique_ptr中保存的任意值仅有一个名字。
请务必注意,C++委员会为std::unique_ptr选择了一个非常恰当的名字。在任何时候,存储在std::unique_ptr中的任何非空指针值都只能在一个std::unique_ptr中出现;标准库以强制执行此操作来设计的。许多编译代码中使用std::unique_ptr的常见问题能够通过学习如何计算std::unique_ptr的数量来解决:一个名字是可以的,但是相同的指针值对应多个名字则不行。
让我们数一些名字。在每一行,计算在位置(无论是否在作用域内)上包含相同指针的std::unique_ptr并且存在的名字数量。如果你在任何一行发现关于相同指针值的名字多余一个,那是一个错误!
12345678910111 ...
Tip of the Week 65 就地安放
“让我解释一下。不,这太多了。让我总结一下。” —— 埃尼戈·蒙托亚
C++11添加了一种元素插入标准容器的新方法:emplace()方法系列。这些方法直接在容器中创建对象,而不是创建一个临时的对象,然后拷贝或者移动这个对象进容器。避免这些副本对于绝大多数对象而言都是更有效率的,并且在标准容器中存储只移动的对象(例如std::unique_ptr)也会更容易。
旧方法和新方法让我们来看一下两种使用vector的方法,第一个示例使用C++11之前的代码:
12345678910class Foo { public: Foo(int x, int y); …};void addFoo() { std::vector<Foo> v1; v1.push_back(Foo(1, 2));}
使用这些旧的push_bach方法,有两个foo对象被构造:这临时的参数和在vector中由临时对象通过移动构造函数构造对象。
我们能够用C++11中的emplace_back代替,并且只有一个对象在vector的内存中被直接构造。由于“emp ...
共享内存实现—Ring-Buffer
上文有提到,Shared Memory Port用来提供读写的通道,在这个通道中传输的缓存被设计为一个Ring-Buffer,提供P0的发现服务数据读写,或者用户自定义的数据读写。
MultiProducerConsumerRingBuffer这个名字有点长,但是顾名思义,Ring-Buffer被设计为多生产者和多消费者的模式,有如下几个特点:
读写操作都是无锁的
同一个RingBuffer中的数据单元(cell)数目是固定的,数据类型都是一样的
消费者(listener)必须注册获得listener来访问数据
当一个数据单元(cell)被推送到buffer,这个数据单元的计数值为当前注册的listener个数
当某个数据单元(cell)被所有的listener都取出之后,这个数据单元会被释放掉
MultiProducerConsumerRingBuffer::Cell1234567891011121314151617181920class Cell { public: const T& data() const ...
FastDDS进程间内存共享
概述
本文描述了一个通过共享内存实现进程间通信模型,通过这个模型,不同的软件组件可以进行数据交换。这个模型的目标是提供了一种基于共享内存的数据传输层,来适配一个实时订阅发布的DDS(Data Distribution Service)框架。
上下文eProsima是接收到了一个商业上的方案通过共享内存传输来改进FastRPTS产品。下面是这一块列出的一些目标。
通过标准的网络传输的改进
减少操作系统内核的系统调用:这对于UDP/TCP是不可避免的(甚至在loopback的设备上)
大数据传输支持:网络传输往往需要把大数据切片传输
避免数据的序列化和反序列化过程:在异构网络中是不可能的,但是在进程间共享内存中是可行的
减少内存拷贝:共享内存可以实现零拷贝,客户端可以直接拿到共享buffer的指针进行访问
目标
提高进程间通信的性能
创建一套可移植的共享内存库(Windows/Linux/MacOS)
文档
测试
示例代码
架构设计理念
Segment:一块固定大小的共享内存,不同的进程可以访问。共享内存快有一个全局的名称,所以任何一个知道这个名称的进程都可以打开这块内存,然后映射到 ...
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: 带超时的递归互斥锁
条件变量通常用于生产者和消费者模型,大致使用过程如下:
拥有条件变量的线程获取互斥量
循环检查某个条件,如果条件不满足阻塞当前条件,否则线程继续向下执行
产品的数量达到上限,生产者阻塞,否则生产者一直生成。。。
产 ...