Preguntas frecuentes (FAQ):

¿Cómo me doy de baja de la lista de correo?

¿Dónde consigo un compilador de C++?

¿Cómo puedo incluir gráficos en mis programas C++?

¿Cómo puedo usar el ratón en mis programas C++?

¿Cómo puedo acceder a bases de datos desde mis programas C++?

¿Por qué mi compilador no admite las funciones de conio.h?

¿Qué diferencias hay entre C y C++?

¿Cómo programar el puerto serie desde Windows?

¿Cómo enviar datos a la impresora desde un programa C o C++?

¿Cómo me doy de baja de la lista de correo?

Es posible que te hayas cansado de recibir correos sobre C++, o que esta lista no sea lo que esperabas, o que existan motivos que te impiden seguir siendo miembro de la lista. En cualquier caso, cuando quieras, puedes reinscribirte al grupo.

Por motivos de seguridad, los moderadores no tramitamos bajas directamente, sino que has de ser tú quien lo haga.

Es muy sencillo. La lista de correo dispone de varios modos de configurar el modo en que se entrega el correo.

La forma más fácil de abandonar la lista es enviando un correo-e en blanco a: c-con-clase-unsubscribe@smartgroups.com

Por supuesto puedes entrar en "C con Clase" de Smartgroups y pinchar en "Leave Group", en el menú izquierdo, bajo "Group".

Pero si realmente no quieres abandonar el grupo y lo que pasa es que te encuentras agobiado con la cantidad de mensajes recibidos, entonces puedes configurar Smartgroups o bien para recibir un resumen diario de los mensajes enviados o bien para no recibir correo-e. Si eliges esta última opción, entonces has de leer los mensajes directamente desde la página de Smartgroups.

Para seleccionar una de las tres opciones (la opción por defecto, es la de recibir mensajes "Inmediatamente"), entra en "C con Clase" en Smartgroups. Pincha en "Your Settings", bajo "Group" en el menú izquierdo. Verás que la opción "Immediately" está seleccionada. Pincha en la lista y selecciona la opción que más te interese. Recuerda que has de pulsar el botón de "Update" para que la opción haga efecto.

¿Dónde consigo un compilador de C++?

Si trabajas con el sistema operativo Windows, puedes conseguir un compilador en Internet, bastante completo, y que recomendamos en esta página. Se trata de Dev-C++

En realidad Dev-C++ es un entorno de programación, es decir, una aplicación que incluye editor, depurador, compilador y enlazador, todo lo necesario para programar en C++. El compilador es GCC, y además es una versión muy reciente, que incluye las últimas modificaciones del lenguaje.

En otros sistemas, como Linux, no habrá problema, normalmente hay un compilador entre las muchas utilidades que se incluyen.

No pudemos hablar de otros sistemas, ya que no los conocemos.

¿Cómo puedo incluir gráficos en mis programas C++?

Los gráficos lo mismo que otras capacidades como el sonido, comunicaciones, control de periféricos no estándares, etc. no forman parte ni del lenguaje C++ ni tampoco de las bibliotecas estándares de ANSI C++. Nuestro curso de C++ trata del lenguaje y algunas funciones de las bibliotecas estándares de ANSI.

Para el tema de los gráficos depende del sistema operativo, entorno, configuración de hardware, compilador, y bibliotecas o API's gráficos. Es un tema que requiere muchas especificaciones. Algunos API's tratan de englobar todos los aspectos gráficos y de configuración, por lo que es posible que no tengas tantas dependencias.

Dicho esto, es necesario saber qué compilador y biblioteca o API gráfica piensas usar y para qué S.O. y entorno, también. Si usas un compilador de Borland anterior a Builder, entonces podrás usar su biblioteca gráfica BGI para MS-DOS. En nuestra página puedes encontrar una referencia de las funciones declaradas en que se asocian a la BGI. Puedes ir a: http://conclase.net/c/borland/. También puedes consultar un artículo escrito por Lola Cárdenas acerca de la BGI, yendo a: /blog/item/bgi.

Si programas para MS-Windows usando el API de MS-Windows, entonces ésta ya contiene las funciones del GDI para gráficos. Puedes consultar los capítulos del GDI en nuestro curso de MS-Windows yendo directamente a: capítulo 16 del curso del API de Windows.

También puedes usar la biblioteca OpenGL que trae funciones tanto para dibujar como para crear y manipular ventanas en cualquiera de los principales y populares sistemas operativos: Ms-Windows, Linux/Unix/SGI/Solaris, y Macintosh. En fin, esto depende de lo que quieres hacer y de cuánto tiempo dispones para ello.

¿Cómo puedo usar el ratón en mis programas C++?

Al igual que los gráficos, el ratón es un periférico que no tiene soporte dentro del estándar de C y C++, y su acceso depende del sistema operativo, y del entorno.

