Para mí, una de las razones para considerar seriamente a boost en cualquier proyecto en C++ es su implementación de functores. Realmente facilita mucho la creación de funciones "callback" y "delegates" sin causar mucha penalización en el rendimiento de un programa.
¿Porqué facilita el trabajo si los callbacks bien se pueden hacer con tan solo un apuntador a función? La respuesta es fácil si estas trabajando con un enfoque orientado a objetos. Para usar un apuntador a una función miembro, esta necesita ser estática o ser llamada con ayuda de su objeto. Las dos cosas bastante engorrosas.
Veamos como se sortea estos inconvenientes usando boost:
#include <iostream>
#include <boost/function.hpp>
#include <boost/bind.hpp>
using namespace std;
class Callback {
public:
void set_callback_function( boost::function<void()> function ) {
this->callback_function = function;
}
void execute_callback_function() {
if( !callback_function.empty() )
callback_function();
}
private:
boost::function<void ()> callback_function;
};
class MiClase {
public:
void funcion() {
cout <<"¡iujú! ¡Si se llamó!" <<endl;
}
};
int main() {
Callback callback;
MiClase miclase;
callback.set_callback_function( boost::bind( &MiClase::funcion, &miclase ) );
callback.execute_callback_function();
return EXIT_SUCCESS;
}
Probando el código en línea
En la siguiente URL, puedes probar este programa en línea: http://www.ideone.com/hbMXbtFR
La biblioteca TinyXML es muy liviana y permite parsear rapidamente XMLs sin tener que hacer mucho formalismo. En este post presento un código de ejemplo:
El xml que queremos parsear como ejemplo es el siguiente:
<?xml version="1.0">
<ELEMENT attribute="attribute">
<CHILD>child</CHILD>
</ELEMENT>
Lo grabamos con el nombre ejemplo.xml.
A continuación el programa en c++ que realiza el parseo:
#include <iostream>
#include "tinyxml.h"
using namespace std;
int main() {
TiXmlDocument doc;
if ( !doc.LoadFile( "ejemplo.xml" ) ) {
cout <<"Error: Can't load file ejemplo.xml" << endl;
return EXIT_FAILURE;
}
string attribute, child_value;
TiXmlElement *element;
element = doc.FirstChildElement( "ELEMENT" );
if ( element->Attribute( "attribute" ) != NULL )
attribute = string ( element->Attribute( "attribute" ) );
cout <<"attribute: " <<attribute <<endl;
TiXmlElement* child = element->FirstChildElement("CHILD");
if ( child != NULL )
child_value = string( ( ( child->FirstChild() )->ToText() )->Value() );
cout <<"child value: " <<child_value <<endl;
return EXIT_SUCCESS;
}
Lo grabamos como main.cpp y lo compilamos con:
g++ -o main main.cpp -ltinyxml
Boost es una excelente librería con todo lo que un buen programador de C++ pudiera necesitar. Además está desarrollada para hacer la programación sencilla, elegante y rápida. A continuación incluyo un ejemplo simple de una clase que encapsula el manejo de hilos usando Boost y que puede servir de prototipo en diversos proyectos:
#include <iostream>
#include <boost/bind.hpp>
#include <boost/thread/thread.hpp>
class Hilo {
public:
Hilo() : keepgoing( true ), thread( boost::bind( &Hilo::do_work, this ) ) { };
~Hilo() {
keepgoing = false;
// Necesitamos esperar a que el hilo se acabe
thread.join();
}
private:
bool keepgoing;
boost::thread thread;
void do_work() {
// Aquí es donde se lleva a cabo toda la acción.
while( keepgoing ) {
std::cout <<"Hilo" << std::endl;
sleep(1);
}
}
};
int main( void ) {
Hilo *hilo = new Hilo();
sleep(20);
delete hilo;
return EXIT_SUCCESS;
}
El programa lo podemos compilar con:
g++ -O2 -o main main.cpp -lboost_thread-mt
Notas:
- Como se puede ver esta aproximación es más parecida a como le hace C# que Java.
- El hilo comienza al crearse el objeto.
- No hay manera de controlar manualmente el tamaño del stack
- Si no hay recursos suficientes se lanza una excepción y el objeto no se crea.
Una clase "quick and dirty" para crear hilos en cajas windows.
Encabezado:
#pragma once
#include <windows.h>
class CThread {
public:
// Constructor por defecto:
CThread(void);
// Destructor virtual
virtual ~CThread(void);
// Esta función inicia el hilo:
bool start();
// Esta función se debe sobrecargar con lo que tiene que hacer el hilo:
virtual void run() = 0;
// Esta función espera a que el hilo se detenga. Se debe llamar en el destructor de la clase derivada.
void join();
private:
unsigned long id;
void *threadHandle;
static DWORD WINAPI entryPoint( void *pthis );
};
Archivo cpp:
#include "StdAfx.h"
#include "thread.h"
CThread::CThread(void) : threadHandle( NULL ) {
MessageBox( NULL, "CThread::CThread", "Aviso", MB_OK );
}
DWORD WINAPI CThread::entryPoint( void *pthis ) {
CThread *pt = ( CThread * ) pthis;
pt -> run();
return 0;
}
bool CThread::start() {
threadHandle = CreateThread( NULL, 0, ( LPTHREAD_START_ROUTINE ) entryPoint, this, 0, &id );
return true;
}
void CThread::join() {
if ( threadHandle != NULL )
WaitForSingleObject( threadHandle, INFINITE );
}
CThread::~CThread(void) {
MessageBox( NULL, "CThread::~CThread", "Aviso", MB_OK );
if ( threadHandle != NULL )
CloseHandle( threadHandle );
}
Como se dislumbra de los comentarios, para utilizarla es necesario crear una clase que herede de Cthrean y sobrecargar la función run con lo que debe hacer nuestro hilo.
Ya es común para un programador, no solamente el tener que leer archivos del disco duro local, sino también leerlos desde un sitio web; Ya sea un web service, una imagen o un XML. Afortunadamente la libreria libcurl resuelve el problema con muy pocas líneas de código.
En este post, pongo un pequeño ejemplo de como se usaría en C++ para pequeños programas de apoyo:
#include <string>
#include <iostream>
#include "curl/curl.h"
class URLFile {
public:
std::string get( const std::string &URL ) {
// Inicialización:
CURL *curl = curl_easy_init();
// URL a consultar
curl_easy_setopt( curl, CURLOPT_URL, URL.c_str() );
// Configuaración del callback
curl_easy_setopt( curl, CURLOPT_WRITEFUNCTION, &URLFile::write_data);
curl_easy_setopt( curl, CURLOPT_WRITEDATA, &data);
// Obtén el archivo:
CURLcode result = curl_easy_perform(curl);
// Al final hay que limpiar todo:
curl_easy_cleanup(curl);
if (result != CURLE_OK) {
return "";
}
return data;
}
private:
// Variable que usamos para guardar el contenido del archivo:
std::string data;
// Callback que utiliza curl:
static size_t write_data( char *buffer, size_t size, size_t nmemb, void *userp ) {
int len = size * nmemb;
if ( buffer != NULL )
( ( std::string * ) userp )->append( buffer, len );
return len;
}
};
int main() {
URLFile file;
std::string test = file.get( "www.machin.com.mx" );
std::cout <<"Test:" <<std::endl <<test <<std::endl;
return 0;
}