Unit Test

Non ho trovato niente di meglio come ambiente di testing per il C++ di CppUnit. Non che ne sia entusiasta, a dire il vero. Capisco che la reflection di Java sia un aiuto notevole per JUnit e che manchi qualcosa di simile per C++. C'é, é vero, la RTTI, ma si appoggia sulla vtable e quindi non é disponibile per tutte le classi C++, ed é inoltre molto rudimentale se paragonata alle funzionalità offerte dalla reflection di Java.

Però mi pare proprio che le varie soluzioni offerte per lo unit test in C++ siamo più farraginose di quello che potrebbero essere.

Ecco un esempio di testing per la classe Fibonacci, che ho mostrato nel post precedente:

#include <CppUnit/UI/Text/TestRunner.h>
#include <CppUnit/Extensions/HelperMacros.h>
#include "Fibonacci.h"

using namespace std;

class TestFibonacci : public CppUnit::TestFixture {
public:
void fib0() {
Fibonacci fib;
CPPUNIT_ASSERT(fib.get(0) == 0);
}

void fib1() {
Fibonacci fib;
CPPUNIT_ASSERT(fib.get(1) == 1);
}

void fib2() {
Fibonacci fib;
CPPUNIT_ASSERT(fib.get(2) == 1);
}

void fib20() {
Fibonacci fib;
CPPUNIT_ASSERT(fib.get(20) == 6765);
}

void fibX3() {
Fibonacci fib;
CPPUNIT_ASSERT(fib.get(2) == 1);
CPPUNIT_ASSERT(fib.get(20) == 6765);
CPPUNIT_ASSERT(fib.get(3) == 2);
}

CPPUNIT_TEST_SUITE(TestFibonacci);
CPPUNIT_TEST(fib0);
CPPUNIT_TEST(fib1);
CPPUNIT_TEST(fib2);
CPPUNIT_TEST(fib20);
CPPUNIT_TEST(fibX3);
CPPUNIT_TEST_SUITE_END();
};

CPPUNIT_TEST_SUITE_REGISTRATION(TestFibonacci);

void testFibonacci() {
CppUnit::TextUi::TestRunner runner;
CppUnit::TestFactoryRegistry ®istry =
CppUnit::TestFactoryRegistry::getRegistry();
runner.addTest(registry.makeTest());

runner.run("", false);
}

La classe TestFibonacci é una CppUnit::TestFixture che definisce le funzioni che verificano le funzionalità del codice relativo. La macro CPPUNIT_ASSERT si occupa di verificare che il valore risultante passatole sia true.

In coda alla classe una serie di macro creano la suite che raccoglie le funzioni che vogliamo includere nel testing. CPPUNIT_TEST_SUITE inizia il blocco, ricevendo in input il nome della classe, segue una CPPUNIT_TEST per ogni funzione da includere nella suite, e alla fine una CPPUNIT_TEST_SUITE_END completa la definizione della suite.

Dopo la creazione della classe, la macro CPPUNIT_TEST_SUITE_REGISTRATION si fa carico di registrare la suite in un registry apposito.

La funzione di test crea un runner (nel mio caso quello testuale) a cui viene aggiunto il test generato dal registry.

Resta da eseguire il testing usando il metodo run(), e guardare i risultati.

Fibonacci in multithreading

Un programmino di test per verificare l'uso della libreria boost thread.

La classe Fibonacci permette di calcolare il numero di fibonacci ma, per velocizzare le operazioni, quando la classe viene instanziata viene creato un nuovo thread che si prende carico di precalcolare i numeri di fibonacci.

Per evitare i problemi di overflow il numero più alto calcolabile é impostato a 40 (via la costante MAX_INDEX).

L'algoritmo per il calcolo del numero é di default quello di calcolo brutale, ma é possibile attivare l'ottimizzazione che permette di riutilizzare i valori già calcolati, velocizzando notevolmente l'esecuzione.

Qui il codice, a seguire qualche commento:

#include <iostream>
#include <vector>
#include <boost/thread.hpp>
#include <boost/thread/condition.hpp>

using namespace std;

