# Aplicaciones de consola con Qt

[Qt](/proyecto-qt-framework-de-desarrollo-de-aplicaciones/) es un *framework* utilizado fundamentalmente para desarrollar aplicaciones con interfaz gráfica. Sin embargo, nada impide que también sea utilizado para crear aplicaciones de línea de comandos.

# QCoreApplication

Al crear un proyecto de aplicación para consola, el asistente de Qt Creator crea un archivo `main.cpp` con un contenido similar al siguiente:

```cpp
#include <QCoreApplication>

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    return a.exec();
}
```

[QCoreApplication](https://doc.qt.io/qt-5/qcoreapplication.html) es una clase que provee un bucle de mensaje para aplicaciones de consola, mientras que para aplicaciones gráficas lo adecuado es usar [QApplication](https://doc.qt.io/qt-5/qapplication.html). El bucle de mensajes es iniciado con la invocación del método [QCoreApplication](https://doc.qt.io/qt-5/qcoreapplication.html)::[exec](https://doc.qt.io/qt-5/qcoreapplication.html#exec)(), que no retorna hasta que la aplicación finaliza. Por ejemplo cuando el método [QCoreApplication](https://doc.qt.io/qt-5/qcoreapplication.html)::[quit](https://doc.qt.io/qt-5/qcoreapplication.html#quit)() es llamado.

Si la aplicación no necesita de un bucle de mensajes, no es necesario instanciar [QCoreApplication](https://doc.qt.io/qt-5/qcoreapplication.html), pudiendo desarrollarla como cualquier programa convencional en C++, solo que beneficiándonos de las facilidades ofrecidas por las clases de [Qt](/proyecto-qt-framework-de-desarrollo-de-aplicaciones/).

Las clases de [Qt](/proyecto-qt-framework-de-desarrollo-de-aplicaciones/) que requieren disponer de un bucle de mensajes son:

* **Controles, ventanas y en general todas las relacionadas con el GUI**.
    
* **Temporizadores**.
    
* **Comunicación entre hilos mediante señales**.
    
* **Red**. Si se usan los métodos síncronos `waitFor*` se puede evitar el uso del bucle de mensajes, pero hay que tener en cuenta que las clases de comunicaciones de alto nivel (`QHttp`, `QFtp`, etc.) no ofrecen dicha API.
    

# Entrada estándar

Muchas aplicaciones de consola interactúan con el usuario a través de la *entrada estándar*, para lo cual se pueden usar tanto las clases de la librería estándar de C++:

```cpp
std::string line;
std::getline(std::cint, line)
```

como los flujos de [Qt](/proyecto-qt-framework-de-desarrollo-de-aplicaciones/):

```cpp
QTextStream qtin(stdin);
QString line = qtin.readLine();
```

Sin embargo, es necesario tener presente que en ambos casos el hilo principal se puede bloquear durante la lectura —hasta que haya datos que leer— lo que impediría la ejecución del bucle de mensajes.

Para evitarlo se puede delegar la lectura de la *entrada estándar* a otro hilo, que se comunicaría con el principal para informar de las acciones del usuario a través del mecanismo de señales y *slots* de [Qt](/proyecto-qt-framework-de-desarrollo-de-aplicaciones/). El procedimiento sería muy similar al que comentamos en [una entrada anterior](/hilos-de-trabajo-usando-senales-y-slots-en-qt/), solo que para leer la *entrada estándar* en lugar de para ordenar un vector de enteros.

# Usando manejadores de señales POSIX con Qt

Los sistemas operativos compatibles con el estándar POSIX implementan un tipo de interrupción por software conocida como [señales POSIX](/introduccion-a-las-senales-posix/), que son enviadas a los procesos para informar de situaciones excepcionales durante la ejecución del programa. El problema es que las *señales POSIX* pueden llegar en cualquier momento, interrumpiendo así la secuencia normal de ejecución de instrucciones del proceso, lo que puede introducir problemas de concurrencia debido al posible acceso del manejador —o de funciones invocadas por el manejador— a datos que estén siendo manipulados por el programa en el momento en que es interrumpido.

Lo cierto es que [sólo unas pocas funciones de la librería del sistema son seguras frente a señales POSIX](https://jesustorres.es/introduccion-a-las-senales-posix#heading-seguridad-respecto-a-las-senales). Y obviamente no hay seguridad de que las funciones del API de [Qt](/proyecto-qt-framework-de-desarrollo-de-aplicaciones/) lo sean, por lo que no podemos invocarlas directamente desde los *manejadores de señal*. Además, [Qt](/proyecto-qt-framework-de-desarrollo-de-aplicaciones/) no integra ninguna solución que encapsule y simplifique la gestión de *señales POSIX*, puesto que estas no están disponibles en sistemas operativos no POSIX y [Qt](/proyecto-qt-framework-de-desarrollo-de-aplicaciones/) solo implementa características portables entre sistemas operativos.

Aun así, en la documentación de [Qt](/proyecto-qt-framework-de-desarrollo-de-aplicaciones/) se describe una forma de usar *señales POSIX*, que en realidad es muy sencilla:

* Basta con que al recibir la señal POSIX el manejador haga algo que provoque que [Qt](/proyecto-qt-framework-de-desarrollo-de-aplicaciones/) emita una señal, antes de retornar. Por ejemplo, escribir algunos bytes en un *socket* que está conectado a otro gestionado por [Qt](/proyecto-qt-framework-de-desarrollo-de-aplicaciones/).
    
* Al volver a la secuencia normal de ejecución del programa, tarde o temprano la aplicación volverá al bucle de mensajes. Entonces la condición será detectada —en el ejemplo anterior, sería que han llegado algunos bytes a través del *socket* que [Qt](/proyecto-qt-framework-de-desarrollo-de-aplicaciones/) gestiona— y se emitiría la señal de [Qt](/proyecto-qt-framework-de-desarrollo-de-aplicaciones/) correspondiente, que invocaría al *slot* al que está conectada, desde donde se podrían ejecutar de forma segura las operaciones que fueran necesarias.
    
Concretamente en el artículo [Calling Qt Functions From Unix Signal Handlers](http://doc.qt.io/qt-5/unix-signals.html) se propone la solución esquematizada en la siguiente ilustración:

![Solución de manejo de señales POSIX en Qt.](https://cdn.hashnode.com/res/hashnode/image/upload/v1635808427530/d4AaxJK2A.png align="left")

Así que comenzaremos declarando una clase que contenga los *manejadores de señal*, los *slots* y otros elementos que comentaremos posteriomente.

```c++
class MyDaemon : public QObject
{
    Q_OBJECT

public:
    explicit MyDaemon(QObject *parent = 0);
    ~MyDaemon();

    // Manejador POSIX de la señal SIGTERM.
    // Esta función será invocada cuando llegue la señal SIGTERM al
    // proceso. Debe ser static para poder pasar el método como
    // puntero de función a las llamadas signal() o sigaction()
    static void termSignalHandler(int unused);
    
public slots:
    // Slot donde se atenderá realmente la señal SIGTERM cuando la
    // convirtamos en la señal de Qt sigTermNotifier->activated()
    void handleSigTerm();

private:
    // Pareja de sockets conectados. Un par por señal a manejar.
    // En este caso es para la señal SIGTERM.
    // En uno de ellos escribiremos desde termSignalHandler(), el otro
    // será gestionado por Qt a través del objeto sigTermNotifier de
    // clase QSocketNotifier.
    static int sigTermSd[2];

    // Objeto para monitorizar uno de los sockets de sigTermSd[2]
    QSocketNotifier *sigTermNotifier;
};
```

En el constructor de la clase anterior, para cada señal que se quiere manejar, se usa la llamada al sistema [socketpair](http://linux.die.net/man/2/socketpair)() para crear una pareja de [sockets de dominio UNIX](http://es.wikipedia.org/wiki/Socket_Unix) anónimos conectados entre sí. Al estar conectados desde el principio, lo que se escribe en uno de los *sockets* de la pareja se puede leer en el otro. Además, se crea un objeto [QSocketNotifier](https://doc.qt.io/qt-5/qsocketnotifier.html) para que gestione uno de los sockets de la pareja, con el objeto de detectar cuándo hay datos disponibles para ser leídos, en cuyo caso [QSocketNotifier](https://doc.qt.io/qt-5/qsocketnotifier.html) envía la señal [QSocketNotifier](https://doc.qt.io/qt-5/qsocketnotifier.html)::[activated](https://doc.qt.io/qt-5/qsocketnotifier.html#activated)().

```c++
MyDaemon::MyDaemon(QObject *parent) : QObject(parent)
{
    // Crear una la pareja de sockets de UNIX conectados
    if (socketpair(AF_UNIX, SOCK_STREAM, 0, sigTermSd)) {
      qFatal("Couldn't create TERM socketpair");
    }
  
    // Crear el objeto para monitorizar el segundo socket de la pareja.
    sigTermNotifier = new QSocketNotifier(sigTermSd[1],
        QSocketNotifier::Read, this);
  
    // Conectar la señal activated() del objeto QSocketNotifier con el
    // slot handleSigTerm() para manejar la señal. Esta señal será
    // emitida cuando hayan datos para ser leidos en el socket
    // monitorizado.
    connect(sigTermNotifier, SIGNAL(activated(int)), this, SLOT(handleSigTerm()));
}
```

Entonces el manejador de señal POSIX lo único que tiene que hacer cuando es invocado es escribir *algo* en el *socket* que no gestiona [QSocketNotifier](https://doc.qt.io/qt-5/qsocketnotifier.html).

```c++
//
// Manejador de la señal POSIX SIGTERM.
//
void MyDaemon::termSignalHandler(int)
{
    // Con enviar un byte es suficiente.
    char a = 1;
    write(sigTermSd[0], &a, sizeof(a));
}
```

Mientras que en el *slot* al que conectamos la señal [QSocketNotifier](https://doc.qt.io/qt-5/qsocketnotifier.html)::[activated](https://doc.qt.io/qt-5/qsocketnotifier.html#activated)() se lee *lo escrito* desde el manejador anterior en el *socket*, para después pasar a tratar la señal como creamos conveniente. Como este *slot* es invocado desde el bucle de mensajes de la aplicación, podemos hacer cualquier acción que nos venga en gana, al contrario de lo que pasa dentro de un manejador de señal POSIX como `MyDaemon::termSignalHandler()`, donde solo podemos invocar [unas pocas funciones](http://en.wikipedia.org/wiki/Unix_signal#POSIX_signals).

```c++
void MyDaemon::handleSigTerm()
{
    // Desactivar la monitorización del socket para que por el momento
    // no lleguen más señales de Qt
    termNotifier->setEnabled(false);

    // Leer y desechar el byte enviado por MyDaemon::termSignalHandler()
    char tmp;
    read(sigTermSd[1], &tmp, sizeof(tmp));

    // ...tu código aquí...
  
    // Todos las acciones que quieras hacer para responder convenientemente
    // a la señal SIGTERM van aquí.
    // P. ej. invocar qApp->quit() para detener la aplicación.

    // Activar la monitorización para que vuelvan a llegar señales de Qt
    termNotifier->setEnabled(true);
}
```

Por conveniencia, podemos añadir una función para asignar el manejador `MyDaemon::termSignalHandler` a la señal SIGTERM usando la llamada al sistema [sigaction](http://linux.die.net/man/2/sigaction)(). Recordemos que los métodos que se van a utilizar como manejadores se declaran como `static` para que puedan ser pasados como un puntero de función a la llamada al sistema [sigaction](http://linux.die.net/man/2/sigaction)().

```c++
bool setupUnixSignalHandlers()
{
    sigaction term;
  
    // Establecer el puntero al método encargado de manejar la señal
    term.sa_handler = &MyDaemon::termSignalHandler;
  
    // Vaciamos el conjunto de señales en sa_mask para indicar que no queremos
    // bloquear la llegada de ninguna señal POSIX mientras manejamos SIGTERM.
    sigemptyset(&term.sa_mask);
  
    // El flag SA_RESTART indica que si la señal interrumpe alguna llamada al
    // sistema, al volver del manejador la llamada al sistema debe continuar
    // y no terminar con un error. Esto es importante para no afectar al
    // comportamiento esperado por Qt de las llamadas al sistema.
    term.sa_flags = SA_RESTART;
  
    // Establecer manejador de la señal SIGTERM
    if (sigaction(SIGTERM, &term, 0) > 0)
        return false;
  
    return true;
}
```
