Menú

Mostrar Mensajes

Esta sección te permite ver todos los mensajes escritos por este usuario. Ten en cuenta que sólo puedes ver los mensajes escritos en zonas a las que tienes acceso en este momento.

Mostrar Mensajes Menú

Mensajes - RayR

#101
El libro Programming Windows, que te mencionaron, solía ser La referencia, pero ya es bastante viejo y, si bien la API no ha cambiado mucho, sí habrá cosas que ya no sean válidas. Yo tenía uno aún más viejo y mucho más básico, aunque no recuerdo ahora el nombre. Pero la idea es aprender en general lo básico de la programación de aplicaciones Windows, como en los links que pones, y de ahí, seguir con la documentación oficial de Microsoft, que lamentablemente en no pocas ocasiones tiene errores... Pero para eso están los foros.
#102
CitarUna consulta respecto al ultimo trozo de código...
En la línea 11, tienes int x;, pero no esta inicializado, que valor se le pasara a la función ?

Basura. Y Es incorrecto intentar acceder al valor de una variable no inicializada, pero fíjate que puse unos puntos suspensivos luego de esa declaración. Eso para dar a entender que hay más código intermedio donde, presumiblemente, se le da un valor a x. No quise inicializarla ahí, pues podría dar la impresión errónea de que ese código sólo funciona si le damos un valor inicial y literal a la variable.

Y coincido en que cuando empiezas es mejor no intentar profundizar demasiado. Las reglas de C++ se han hecho demasiado complicadas. Sin ir más lejos, el especificador constexpr probablemente ni siquiera tendría razón de aparecer en un manual introductorio, pero clar, el de Stroustrup no es un texto básico. Aunque tampoco te sientas mal por haberlo comprado; si bien no es recomendable para alguien que apenas empieza, sí que es un muy buen libro y en algún punto te puede servir bastante.

Dicho lo anterior, si de pronto tienes dudas que simplemente no te puedes quitar de la mente (a veces pasa), puedes preguntar y, si ves que las respuestas son complicadas y no las entiendes aún, guardarlas para más adelante. Yo en su momento las almacenaba en un documento (por si después ya no recordaba dónde pregunté, o ya no existía la página) y con eso me quitaba la "comezón", pues sabía que mi respuesta estaba ahí, esperando para cuando tuviera los conocimientos necesarios.
#103
No es tanto que int sea especial, sino que en realidad los flotantes son el "problema". De hecho, todo esto no es específico de constexpr. Siempre ha sido limitado lo que se puede hacer con este tipo de datos. En general, no se pueden usar donde se necesiten expresiones constantes (conocidas en tiempo de compilación). Por ejemplo, no es posible usar flotantes como parámetros sin tipo de templates. Tampoco se pueden usar en static assertions, que funcionan perfectamente con const int, char, short, etc., pero no flotantes. Pero en ambos casos podemos usar flotantes constexpr. Si algunos compiladores permiten hacer algunas de estas cosas, simplemente están siendo demasiado permisivos, pero no es estándar.

La razón exacta por la que los flotantes están tan limitados en C++ no la sé, pero es casi seguro que (al menos una de las razones) sea por las diferencias en la representación de este tipo de datos. A lo largo de los años ha habido varios formatos distintos y cada arquitectura usaba el que quería, por lo que no había un único estándar. Las especificaciones de C y C++ siempre dejaron al criterio de cada compilador usar el que quisieran. Permitir el uso de flotantes en expresiones verdaderamente constantes habría dado lugar a posibles problemas, sobre todo por la compilación cruzada. Esto se refiere a la posibilidad de usar, por ejemplo, un compilador en Windows, corriendo sobre arquitectura x86/x86-64 (Intel y AMD), para generar binarios/ejecutables de una arquitectura o sistema operativo diferente (por ejemplo, Linux sobre ARM). Dado que las expresiones constantes se generan en tiempo de compilación, es posible que el valor calculado sea distinto a lo que se calcularía durante la ejecución del programa (ya que el ejecutable generado estaría funcionando en un sistema operativo o procesador distinto a aquél en el que se ejecutaba el compilador que lo generó). De hecho, sobre los constexpr el estándar dice justamente esto, que para flotantes no se puede garantizar que los valores generados en la compilación sean iguales a los que se calcularían en la ejecución. Esto podría darnos problemas si mezcláramos floats const y no const.