class Fibonacci {
public:
static const int MAX_INDEX = 40;

Fibonacci(bool optim = false);
~Fibonacci();

unsigned int get(unsigned int index);

private:
std::vector<unsigned int> values;
bool optim;

boost::mutex mx;
boost::condition cond;
boost::thread tCalc;

unsigned int getValue(unsigned int index);
void precalc();
};

const int Fibonacci::MAX_INDEX;

unsigned int Fibonacci::getValue(unsigned int index) {
if(optim)
{
boost::mutex::scoped_lock lock(mx);
if(index < values.size())
return values.at(index);
}

switch (index) {
case 0:
return 0;
case 1:
return 1;
default:
boost::this_thread::interruption_point();
return getValue(index - 2) + getValue(index - 1);
}
}

void Fibonacci::precalc() {
for(int iteration = 0; iteration <= MAX_INDEX; ++iteration)
{
unsigned int value = getValue(iteration);
boost::mutex::scoped_lock lock(mx);
values.push_back(value);
cond.notify_one();
}
}

Fibonacci::Fibonacci(bool optim) : optim(optim) {
values.reserve(MAX_INDEX);
tCalc = boost::thread(&Fibonacci::precalc, this);
}

Fibonacci::~Fibonacci() {
tCalc.interrupt();
tCalc.join();
}

unsigned int Fibonacci::get(unsigned int index) {
if(index < 0 || index > MAX_INDEX)
return 0;
boost::mutex::scoped_lock lock(mx);
while(index >= values.size()) {
cout << "Please wait ..." << endl;
cond.wait(mx);
}
return values.at(index);
}


void fib() {
Fibonacci fib;

int input;
while(true) {
cout << "Your input [1 .. " << Fibonacci::MAX_INDEX << "]: ";
cin >> input;
if(input < 1 || input > Fibonacci::MAX_INDEX)
break;
cout << "Fibonacci of " << input << " is " << fib.get(input) << endl;
}
cout << "Bye" << endl;
}

Per usare la classe basta instanziarla, cosa che fa partire il thread che fa partire il precalcolo, e quindi chiamare il metodo get() specificando quale numero di fibonacci si voglia ottenere.

Il costruttore accetta un parametro opzionale che permette di ottimizzare l'algoritmo di calcolo. Nel test uso la versione senza ottimizzazione, in modo da osservare meglio il funzionamento del programma.

Altra cosa che fa il costruttore é istanziare un oggetto boost::thread sulla funzione Fibonacci::precalc() dell'oggetto corrente.

Il thread esegue la funzione precalc() che fa un loop determinato da MAX_INDEX per pre-calcolare i numeri di fibonacci. Delega alla funzione ricorsiva getValue() il calcolo del valore specifico, quindi mette un lock sul mutex che controlla il vettore, fa una push_back e quindi notifica alla condizione sul lock che qualcosa é cambiato nella cache.

La funzione getValue(), se l'ottimizzazione non é abilitata, genera il numero di fibonacci per ricorsione. Altrimenti ritorna il valore già calcolato e memorizzato nella cache - se esiste.

Da notare l'uso di boost::this_thread::interruption_point(); per definire un punto in cui é possibile interrompere l'esecuzione del thread.

Mentre un thread lavora per la generazione dei numeri di fibonacci, l'altro resta in attesa che l'utente immetta quale numero voglia calcolare.

La get() si occupa di andare a leggere nella cache se il numero richiesto sia già stato calcolato. Per far ciò crea un lock sul mutex che controlla l'accesso al vettore, se vede che il numero non é ancora stato generato, segnala all'utente che deve pazientare e si mette in attesa sulla condizione. Abbiamo visto sopra che ogni volta che un nuovo numero di fibonacci viene messo nella cache l'altro thread manda una notifica alla condizione. Ricevuta la notifica controlliamo di nuovo se la condizione é soddisfatta, e così via finché abbiamo il via libera all'accesso alla cache.

Ultima cosa che resta da vedere é il distruttore della classe, che manda un interrupt all'altro thread, e quindi fa una join.

Boost thread_specific_ptr

Nell'articolo di Bill Kempf su Dr.Dobb's che fa da introduzione alla libreria thread di boost si parla anche della possibilità di allocare memoria specifica ad un thread usando il supporto messo a disposizione dalla libreria boost thread.

