Это мой персональный дневник. Пишу, по большей части, про историю, свою жизнь и немного про программирование.

«Гопник-2»: убрал printf

Закончил убирать функцию printf, сегодня вколотил последний гвоздь в крышку её гроба. Я почему-то наивно полагал, что на этом моя работа по избавлению от char * будет окончена, но не учёл, что в коде довольно много (больше 150) других мест, где ещё используется этот тип.

В процессе замены встретилось вот такое. Загадка, всё ли хорошо в следующем коде?

std::cout << fmt::format(" {}(+{})", main_hero->inv[i].name, main_hero->inv[i].armo)

Мой компилятор считает, что всё, но сборка на «Гитхабе» падает с ошибкой «Binding packed field error».

Получается, я по неведомой причине не могу обратиться по ссылке на поле внутри упакованной структуры (fmt::format принимает параметры по ссылке).

Структуру я пакую, чтобы нивелировать различия на бинарном уровне между компиляторами, — так как я её сохраняю и восстанавливаю как есть. В общем, от упаковки не отказаться, пришлось присвоить значения промежуточным переменным:

const auto name = main_hero->inv[i].name;
const auto armo = main_hero->inv[i].armo;

std::cout << fmt::format(" {}(+{})", name, armo);

Выглядит, как костыль, но самое главное — почему один компилятор требует промежуточные переменные, а другой умеет и без них?

Я бы понял, если бы «Гитхаб» собирал мне бинарники для какой-то экзотической архитектуры, где есть какие-то особенности адресации, но тут такого ведь и в помине нет.

1 комментарий
Сергей Чебан 2023

Думаю, это https://gcc.gnu.org/bugzilla/show_bug.cgi?id=36566.
Вообще, использовать упакованные структуры таким образом — не очень хорошая идея. Да, fmt::format() должен работать, но:

  1. В обычном случае данные располагаются в памяти таким образом, чтобы процессор мог к ним эффективно обращаться. Для упакованных структур это не верно.
  2. Сама по себе упаковка данных не гарантирует двоичной совместимости. Например, при переносе данных между архитектурами big-endian и little-endian проблемы весьма вероятны.
    Лучше бы использовать для хранения данных что-то более стандартное. Варианты навскидку — json и protobuf.

Microsoft, кстати, в своё время налетел на эту проблему со своим форматом .doc. У них был формат файла, хранящий данные в таких вот структурах, а потом хренак, и появилась платформа x64.

Евгений Степанищев 2023

Ссылка про это, да, но я не нашёл там хорошего объяснения.

В обычном случае данные располагаются в памяти таким образом, чтобы процессор мог к ним эффективно обращаться. Для упакованных структур это не верно.

Согласен, но это текстовый квест, у него ровно никаких проблем с производительноусть.

Сама по себе упаковка данных не гарантирует двоичной совместимости. Например, при переносе данных между архитектурами big-endian и little-endian проблемы весьма вероятны.

Безусловно, но я ведь игру не сам писал, она мне досталось, моя задача была её максимально быстро починить. Сначала я подумал, что было бы неплохо сохранять структуру, которой не хватает для работы сохранения и восстановления целиком — это было максимально дёшево с точки зрения программирования, потом стал героически преодолевать трудности, которые из этого произошли.

Лучше бы использовать для хранения данных что-то более стандартное. Варианты навскидку — json и protobuf.

Да, мы с одним из контрибьюторов о том же ночью переписывались. Есть ещё мысль перейти на sqlite.

У них был формат файла, хранящий данные в таких вот структурах, а потом хренак, и появилась платформа x64.

А как иначе-то было в те времена делать? Всё экономили, ресурсов мало было, другого пути просто не было. Кстати, никогда не слышал, что у них были какие-то сложности с форматом doc на 64 битах.