En realidad hoy ya no es tanto problema, ya que casi todo el mundo ha adoptado el estándar IEEE 754 para flotantes, así que veo poco probable que haya muchas diferencias, aún usando compilación cruzada. ¿Por qué no decidieron entonces hacer más flexibles también las restricciones de const float/double? Supongo que por compatibilidad con estándares anteriores y para dejar bien clara la distinción. Es decir, quien use flotantes constexpr, en teoría lo hará siendo consciente de la posibilidad de discrepancias anteriormente mencionada (el estándar es muy claro en eso, y me parece que también deberían serlo los autores de libros).

Citar1) Las funciones declaradas como constexpr solo van a retornar literales constantes, ej:
y nunca variables:

No. De hecho, sí pueden perfectamente devolver variables. Esto funciona correctamente (C++14 en adelante):

Código (cpp) [Seleccionar]
constexpr int cuadrado(int n) {
   int x = n * n;
   return x;
}

...

constexpr int numero = cuadrado(9);

// Esto tambien es valido. Ver explicacion en ultimo parrafo
int x;
...
int variable = cuadrado(x);
const int constante = cuadrado(x);

// Pero, naturalmente, no esto
constexpr int erroneo = cuadrado(x);


Las reglas de las funciones constexpr son algo complicadas, pero básicamente lo que necesitan es poder calcular el valor de retorno en tiempo de compilación.* Dado que en la línea 8 le estamos mandando un valor constante, 9 (también podríamos mandarle un const int debidamente inicializado), se cumple la condición.

*Eso no es del todo cierto. Como en las ultimas lineas del código de arriba, se le puede enviar variables como parámetros a una función constexpr, y hacer que no se pueda calcular en la compilación, pero entonces no podemos usar su resultado en lugares donde se espere una expresión constante.
#104
Creo que mi mensaje da a entender lo contrario a lo que quise. Eso me pasa por escribir en la noche... Disculpa si parecía que te criticaba. Al contrario. Lo de "en el remoto caso" pretendía ser una crítica mi propio post, que, como de costumbre, me quedó mucho más largo de lo que hubiera querido, pero no se me da muy bien resumir.

En lo de aparentar tampoco me refería a ti. Pensaba en algunos programadores, por ejemplo, de stackoverflow, a los que más que ayudar lo que les interesa es presumir. Por ejemplo, alguien dice que apenas inicia en C++ y necesita ayuda para un programa que convierta divisas usando doubles... y le dan una complejísima función con templates, que funciona con double, int, y hasta números romanos y cadenas de texto tipo "doscientos cincuenta", las cuales la función parsea por medio de expresiones regulares  ;D. Todo eso cuando el usuario (que dejó bien claro que era principiante) sólo necesitaba convertir un p$% double. Lo peor es que gente así a veces termina haciendo software comercial o freeware u open source, y por eso tenemos programas lentos y llenos de errores. En fin, lo que quería dar a entender es que eso que decías de que normalmente haces las cosas simples no tiene nada de malo. Sí es útil aprender las cosas complejas, por si un día las necesitas, pero no te preocupes demasiado en querer aprenderlo todo.  Nadie usa ni necesita todo lo que un lenguaje ofrece. Es mucho más útil y meritorio conocer bien un cierto porcentaje del lenguaje (y de su biblioteca estándar) y saber hacer cosas útiles con APIs de terceros, que aprender de memoria todos los algoritmos y contenedores de la STL (incluso las que nadie en el mundo real usa) pero ser incapaz de mostrar una ventana con un botón, o enviar un mensaje de texto por la red, o cualquier otra cosa que no sea parte del lenguaje (y como seguro ya has visto, con el puro C++ estándar no se llega muy lejos). Yo, por ejemplo, no estoy muy actualizado a lo más moderno de C++. Específicamente, no he profundizado mucho en C++17, simplemente porque hay mil cosas más interesantes y productivas que hacer con mi tiempo. Creo que del post anterior queda claro que no soy precisamente fan de todas las decisiones que últimamente toman quienes deciden la evolución del lenguaje.

