Typedef

parola chiave dei linguaggi di programmazione C e C++

typedef è una parola chiave dei linguaggi di programmazione C e C++. Lo scopo della typedef è quello di assegnare dei nomi alternativi a dei tipi di dato esistenti, solitamente a quelli la cui dichiarazione standard è troppo ingombrante, magari confusionale, oppure per rendere il codice riutilizzabile più facilmente tra un'implementazione e un'altra.

Per le convenzioni del C (delle librerie standard), i tipi dichiarati con typedef finiscono con la stringa "_t" (per esempio, size_t, time_t).

Esempi di utilizzo modifica

Per indicare cosa rappresenta una variabile modifica

Una typedef può essere usata per indicare come una variabile rappresenta qualcosa, ad esempio un'unità di misura:

int current_speed ;
int high_score ;
...

void congratulate(int your_score) {
    if (your_score > high_score)
...

Ora si consideri:

typedef int km_per_hour ;
typedef int points ;

km_per_hour current_speed ;
points high_score ;
...

void congratulate(points your_score) {
    if (your_score > high_score)
...

Entrambe le sezioni di codice implementano la stessa operazione. Ciononostante, l'uso della typedef nel secondo esempio rende più chiaro il fatto che, nonostante le due variabili sono rappresentate dallo stesso tipo di dato (int), le informazioni in esse contenute sono logicamente incompatibili. La dichiarazione in congratulate() di your_score indica al programmatore che current_speed (o qualunque altra variabile non dichiarata come points) non dovrebbe essere passata come argomento. Questo non sarebbe stato così visibile se entrambe fossero state dichiarate come interi. Si noti che l'indicazione è utile soltanto per i programmatori; Il compilatore C/C++ considera entrambe le variabili come intere, e non segnalerà alcuna incompatibilità se si passasse current_speed come argomento alla funzione congratulate().

Semplificare una dichiarazione modifica

Una typedef può essere utilizzata per semplificare la dichiarazione di un tipo di dato composto (struct, union) oppure di un puntatore.

struct var {
    int data1;
    int data2;
    char data3;
};

Sopra è stato definito il tipo di dato struct var. Per dichiarare una variabile di questo tipo, in C, è richiesto l'uso della parola chiave struct (questo non vale per il C++):

struct var a;

Una typedef può essere utilizzata per eliminare la necessità di ripetere la parola struct. Per esempio con:

typedef struct var newtype;

ora possiamo creare una variabile di questo tipo con:

newtype a;

Si noti che la definizione della struttura e la typedef possono essere combinate in una singola istruzione:

typedef struct var {
    int data1;
    int data2;
    char data3;
} newtype;

Nel C++, in contrasto con il C, le parole chiave struct, class, e enum sono opzionali nelle dichiarazioni delle variabili che sono separate dalle definizioni.

struct var x;       // Questo è ammesso
var y;              // Anche questo è consentito

Così, var può essere utilizzata ogniqualvolta newtype può essere usata. In ogni modo, non è vero il contrario. Per esempio, il metodo costruttore per var non può essere chiamato newtype.

Utilizzare typedef con puntatori modifica

Le typedef possono inoltre semplificare le dichiarazioni per i tipi puntatore. Si consideri questo:

struct Node {
    int data;
    struct Node *nextptr;
};

Nel C, è possibile dichiarare più variabili dello stesso tipo in una singola istruzione, pure mischiando puntatori e non puntatori. Ad ogni modo, è necessario apporre un asterisco davanti ad ogni variabile per renderla un puntatore.

struct Node *startptr, *endptr, *curptr, *prevptr, errptr, *refptr;

Un programmatore probabilmente presupporrebbe che errptr fosse in realtà di tipo Node *, ma per un errore di battitura errptr è di tipo Node. Ciò porterà ad un errore di sintassi.

Definendo una typedef per Node *, si è sicuri che tutte le variabili saranno di tipo puntatore.

typedef struct Node *NodePtr;
...
NodePtr startptr, endptr, curptr, prevptr, errptr, refptr;

Utilizzare il typedef con cast di tipo modifica

Si crea una typedef usando la sintassi della dichiarazione di tipo, ma può essere usata come se fosse stata creata usando la sintassi del cast di tipo. Ad esempio, in ciascuna riga di codice dopo la prima riga di:

typedef int (*funcptr)(double);         // puntatore a funzione con argomento double e tipo di ritorno int
funcptr x = (funcptr) NULL;             // C o C++
funcptr y = funcptr(NULL);              // C o C++
funcptr z = static_cast<funcptr>(NULL); // solo C++

funcptr viene usato nella parte sinistra per dichiarare una variabile e nella parte destra dell'assegnamento per convertire un valore. Quindi, le typedef possono essere usate dai programmatori che non vogliono capire come convertire la sintassi dichiarativa in sintassi di cast.

Nota che, senza typedef, in genere non è possibile usare sintassi dichiarativa e di cast in maniera intercambiabile. Ad esempio:

int (*x)(double)  = (int (*)(double)) NULL; // Questo è ammissibile
int (*)(double) y = (int (*)(double)) NULL; // Il lato sinistro dell'assegnamento non è ammissibile
int (*z)(double)  = (int (*NULL)(double));  // Il lato destro dell'assegnamento non è ammissibile

Preoccupazioni sull'utilizzo modifica

Alcuni si oppongono all'uso intensivo delle typedef. La ragione principale è il fatto che i typedef nascondono il vero tipo di dato della variabile. Per esempio, Greg Kroah-Hartman, un hacker esperto e sviluppatore del kernel Linux, scoraggia il loro uso per tutto ad eccezione della dichiarazione del prototipo di una funzione. Sostiene che questa pratica non solo offusca il codice senza alcun bisogno, ma può anche causare confusione nel programmatore che fa accidentalmente cattivo uso di grosse strutture dati pensando che esse siano di tipo elementare.[1]

Altri sostengono che l'utilizzo delle typedef renda più facile la revisione del codice. Il K&R dichiara che ci sono due ragioni per l'utilizzo delle typedef. La prima, rendono il codice più portabile. Invece di dover cambiare un tipo di dato ogniqualvolta esso compare nell'intero sorgente, si cambia soltanto una typedef. La seconda, le typedef possono rendere più facile da comprendere una dichiarazione complessa.

Utilizzo nel C++ modifica

Nel C++ i nomi di tipo possono essere molto complicati e typedef fornisce un meccanismo per assegnare un nome semplice al tipo.

std::vector<std::pair<std::string, int> > values;
for (std::vector<std::pair<std::string, int> >::const_iterator i = values.begin(); i != values.end(); ++i)
{
   std::pair<std::string, int> const & t = *i;
   // fa qualcosa
}

e

typedef std::pair<std::string, int> value_t;
typedef std::vector<value_t> values_t;

values_t values;
for (values_t::const_iterator i = values.begin(); i != values.end(); ++i)
{
   value_t const & t = *i;
   // fa qualcosa
}

Uso con template nel C++ modifica

Non c'è un modo diretto per avere una typedef in un template in C++. Ad esempio, per far sì che value_t<T> rappresenti std::pair<std::string, T> per ogni class T non è possibile usare:

template<class T>
typedef std::pair<std::string, T> value_t<T>; // Non funziona

Ad ogni modo, se si vuole accettare value_t<T>::foo, o qualcosa di simile, anziché value_t<T>, allora si può ottenere il risultato desiderato con una typedef all'interno di una classe o struttura contenuta in un template:

template<class T>
struct value_t
{
    typedef std::pair<std::string, T> foo; // Fa sì che value_t<T>::foo rappresenti std::pair<std::string, T>
};

Dal C++11 è possibile utilizzare un'alias declaration utilizzando la keyword using per ottenere il risultato voluto:

template<typename T>
using TaggedValue = std::pair<std::string, T>; // legale dal C++11

void foo(TaggedValue<int> foo);

Altri linguaggi modifica

In svariati linguaggi di programmazione funzionale, come l'Haskell, Miranda, OCaml, ecc., è possibile definire i cosiddetti type synonyms, sinonimi di tipo, che corrispondono alle typedef in C. Un esempio scritto in Haskell:

type PairOfInts = (Int, Int)

Questo esempio definisce un sinonimo di tipo chiamato "PairOfInts" che ha lo stesso significato di una coppia di interi.

Il C# include una caratteristica simile al typedef del C: lo svantaggio è però che deve essere dichiarata per ogni file separatamente.[2]

using newType = global::System.Runtime.Interop.Marshal;
using otherType = Enums.MyEnumType;

Note modifica

  1. ^ Kroah-Hartman, Greg, Proper Linux Kernel Coding Style, su linuxjournal.com, 01/07/2002. URL consultato il 23/09/2007.
  2. ^ [1] Articolo MSDN sulla typedef in C#

Voci correlate modifica