L’ultimo utente

Preambolo

Inauguro questa piccola “Galleria degli orrori”, con alcune chicche trovate qui e lì, vagando per il codice altrui.

Ovviamente non vuole essere un modo per mostrarsi superiori, lo considero più un monito per me stesso che uno scherno per altri.

Recuperare l’ultimo id inserito


$user = Userlib::createnew_user('0', $_POST["str_Username"], $_POST["str_Password"],
  #interminabile lista di parametri, ovviamente non filtrati);

$recupero_id = Userlib::list_user(" 1=1 ORDER BY id_user DESC LIMIT 1 "); # Mai sentito parlare di operazioni concorrenti?

Assert in C

Ultimamente sto programmando un po’ di più in C per un progetto universitario di cui parlerò in futuro.
Una funzionalità che sto sfruttando per assicurarmi che siano sempre verificate le precondizioni all’inizio di una funzione sono le assert (o, se preferite, le asserzioni).

Che cosa sono

Le asserzioni sono dei semplici predicati che indicano una condizione che deve essere verificata in un certo punto del programma. Quindi qualsiasi test che ritorna un valore booleano può essere utilizzato come argomento per una asserzione.

Esempio

Ad esempio, se volessi che una funzione accetti solo numeri positivi potrei scrivere:

/* @file assert.c */
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>

int func(int x)
{
    assert(x > 0);
    return x;
}

int main(void)
{
    int i = 0;
    printf("Numero: %d\n", func(i));
    return 0;
}

Eseguendo questo programma otterrei:

[justb@dellill C]$ gcc -Wall assert.c -o assert.out && ./assert.out
assert.out: assert.c:8: func: Assertion `x > 0' failed.
Annullato

L’esecuzione è stata annullata, ottenendo informazioni utili su cosa è andato storto, ma soprattutto dove è andato storto.

In produzione

Chiaramente la bruta interruzione di un programma non è sempre un comportamento accettabile. Secondo me l’uso delle asserzioni è molto utile durante lo sviluppo di un’applicazione, ma non può sostituire una robusta opera di error checking.
Inoltre, per applicazioni di una certa dimensione, l’aggiunta di molte asserzioni può causare un calo delle prestazioni, dovuto ai molti controlli aggiuntivi.

Così, per ottenere il meglio dei due approcci, è possibile indicare al preprocessore di ignorare le asserzioni nella build di produzione, passando il flag NDEBUG:

[justb@dellill C]$ gcc -Wall -DNDEBUG assert.c -o assert.out && ./assert.out
Numero: 0

In questo modo non vi sarà nessun overhead nel programma finale dovuto alle varie assert.

Sviluppare nell’ambiente giusto

E non sto parlando di pulizia della vostra camera o del vostro ufficio.

Oggi mi è capitata una cosa che non auguro a nessuno sviluppatore. La colpa è stata sicuramente mia, in quanto potevo stare più attento, ma almeno è una storia a lieto fine.

Primo lavoro, prima consegna, primo incontro per verificare il vostro operato.
Siete stati svegli la notte, avete studiato tutto il codice ereditato nel minimo dettaglio, avete sistemato tutti i warning in cui vi siete imbattuti, refactoring, test, aggiunto commenti. Insomma volevate dimostrare di aver lavorato sodo e bene.

Arrivate in orario, accendete il pc per mostrare il lavoro.
“No, proviamolo direttamente sul server..hai una penna usb?”
L’avete. Caricate i file sulla pennina.
“Hai cambiato il database?”
“Sì, ho aggiunto dei campi necessari. Ecco il dump.”

Brillanti! Impeccabili!

Carica l’applicativo. “Bene proviamo a inserire un….” BAAAAM. Vi si presenta una terribile schermata bianca con questa scritta:

syntax error, unexpected T_OBJECT_OPERATOR in File.php on line 68

Panico. Alarm. Alarm. Non può essere. Ovviamente sulla vostra macchina funziona tutto, tuttissimo.
Alla riga 68 va tutto bene, c’è una semplicissima query al database:

$var = $DB->Execute($sqlQuery)->FetchRow();

Cosa può esserci di sbagliato in questa riga? Sono tutte funzioni di libreria, non avete toccato niente. Un semplicissimo method chaining. Semplice? Davvero?

Come potevate mai pensare che il semplicissimo method chaining non era sempre stato tra le caratteristiche fornite dal vostro linguaggio di programmazione? E che, guardacaso, la versione installata sul server era precedente a quella installata sulla macchina su cui avete sviluppato. Quindi, errore! Ve la siete giocata male.

Fortunatamente, sul server era installata anche la versione più recente, quindi è bastato cambiare ambiente per far funzionare il tutto. Ma poteva non essere il caso. E vi sareste trovati a dover spezzare tutte le righe di codice in cui avevate utilizzato il method chaining:

$result = $DB->Execute($sqlQuery);
$var = $result->FetchRow();

La morale della storia quindi è: assicuratevi sempre di sviluppare nell’ambiente giusto. Altrimenti i risultati possono essere imprevedibili. E, visto che ci siete, aprite un po’ la finestra della stanza!

Visualizzare select di tabelle con molti campi in MySQL

Se capita di dover effettuare una SELECT su una tabella da linea di comando, i risultati verranno visualizzati in una tabella in formato ASCII.
Nel caso in cui la tabella contiene molti campi, questa tabella risulterà non formattata correttamente, con righe spezzate su più livelli.
Per visualizzare un record alla volta si può utilizzare il flag \G dopo il comando.

Ad esempio:

mysql> select * from prova;
+----+---------+--------+------+
| id | testo   | numero | car  |
+----+---------+--------+------+
|  1 | Uno     |      0 | r    |
|  2 | Due     |      0 | r    |
|  3 | Tre     |      0 | r    |
|  4 | Quattro |      0 | r    |
|  5 | Cinque  |      0 | r    |
|  6 | Sei     |      0 | r    |
|  7 | Sette   |      0 | r    |
|  8 | Otto    |      0 | r    |
|  9 | Nove    |      0 | r    |
+----+---------+--------+------+
9 rows in set (0.00 sec)

mysql> select * from prova\G
*************************** 1. row ***************************
    id: 1
 testo: Uno
numero: 0
   car: r
*************************** 2. row ***************************
    id: 2
 testo: Due
numero: 0
   car: r
*************************** 3. row ***************************
    id: 3
 testo: Tre
numero: 0
   car: r
*************************** 4. row ***************************
    id: 4
 testo: Quattro
numero: 0
   car: r
*************************** 5. row ***************************
    id: 5
 testo: Cinque
numero: 0
   car: r
*************************** 6. row ***************************
    id: 6
 testo: Sei
numero: 0
   car: r
*************************** 7. row ***************************
    id: 7
 testo: Sette
numero: 0
   car: r
*************************** 8. row ***************************
    id: 8
 testo: Otto
numero: 0
   car: r
*************************** 9. row ***************************
    id: 9
 testo: Nove
numero: 0
   car: r
9 rows in set (0.00 sec)

Update con Join in MySQL

Mi sono trovato a dover aggiornare un attributo di una relazione, avendo come input l’identificativo di un’altra relazione, collegata alla prima tramite vincolo di chiave esterna.
In MySQL:

UPDATE tabella1 t1 JOIN tabella2 t2
ON t1.id=t2.attributoConFK SET t1.campo=t1.campo+1;

Processare un URL con PHP

NOTA

Come al solito l’ignoranza (la mia) aguzza l’ingegno: in ogni caso penso sia meglio utilizzare la funzione di libreria parse_url

Oggi facevo un po’ di prove con il PHP sdk di Facebook. Mi sono ritrovato a dover recuperare le varie parti della query string di un URL per poterle poi utilizzare nel codice. In pratica mi ritrovavo con un URL tipo:


https://graph.facebook.com/me/friends?access_token=AAACrnu&limit=20&method=GET&offset=20&__after_id=719091958

e avevo bisogno di un array di questo tipo:

array(5) {
  ["access_token"]=>
  string(7) "AAACrnu"
  ["limit"]=>
  string(2) "20"
  ["method"]=>
  string(3) "GET"
  ["offset"]=>
  string(2) "20"
  ["__after_id"]=>
  string(9) "719091958"
}

Ho scritto così questa semplice funzione:

/**
 * Returns an array with url query parts
 * @author Giustino Borzacchiello
 *
 * From this:
 * http://domain.com?a=foo&b=bar&c=ciccio
 * to this:
 * array(3) {
 *   ["a"]=>
 *   string(3) "foo"
 *   ["b"]=>
 *   string(3) "bar"
 *   ["c"]=>
 *   string(6) "ciccio"
 * }
 *
 * @param $url Url from which get the query parts
 *
 * */
function getUrlQueryParts($url)
{
    # Recupera la query string dell'url
    $query = parse_url($url, PHP_URL_QUERY);
    # Suddividi la stringa in corrispondenza delle &
    $queryPieces = explode('&', $query);
    # Prepara l'array da restituire
    $result = array();
    foreach($queryPieces as $query)
    {
        # Separa ogni elemento in corrispondenza del simbolo =
        # La funzione explode restituisce un array di due elementi
        $query = explode('=', $query);
        # Recupera chiave e valore
        $key = $query[0];
        $val = $query[1];
        # Salva chiave e valore nell'array risultato
        $result[$key] = urldecode($val);

    }
    return $result;
}

Spero che sia utile! :)

Appunti su Yii Framework/1

Ogni tanto mi piace cominciare a studiare qualcosa di nuovo. Tempo fa decisi di studiare Yii Framework ma una serie di eventi mi impedì di cominciare. Oggi ho deciso di riprendere.

Questi sono una serie di appunti sconclusionati che ho preso in questa prima giornata di studio. Sicuramente ci sono altre cose che ho imparato e che sono troppo pigro per scrivere, ma cercherò di tenere una lista abbastanza aggiornata.

Controllare i requisiti per Yii

Una volta scaricato l’archivio, basta visitare http://host/path/di/Yii/requirements/index.php per avere una panoramica dettagliata sul supporto a Yii da parte del nostro server.

Creare un’applicazione

Per creare un’applicazione è possibile utilizzare il comando yiic:

[justb@dellill YiiApps]$ yiic webapp demoApp
Create a Web application under '/home/justb/public_html/YiiApps/demoApp'? [Yes|No] y

Creare un link ad una pagina

Per creare un link ad una pagina, invece di scriverlo staticamente, è possibile sfruttare Yii CHtml:

<?php echo CHtml::link("TestoLink", array('controller/azione')); ?>

Importare una classe

Yii fornisce una valida alternativa ai metodi include/require: Yii:import:

Yii::import('application.controllers.MioNomeController');

Yii::import è più efficiente degli altri metodi, in quanto non include il file finché non ne esiste un riferimento. Come input accetta una stringa che rappresenta il path della classe da includere (application è mappato sulla cartella protected)

Test

I test unitari vanno collocati nella directory demoApp/protected/tests/unit/. Devono essere delle classi che estendono CTestCase:

<?php
class ProvaTest extends CTestCase
{
  ...
}

Se la classe da testare effettua operazioni con il database, allora estende la classe CDbTestCase:

class DbRelatedTest extends CDbTestCase
{
  ...
}

Accedere al database

La connessione al database è specificata come ‘component’ di Yii: in questo modo è possibile accedere a tale risorsa da qualsiasi classe tramite Yii::app()->db

Yii framework logo

Diario Progetto LASD – 07/11/11

Oggi ho ripreso a lavorare al progetto per il corso di Laboratorio di Algoritmi e strutture dati.

Tra le cose che ho fatto:

  • Aggiunta possibilità di estrazione dalla testa della lista, nella libreria lista
  • Creata l’interfaccia per la gestione di un insieme di vertici
  • Iniziata la prima bozza per la versione dell’insieme di vertici basato su array

Per quanto riguarda l’ultimo punto ho creato una struct così formata:

struct jvset_tag
{
    J_VERTEX **Vertices;   /**< Array contenente i vertici */
    int NumActiveVertices; /**< Numero di vertici inseriti nell'insieme */
    int NextFreeIndex;     /**< Indice della prossima locazione libera */
    int Size;              /**< Numero totale di vertici */
    J_LIST *FreeList;      /**< Lista delle locazioni libere */
};

Ho scritto anche le funzioni per la gestione dell'inizializzazione dell'insieme e per la relativa deallocazione.

Il prossimo passo è scrivere le funzioni di aggiunta e rimozione dei vertici.

Sto pensando però che forse dovrei tenere traccia anche delle locazioni dell'array occupate, e non solo di quelle libere, per permettere di effettuare la visita solo sui vertici effettivamente inseriti e non su tutta la dimensione dell'array.

Joel Spolsky on hiring

Quote

Two of the biggest challenges in technical hiring are identifying people who are smart but don’t get things done and people who get things done but aren’t smart. A company in a competitive industry needs to avoid hiring both classes of people.

“People who are smart but don’t get things done often have PhDs and work in big companies where nobody listens to them because they are completely impractical,” explains Spolsky.

“People who get things done but are not smart will do stupid things, seemingly without thinking about them, and somebody else will have to come clean up their mess later.”

Tratto da: How would you move mount Fuji? di William Poundstone.

Error handling in C: una panoramica personale

Il C non fornisce un meccanismo standard per la gestione degli errori interno al linguaggio (come le eccezioni) quindi è compito del programmatore decidere come procedere.

Le prime volte che ho programmato in C, pensavo di risolvere la questione facilmente, semplicemente emulando le funzioni della libreria standard. Purtroppo mi sono dovuto scontrare con la dura realtà: le interfacce della libreria standard sono parecchio discordanti su come segnalare una situazione di errore.

Ad esempio atoi restituisce 0 se non riesce a convertire la stringa passata in ingresso, il che è strano, perché 0 è un elemento del dominio della funzione. Infatti il programma:

//Esempio di output di atoi
//

#include <stdio.h>
#include <stdlib.h>

int main(void)
{
    const char uno[] = "uno";
    const char due[] = "2";
    const char zero[] = "0";

    int num1 = atoi(uno);
    int num2 = atoi(due);
    int num0 = atoi(zero);

    printf("atoi(\"uno\") vale %d\n", num1);
    printf("atoi(\"2\") vale %d\n", num2);
    printf("atoi(\"0\") vale %d\n", num0);

    return 0;
}

produrrà il seguente output:

[justb@dellill]$ ./atoi.out 

atoi("uno") vale 0
atoi("2") vale 2
atoi("0") vale 0

Oppure le funzioni della famiglia *alloc, che restituiscono NULL in caso di errore ed un puntatore all’area di memoria allocata in caso di successo, oppure ancora printf che restituisce il numero di caratteri stampati in caso di successo ed un numero negativo in caso contrario.
Insomma sembra che ogni funzione voglia dire la sua sull’argomento.

Tabula rasa: una libreria da zero

Sviluppare una libreria da zero porta quindi alcuni vantaggi.
Uno di questi è la possibilità di creare delle interfacce coerenti che consentano di presumere la posizione dei parametri e i valori di ritorno delle funzioni, tanto per fare un esempio, oppure di gestire coerentemente le situazioni d’errore.

Facendo alcune ricerche ho notato che esistono due orientamenti principali per la gestione degli errori in C:

  • Restituire lo stato dell’operazione, passando eventuali parametri di output via puntatore
  • Inserire in ogni metodo della libreria un parametro di output contenente lo stato dell’operazione

Restituire lo stato dell’operazione

In questo modello, si creano interfacce che restituiscono sempre un booleano (oppure un intero), indicando l’esito dell’operazione, ad esempio:

//Definizione di myFun
bool myFun(int InParam, int *OutParam);
//Implementazione di myFun
bool myFun( int InParam, int *OutParam )
{
    //Se non ci sono problemi il valore di ritorno sarà true
    bool status = true;

    if( condizioniFavorevoli ) {
        *OutParam = InParam * 2;
    } else {
        //ERRORE! Computazione fallita!
        status = false;
    }

    return status;
}

La variabile condizioniFavorevoli rappresenta una possibile situazione di errore in cui potrebbe incorrere myFun, mentre il parametro di uscita OutParam è passato tramite puntatore.
Un possibile utilizzo di questa funzione potrebbe essere il seguente:

/* Test per myFun
 * Compilare con -DFALSE per simulare una condizione di errore
 */
#include <stdio.h>
#include <stdbool.h> //C99
#include <stdlib.h>

#ifndef FALSE
bool condizioniFavorevoli = true;
#else
bool condizioniFavorevoli = false;
#endif

//Definizione di myFun
...

int main(void)
{
    bool status;
    int result;

    status = myFun( 3, &result );
    if( !status ) {
        fprintf(stderr, "Errore in myFun\n");
        exit(1);
    }

    printf("Il risultato di myFun: %d\n", result);

    return 0;
}

//Implementazione di myFun
...

Un possibile miglioramento sul tema, come consigliato in questa risposta su StackOverflow è quello di creare un tipo che contenga tutti i possibili errori utilizzati nella libreria, ed utilizzarlo al posto del semplice booleano, rendendo il codice più espressivo. Ad esempio:

/*
 * errors.h
 *
 * */

#ifndef STATUS_ERROR
#define STATUS_ERROR

typedef enum
{
    SUCCESS,
    E_DIVIDE_ZERO,
    E_PASS_ONE
} STATUS;

#endif
#include "errors.h"

STATUS print_divide_ten(int input)
{
    STATUS ReturnStatus;

    ReturnStatus = SUCCESS;

    if( input == 0 ) {
        ReturnStatus = E_DIVIDE_ZERO;
    } else if( input == 1 ) {
        ReturnStatus = E_PASS_ONE;
    } else {
        printf("Faccio la divisione %d\n", 10 / input);
    }

    return ReturnStatus;
}

L’utilizzo è praticamente lo stesso del caso precedente, solo che in questo caso è possibile decidere come comportarsi a seconda del valore di ritorno.
Aggiungendo una funzione che trasponga il codice di errore in un messaggio significativo per l’utente si ha a disposizione una buona infrastruttura per la gestione degli errori:

void explain_error_code( STATUS code )
{
    switch(code)
    {
        case SUCCESS:
            printf("Tutto ok\n");
            break;
        case E_DIVIDE_ZERO:
            printf("È stata tentata una divisione per zero\n");
            break;
        case E_PASS_ONE:
            printf("Il valore uno non è accettato");
            break;
        default:
            printf("Status code sconosciuto\n");
            break;
    }
}

Stato passato tramite puntatore

L’altro modello di gestione degli errori consiste nell’utilizzare il valore di ritorno della funzione per un eventuale output, mentre lo stato è passato tramite un parametro della funzione.

La funzione di esempio si trasformerebbe nel modo seguente:

int myFun2( int InParam, bool *Status )
{
    *Status = true;

    if( condizioniFavorevoli ) {
        return InParam * 2;
    } else {
        *Status = false;
    }

    return -1;
}

Mentre un possibile esempio di utilizzo sarebbe:

int main(void)
{
    bool status;
    int result;

    result = myFun2( 3, &status );
    if( !status ) {
        fprintf(stderr, "Errore in myFun\n");
        exit(1);
    }

    printf("Il risultato di myFun: %d\n", result);

    return 0;
}

Chiaramente anche in questo caso è possibile utilizzare una struct contenente le diverse tipologie di stati di ritorno da utilizzare nel programma chiamante.

Considerazioni

Ho letto commenti a favore e contro dell’uno e dell’altro metodo di gestione degli errori: c’è chi ritiene che l’output gestito tramite valore di ritorno sia più “naturale”, e chi invece sostiene che un eventuale situazione d’errore passi inosservata utilizzando un parametro di output.

Personalmente, ritengo che le due notazioni siano equivalenti in quanto, soprattutto in linguaggi come il C, il controllo degli errori debba essere ai limiti del maniacale.
Piuttosto credo sia meglio concentrarsi sulla coerenza delle interfacce: ovvero scegliere un metodo di gestione degli errori per la libreria in sviluppo, e mantenere sempre lo stesso stile. In questo modo si facilita il lavoro all’utilizzatore finale della libreria, che può contare su un comportamento quantomeno prevedibile degli strumenti che ha a disposizione.

Bonus: implementare le eccezioni in C

Facendo ricerche su questo argomento ho appreso che è possibile simulare la gestione delle eccezioni in C utilizzando la coppia di funzioni setjmp/longjmp.

Una prima implementazione è possibile trovarla sul sito di Francesco Nidito, mentre per una trattazione più approfondita è possibile consultare C Interfaces and Implementations: Techniques for Creating Reusable Software.

Per un altro esempio d’uso delle funzioni setjmp/longjmp, potete consultare questo post dell’amico Gian Paolo “JP” Ghilardi