Lo del enfoque comercial te lo digo porque intentar hacer un programa casi libre de fallos, y sobre todo, terminado, es mucho más difícil de lo que parece, y se aprende mucho haciéndolo. Pero eso lo puedes dejar para cuando tengas más experiencia con las APIs que estás aprendiendo. Creo que hace poco empezaste con la API de Windows, así que lo mejor es empezar con cosas no tan complicadas. La calculadora es un muy buen ejercicio. Es algo útil (no en vano casi todos los SO incluyen una), relativamente sencillo y seguro que te sirve para aprender en general cómo funcionan las aplicaciones de Windows, los eventos, etc. Pero cuando llegues a un punto en que sientas que manejas bien las GUI, sockets, o lo que sea que te interese, el siguiente paso para aprender bien, bien las APIs es hacer aplicaciones completas, aunque sean pequeñas.
#105
A mí tampoco me gusta usar cosas complicadas a menos que sea necesario. Cuando uno empieza en esto, a veces quiere hacer todo muy fancy, como dices, pero es absurdo hacerlo cuando una solución más simple sirve. Claro, a veces se justifica usar cosas complejas, por ejemplo, por cuestiones de rendimiento, pero usarlas sólo porque sí es una de las formas más fáciles de identificar a alguien que quiere aparentar saber más de lo que en realidad sabe  :P .

Por eso, soy de los muchos (y cada vez somos más) que piensan que los miembros del comité de estandarización de C++ están perdiendo el rumbo y cada vez se desconectan más de la realidad. Es decir, de pronto agregan cosas útiles, pero asimismo agregan/cambian muchas que al final nadie en el mundo real termina utilizando, y que lo único que ocasionan es complicar el lenguaje, incluso para quienes no las usan  :-\. De hecho, más de algún miembro y ex miembro lo ha reconocido. Pero esto es tema para otra ocasión...

Mi consejo (y ojalá todos los libros y los profesores lo enseñaran) es seguir haciendo programas, cada vez más completos. Al empezar es importante tener un buen manual, para no andar a ciegas, e ir practicando todo lo que se va aprendiendo. Luego hay que aprender a usar APIs externas, por ejemplo, para GUI, programación gráfica, bases de datos, redes, etc. según nos interese. Una vez que hemos practicado y hecho muchos programas sencillos, la única forma de dar el siguiente paso es intentar hacer programas completos. Ojo con esto, que si seguimos haciendo programitas de juguete, como los ejercicios de manuales y cursos, nunca vamos a avanzar. Lo que hay que hacer es programas útiles, aunque sean pequeños, y tratar de que tengan calidad "comercial", es decir, que estén terminados y sean lo más a prueba de errores posible. Por ejemplo, un programa de modo texto que guarde datos de empleados en archivos sólo sirve como ejercicio de práctica. En lugar de eso, tratar de hacer algo como un software de punto de venta con GUI y bases de datos. Al desarrollarlo, imaginar que lo vamos a vender. Intentar pensar en todas las funciones que una tienda podría necesitar. Incluso podemos fijarnos, aunque sea a grandes rasgos, cómo funcionan los de lugares como Walmart y replicarlo. Otra opción podría ser un programa de mensajería instantánea. Empezar con textos, luego implementar la función de enviar emojis, quizás sonidos que se reproduzcan al recibirlos, y finalmente archivos en general. Algo que yo hice en su momento fue un programa para diseñar tarjetas de presentación. Simplemente permitía poner y mover textos, imágenes, y finalmente, guardarlas o imprimirlas. Muy sencillo y básico, pero realmente lo importante aquí es que estén terminados y funcionen bien. Los videojuegos también pueden ser una opción interesante. Se puede hacer algo sencillo, tipo Space Invaders, Pacman, Mario Bros., etc., pero hay que terminarlos: pantalla de inicio, opciones, pantalla de Game Over, quizás guardar/cargar partida. Puede ser sólo un nivel pero que sea completo. Que al menos ese nivel tenga implementado TODO lo que haría un juego comercial. También es importante probar y probar el programa, hacer cosas absurdas, meterle datos erróneos, esforzarnos por hacer que el falle, y cuando lo haga, solucionarlo, hasta que tengamos algo que, aunque sencillo, se parezca a un producto comercial finalizado. Porque es muy común empezar algo, y cuando termina la parte "divertida" y más sencilla, darlo por terminado y engañarnos diciendo que si qusiéramos lo podríamos acabar fácilmente, pero no tenemos tiempo. Y es que ahí, al darle los toques finales, al perfeccionarlo, es donde más aprendemos. Ahí nos encontramos con cosas que no sabemos cómo resolver y nos obligan a buscar información que de otra forma ni siquiera se nos hubieran pasado por la mente.