No se puede dar una respuesta concreta a este tipo de preguntas, ya que depende de cada caso.

En la zona de código fuente hay un ejemplo para usar el ratón desde DOS. En otros entornos, como Windows, el API contiene las funciones necesarias para acceder al ratón, en otros sistemas hay distintas alternativas.

¿Cómo puedo acceder a bases de datos desde mis programas C++?

De nuevo estamos ante un tema muy amplio, sobre todo porque existen muchos motores de bases de datos diferentes.

En la página no hemos profundizado mucho en estos temas, todavía. La única aproximación es el pequeño curso sobre manejo de ficheros, aún sin terminar. Pero esto no es más que una forma de reinventar la rueda, es mucho el código que hay que escribir para manejar bases de datos con la potencia necesaria para aplicaciones serias, pero lo que se cuenta en el curso puede ser suficiente para pequeñas aplicaciones.

Para aplicaciones más grandes se puede recurrir a motores de bases de datos más profesionales. Uno de ellos, lo suficientemente potente y lo que es mejor, gratis, es MySQL, que incluye APIs para distintos lenguajes y sistema operativos.

Existen otros motores de bases de datos, como el de Microsoft, y no debería ser muy complicado encontrar información en la red sobre el modo de usarlo.

Está fuera de nuestros objetivos ilustrar el uso de esas bibliotecas, aunque es posible que en un futuro más o menos cercano, incluyamos algo sobre MySQL.

¿Por qué mi compilador no admite las funciones de conio.h?

La biblioteca "conio" no es estándar, aunque su uso es tan corriente que a menudo se considera como tal. La culpa de esto la tiene Borland ®, y sus compiladores de Pascal, C y C++.

Durante mucho tiempo, los compiladores de Borland han sido prácticamente los únicos que estaban al alcance de la mayoría de los usuarios, y eran muy populares, en gran medida también debido a su buena calidad.

Borland siempre ha incluído funciones para acceder a ciertas características de la consola de MS-DOS, como colores, coordenadas, lecturas sin eco, etc. y a agrupado esas funciones en una biblioteca llamada "conio". Los programas ganaban mucho en apariencia cuando se recurría a esas funciones.

Pero la informática ha evolucionado, y MS-DOS ya no es un sistema operativo popular. Los nuevos sistemas no usan la misma consola que MS-DOS, y por lo tanto, las funciones de "conio" que se basan directamente en la BIOS de DOS, no funcionan en otros sistemas.

De hecho, los compiladores de Borland tampoco son tan omnipresentes como antaño, ahora hay más variedad, y la mayoría de los compiladores no incluyen esa biblioteca, recordemos que no es estándar.

Cuando se intenta adaptar código escrito para compiladores Borland a otros compiladores o a otros sistemas operativos aparecen los problemas, y cosas tan sencillas como borrar la pantalla, se convierten en auténticos problemas.

Otros entornos de consola incluyen bibliotecas alternativas para las mismas funciones que "conio", pero no se llaman igual, ni usan los mismos parámetros y a menudo no son fáciles de sustituir. Es el precio que hay que pagar por apartarse del estándar.

En el caso de Dev-C++ existen emuladores, uno de ellos puedes conseguirlo en esta página: conio para Dev-C++.

¿Qué diferencias hay entre C y C++?

C++ evolucionó a partir del C, por lo tanto, C es un "subconjunto" de C++. Eso quiere decir que C está incluido en C++, en general, casi cualquier programa escrito en C podrá compilarse con un compilador de C++, y funcionará.

Así que casi todas las diferencias son del tipo de qué tiene C++ y no tiene C.

La principal diferencia es que C++ está orientado a objetos, es decir tiene clases.

Los creadores de C++ crearon las clases basándose en una característica que ya tenía C: las estructuras. Estas se han mejorado y admiten funciones además de datos como miembros. Para mantener cierta coherencia con C, y mantener el concepto de estructura en su idea original, se creó un nuevo concepto class.

Pero las diferencias no terminan ahí. Muchas de las mejoras introducidas para implementar las clases se pueden usar fuera de ellas, como la sobrecarga de funciones, y operadores, y los parámetros con valores por defecto.

También hay nuevos operadores, entre los que destacan new y delete, que mejoran sensiblemente el tratamiento de la memoria dinámica.

Otra capacidad introducida son las referencias, normalmente usadas en los parámetros de las funciones. En sentido estricto, no aportan nueva funcionalidad a los programas, ya que se pueden simular mediante punteros, pero sí aportan legibilidad y claridad.

En C++ es obligatorio usar prototipos de las funciones, en C no es necesario. Esta diferencia sí puede ser importante a la hora de migrar código de C a C++. La mayoría de los problemas que encuentras los programadores de C cuando usan compiladores de C++, surgen de ésta característica.

