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

#71
De la API de Windows necesitas conocer muy bien cómo se crean y cómo funcionan las ventanas, eventos y mensajes, controles, y cómo crear dlls. Mucho de esto lo puedes encontrar en el primer link que puse. Básicamente, de ahí sáltate todo lo de GDI, que es para gráficos y no lo necesitas.

COM es una tecnología de Microsoft para crear componentes que en teoría se pueden usar desde programas hechos en cualquier lenguaje, independientemente del lenguaje en el que el componente haya sido creado. Los archivos que contienen estos componentes se llaman servidores COM, que pueden ser .exe (en cuyo caso se llaman servidores fuera de proceso) o .dll (servidores dentro de proceso) con ciertas características especiales. Las extensiones del Shell se deben crear como servidores COM; en concreto, el tipo de extensión que tú necesitas crear es un servidor dentro de proceso, es decir, una dll de COM. Para tu ejemplo, básicamente cuando haces clic derecho/propiedades sobre un archivo, el proceso explorer (el shell) busca en el registro de Windows a ver si hay algún componente COM registrado para ese tipo de archivo y, en su caso, carga la dll que lo contiene (el servidor COM que creamos) y llama a una función dentro de ella y ésta se encarga de crear la pestaña extra y agregarla al cuadro de diálogo. Así es como funciona el mecanismo de las extensiones y por eso hay que saber COM.

Técnicamente se puede programar COM en otros lenguajes, como VB6 o Delphi, pero uno está obsoleto y el otro ya no se usa mucho. Y aunque C# y VB.Net también sirven para COM en general, no se deben usar para este tipo de extensiones al shell, lo que deja a C++ básicamente como la única opción. Y deberías usar Visual Studio porque en caso contrario, y por varias cuestiones que serían largas de explicar, casi tendrás garantizados muchos problemas y quebraderos de cabeza.

Y sí, podrías poner aquí o abrir tema nuevo si tienes otras dudas.
#72
Por supuesto que MinGW se puede usar para programación con la API de Windows, pero es limitado. Obviamente no me refiero a cosas simples como #defines o archivos sencillos de texto, que normalmente se pueden crear o modificar sin mayor problema. Pero Windows se compone de un montón de APIs y tecnologías y MinGW sólo es compatible con un subconjunto de ellas y muchas veces de forma incompleta, y eso no se arregla modificando archivos de texto. Sin ir muy lejos, hay dos formas estándar (una obsoleta y una moderna) en la API de Windows de mostrar un cuadro de diálogo para seleccionar exclusivamente directorios (no archivos), y ninguna de las dos se puede en MinGW. No queda más que programar una solución propia, o hacer modificaciones importantes y muy tediosas a lo que ya se incluye. MinGW-w64 sí que soporta la forma obsoleta, pero la actual, introducida con Windows Vista, no hay manera.

En este caso, es aún peor, ya que las extensiones al shell se deben implementar dentro de lo que se conoce como servidores COM dentro de proceso (in-process). COM es una especificación en la que normalmente se usan tecnologías propias de Microsoft. Por ejemplo, un lenguaje de Microsoft, que no es C++, junto con un un compilador propietario de Microsoft (que se incluye con Visual Studio), el cual genera unos archivos binarios en un formato específico. Aclarar que, para facilitar nuestro trabajo, nosotros como programadores podemos crear simplemente nuestros componentes, en C++, y las herramientas de Visual Studio se encargan del resto, como invocar al compilador antes mencionado. Por supuesto, MinGW no proporciona ni admite nada de esto. Aclaro también que en internet se puede encontrar información sobre crear objetos COM con C puro (lo que implicaría que se puede con MinGW), pero no son componentes COM totalmente "reales", no cumplen al 100% con la especificación. Los podríamos usar con nuestros programas, pero no de forma universal, que es el objetivo de COM. Aunque estrictamente hablando, para el caso concreto de extensiones al shell, no se necesita implementar todo lo de un servidor COM general, hay que usar las herramientas de Microsoft para evitar problemas.

Editado un par de typos y, aprovechando, unas puntualizaciones.
#73
Eso se llama extensiones al shell. Es tema avanzado y necesitas conocer muy bien la API de Windows y, aunque sea a nivel introductorio/intermedio, la creación de servidores COM. Algunos links útiles:

http://winapi.conclase.net/
https://docs.microsoft.com/en-us/windows/win32/com/the-component-object-model

Luego de eso es buscar "Shell extensions".