Después de hacer unos cuantos programas completos, una buena forma de poner a prueba el conocimiento es probar a leer formatos reales. Por ejemplo, intentar hacer un lector/visualizador de imágenes básico, pero programando todo el código de apertura de archivo, sin bibliotecas para eso. El formato BMP es muy sencillo y está bien documentado. El PNG tampoco es tan complicado, y lo "mejor": su documentación deja mucho que desear. Es un estándar, por lo que la especificación está disponible de forma gratuita. De hecho, si no me falla la memoria (hice un lector hace más de dos años), para implementarlo hay que leer 3 especificaciones distintas, cada una peor explicada que la anterior  ;D. Y es que así suelen ser casi todas las especificaciones oficiales, muy mal redactadas, pero aprender a descifrarlas es una habilidad útil. Igualmente importante, los formatos "reales" fueron creados no para facilitarle la vida a un estudiante sino para ser útiles y eficientes, por lo que implementarlos no es trivial. Es una muy buena práctica.

En fin, en el remoto caso de que hayas leído hasta aquí, ése es el mejor consejo que puedo darte.
#106
Nah, no pasa nada. Los que estamos aquí es porque nos gusta tratar de ayudar/colaborar. Es sólo que a veces hay gente que pida ayuda, y cuando se les da una opción la rechazan sin probar. Me ha tocado ver usuarios a los que les dan una solución que claramente es correcta, y responden: "mmm, no, no creo que sea eso. Mejor intentaré hacer esto otro...", y dices: ¿entonces para qué #%& piden ayuda?  :P. Pero no es tu caso.

Saludos, y si tienes más preguntas, no dudes en postearlas.
#107
Cita de: hextakatt en  9 Junio 2019, 00:33 AM
Bien, ha vuelto a funcionar otra vez milagrosamente

Ése es el sello distintivo de un error de memoria. Un programa con un problema así a veces funciona y a veces no. Las cosas no se arreglan por arte de magia. Si el programa no funciona siempre, tiene un error. No hay más.

Esto:

/* Problema solucionado con padding */
char* s[16] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};


No soluciona nada. De hecho, no cambia nada. Las variables globales no inicializadas explícitamente, son automáticamente inicializadas a 0 (en el caso de los arreglos, todos sus elementos son inicializados a 0). Es decir, exactamente eso que hiciste ahí, el compilador ya lo hacía por ti antes.