In termini di prestazioni, usare la "thread local storage" o "thread specific storage", non é considerato in genere una idea molto brillante. Ma ci sono alcuni casi in cui questa risulta essere la soluzione più ragionevole.

La libreria boost thread mette a disposizione uno smart pointer, boost::thread_specific_ptr, che viene inizializzato a NULL della cui distruzione si fa carico la libreria stessa.

L'utilizzo di questo smart pointer viene mostrato nell'esempio a seguire:

#include <iostream>
#include <boost/thread/thread.hpp>
#include <boost/thread/mutex.hpp>
#include <boost/thread/tss.hpp>

using namespace std;

namespace {
boost::thread_specific_ptr<int> ptr;
boost::mutex mio;

class Count
{
private:
int step;

public:
Count(int step) : step(step) { }

void operator()()
{
if (ptr.get() == 0) {
ptr.reset(new int(0));
}

for (int i = 0; i < 10; ++i)
{
*ptr += step;
boost::mutex::scoped_lock lock(mio);
cout << boost::this_thread::get_id() << ": "
<< *ptr << endl;
boost::this_thread::sleep(boost::posix_time::milliseconds(15));
}
}
};
}

void dd05()
{
boost::thread t1(Count(1));
boost::thread t2(Count(-1));
t1.join();
t2.join();
}

Vengono creati due thread, associati ad un functor che viene costruito col parametro passato che viene utilizzato in un loop per modificare un valore locale al thread.

Per prima cosa verifichiamo che l'istanza di boost::thread_specific_ptr sia NULL, e la inizializziamo come richiesto. Poi facciamo un loop che incrementa di passo in passo la nostra variabile locale al thread e la stampa su cout, opportunamente protetto da un lock su mutex. Ho aggiunto uno sleep sul thread in modo da rendere più movimentata la competizione dei thread sul mutex.

Boost mutex e condizioni

Proseguendo la lettura dell'articolo di Bill Kempf su Dr.Dobb's che fa da introduzione alla libreria thread di boost, arriviamo alle variabili di condizione.

A volte non basta un lock su una risorsa condivisa, dato che occorre che la risorsa sia in un particolare stato prima di poterla usare. E questo é proprio lo scopo delle variabili di condizione.

L'idea generale é che il thread blocca un mutex sulla risorsa condivisa e quindi verifica che lo stato sia quello atteso. Se non lo é si mette in attesa sulla condizione. Facendo ciò libera il mutex, permettendo l'accesso alla risorsa da parte di un altro thread. Quando lo stato diventa tale per cui il thread in attesa sulla condizione può tornare ad agire, riceve una notifica che gli permette di ripartire.

Nell'esempio che segue, tratto dall'articolo e leggermente modificato da me, vengono creati due thread, uno per leggere e uno per scrivere su di un buffer condiviso. Per essere sicuri che si inizi a leggere quando non c'é ancora niente da leggere - e quindi avere certamente una situazione in cui é necessario che il lettore venga messo in attesa - ho inserito una pausa nel thread principale tra la creazione del thread in lettura e quello in scrittura.

La funzione utilizzata é boost::this_thread::sleep() che fa "dormire" il thread per un determinato intervallo (o fino a un certo momento). Per specificare quanto tempo debba dormire il thread é possibile usare la funzione boost::posix_time::milliseconds() che converte un intero in millisecondi.

Le due funzioni reader e writer sono simmetriche, leggono e scrivono su di un buffer rappresentato dalla classe Buffer. Notiamo che mettono un lock su un mutex per accedere la risorsa condivisa cout.

La classe Buffer é praticamente un wrapper costruito intorno alla risorsa condivisa, una lista circolare di interi. Il nostro problema é che non possiamo leggere da questo container quando é vuoto, e nemmeno scriverci quando é pieno. Dunque, prima di leggere e scrivere dobbiamo verificare la sua condizione.

Vediamo la funzione put(). Per prima cosa crea uno scoped_lock sul mutex che sorveglia l'accesso al circular_buffer, quindi controlla se é pieno. Se questo é il caso, stampa un messaggio (controllando l'accesso a cout con uno scoped_lock sul mutex di competenza) e si mette in attesa sulla condizione relativa al lock.

