Visto che proprio questo fine settimana l’ho un po’ “ristrutturato”, ne approfitto per presentarvi il mio server:

Zerogoki
Zerogoki

Si tratta di un vecchio notebook, con cpu Intel Core 2 Duo a 2,5 Ghz, con 4 GB di RAM.
E’ solo un server domestico quindi, ma con un bel po’ di servizi sopra: il blog/sito wordpress che state vedendo adesso, SkyPlanner, un media center web sviluppato da me, e una serie di servizi interni (ssh, database, file server).

La connessione ad internet è su fibra ottica, 100 Mbit in download, 10 in upload.

Il recente “restyling” è consistito nel togliere completamente la scheda madre dall’involucro “stipato” tipico dei portatili, in modo da far respirare un po’ di più le componenti (la temperatura è drasticamente calata, difatti). Ho anche tolto alcune componenti inutili, come il lettore dvd o la scheda di rete wireless e il bluetooth, in modo da ridurre anche i consumi.

La batteria originale del notebook è invece ancora lì, molto utile durante eventuali cali o interruzioni di tensione, per mantenere il server up and running.

template<typename T>
class Fill {
private:
  T *array;
  long _size;
  T _value;
public:
  Fill(T *a) : array(a) {}
  Fill &size(long s) { _size = s; return *this; }
  Fill &with(T value) { _value = value; return *this; }
  ~Fill() {
    for(long i=0; i<_size; i++) array[i] = _value;
  }
};

Utilizzo snippet:

int array[10];
Fill<int>(array).size(10).with(1);

Ecco un po’ di teoria di cosa succede.

RAII è una tecnica che permette di sfruttare una caratteristica del c++ che lo differenzia dai linguaggi con garbage collector (Java, ad esempio): la certezza di quando il distruttore della classe verrà chiamato.

L’idea è di sfruttare entrata ed uscita dallo scope di una variabile per effettuare acquisizione e deallocazione delle risorse. O per dirla in altri termini, per eseguire istruzioni all’ingresso e all’uscita di uno scope.

In questo caso stiamo creando una istanza anonima della classe Filler.

Alla sua inizializzazione passiamo al costruttore

Fill<int>(T *a) : array(a) {}

un array, che vogliamo riempire. Il costruttore lo memorizzerà nel suo field “array”.

Con il metodo “size” diciamo quanti elementi dell’array vogliamo riempire, mentre col metodo “with” impostiamo il valore con cui riempire l’array.

Entrambi questi metodi tornano un riferimento a “this”, ossia all’istanza corrente, in modo da “tenerla viva” nello scope, e permettendo di effettuare method chaining.

Infine, quando la variabile scompare dallo scope (ossia subito, visto che è anonima), viene chiamato il distruttore, che contiene il ciclo for che riempie l’array col valore che abbiamo impostato.

E’ interessante notare come, non essendoci nessun metodo che esplicitamente riempie l’array, l’ordine delle chiamate è perfettamente invertibile: avrei infatti potuto ugualmente scrivere

Fill<int>(array).with(1).size(10);

E funzionerebbe nello stesso identico modo, dato che il riempimento vero e proprio verrà comunque effettuato nel distruttore.

Si tratta ovviamente di un esempio relativamente banale, ma che fa intuire la potenza della tecnica.

Basti pensare ad altre applicazioni, come l’apertura di un file con chiusura automatica quando la variabile RAII esce dallo scope, o una transazione che inizia nel costruttore, e viene automaticamente committata nel distruttore, o addirittura, nel c++11, l’esecuzione di una lambda quando la variabile RAII esce dallo scope.

class Scope {
  public:
    Scope(std::function<void()> onExit) : _onExit(onExit) {}
    ~Scope() { _onExit(); }
  private:
    std::function<void()> _onExit;
};

RAII viene molto usato sopratutto per gestire al meglio le eccezioni: non è infatti necessario un blocco finally come in Java, dato che sia in caso di eccezione che nel flusso normale la variabile viene comunque deallocata, e il distruttore invocato.