c++ input gets standards-compliance

c++ - ¿Qué está pasando con ''gets(stdin)'' en el coderbyte del sitio?



input standards-compliance (3)

Coderbyte es un sitio de desafío de codificación en línea (lo encontré hace 2 minutos).

El primer desafío de C ++ con el que te encuentras tiene un esqueleto de C ++ que debes modificar:

#include <iostream> #include <string> using namespace std; int FirstFactorial(int num) { // Code goes here return num; } int main() { // Keep this function call here cout << FirstFactorial(gets(stdin)); return 0; }

Si está poco familiarizado con C ++, lo primero * que aparece en sus ojos es:

int FirstFactorial(int num); cout << FirstFactorial(gets(stdin));

Así que, de acuerdo, el código de llamadas gets que está en desuso desde C ++ 11 y se elimina desde C ++ 14, lo cual es malo en sí mismo.

Pero luego me doy cuenta: se gets de tipo char*(char*) . Por lo tanto, no debería aceptar un parámetro FILE* y el resultado no debería ser utilizable en lugar de un parámetro int , pero ... no solo se compila sin advertencias o errores, sino que se ejecuta y pasa el valor de entrada correcto. a FirstFactorial .

Fuera de este sitio en particular, el código no se compila (como se esperaba), entonces, ¿qué está pasando aquí?

* En realidad, el primero está using namespace std pero eso es irrelevante para mi problema aquí.


Estoy intrigado. Entonces, es hora de poner las gafas de investigación y, como no tengo acceso al compilador ni a las banderas de compilación, necesito ser creativo. También porque nada de este código tiene sentido, no es una mala idea cuestionar todos los supuestos.

Primero vamos a comprobar el tipo real de gets . Tengo un pequeño truco para eso:

template <class> struct Name; int main() { Name<decltype(gets)> n; // keep this function call here cout << FirstFactorial(gets(stdin)); return 0; }

Y eso se ve ... normal:

/tmp/613814454/Main.cpp:16:19: warning: ''gets'' is deprecated [-Wdeprecated-declarations] Name<decltype(gets)> n; ^ /usr/include/stdio.h:638:37: note: ''gets'' has been explicitly marked deprecated here extern char *gets (char *__s) __wur __attribute_deprecated__; ^ /usr/include/x86_64-linux-gnu/sys/cdefs.h:254:51: note: expanded from macro ''__attribute_deprecated__'' # define __attribute_deprecated__ __attribute__ ((__deprecated__)) ^ /tmp/613814454/Main.cpp:16:26: error: implicit instantiation of undefined template ''Name<char *(char *)>'' Name<decltype(gets)> n; ^ /tmp/613814454/Main.cpp:12:25: note: template is declared here template <class> struct Name; ^ 1 warning and 1 error generated.

gets está marcado como en desuso y tiene la firma char *(char *) . Pero entonces, ¿cómo es FirstFactorial(gets(stdin)); compilando?

Probemos algo más:

int main() { Name<decltype(gets(stdin))> n; // keep this function call here cout << FirstFactorial(gets(stdin)); return 0; }

Lo que nos da:

/tmp/286775780/Main.cpp:15:21: error: implicit instantiation of undefined template ''Name<int>'' Name<decltype(8)> n; ^

Finalmente estamos consiguiendo algo: decltype(8) . Así que la totalidad de gets(stdin) se reemplazó textualmente con la entrada ( 8 ).

Y las cosas se ponen más raras. El error del compilador continúa:

/tmp/596773533/Main.cpp:18:26: error: no matching function for call to ''gets'' cout << FirstFactorial(gets(stdin)); ^~~~ /usr/include/stdio.h:638:14: note: candidate function not viable: no known conversion from ''struct _IO_FILE *'' to ''char *'' for 1st argument extern char *gets (char *__s) __wur __attribute_deprecated__;

Así que ahora obtenemos el error esperado para cout << FirstFactorial(gets(stdin));

Busqué una macro y, como #undef gets parece no hacer nada, parece que no es una macro.

Pero

std::integral_constant<int, gets(stdin)> n;

Se compila.

Pero

std::integral_constant<int, gets(stdin)> n; // OK std::integral_constant<int, gets(stdin)> n2; // ERROR wtf??

No con el error esperado en la línea n2 .

Y nuevamente, casi cualquier modificación de main hace que la línea cout << FirstFactorial(gets(stdin)); Escupe el error esperado.

Además, el stdin realidad parece estar vacío.

Así que solo puedo concluir y especular que tienen un pequeño programa que analiza la fuente e intenta (mal) reemplazar " gets(stdin) con el valor de entrada del caso de prueba antes de introducirlo en el compilador. Si alguien tiene una teoría mejor o si realmente sabe lo que está haciendo, ¡compártala!

Esto es obviamente una muy mala práctica. Mientras investigaba esto, encontré que hay al menos una pregunta aquí ( example ) sobre esto y porque la gente no tiene idea de que hay un sitio por ahí que hace esto, su respuesta es "no usar gets usa ... en lugar de", que es de hecho, es un buen consejo, pero solo confunde más el OP ya que cualquier intento de una lectura válida desde una entrada estándar fallará en este sitio.

TLDR

gets(stdin) no es válido C ++. Es un truco que usa este sitio en particular (por las razones que no puedo entender). Si desea continuar presentando en el sitio (no lo estoy respaldando ni lo estoy rechazando), tiene que usar este constructo que de otra manera no tendría sentido, pero tenga en cuenta que es frágil. Casi cualquier modificación a main escupirá un error. Fuera de este sitio use métodos normales de lectura de entrada.


Probé la siguiente adición a main en el editor de Coderbyte:

std::cout << "gets(stdin)";

Donde aparece el fragmento misterioso y enigmático gets(stdin) dentro de una cadena literal. Esto no debería ser transformado por nada, ni siquiera por el preprocesador, y cualquier programador de C ++ debería esperar que este código imprima las cadenas exactas gets(stdin) en la salida estándar. Y sin embargo, vemos el siguiente resultado, cuando se compila y ejecuta en coderbyte:

8

Donde el valor 8 se toma directamente del conveniente campo de ''entrada'' debajo del editor.

A partir de esto, queda claro que este editor en línea está realizando operaciones ciegas de búsqueda y reemplazo en el código fuente, apariciones de sustitución de gets(stdin) con la "entrada" del usuario. Personalmente llamaría a esto un mal uso del lenguaje que es peor que las macros de preprocesador descuidadas.

En el contexto de un sitio web de desafío de codificación en línea, me preocupa esto porque enseña prácticas no convencionales, no estándar, sin sentido y al menos inseguras como gets(stdin) , y de una manera que no se puede repetir en otros plataformas

Estoy seguro de que no puede ser tan difícil simplemente usar std::cin y simplemente transmitir la entrada a un programa.


Soy el fundador de Coderbyte y también el tipo que creó esto fue hackeado.

Los comentarios en esta publicación son correctos de que es una forma de buscar y reemplazar, así que déjame explicarte por qué hice esto realmente rápido.

En el día en que creé el sitio por primera vez (alrededor de 2012), solo era compatible con JavaScript. No había forma de "leer en la entrada" en JavaScript ejecutándose en el navegador, por lo que habría una función foo(input) y usé la función readline() de Node.js para llamarla como foo(readline()) . Excepto que era un niño y no lo sabía mejor, así que literalmente reemplacé readline() con la entrada en tiempo de ejecución. Así que foo(readline()) convirtió en foo(2) o foo("hello") que funcionó bien para JavaScript.

Alrededor de 2013/2014, agregué más idiomas y utilicé servicios de terceros para evaluar el código en línea, pero fue muy difícil realizar la entrada / salida estándar con los servicios que estaba usando, así que me quedé con la misma búsqueda y reemplazo de idiomas. como Python, Ruby, y finalmente C ++, C #, etc.

Pasado el día de hoy, ejecuté el código en mis propios contenedores, pero nunca actualicé la forma en que funciona stdin / stdout porque la gente se ha acostumbrado al hackeo extraño (algunas personas incluso han publicado en foros que explican cómo solucionarlo).

Sé que no es la mejor práctica y que no es útil que alguien que está aprendiendo un nuevo lenguaje vea hacks como este, pero la idea era que los nuevos programadores no se preocuparan por leer la entrada en absoluto y simplemente se centraran en escribir el algoritmo para resolver el problema. problema. Una queja común acerca de los sitios de desafío de codificación hace años era que los nuevos programadores pasaban mucho tiempo solo para averiguar cómo leer desde stdin o leer líneas de un archivo, así que quería que los nuevos programadores evitaran este problema en Coderbyte.

Estaré actualizando toda la página del editor pronto junto con el código predeterminado y la lectura stdin para idiomas. Esperemos que luego los programadores de C ++ disfruten usando más Coderbyte :)