Abbiamo dunque un mutex, un lock che usa il mutex per gestire l'accesso alla risorsa e una condizione sul lock che permette al thread di passare il controllo in attesa di tempi migliori.

Quando abbiamo la possibilità, mettiamo il valore passato nel container e notifichiamo alla condizione che ne abbiamo cambiato lo stato.

La get() funziona in modo equivalente: se il container é vuoto si mette in attesa. Appena possibile legge un elemento, lo elimina dal container e notifica alla condizione che lo stato del container é cambiato.

Questo il codice risultante:

#include <iostream>
#include <boost/thread/thread.hpp>
#include <boost/thread/mutex.hpp>
#include <boost/thread/condition.hpp>
#include <boost/circular_buffer.hpp>

using namespace std;

namespace {
boost::mutex mio;

class Buffer
{
private:
static const int BUF_SIZE = 3;

boost::mutex mcb;
boost::condition cond;
boost::circular_buffer<int> cb;

public:
Buffer(int size = BUF_SIZE) : cb(size) {}

void put(int m) {
boost::mutex::scoped_lock lock(mcb);
if(cb.full())
{
{
boost::mutex::scoped_lock lock(mio);
cout << "Buffer is full. Waiting..." << endl;
}
while (cb.full())
cond.wait(lock);
}
cb.push_back(m);
cond.notify_one();
}

int get()
{
boost::mutex::scoped_lock lock(mcb);
if (cb.empty())
{
{
boost::mutex::scoped_lock lock(mio);
cout << "Buffer is empty. Waiting..." << endl;
}
while (cb.empty())
cond.wait(lock);
}

int i = cb.front();
cb.pop_front();
cond.notify_one();
return i;
}
};

const int Buffer::BUF_SIZE;

Buffer buf;

const int ITERS = 20;

void writer()
{
for (int n = 0; n < ITERS; ++n)
{
{
boost::mutex::scoped_lock lock(mio);
cout << "sending: " << n << endl;
}
buf.put(n);
}
}

void reader()
{
for (int x = 0; x < ITERS; ++x)
{
int n = buf.get();
{
boost::mutex::scoped_lock lock(mio);
cout << "received: " << n << endl;
}
}
}
}

Boost thread: functor o funzione

Ancora leggendo l'articolo di Bill Kempf su Dr.Dobb's che fa da introduzione alla libreria thread di boost.

Qui vediamo come passare al costruttore dell'oggetto boost::thread l'indirizzo di una funzione e parametri per la funzione stessa.

L'esempio del post precedente usava un functor nella creazione del thread, ma é possibile ottenere lo stesso effetto con una funzione.

Nelle versioni di qualche anno fa di boost, si otteneva l'effetto con un binding tra puntatore alla funzione e parametro, nelle versioni più recenti basta, ancor più semplicemente, passare i parametri alla funzione dopo il puntatore nel costruttore del thread.

L'esempio qui sotto riporta la "vecchia" implementazione, commentata, e la nuova:


#include <boost/thread/thread.hpp>
#include <boost/thread/mutex.hpp>
#include <boost/bind.hpp>
#include <iostream>

using namespace std;

namespace {
boost::mutex mio;

void count(int multi)
{
for (int i = 0; i < 10; ++i)
{
boost::mutex::scoped_lock lock(mio);
cout << boost::this_thread::get_id() << ": "
<< multi * i << endl;
}
}
}

void dd03()
{
// boost::thread t1(boost::bind(&count, 1));
// boost::thread t2(boost::bind(&count, 2));

boost::thread t1(&count, 1);
boost::thread t2(&count, -1);

t1.join();
t2.join();
}

Boost mutex

Continuo la lettura dell'articolo di Bill Kempf su Dr.Dobb's che fa da introduzione alla libreria thread di boost.

Vediamo l'uso della mutua esclusione, ovvero mutex, al fine di permettere la gestione di risorse condivise tra diversi thread.

Ho leggermente modificato l'esempio proposto nell'articolo mantenendone l'impianto complessivo. Sono creati due thread che eseguono lo stesso functor. In pratica si esegue un ciclo for che stampa sulla consolle l'identificatore del thread a un valore funzione della variabile di ciclo e del parametro passato al functor.

