У меня есть несколько упакованных структур, которые я буду записывать в файл отображения памяти.Все они 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>;
Чтобы смоделировать файл отображения памяти, я создал небольшой объект с некоторым встроенным хранилищем и размером:
При добавлении кортежа он использует размещение 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;
}