Y ya te mencioné al menos 3 casos en los que tienes garantizado que tu programa fallará, y es probable que alguno de ellos se esté dando y es lo que te está causando problemas., por lo que hay la posibilidad de que las modificaciones que te puse lo solucionen. No lo garantizo, obviamente, pero es posible. Si no lo intentas ni sigues nuestras sugerencias, no hay mucho más que hacer.
#108
Las únicas soluciones son las que te dio YreX-DwX. Y ojo, que realmente no es válido usar auto en parámetros de funciones, ni siquiera en C++17. Algunos compiladores permiten cosas como ésa, pero no es estándar, por lo que no hay garantía de que siempre funcione. De todas formas en este caso no es relevante, ya que lo que determina el valor a imprimir es el tipo del array. Es decir, podrías cambiar auto por char, y seguiría sin hacer diferencia, ya que lo que estás imprimiendo es el valor apuntado por el iterador del array, que es de tipo int.
#109
Serviría tener más datos, como ya te indicaron. Sin embargo, hay unas cuantas cosas claramente incorrectas. Por ejemplo, ¿cómo vas a saber cuántos elementos termina teniendo file_names_in_dir? Lo declaraste con tamaño de 16, pero podría terminar con menos. Por ejemplo, si el directorio que estás explorando sólo tiene 10 entradas válidas, a partir de esa posición, el arreglo contendrá punteros inválidos. Si intentas imprimir 16, obviamente se van a producir errores. Peor aún, si esta línea resulta ser true cuando i vale 0:

if (drce[i].file_name[0] == 0x00) {

Se ejecutará el break, con lo que file_names_in_dir estará vacío, así que en cuanto trates de imprimir cualquiera de sus elementos habrá problemas. Finalmente, ¿qué pasa si el directorio contiene, digamos, 30 archivos? Vas a estar almacenando datos en file_names_in_dir[16] hasta file_names_in_dir[29], que no son posiciones válidas, por lo que estarás sobrescribiendo memoria ocupada probablemente por otras variables del programa. Para empezar deberías hacer cambios como que read_directory devuelva un int (o size_t, o lo que quieras) indicando el número de elementos que realmente termina teniendo file_names_in_dir. Y verifica que no te pases del límite. En esencia, haz estos cambios:

int read_directory(void)
{
...
...
if (aaa < 16) {
    file_names_in_dir[aaa] = drce[i].file_name;
    ++aaa;
}
}
return aaa;
}


Ni idea de donde salio la ultima string, talvez sucedio la gran casualidad que entre en la memoria de otro proceso que tiene ese string.

Imposible. Todos los sistemas operativos modernos aíslan los procesos, por lo que no hay manera de acceder a la de otro de forma accidental.
#110
El problema es que static_cast no devuelve un tipo sino un valor, por eso no se puede hacer type alias. Se me ocurre que podrías ahorrarte algo de trabajo con una función template constexpr (esto último para que funcione con el switch):

Código (cpp) [Seleccionar]
template<typename TipoRetorno, typename TipoOrigen>
constexpr TipoRetorno Convierte(const TipoRetorno& dest, TipoOrigen valor)
{
    return static_cast<TipoRetorno>(valor);
}
...
     if(opciones > Convierte(opciones, coche::rojo) && opciones <= Convierte(opciones, moto::rojo) )
      {
           do{
              if(opciones...
              {
                       switch (opciones2)
                       {
                        case Convierte(opciones2, coche::verde): {estadisticasopcionAnterior = 's';}


Básicamente, Convierte(A, B) significa: convierte el valor de B al tipo de A. No lo probé, pero debería funcionar. No es una gran mejora respecto a lo que estás haciendo, pero te ahorrarías estar teniendo que buscar y poner los tipos de las variables, ya que aquí la función las deduce. Por eso el parámetro dest: no se usa en la función, pero está ahí para que deduzca automáticamente su tipo y no tengas que escribirlo. Sólo habría que tener cuidado, ya que sería muy fácil poner opciones2 donde debías poner opciones, y así...

Aún así, si quieres seguir usando enum, quizás sería una mejor idea volver a los enum simples y ponerles a los valores nombres tipo coche_rojo, moto_rojo. Creo que no perderías mucha legibilidad y te evitarías totalmente las conversiones.

En fin, te dejo estas opciones que creo que son las más simples para tu problema.