Monitorizar E/S

Una característica elegante de Glib (una de las bibliotecas subyacentes de gtkmm) es la capacidad de verificar los datos en un descriptor de archivos. Esto es especialmente útil para aplicaciones de red. Se usa el siguiente método para lograrlo:

sigc::connection Glib::SignalInput::connect(const sigc::slot<bool,Glib::IOCondition>& slot,
                                    int fd, Glib::IOCondition condition,
                                    int priority = Glib::PRIORITY_DEFAULT);

El primer argumento es un «slot» al que desea que se llame cuando ocurra el evento especificado (vea el tercer argumento) en el descriptor de archivo que especifica usando el segundo argumento. El tercer argumento puede ser uno o más (usando |) de:

  • Glib::IO_IN: llame a su método cuando haya datos listos para leerse en su descriptor de archivos.
  • Glib::IO_OUT: llame a su método cuando el descriptor de archivos esté listo para la escritura.
  • Glib::IO_PRI: llame a su método cuando el descriptor de archivos tenga datos urgentes para leer.
  • Glib::IO_ERR: llame a su método cuando haya ocurrido un error en el descriptor de archivos.
  • Glib::IO_HUP: llame a su método cuando se cuelgue (la conexión se ha roto, normalmente por tuberías y «sockets»).

El valor de retorno es una sigc::connection que puede usarse para detener la monitorización del descriptor de archivos usando su método disconnect(). El manejador de señales slot debe declararse de la siguiente manera:

bool input_callback(Glib::IOCondition condition);

donde condition se especifica como se mencionó previamente. Como de costumbre, el «slot» se crea con sigc::mem_fun() (para un método miembro de un objeto), o sigc::ptr_fun() (para una función).

A continuación se muestra un pequeño ejemplo. Para usar el ejemplo, sólo ejecútelo desde un terminal; no crea una ventana. Creará una tubería llamada testfifo en la carpeta actual. Después inicie otro intérprete de comandos y ejecute echo "Hello" > testfifo. El ejemplo imprimirá cada línea que introduzca hasta que ejecute echo "Q" > testfifo.

Código fuente

File: main.cc (For use with gtkmm 3, not gtkmm 2)

#include <build/config.h>
#include <gtkmm/application.h>
#include <glibmm/main.h>
#include <glibmm/iochannel.h>
#include <fcntl.h>
#include <iostream>

#include <unistd.h> //The SUN Forte compiler puts F_OK here.

//The SUN Forte compiler needs these for mkfifo:
#include <sys/types.h>
#include <sys/stat.h>

Glib::RefPtr<Gtk::Application> app;

int read_fd;
Glib::RefPtr<Glib::IOChannel> iochannel;

/*
  send to the fifo with:
  echo "Hello" > testfifo

  quit the program with:
  echo "Q" > testfifo
*/

// this will be our signal handler for read operations
// it will print out the message sent to the fifo
// and quit the program if the message was 'Q'.
bool MyCallback(Glib::IOCondition io_condition)
{

  if ((io_condition & Glib::IO_IN) == 0) {
    std::cerr << "Invalid fifo response" << std::endl;
  }
  else {
   Glib::ustring buf;

   iochannel->read_line(buf);
   std::cout << buf;
   if (buf == "Q\n")
     app->quit();

  }
  return true;
}


int main(int argc, char *argv[])
{
  app = Gtk::Application::create(argc, argv, "org.gtkmm.example");

  if (access("testfifo", F_OK) == -1) {
    // fifo doesn't exit - create it
    #ifdef HAVE_MKFIFO
    if (mkfifo("testfifo", 0666) != 0) {
      std::cerr << "error creating fifo" << std::endl;
      return -1;
    }
    #else
      std::cerr << "error creating fifo: This platform does not have mkfifo()"
          << std::endl;
    #endif //HAVE_MKFIFO
  }

  read_fd = open("testfifo", O_RDONLY);
  if (read_fd == -1)
  {
    std::cerr << "error opening fifo" << std::endl;
    return -1;
  }

  // connect the signal handler
  Glib::signal_io().connect(sigc::ptr_fun(MyCallback), read_fd, Glib::IO_IN);

  // Creates a iochannel from the file descriptor
  iochannel = Glib::IOChannel::create_from_fd(read_fd);

  // and last but not least - run the application main loop
  app->run();

  // now remove the temporary fifo
  if(unlink("testfifo"))
    std::cerr << "error removing fifo" << std::endl;

  return 0;
}