La parte interessante é che i due thread operano su una risorsa condivisa, lo stream di output verso la consolle, che deve essere quindi regolamentata nel suo accesso, per evitare che le stringhe mandate a video si sovrappongano.

Per far ciò usiamo un mutex che fa da semaforo d'accesso alla regione critica, bloccando l'accesso per mezzo di un lock, per la precisione un boost::mutex::scoped_lock, ovvero un lock che termina automaticamente quando la variabile che lo determina esce di scopo. Così abbiamo la certezza che anche in caso di eccezioni il lock sul mutex venga tolto.

#include <boost/thread/thread.hpp>
#include <boost/thread/mutex.hpp>
#include <iostream>

using namespace std;

namespace {
boost::mutex mio;

class Count
{
int multi;
public:
Count(int multi) : multi(multi) { }

void operator()()
{
for (int i = 0; i < 10; ++i)
{
boost::mutex::scoped_lock lock(mio);
cout << "Thread " << boost::this_thread::get_id()
<< ", result: " << i*multi << endl;
}
}
};
}

void dd02()
{
boost::thread t1(Count(1));
boost::thread t2(Count(-1));
t1.join();
t2.join();
}

Creare un thread con boost

Un buon modo per avere un'idea della libreria thread di boost é leggersi questo articolo di Bill Kempf su Dr.Dobb's.

E' un po' vecchiotto, maggio 2002, ma mi pare ancora utile. Per gli aggiornamenti si fa riferimento ad un articolo del 2008 firmato da Anthony Williams.

Per creare un nuovo thread con boost creiamo un oggetto boost::thread passando la funzione o il functor su cui insiste il nuovo thread.

Il primo esempio presentato, che ho leggermente modificato rispetto a quanto presentato nell'articolo, ci fa vedere come creare un thread che esegue il codice di una funzione:

#include <boost/thread/thread.hpp>
#include <iostream>

using namespace std;

namespace {
void hello()
{
cout << "This is the thread " << boost::this_thread::get_id() << endl;
}
}

void dd01()
{
cout << "We are currently in the thread " << boost::this_thread::get_id() << endl;
boost::thread t(&hello);
t.join();
cout << "Back to the thread " << boost::this_thread::get_id() << endl;
}

Basta passare al costruttore dell'oggetto boost::thread l'indirizzo della funzione. La funzione join(), poi, ci permette di terminare il thread.

Ho usato la funzione boost::this_thread::get_id(), di cui non si parla nell'articolo perché a quei tempi ancora non esisteva, per mostrare l'id dei due thread.

Boost bind

Da Beyond the C++ Standard Library: An Introduction to Boost, di Björn Karlsson.

bind

La STL mette a disposizione bind1st e bind2nd per permettere il binding a functor già esistenti. Boost estende questo concetto con bind.

Per prima cosa, mi rivedo un uso di bind1st:

#include <iostream>
#include <functional>
#include <algorithm>
using namespace std;

int main () {
int numbers[] = {10,20,30,40,50,10};
int cx = count_if (numbers, numbers+6, bind1st(equal_to<int>(), 10) );
cout << "There are " << cx << " elements that are equal to 10.\n";
}

Chiamo la count_if passando come predicato il functor equal_to che viene adattato dalla bind1st in modo da usare, oltre al parametro passato dalla count_if, ovvero l'elemento corrente nella scansione della sequenza, un valore fisso - 10 nel nostro caso.

Riscrivere questo specifico esempio con boost:bind non porta un gran vantaggio, anzi, a ben vedere, sembra piuttosto una soluzione peggiore, dato che occorre specificare il segnaposto (_1) utilizzato.

Ma boost:bind é più potente di bind1st, e lo vediamo quando vogliamo usare una funzione e non un functor (come é obbligatorio fare per bind1st).

Vero é che spesso é possibile adattare una funzione alle specifiche di bind1st ma questo comporta un appesentimento del codice, e non sempre é possibile farlo.

Altro punto a favore di boost::bind é la possibilità di innestare diverse chiamate, come nell'ultimo esempio d'uso che riporto.

#include <iostream>
#include <functional>
#include <algorithm>
#include <vector>
#include <boost/bimap.hpp>

using namespace std;

