gpt4 book ai didi

c++ - 我可以将新的 std::tuple 放入内存映射区域,然后再读回吗?

转载 作者:塔克拉玛干 更新时间:2023-11-03 01:41:47 27 4
gpt4 key购买 nike

我有一些打包的结构,我将把它们写入内存映射文件。它们都是 POD。

为了适应我正在做的一些通用编程,我希望能够编写一个 std::tuple几个打包结构。

我担心写一个 std::tuple 的成员到我映射区域的地址,然后将该地址转换回 std::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));

我定义了一个 std::tuple这些结构:

using Tup = std::tuple<Foo, Bar>;

为了模拟内存映射文件,我创建了一个带有一些内联存储和大小的小对象:

当添加一个元组时,它使用 placement 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);

当我访问元组中的结构时,它们具有正确的值。

此外,通过 valgrind 运行不会抛出任何错误。

  • 我正在做的是明确的行为吗?
  • 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 个字节,这是 100 个 Tup)。

当您映射页面时,它们在 x86 上至少从 4k 边界开始。如果您的存储从页面开始,那么该存储适合任何高达 4k 的 power-2 对齐方式。


I'm worried that writing the members of a std::tuple to my mapped region's address, and then later casting that address back to a std::tuple is going to break.

只要通过映射内存进行通信的应用程序使用相同的 ABI,就可以按预期工作。

关于c++ - 我可以将新的 std::tuple 放入内存映射区域,然后再读回吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52452173/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com