我可以将新的 std::tuple 放入内存映射区域,并在以后读回吗?
Can I placement new a std::tuple into a memory mapped region, and read it back later?
我有一些打包的结构,我将写入内存映射文件。他们都是 POD。
为了适应我正在做的一些通用编程,我希望能够编写几个打包结构的std::tuple
。
我担心将std::tuple
的成员写入我的映射区域的地址,然后将该地址转换回std::tuple
会中断。
我写了一个小型的 examplar 程序,它似乎确实有效,但我担心我有未定义的行为。
这是我的结构:
struct Foo
{
char c;
uint8_t pad[3];
int i;
double d;
} __attribute__((packed));
struct Bar
{
int i;
char c;
uint8_t pad[3];
double d;
} __attribute__((packed));
我定义了这些结构的std::tuple
:
using Tup = std::tuple<Foo, Bar>;
为了模拟内存映射文件,我创建了一个具有一些内联存储和大小的小对象:
添加元组时,它使用放置 new 在内联存储中构造元组。
struct Storage
{
Tup& push_back(Tup&& t)
{
Tup* p = reinterpret_cast<Tup*>(buf) + size;
new (p) Tup(std::move(t));
size += 1;
return *p;
}
const Tup& get(std::size_t i) const
{
const Tup* p = reinterpret_cast<const Tup*>(buf) + i;
return *p;
}
std::size_t size = 0;
std::uint8_t buf[100];
};
为了模拟写入文件,然后再次读取它,我创建了一个Storage
对象,填充它,复制它,然后让原始对象超出范围。
Storage s2;
// scope of s1
{
Storage s1;
Tup t1 = { Foo { 'a', 1, 2.3 }, Bar { 2, 'b', 3.4 } };
Tup t2 = { Foo { 'c', 3, 5.6 }, Bar { 4, 'd', 7.8 } };
Tup& s1t1 = s1.push_back(std::move(t1));
Tup& s1t2 = s1.push_back(std::move(t2));
std::get<0>(s1t1).c = 'x';
std::get<1>(s1t2).c = 'z';
s2 = s1;
}
然后我使用Storage::get
读取我的元组,它只执行内联存储的reinterpret_cast<Tup&>
。
const Tup& s2t1 = s2.get(0);
当我访问元组中的结构时,它们具有正确的值。
此外,运行瓦尔格林德不会引发任何错误。
- 我正在做的事情是定义的行为吗?
- 从我的内联存储
reinterpret_cast
到std::tuple
元组是否最初是在那里新放置的(放入一个将被关闭然后重新映射和重新读取的文件(?
内存映射文件:
我使用的实际存储是投射到boost::mapped_region
上的结构。
结构为:
struct Storage
{
std::size_t size;
std::uint8_t buf[1]; // address of buf is beginning of Tup array
};
我按如下方式投射它:
boost::mapped_region region_ = ...;
Storage* storage = reinterpret_cast<Storage*>(region_.get_address());
下面回答中提到的对齐问题会成为问题吗?
完整示例如下:
#include <cassert>
#include <cstdint>
#include <tuple>
struct Foo
{
char c;
uint8_t pad[3];
int i;
double d;
} __attribute__((packed));
struct Bar
{
int i;
char c;
uint8_t pad[3];
double d;
} __attribute__((packed));
using Tup = std::tuple<Foo, Bar>;
struct Storage
{
Tup& push_back(Tup&& t)
{
Tup* p = reinterpret_cast<Tup*>(buf) + size;
new (p) Tup(std::move(t));
size += 1;
return *p;
}
const Tup& get(std::size_t i) const
{
const Tup* p = reinterpret_cast<const Tup*>(buf) + i;
return *p;
}
std::size_t size = 0;
std::uint8_t buf[100];
};
int main ()
{
Storage s2;
// scope of s1
{
Storage s1;
Tup t1 = { Foo { 'a', 1, 2.3 }, Bar { 2, 'b', 3.4 } };
Tup t2 = { Foo { 'c', 3, 5.6 }, Bar { 4, 'd', 7.8 } };
Tup& s1t1 = s1.push_back(std::move(t1));
Tup& s1t2 = s1.push_back(std::move(t2));
std::get<0>(s1t1).c = 'x';
std::get<1>(s1t2).c = 'z';
s2 = s1;
}
const Tup& s2t1 = s2.get(0);
const Tup& s2t2 = s2.get(1);
const Foo& f1 = std::get<0>(s2t1);
const Bar& b1 = std::get<1>(s2t1);
const Foo& f2 = std::get<0>(s2t2);
const Bar& b2 = std::get<1>(s2t2);
assert(f1.c == 'x');
assert(f1.i == 1);
assert(f1.d == 2.3);
assert(b1.i == 2);
assert(b1.c == 'b');
assert(b1.d == 3.4);
assert(f2.c == 'c');
assert(f2.i == 3);
assert(f2.d == 5.6);
assert(b2.i == 4);
assert(b2.c == 'z');
assert(b2.d == 7.8);
return 0;
}
您可能希望对齐std::uint8_t buf[100]
存储,因为未对齐的访问是未定义的行为:
aligned_storage<sizeof(Tup) * 100, alignof(Tup)>::type buf;
(最初你有 100 个字节,这是 100Tup
秒(。
当您映射页面时,它们从 x86 上的至少 4k 边界开始。如果您的存储从页面开始开始,则该存储适合任何高达 4k 的 power-2 对齐。
我担心将
std::tuple
的成员写入我的映射区域的地址,然后将该地址转换回std::tuple
会中断。
只要通过映射内存进行通信的应用程序使用相同的 ABI,就可以按预期工作。
Tup* p = reinterpret_cast<Tup*>(buf) + size;
new (p) Tup(std::move(t));
是未定义的行为,因为buf
可能没有正确对齐Tup
。做这种事情的正确方法是使用std::aligned_storage
.
- 删除一个线程上有数百万个字符串的大型哈希映射会影响另一个线程的性能
- C++映射:具有自定义类的运算符[]不起作用(总是返回0)
- 将函数类成员映射到类本身内部
- 如何在 C# 中映射双 C 结构指针?
- 如何在C++中使用结构生成映射
- 使用std::函数映射对象方法
- 如何加载(或映射)文件部分的最大大小,但适合在Windows上的RAM
- C++映射分割错误(核心转储)
- 内联映射初始化的动态atexit析构函数崩溃
- 使用"std::unordereded_map"映射到"std::list"对象
- 如何从多映射中删除特定的重复项
- 在未初始化映射的情况下,将值插入到映射的映射中
- QT通过C++添加映射QML项目
- 我可以将新的 std::tuple 放入内存映射区域,并在以后读回吗?
- 内存映射区域初始数据
- 如何在 Windows 映射程序的 DLL 之前保留内存区域?
- QML映射可见区域
- 在地址映射区域的权限错误
- 写入大于系统内存的内存映射区域
- java socket.read to b[] 忽略从 JNI 映射的区域