class A {
int value_;
public:

A(int value) : value_(value) {
}

void print() {
cout << "Value is: " << value_ << endl;
}
};

void doSomething(int a, int b) {
if (a == b) {
cout << "done" << endl;
}
}

int main() {
int numbers[] = {10, 20, 30, 40, 50, 10};

int cx = count_if(numbers, numbers + 6, bind1st(equal_to<int>(), 10));
cout << cx << endl;

cx = count_if(numbers, numbers + 6, boost::bind(equal_to<int>(), _1, 10));
cout << cx << endl;

for_each(numbers, numbers + 6, boost::bind(&doSomething, _1, 10));

vector<A> v;
v.push_back(10);
v.push_back(20);
v.push_back(30);
v.push_back(40);

for (vector<A>::iterator it = v.begin(); it != v.end(); ++it) {
it->print();
}

cout << "for_each:" << endl;
for_each(v.begin(), v.end(), mem_fun_ref(&A::print));

cout << "for_each w/ boost::bind:" << endl;
for_each(v.begin(), v.end(), boost::bind(&A::print, _1));

cout << "count w/ nested boost::bind:" << endl;
int count = count_if(numbers, numbers + 6,
boost::bind(logical_and<bool>(),
boost::bind(greater<int>(), _1, 15),
boost::bind(less_equal<int>(), _1, 40)));
cout << count << endl;
}

Boost variant

Da Beyond the C++ Standard Library: An Introduction to Boost, di Björn Karlsson.

variant

Con boost::variant si ha una miglior gestione di un tipo generico rispetto a quanto offerto da boost::any.

In particolare, all'atto della creazione di un oggetto possiamo specificare quali tipi sono accettabili in quel specifico caso.

Per estrarre l'oggetto dal variant possiamo usare la funzione get<>(), che funziona come any_cast<>(). Il suo svantaggio é che tende a rendere il programma ingestibile.

Quando possibile é opportuno usare un functor che specializzi la classe static_visitor. Questa deve avere l'implementazione dell'operator() per ogni tipo gestito dal variant che lo usa. Se ne manca uno, si ha un errore in compilazione.

Un esempio dovrebbe chiarire quanto scritto sopra:

#include <iostream>
#include <boost/variant.hpp>

using namespace std;

class Printer : public boost::static_visitor<void> {
public:
void operator()(int& i) const {
cout << "It's an int: " << i << endl;
}

void operator()(string& s) const {
cout << "It's a string: " << s << endl;
}

void operator()(double& d) const {
cout << "It's a double: " << d << endl;
}
};

int main() {
boost::variant<int,string,double> var("Hello");

string hello = boost::get<string>(var);

cout << "Var contains: " + hello << endl;

try {
int wrong = boost::get<int>(var);
cout << "Unexpected: " << wrong << endl;
}
catch(const boost::bad_get& exc) {
cout << "There's not an int in: " << exc.what() << endl;
}

int* pWrong = boost::get<int>(&var);
if(pWrong == NULL) {
cout << "There's not an int in var." << endl;
}

Printer p;
boost::apply_visitor(p, var);
}

Boost any

Da Beyond the C++ Standard Library: An Introduction to Boost, di Björn Karlsson.

any

La classe boost::any permette di trattare allo stesso modo oggetti di tipo diverso, permettendo ad esempio di usare un container STL per una collezione di oggetti eterogenei.

Nell'esempio che segue, metttiamo oggetti di svariati tipi in un container di boost::any. Dato che il costruttore di any non é esplicito, ci possiamo anche risparmiare la fatica di fare un cast che viene fatto, per l'appunto, implicitamente.

La funzione anyPrint() ci mostra come estrarre l'oggetto da any usando any_cast<> sull'indirizzo dell'oggetto any. Se l'oggetto non é del tipo atteso, any_cast<> ritorna NULL. Alternativamente, se il fallimento del casting fosse considerabile un errore, potremmo tentare di applicare any_cast<> sull'oggetto. In questo caso il fallimento porterebbe ad una eccezione - come si vede nel caso della gestione del tipo string nella stessa funzione.

#include <iostream>
#include <string>
#include <utility>
#include <vector>
#include "boost/any.hpp"

using namespace std;

