¿Por qué el estándar C ++ maneja el archivo buscando de la manera que lo hace?

17

C ++ usa el streamofftipo para representar un desplazamiento dentro de una secuencia (archivo) y se define de la siguiente manera en [stream.types]:

using streamoff = implementation-defined ;

El tipo streamoff es sinónimo de uno de los tipos integrales básicos firmados de tamaño suficiente para representar el tamaño de archivo máximo posible para el sistema operativo. 287)

287) Típicamente largo largo.

Esto tiene sentido porque permite buscar dentro de archivos grandes (en oposición al uso long, que puede tener solo 32 bits de ancho).

[filebuf.virtuals] define basic_filebufla función de buscar dentro de un archivo de la siguiente manera:

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

off_typees equivalente a streamoff, vea [iostreams.limits.pos]. Sin embargo, el estándar continúa explicando los efectos de la función. Estoy irritado por la última oración, que requiere una llamada a fseek:

Efectos : Dejar widthdenotar a_codecvt.encoding(). Si is_open() == false, o off != 0 && width <= 0, entonces la operación de posicionamiento falla. De lo contrario, si way != basic_ios::curo off != 0, y si se emitió la última operación, actualice la secuencia de salida y escriba cualquier secuencia sin desplazamiento. Luego, busque la nueva posición: si width > 0, llame fseek(file, width * off, whence), de lo contrario llame fseek(file, 0, whence).

fseekacepta un longparámetro Si off_typey streamoffse definen como long long(como lo sugiere el estándar), esto podría conducir a una conversión descendente a la longhora de llamar fseek(file, width * off, whence)(lo que podría generar errores potencialmente difíciles de diagnosticar). Esto pone en tela de juicio toda la justificación para introducir el streamofftipo en primer lugar.

¿Es esto intencional o un defecto en el estándar?

jceed2
fuente
8
se ve el defecto.
Yakk - Adam Nevraumont
Creo que veo que gcc libstdc ++ usa fseeko64 .
KamilCuk
1
Por casualidad, no parece que seekoffnecesariamente se use fseek debajo del capó. Más bien, el comportamiento (¿presumiblemente familiar?) De fseekse utiliza para explicar lo que seekoffestá haciendo.
jjramsey
@jjramsey Esta fue mi impresión también. Sin embargo, la forma en que está redactada parece sugerir un requisito más que una explicación.
jceed2
1
@jjramsey Estoy de acuerdo en que la parte "Efectos" puede interpretarse razonablemente en el sentido de que en realidad no tiene que llamar fseeksiempre que haga algo con el mismo efecto. Pero fseekcon un desplazamiento menor LONG_MINo mayor que LONG_MAXno tiene ningún efecto, por lo que la explicación es, en el mejor de los casos, incompleta, al menos para implementaciones donde streamoffes más amplia que long.
Keith Thompson

Respuestas:

6

Creo que la conclusión de que está sacando de esto, que hay una falta de coincidencia entre las transmisiones de C ++ y fseekque dará lugar a errores de tiempo de ejecución, es incorrecta. La situación parece ser:

  1. En sistemas donde longes de 64 bits, streamoffse define como long, y la seekofffunción invoca fseek.

  2. En los sistemas donde longhay 32 bits pero el sistema operativo admite compensaciones de archivos de 64 bits, streamoffse define como long longe seekoffinvoca una función llamada fseekoo fseeko64que acepta un desplazamiento de 64 bits.

Aquí hay un fragmento de la definición de seekoffen mi sistema 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 significa Soporte para archivos grandes .

Conclusión: Si bien el estándar sugiere una definición para streamoffeso que aparentemente entra en conflicto con el requisito que seekoffinvoca fseek, los diseñadores de bibliotecas entienden que deben llamar a la variante fseekque acepta la gama completa de compensaciones que admite el sistema operativo.

Willis Blackburn
fuente
@ypnos No voté en contra y esta respuesta me parece útil. Supongo que alguien rechazó el voto porque se pierde el punto. El problema no es que haya implementaciones sensatas que ignoren el estándar a este respecto, sino que es necesario ignorar el estándar para que la implementación sea sensata.
jceed2
66
The situation seems to be:- La situación es que la aplicación no está permitido no llamar fseeken seekoff. Debe llamar fseek, no lo hace, el estándar dice que tiene que hacerlo. Puedo argumentar que esta implementación no es válida. Creo que no responde la pregunta. Och, encontró llvm , llama fseeko.
KamilCuk
Al igual que para su información, VC ++ requiere _fseeki64esta función; que también parece violar lo que dice el estándar.
ChrisMM
1
Este es un caso de implementadores que se dan cuenta del problema e ignoran el estándar. Me alegro de que lo hayan hecho, pero el estándar realmente necesita ser arreglado.
NathanOliver
1
Algunas personas están tomando el estándar demasiado literalmente. No es exigente que la implementación literalmente llame a una función llamada fseek. En otra parte, el estándar describe algo como "como si llamara fseek(...)". Si se preocupara tanto por llamar literalmente fseek, esa declaración sería diferente. En serio, ¿qué harías si implementaras una biblioteca C ++? ¿Insistiría en llamar fseekcon los 32 bits inferiores de un desplazamiento de archivo de 64 bits, porque un documento se lo indica? ¿Tus clientes te lo agradecerían?
Willis Blackburn