Para eso usarías C++ y necesitas Visual C++. Otros compiladores como MinGW, que usan por defecto Dev-C++ y Code::Blocks, no sirven para esto.
#74
Lo ideal sería hacer un número muy grande de pruebas, incluso usando semillas iguales en ambos compiladores, para tratar de eliminar algunas variables y evitar resultados atípicos y así hacernos una mejor idea de lo que sucede. De cualquier forma, como bien dice dijsktra, es de esperar que haya ciertas variaciones, siempre y cuando al final las funciones tengan un comportamiento lineal. Luego, por supuesto, ya entran en juego los compiladores; algunos sabrán optimizar mejor algunas funciones u otras; o incluso partes de una función, lo que podría explicar resultados aparentemente inconsistentes: quizás con ciertas distribuciones o tamaños, algunos "branches" tienden a ejecutarse más frecuentemente,y si resulta que un compilador no hizo un buen trabajo optimizando esa parte específica del código, puede dar resultados muy pobres cuando con otros tamaños brillaba.

Aún así, a veces distintos procesadores, incluso del mismo fabricante, pueden comportarse de forma distinta, lo cual es frustrante, pero, en fin...  Yo lo probé con lo que tengo ahora que es MinGW-w64 en su versión de 64 bits, por lo que también compilé para esa arquitectura en Visual Studio 2019. Eso hace que mis resultados sean distintos. Por eso, y porque tampoco hice muchas pruebas, no pongo los números, pero sí algunas cosas que encontré por si acaso son de interés. En mi caso, casi siempre k_paired() se comporta un poco mejor (más o menos un 8%) que n_pares(), para todos los tamaños y en ambos compiladores, pero especialmente en MinGW-w6. Además, con todas las funciones, y sobre todo con k_paired(), MinGW-w64 generó mejores resultados (cerca del 10%). Tomaría mucho tiempo hacer un buen análisis, pero de forma breve, en una revisión rápida al código generado, veo que Visual C++ hizo inline todas las funciones, mientras que MinGW-w64 no lo hizo con ninguna. En este caso, eso por sí sólo no debería influir mucho, pero pudiera ser útil hacer experimentos cambiando eso. Centrándome en k_paired() y la diferencia de su ejecución entre compiladores (que me pareció lo más interesante), MinGW-w64 generó código con más instrucciones y que a simple vista pudiera parecer menos eficiente, pero en realidad lo que hizo es un loop unrolling parcial. Pongo aquí un pseudocódigo resumido de lo que hace el ensamblador que genera este compilador:

bucle:
resta = v4[n] - v4[m]
if (resta > k) goto block3
block2:
tmp++
sumando = (resta == k)
r += sumando
if (tmp >= N) goto fin
n = tmp
resta = v4[n] - v4[m]
if (resta <= k) goto block2
block3:
m++
if (tmp > N) goto fin
goto bucle
fin:
return r


Lo interesante es ver que hace la comparación de la resta con k en dos partes diferentes. El código generado por Visual C++ es muy similar, aunque no hace esa segunda resta y comparación, sino que directamente salta de vuelta al inicio del bucle, que sería lo más normal, dado el código fuente original. Los saltos no son operaciones muy costosas, pero a los procesadores les gusta el código secuencial; no por nada el compilador decidió hacer esa optimización, así que esto podría explicar el peor rendimiento de VC++. Y es que realmente no hay ninguna otra diferencia importante entre el código generado por los compiladores para esa función. Es probable que con las otras funciones y con el MinGW original y VC++ compilando a 32 bits pase algo similar (o no) pero eso ya requeriría dedicarle algo más de tiempo.

Edito:

Como me quedó la curiosidad, quise corroborar mi conjetura y modifiqué el ensamblador generado por VC++ para incluir la optimización de MinGW-w64 (con lo que además  se elimina un salto adicional que hacía VC++, y que se me pasó mencionar en el párrafo anterior),  y efectivamente, eso cambia todo. La nueva función modificada es alrededor de un 20% más rápida que la que genera VC++, aunque la ganancia tiende a disminuir con vectores muy grandes. Aún así, incluso en el peor de los casos, obtuve una mejora del 10%. Y de hecho, con esta función, VC++ ahora supera ligera, pero consistentemente, a MinGW-w64 (en el caso de k_paired, las otras funciones no las toqué). Esto también tiene su explicación: en el código que genera MinGW-w64, en cada iteración del bucle se calcula la dirección de v4[n] mediante (v4+n*4), y lo mismo para v4[m], mientras que VC++ usa directamente punteros que simplemente incrementa ptrN+=4, ptrM+=4, ahorrándose las multiplicaciones. Aquí tenemos un caso en el que cada compilador optimizó mejor una parte de la función, y lo hizo peor en otra, aunque, por supuesto, la disminución de saltos de MinGW-w64 tuvo más peso. Si a alguien le interesa, le puedo pasar el código ensamblador de la función, que es una copia de lo generado por el compilador, y sólo le hice la modificación mencionada. Lamentablemente, como Visual C++ no admite inline asm en 64 bits, se tendría que hacer un archivo .asm externo con la función y enlazarlo a su proyecto.