class A {
public:

void some_function() {
cout << "A::some_function()" << endl;
}
};

class B {
public:

void some_function() {
cout << "B::some_function()" << endl;
}
};

class C {
public:

void some_function() {
cout << "C::some_function()" << endl;
}
};

typedef pair<bool, double> BoolDouble;

void anyPrint(boost::any& a) {
if (A* pA = boost::any_cast<A> (&a)) {
pA->some_function();
} else if (B* pB = boost::any_cast<B> (&a)) {
pB->some_function();
} else if (C* pC = boost::any_cast<C> (&a)) {
pC->some_function();
} else if (int* pInt = boost::any_cast<int> (&a)) {
cout << *pInt << endl;
} else if (BoolDouble* pPair = boost::any_cast<BoolDouble> (&a)) {
if (pPair->first)
cout << pPair->second << endl;
} else {
try {
cout << boost::any_cast<string> (a) << '\n';
} catch (boost::bad_any_cast&) {
cout << "Oops!\n";
}
}
}

int main() {
cout << "Example of using any.\n\n";

vector<boost::any> store_anything;

store_anything.push_back(A());
store_anything.push_back(B());
store_anything.push_back(C());

// While we're at it, let's add a few other things as well
store_anything.push_back(string("This is fantastic! "));
store_anything.push_back(3);
store_anything.push_back(make_pair(true, 7.92));
store_anything.push_back(3.14);

for_each(store_anything.begin(), store_anything.end(), anyPrint);
}

Boost regex

Per poter usare le esperessioni regolari di boost occorre per prima cosa generare la libreria regex.

Io uso cygwin in coppia con netbeans, e questi sono i passaggi che ho dovuto seguire per poter compilare ed eseguire un primo programma di prova in questo contesto.

Dalla directory /libs/regex/build ho richiamato la make con questa sintassi:

make -fgcc.mak

Il cui risultato finale é stato quello di generare due librerie (una "normale" e una di debug):

gcc/libboost_regex-gcc-1_40.a
gcc/libboost_regex-gcc-d-1_40.a

In Netbeans ho creato un progetto, nelle cui proprietà ho aggiunto nella sezione Build-Linker-Libraries la libreria regex ("normale") appena creata.

Ho aggiunto al progetto un file di esempio:

#include <boost/regex.hpp>
#include <iostream>

using namespace std;

int main(int argc, char** argv) {
boost::regex reg("(A.*)");

if(boost::regex_match("Doesn't match", reg))
cout << "unexpected!";
if(boost::regex_match("A good match", reg))
cout << "matching" << endl;
}

Ho creato la regex "(A.*)", e ho verificato che solo la seconda stringa le corrisponde.

Boost weak_ptr

Da Beyond the C++ Standard Library: An Introduction to Boost, di Björn Karlsson.

weak_ptr

Un weak_ptr é un osservatore di un shared_ptr. Quando lo shared_ptr rilascia la risorsa associata, mette a null il puntatore associato a weak_ptr. In questo modo weak_ptr non assume la proprietà della risorsa, e non rischia di detenere un puntatore che potrebbe rimanere disassociato alla risorsa stessa.

Il metodo lock() di waek_ptr permette di ottenere un shared_ptr da un lock_ptr, gestendo il reference count come atteso.

Ecco un esempio per weak_ptr:

#include <iostream>
#include <boost/shared_ptr.hpp>
#include <boost/weak_ptr.hpp>

using namespace std;
using namespace boost;

int main() {

boost::weak_ptr<int> w;
if(w.expired())
cout << "1: weak_ptr initialized as expired" << endl;

{
shared_ptr<int> p(new int);
if(p.use_count()==1)
cout << "2: reference count for shared_ptr set to 1" << endl;
w=p;
if(p.use_count()==1)
cout << "3: weak_ptr observe without changing ref count" << endl;

shared_ptr<int> p2(w);
if(p.use_count()==2)
cout << "4: creating a shared_ptr from a weak_ptr" << endl;
}
if(w.expired())
cout << "5: weak_ptr expires" << endl;

shared_ptr<int> p3=w.lock();
if(p3 == false)
cout << "6: shared_ptr from expired weak_ptr is not initialized" << endl;
}