Можно ли безопасно получить указатель на неинициализированный контент std :: необязательный для чтения в память из двоичного файла без конструирования содержимого по умолчанию? - PullRequest
0 голосов
/ 19 марта 2020

Я рефакторинг некоторого устаревшего кода, который считывает некоторые двоичные данные из файла в struct. Мне пришло в голову, что изменение переменной на std :: option может гарантировать, что переменная действительно прочитана (инициализирована) перед ее использованием. Но код чтения файла требует адрес переменной. Есть ли способ (предполагаемый способ не взломать), чтобы сказать std :: option "дать мне указатель на ваш неинициализированный T, чтобы я мог записать содержимое этой памяти" и "go вперед и измените ваше состояние с пустого на полноценное сейчас "?

(то есть вместо назначения значения по умолчанию T (что, если T не имеет конструктора по умолчанию), а затем получения адреса значения по умолчанию.)

Упрощенный пример:

struct FILE_HEADER { /* some data members... */ };

class FileFrobulator
{
private:
   FILE_HEADER fileHead;

public:
   void called_first(IFileReader* reader)
   {
       // ...
       reader->read(&fileHead, sizeof(FILE_HEADER));
       // ...
   }

   void called_later(IFileReader* reader)
   {
      // ...
      // use fileHead.foo, fileHead.bar, etc. while reading rest of file
      // ...
   }
};

Вопрос в том, могу ли я изменить элемент на std::optional<FILE_HEADER> fileHead;, что же мне поменять для строки, которая в данный момент читает reader->read(&fileHead, sizeof(FILE_HEADER));?

Я мог бы сделать это:

fileHead = FILE_HEADER();
reader->read(&*fileHead, sizeof(FILE_HEADER));

Вы, возможно, ранее возражали, что функция, принимающая адрес неинициализированного T в std :: option и устанавливающая необязательную, так как больше не пустая, рискует случайно оставляя необязательное помеченное значение-ful, оставаясь при этом неинициализированным, если пользователь фактически не записывает память. Однако обратите внимание, что приведенный выше пример кода представляет собой аналогичный, хотя и меньший риск: если сгенерирует reader-> read (), необязательный больше не будет пустым, но он также недопустим для использования. Созданный по умолчанию T, вероятно, лучше, чем неинициализированный T, однако, если FILE_HEADER является C структурой (что в данном случае таковым является), его члены все еще неинициализированы!

Возможно, это лучше:

FILE_HEADER temp;
reader->read(&temp, sizeof(FILE_HEADER));
fileHead = temp;

Но оба они включают избыточную инициализацию и / или копирование (возможно, оптимизированное компилятором).

1 Ответ

0 голосов
/ 19 марта 2020

Есть ли способ (предполагаемый способ, а не взлом) сказать std :: option "дать мне указатель на ваш неинициализированный T, чтобы я мог записать содержимое этой памяти" и "go вперед и измените ваше состояние с пустого на полноценное сейчас "?

Обе эти вещи будут все ie.

Пустое optional<T> не имеет T, инициализировано или нет, поэтому вы получите указатель / ссылку на объект, который не существует. И состояние optional<T> должно отражать, существует ли T в пределах optional<T>. Поскольку копирование битов само по себе не приводит к появлению T, если пользователь непосредственно не создаст T, он не будет существовать. Так что притворяться, что T существует, когда его нет, также ложно.

Чтобы делать то, что вы собираетесь, у вас должна быть просто функция, которая возвращает an optional<FILE_HEADER>:

fileHead = readFileHeader(reader);

Если сработает функция чтения, то значение в FileFrobulator никогда не будет установлено. Вы можете даже сделать это лямбда-функцией, если захотите:

fileHead = [](IFileReader* reader) {
  std::optional<FILE_HEADER> ret(std::in_place);
  reader->read(&*ret, sizeof(FILE_HEADER));
  return ret;
}(reader);
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...