Hagamos caso a lo que dicen Scott Meyers y Andrei Alexandrescu en las conferencias mencionadas atrás. Para escribir programas eficientes en el mundo real, es crucial recurrir al bajo nivel. Aquí se cambiaron sólo 4 líneas de código y se obtuvo una función más rápida que las generadas por cualquiera de estos dos compiladores, con todo y -O3 u /O2. Y fue algo hecho muy a lo rápido.

Me quedo con esto de Alexandrescu (énfasis mío):

CitarThrow the structures & algos book away
Research papers & industry is where it's at
#75
Qué gusto ver que cada vez sea más común ver presentaciones como la de Andrei Alexandrescu en las conferencias "grandes". Parece que siguen rindiendo frutos los esfuerzos de varias personalidades del software por concientizar sobre la necesidad de escribir software eficiente.

Creo que dice mucho el hecho de que algunos pesos pesados de C++ presenten como novedades y descubrimientos cosas que ya eran bien sabidas hace un par de décadas por cualquiera interesado en la optimización. Y esto en una charla dirigida a profesionales. De hecho, yo vi la conferencia sobre la caché de Scott Meyers por unos amigos que me enviaron el link, donde ponían que iba a ver a Scott Meyers descubriendo el agua tibia. Pero hablando en serio, sí tiene mérito que difundan ese tipo de información. Durante mucho tiempo, mucha gente en la industria le ha restado importancia a la optimización de bajo nivel debido a la falacia de que "las computadoras ya son lo suficientemente rápidas", o con el argumento de que ese tipo de optimización es dependiente de la arquitectura. Como si los programadores y nuestros clientes usáramos máquinas abstractas para hacer nuestro trabajo...

Aunque tiene su parte de verdad lo que dice Alexandrescu, en cuanto a que los libros que típicamente se usan en las universidades casi no mencionan nada de eso, sí que ha habido varias publicaciones, algo más especializadas, que tocaban esos temas. Y si de literatura "clásica" hablamos, ahí están, y estaban, los manuales oficiales de los procesadores, que siempre fueron la referencia #1 a consultar si se quería optimizar, así que no había excusa. Otra cosa es que muchos hayan vivido en una burbuja teórica/académica. Incluso antes uno podía solicitar a Intel el paquete de versiones impresas de sus manuales oficiales y los enviaban gratuitamente. No siempre, pero a veces sí. Yo aún tengo mi colección de 2004 y ahí se explican prácticamente todas esas "revelaciones" de las que hablan Alexandrescu y Scott Meyers. Igualmente tengo algunas revistas de los 90 y principios de 2000 donde también se exponía esa información. De hecho, llevo unas semanas hurgando en mis códigos viejos, pues estoy pensando liberarlos como open source, y tengo archivos desde 1998 con funciones en las que comenté que eran más eficientes que otras versiones aparentemente más optimizadas, debido a las cachés del procesador y la predicción de saltos (branch prediction). Es decir, muchos, incluyéndome a mí, que era un novato, ya conocíamos y aplicábamos todo eso en los años noventa. Como digo, la información estaba, para quien se interesara en buscarla. Y la realidad es que casi ninguno de los ejemplos que se muestran en esas conferencias tiene nada de sorprendente. Que se presenten de esa forma, cuando la mayoría son más bien obviedades, dice mucho sobre el triste estado actual de buena parte de la industria. Por eso me parece tan relevante que estos asuntos dejen de ser temas de nicho y cada aparezcan más en los grandes eventos. Ojalá empiecen a permear en la academia y podamos tener mejor software en el futuro.
#76
Si te sale FALSO será que estás omitiendo el "" final. Pégalas tal cuál las las puse, cambiando obviamente el nombre de celdas y debería funcionar. Para que salga todo en una celda, simplemente concatena las 3 funciones con el símbolo &:

=SI(A1 >= 0, A1 & " años ", "") & SI(blabla...) & SI(blabla)
#77
Programación C/C++ / Re: ¿SetPixel con un array?
12 Septiembre 2019, 05:06 AM
Sí, naturalmente, fprintf va a guardar los caracteres que le digas, pero el resultado no es un bmp. Como te dije, los bmp, igual que cualquier otro formato, tienen una estructura específica: dos cabeceras con información del archivo, y además los valores de color de los pixels deben seguir un orden específico que determina el formato, y algunas otras cosas. Sólo un archivo que contenga todo esto es un bmp, porque independientemente de la extensión, lo que define el tipo de un archivo es su contenido. Y también te decía que para eso no podrías usar fprintf, ya que esta función guarda texto. El BMP es un formato binario y, entre otras cosas, sus cabeceras contienen algunos campos que son enteros de 16 y 32 bits. Esos no los puedes guardar con fprintf porque los convertirá a caracteres. Por ejemplo, si tu imagen tiene un ancho de 1280 pixels, al intentar guardar el campo que especifica esa información, lo que en realidad se escribirá no será ese valor, sino los códigos ASCII de los caracteres '1', 2', '8' y '0'. Y así con cualquier otro número. Es decir, vas a tener un archivo no válido. En realidad sí hay alguna forma en que se podría hacer, pero requeriría bastante trabajo y sería muy ineficiente, ya que básicamente sería forzar a fprintf a hacer algo para lo que no fue diseñada. frwite es la función a usar para información binaria.

De cualquier forma, lo que yo te recomendaba era que escribieras alguna función para poder leer archivos bmp. De esa manera puedes crear las imágenes con prácticamente cualquier editor de imágenes, no sólo GIMP, y te evitas tener que lidiar con esa cabecera generada. Naturalmente, para esto tampoco usarías una función de lectura de texto como fscanf, sino fread.
#78
Suponiendo que los números de años, meses y días estén en las celdas A1, A2, y A3, así:

=SI(A1 >= 0, A1 & " años", "")
=SI(A2 > 0, A2 & " meses", "")
=SI(A3 > 0, A3 & " días", "")
#79
Programación C/C++ / Re: ¿SetPixel con un array?
11 Septiembre 2019, 01:12 AM
¿Lo exportaste a un BMP con un simple fprintf  :o? ¿Cómo? Primero que nada, fprintf no es apta para archivos binarios, para eso usarías algo como fwrite. Pero además, lo que deduzco de lo que dices es que guardaste el contenido de la cabecera generada por GIMP en un archivo con extensión bmp, ¿cierto? Porque tampoco puedes hacer eso. Cada formato tiene su estructura y su forma de almacenar la información. El bmp lo debes generar desde GIMP o cualquier otro editor de imágenes, y luego, lo abres en tu programa y lo muestras en la ventana.

Hay varias formas de hacerlo. Una es cargarlo con LoadImage y usar funciones de GDI para copiarlo a un DC de memoria y luego de ahí a tu ventana, con la función BitBlt. A mí no me gusta esta forma, ya que limita lo que puedes hacer, pero es la más simple. Me parece mejor abrir y leer el archivo por uno mismo, y supongo que también es lo que te serviría más si quieres usar SetPixel. Aquí leerías del archivo (con fread), primero una estructura BITMAPFILEHEADER, y usando la información que contendrá la estructura, procedes a reservar memoria para un puntero unsigned char, y leer ahí los pixels, igualmente con fread. La otra opción, la más didáctica y que te da más control, y que te recomendaría si te interesa la programación gráfica, es leer todo el archivo "manualmente". Como te mencioné, el formato BMP es muy simple. Tienes una descripción en la Wikipedia, que aunque parece complicada, no lo es. Es larga porque explica todos los campos de las dos cabeceras que contienen los BMP, pero en realidad sólo unos cuantos son importantes. Realmente con un par de horas que le dediques podrías tener lista una función o funciones para leer bmps. O si no, ahí tienes las otras dos opciones para elegir. Ya sólo toca leer un poco y programar.
#80
Programación C/C++ / Re: Problema con las Listas C++
10 Septiembre 2019, 00:48 AM
Una lista, como te dice string Manolo, es simplemente un contenedor, sin limitaciones de acceso a sus elementos. Te parecen similares a las colas porque comúnmente estructuras como colas y pilas se implementan internamente justamente con listas enlazadas, sólo que con restricciones en cuanto a dónde puedes insertar y tomar elementos, por lo que son más simples en ese sentido. Pero eso son detalles de su implementación y nada impide crear una cola usando alguna otra estructura interna, así que no hay que mezclar los conceptos.

Cita de: Beginner Web en  8 Septiembre 2019, 23:04 PM
Y sin olvidar que una lista ocupa el doble de tamaño en memoria que un arreglo convencional :')  ;-)

Bueno, no necesariamente. Una lista enlazada simple le añade a cada elemento 4 u 8 bytes (el tamaño de un puntero). Si los elementos de la lista son, digamos, structs, y ocupan 32 bytes, entonces una lista ocupará un 12.5 o 25% más que si se usara un arreglo.