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 :)