C++ introduce el nuevo formato de comentarios que comienzan con // y terminan al terminar la línea.

También se han introducido varias mejoras en la conversión de tipos (casting). C++ incorpora nuevos conceptos de casting, por otra parte necesarios, debido sobre todo a la nueva problemática de la programación orientada a objetos: herencia y polimorfismo.

En C++ no es obligatorio especificar void en la lista de argumentos de una función que no los usa, en C es sí lo es.

Para que C++ pueda usar funciones definidas para C en bibliotecas "run-time", es necesario declararlas como extern "C".

C++ admite la declaración de variables locales dentro de sentencias como for, while, etc. Esto no está permitido en C. Por ejemplo:

 for(int i = 0; i < 10; i++);

Aunque typedef aún se admite en C++, y se sigue usando, ya no es necesario su uso con estructuras y uniones, en C++ no es obligatorio especificar las struct o union en la declaración de variables.

¿Cómo programar el puerto serie desde Windows?

Una de las cosas que resulta más problemática a la hora de migrar desde entornos como MS-DOS a Windows es el manejo de los puertos.

Windows no permite usar los puertos directamente. En lugar de eso hay que usar el puerto serie como si se tratara de un fichero.

El tratamiento es radicalmente distinto a como se hace en DOS. En Windows, el puerto serie se usa como si fuera un fichero corriente, es decir, hay que abrirlo, leer y escribir en él, y cerrarlo cuando no se necesite más.

Para abrir el puerto se usa una rutina de éste estilo:

