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

17

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типа во-первых.

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

jceed2
источник
8
Дефект выглядит так.
Якк - Адам Невраумонт
Я думаю, что я вижу, что gcc libstdc ++ использует fseeko64 .
KamilCuk
1
Навскидку, это не выглядит как seekoffобязательно использования fseek под капотом. Скорее, (предположительно знакомое?) Поведение fseekиспользуется, чтобы объяснить, что seekoffделает.
Джрамси
@jjramsey Это тоже мое впечатление. Тем не менее, способ, которым это сформулировано, кажется, предлагает требование, а не объяснение.
jceed2
1
@jjramsey Я согласен, что часть «Эффекты» может быть разумно истолкована как означающая, что ей на самом деле не нужно звонить, fseekесли она делает что-то с тем же эффектом. Но fseekсо смещением, меньшим LONG_MINили большим, чем LONG_MAXне имеет значения, поэтому объяснение в лучшем случае неполное, по крайней мере для реализаций, где streamoffоно шире long.
Кит Томпсон

Ответы:

6

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

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

  2. В системах, где long32-разрядная версия, но ОС поддерживает 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который принимает полный диапазон смещений, поддерживаемых ОС.

Уиллис Блэкберн
источник
@ypnos Я не понизил голос и считаю этот ответ полезным. Я предполагаю, что кто-то отказался от голосования, потому что это упускает смысл. Проблема не в том, что существуют здравые реализации, которые игнорируют стандарт в этом отношении, проблема в том, что стандарт должен игнорироваться, чтобы реализация была в здравом уме.
jceed2
6
The situation seems to be:- Ситуация такова , что реализация не допускается не звонить fseekв seekoff. Это должно вызвать fseek, это не, стандарт говорит, что это должно. Я могу утверждать, что эта реализация недействительна. Я считаю, что это не отвечает на вопрос. Оч, нашел llvm , звонит fseeko.
KamilCuk
Подобно тому, как FYI, VC ++ вызывает _fseeki64эту функцию; что также, кажется, нарушает то, что говорит стандарт.
ChrisMM
1
Это тот случай, когда разработчики понимают проблему и игнорируют стандарт. Я рад, что они сделали, но стандарт действительно должен быть установлен.
Натан Оливер
1
Некоторые люди воспринимают стандарт слишком буквально. Это не требует, чтобы реализация буквально вызывала вызываемую функцию fseek. В другом месте стандарт описывает что-то как «как будто по телефону fseek(...)». Если бы он так сильно заботился о буквальном вызове fseek, это утверждение было бы другим. Серьезно, что бы вы сделали, если бы реализовывали библиотеку C ++? Вы бы настаивали на вызове fseekс младшими 32 битами смещения 64-битного файла, потому что документ говорит вам? Будут ли ваши клиенты благодарить вас за это?
Уиллис Блэкберн