Почему стандарт C++ обрабатывает файл так, как он это делает?

C++ использует тип streamoff для представления смещения в (файловом) потоке и определяется следующим образом в [stream.types]:

using streamoff = implementation-defined ;

Тип streamoff является синонимом одного из основных целочисленных типов со знаком достаточного размера, чтобы представить максимально возможный размер файла для операционной системы. 287)

287) Типично длинный длинный.

Это имеет смысл, поскольку позволяет выполнять поиск в больших файлах (в отличие от использования long, ширина которого может составлять всего 32 бита).

[filebuf.virtuals] определяет функцию basic_filebuf для поиска в файле следующим образом:

pos_type seekoff(off_type off, ios_base::seekdir way, ios_base::openmode which = ios_base::in | ios_base::out) override;

off_type эквивалентно streamoff, см. [iostreams.limits.pos]. Однако затем стандарт продолжает объяснять эффекты функции. Меня раздражает самое последнее предложение, которое требует вызова fseek:

Эффекты: пусть width обозначает a_codecvt.encoding(). Если is_open() == false или off != 0 && width <= 0, то операция позиционирования не выполняется. В противном случае, если way != basic_ios::cur или off != 0, и если была выведена последняя операция, то обновить выходную последовательность и записать любую несдвигаемую последовательность. Далее ищите новую позицию: если width > 0, звоните fseek(file, width * off, whence), иначе звоните fseek(file, 0, whence).

fseek принимает параметр long. Если off_type и streamoff определены как long long (как это предлагается стандартом), это может привести к преобразованию с понижением частоты в long при вызове fseek(file, width * off, whence) (что потенциально затрудняет диагностику ошибок). Это ставит под сомнение все обоснование введения типа streamoff в первую очередь.

Это сделано намеренно или дефект стандарта?


person jceed2    schedule 13.12.2019    source источник
comment
дефект похоже.   -  person Yakk - Adam Nevraumont    schedule 13.12.2019
comment
Кажется, я вижу, что gcc libstdС++ использует fseeko64.   -  person KamilCuk    schedule 13.12.2019
comment
На первый взгляд, это не похоже на то, что seekoff обязательно использует fseek под капотом. Скорее, (предположительно знакомое?) поведение fseek используется для объяснения того, что делает seekoff.   -  person jjramsey    schedule 13.12.2019
comment
@jjramsey Это было и мое впечатление. Однако то, как это сформулировано, кажется, предполагает требование, а не объяснение.   -  person jceed2    schedule 13.12.2019
comment
Это в параграфе, описывающем эффекты.   -  person Peter    schedule 14.12.2019
comment
@jjramsey Я согласен с тем, что часть «Эффекты» можно разумно интерпретировать как означающую, что на самом деле ей не нужно вызывать fseek, если она делает что-то с тем же эффектом. Но fseek со смещением меньше LONG_MIN или больше LONG_MAX не действует, поэтому объяснение в лучшем случае неполное, по крайней мере для реализаций, где streamoff шире, чем long.   -  person Keith Thompson    schedule 14.12.2019


Ответы (1)


Я думаю, что вывод, который вы делаете из этого, о несоответствии между потоками C++ и fseek, который приведет к ошибкам во время выполнения, неверен. Ситуация выглядит так:

  1. В системах, где long является 64-битным, streamoff определяется как long, а функция seekoff вызывает fseek.

  2. В системах, где long является 32-битным, но ОС поддерживает 64-битные смещения файлов, streamoff определяется как long long, а seekoff вызывает функцию с именем fseeko или fseeko64, которая принимает 64-битное смещение.

Вот фрагмент определения seekoff в моей системе Linux:

#ifdef _GLIBCXX_USE_LFS
    if (!fseeko64(_M_file, __off, __whence))
      __ret = std::streampos(ftello64(_M_file));
#else
    if (!fseek(_M_file, __off, __whence))
      __ret = std::streampos(std::ftell(_M_file));
#endif

LFS расшифровывается как Поддержка больших файлов.

Вывод: Хотя стандарт предлагает определение для streamoff, которое якобы противоречит требованию, чтобы seekoff вызывало fseek, разработчики библиотек понимают, что они должны вызывать вариант fseek, который принимает полный диапазон смещений, поддерживаемых ОС.

person Willis Blackburn    schedule 13.12.2019
comment
@ypnos Я не минусовал и считаю этот ответ полезным. Я думаю, кто-то проголосовал против, потому что это не соответствует сути. Проблема не в том, что есть разумные реализации, которые игнорируют стандарт в этом отношении, проблема в том, что стандарт необходимо игнорировать, чтобы реализация была разумной. - person jceed2; 13.12.2019
comment
The situation seems to be: - Ситуация такова, что реализация не позволяет не вызывать fseek в seekoff. Он должен вызывать fseek, это не так, стандарт говорит, что он должен . Я могу утверждать, что эта реализация недействительна. Я считаю, что это не отвечает на вопрос. Ох, нашел llvm, он вызывает fseeko. - person KamilCuk; 14.12.2019
comment
Так же, как FYI, VC++ вызывает _fseeki64 для этой функции; что также, кажется, нарушает то, что говорит стандарт. - person ChrisMM; 14.12.2019
comment
Это тот случай, когда разработчики осознают проблему и игнорируют стандарт. Я рад, что они это сделали, но стандарт действительно нужно исправить. - person NathanOliver; 14.12.2019
comment
Некоторые люди воспринимают стандарт слишком буквально. Это не требует, чтобы реализация буквально вызывала функцию с именем fseek. В другом месте стандарт описывает что-то как бы путем вызова fseek(...). Если бы он так заботился о буквальном вызове fseek, это утверждение было бы другим. Серьезно, что бы вы сделали, если бы реализовывали библиотеку C++? Вы бы настаивали на вызове fseek с младшими 32 битами 64-битного смещения файла, потому что документ говорит вам об этом? Будут ли ваши клиенты благодарны вам за это? - person Willis Blackburn; 14.12.2019
comment
Если мы воспримем стандарт буквально, это будет означать, что реализация библиотеки ввода-вывода C++ в системе, в которой просто нет API с именем fseek, будет невозможно. Я не думаю, что это было намерением. - person Willis Blackburn; 14.12.2019
comment
Если буквальное понимание стандарта приводит к абсурдным выводам, это признак того, что стандарт необходимо исправить. Это не означает, что это серьезная проблема, особенно если разработчики руководствуются здравым смыслом, а не делают в точности то, что говорит стандарт. (fseek является частью стандартной библиотеки C и, следовательно, частью стандартной библиотеки C++, поэтому ваш второй комментарий не применяется.) Если стандарт в другом месте использует фразу, как если бы вызывал fseek(), то очевидное исправление состоит в том, чтобы использовать это та же самая фраза здесь, хотя я также хотел бы примечание, поясняющее, что ограничения fseek не применяются. - person Keith Thompson; 14.12.2019