----8<------
// Variables necesarias, globales o de la clase base.
HANDLE idComDev;
OVERLAPPED ov;
DCB dcb;
DWORD n;
bool AbrirCOM()
{
   bool fSuccess;
   idComDev = CreateFile(Opcion.Puerto, GENERIC_READ | GENERIC_WRITE,
      0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
   if(idComDev ==INVALID_HANDLE_VALUE)
   {
      MessageBox(hWnd, "Inicialización puerto serie",
        "ERROR: CreateFile", MB_ICONINFORMATION | MB_OK);
      return false;
   }
   fSuccess = GetCommState(idComDev, &dcb);
   if(!fSuccess)
   {
      MessageBox(hWnd, "Inicialización puerto serie",
        "ERROR: GetCommState", MB_ICONINFORMATION | MB_OK);
      return false;
   }
   dcb.BaudRate = (DWORD)Velocidad;
   dcb.ByteSize = (BYTE)NBits;
   dcb.Parity = (BYTE)Paridad;
   dcb.StopBits = (BYTE).StopBits;
   fSuccess = SetCommState(idComDev, &dcb);
   if(!fSuccess)
   {
      MessageBox(hWnd, "Inicialización puerto serie",
        "ERROR: SetCommStatus", MB_ICONINFORMATION | MB_OK);
      return false;
   }
   if(!SetCommMask(idComDev, EV_RXCHAR))
   {
      MessageBox(hWnd, "No se pudo activar la el evento de lectura.",
        "Mensaje para el usuario", MB_ICONEXCLAMATION | MB_OK);
      SetCommMask(idComDev, 0);
      CloseHandle(idComDev);
      return false;
   }
   ov.hEvent = CreateEvent(NULL, /* no security attributes */
      FALSE, /* auto reset event */
      FALSE, /* not signaled */
      NULL /* no name */);
   assert(ov.hEvent);
   ov.Offset = NULL;
   ov.OffsetHigh = NULL;
   return true;
}
----8<------

En el ejemplo se ve que también hay que crear un evento para que se avise al programa cada vez que haya datos disponibles en el puerto serie.

Cerrar el puerto serie:

----8<------
void CerrarCOM()
{
   SetCommMask(idComDev, 0);
   CloseHandle(idComDev);
}
----8<------

Escribir en el puerto serie:

----8<------
void WriteCom(char *buf)
{
   char Buffer[1024];
   DWORD l, p;

   strcpy(Buffer, buf);
   l = strlen(Buffer);
   p = 0;
   while(l) {
      if(!WriteFile(idComDev, &Buffer[p], l, &n, &ov)) {
         if(GetLastError() != 997) {
            // Error de comunicación
         }
         return;
      }
      p += n;
      l -= n;
   }
}
----8<------

Para saber si hay datos en el buffer de entrada se usa el evento:

----8<------
if(WaitCommEvent(idComDev, &dwEvtMask, &ov))
   if(dwEvtMask & EV_RXCHAR) LeeDatos();
----8<------

Estas líneas a veces se suelen colocar en un timer, pero es preferible crear un thread (hilo) para ello. Esto impedirá que se sobrecargen los temporizadores y que se pierdan datos de entrada.

Leer datos:

----8<------
void ReadCom(char *buf)
{
   DWORD x;

   ClearCommError(idComDev, &x, &cs);
   ReadFile(idComDev, buf, cs.cbInQue, &x, &ov);
}
----8<------

Esto es sólo una orientación, yo he usado estas funciones en mis programas, y funcionan. Pero es muy probable que requieran algunos retoques o aclaraciones.

Iremos agrupando las preguntas y respuestas que vayan surgiendo en la lista y que se repitan frecuentemente, de este modo, cuando alguien haga una pregunta de este tipo se le remitirá directamente a esta página y evitaremos repetir las mismas respuestas a las mismas preguntas una y otra vez.

En realidad, si un tema es recurrente en la lista de correo, se escribirá un artículo sobre el tema. Eso es lo que está pasando ahora. Esta sección se quedará sólo para aclarar preguntas cortas.

¿Cómo enviar datos a la impresora desde un programa C o C++?

Lo más sencillo es usar la impresora directamente, aunque no estoy seguro de que esto funcione, ya que tengo la impresora averiada:

----8<------
#include <fstream>

using namespace std;

int main()
{
   ofstream impresora;
   impresora.open("LPT1");

   impresora << "Hola, mundo\f" << endl;
   impresora.close();
   return 0;
}
----8<------

El carácter '\f' es el form-feed, sirve para finalizar la página y expulsar el papel.

Ahora bien, usando el API, y el spooler de la impresora, es más complicado, aunque también más recomendable.

No es un tema corto de explicar, pero te haré un pequeño resumen.

Hay varias funciones del API que debes usar:

  • DeviceCapabilities: Te ayudará a saber cosas como las dimensiones del papel, la resolución en puntos por pulgada, etc.
  • StartDoc : Comienza la generación de un documento a imprimir.
  • StartPage : Comienza una página.
  • EndPage : Termina la página actual.
  • EndDoc : Termina la impresión del documento actual.

El mecanismo sigue la siguiente estructura:

  • Crear el contexto de dispositivo para la impresora (DC).
  • Comenzar el documento: StartDoc.
  • Salida al DC de impresora comenzando y terminando las páginas necesarias: StartPage, EndPage.
  • Terminar el documento. EndDoc.
  • Liberar el DC de impresora.

Ahí va un ejemplo, antes de nada necesitamos un DC para la impresora. Todas las funciones gráficas que sirven para un DC de pantalla se pueden usar también en un DC de impresora:

----8<------
BOOL PrintRect(HDC hPrintDC)
{
    DOCINFO diInfo;
    char DocName[5] = "hello";

    diInfo.cbSize = sizeof(DOCINFO);
    diInfo.lpszDocName = (LPSTR)DocName;
    diInfo.lpszOutput = NULL;
    StartDoc(hPrintDC, (LPDOCINFO)&diInfo);
    StartPage(hPrintDC);
    Rectangle(hPrintDC, 0, 0, 100, 100);
    EndPage(hPrintDC);
    EndDoc(hPrintDC);
}
----8<------

Para crear un DC de impresora se necesita la función:

HDC CreateDC(
    LPCTSTR  lpszDriver,        // address of string specifying driver name
    LPCTSTR  lpszDevice,        // address of string specifying device name
    LPCTSTR  lpszOutput,        // do not use; set to NULL
    CONST DEVMODE  *lpInitData  // address of optional printer data
   );

Los parámetros necesarios se pueden obtener del registro, si quieres usar la impresora por defecto. O también puedes obtener una lista de las impresoras del sistema:

BOOL EnumPrinters(
    DWORD  Flags,       // types of printer objects to enumerate
    LPTSTR  Name,       // name of printer object
    DWORD  Level,       // specifies type of printer info structure
    LPBYTE  pPrinterEnum,       // pointer to buffer to receive printer info structures
    DWORD  cbBuf,       // size, in bytes, of array
    LPDWORD  pcbNeeded, // pointer to variable with no. of bytes copied (or required)
    LPDWORD  pcReturned // pointer to variable with no. of printer info. structures copied
   );

Para usarla:

----8<------
   LPBYTE buffer;
   PRINTER_INFO_1 *piPrinter;
   DWORD necesita, noprinters;

   EnumPrinters(PRINTER_ENUM_LOCAL, NULL, 1, buffer, 0, &necesita, &noprinters);
   buffer = new BYTE[necesita];
   EnumPrinters(PRINTER_ENUM_LOCAL, NULL, 1, buffer, necesita, &necesita, &noprinters);
   piPrinter = (PRINTER_INFO_1*)buffer;
----8<------

PRINTER_INFO_1 tiene esta estructura:

typedef struct _PRINTER_INFO_1 { // pri1
    DWORD  Flags;
    LPTSTR pDescription;
    LPTSTR pName;
    LPTSTR pComment;
} PRINTER_INFO_1;

Espero que este rollo sea suficiente...