Direttive condizionali.
Oltre alla direttiva #define, per maggiori dettagli vedere Direttive del preprocessore, esistono anche direttive condizionali che consentono di fare la cosiddetta compilazione condizionale: avendo la stessa logica di if-else, questi costrutti consentono fare una scelta prima di compilare il codice stesso. Un ottimo esempio è lo stesso "core" di Arduino: la maggior parte delle funzioni sono scritte con le specifiche di ciascun processore e, prima di compilare il codice, viene selezionato quello che corrisponde al microcontrollore attualmente selezionato tra le numerose opzioni per l'implementazione della funzione. In poche parole, la compilazione condizionale ti consente di includere o escludere condizionalmente l'uno o l'altro codice dalla compilazione principale, ovvero prima il preprocessore analizza il codice, qualcosa è incluso in esso, qualcosa non lo è e quindi ha luogo la compilazione. Inoltre non possiamo dichiarare alcuna costante o macro tramite #define più di una volta, questo porterà a un errore. La compilazione condizionale consente la ramificazione ove possibile. Per la compilazione condizionale sono a nostra disposizione le direttive:
- #if - analogo di if in un costrutto logico
- #elif - analogo di else if in un costrutto logico
- #else - analogo di else in una costruzione logica
- #endif - una direttiva che termina un costrutto condizionale
- #ifdef - se definito
- #ifndef - se non definito
- defined - questo operatore restituisce true se la parola specificata è "definita" tramite #define e false in caso contrario. Usato per i costrutti di compilazione condizionale.
Come usarli, vediamo un esempio:
#define TEST 0 // definisce TEST come 0
#if (TEST == 0) // se TEST 0
#define VAL 5 // definisce VAL come 5
#elif (TEST == 1) // TEST 1
#define VAL 7 // definisce VAL come 7
#else // in caso contrario
#define VAL 32 // definisce VAL come 32
#endif // fine della condizione
Pertanto, abbiamo ottenuto una certa costante VAL, che dipende dall'impostazione TEST. Il costruito ti consente di includere o escludere parti di codice prima della compilazione, ad esempio, ecco un pezzo sul debug:
#define DEBUG 1
void setup()
{
#if (DEBUG == 1)
Serial.begin(9600);
Serial.println("Electron32");
#endif
}
Il preprocessore ha altre due direttive: #ifdef e #ifndef, ti permettono di includere o escludere sezioni di codice in base alla condizione: #ifdef - è definito, #ifndef - non definito. Definito o non definito - stiamo parlando ovviamente di #define.
#define TEST // definisce TEST
#ifdef TEST // se TEST è definito
#define VAL 5 // definisce VAL come 5
#else #else // se commentato #define TEST
#define VAL 7 // definisce VAL come 7
#endif // fine della condizione
È sulla compilazione condizionata che si costruisce tutta l'universalità delle librerie per Arduino, perché quando si sceglie una scheda viene automaticamente "creata" una definizione per il nome del microcontrollore, che si presenta così:
- __AVR_ATmega32U4__
- __AVR_ATmega2560__
- __AVR_ATmega328P__
- ...
Ciò consente di creare codice generico utilizzando il costrutto #ifdef o #if defined():
#if defined(__AVR_ATmega32U4__)
// codice per Leonardo/Micro/Pro Micro
#elif defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)
// codice per Mega (1280 o 2560)
#elif defined(__AVR_ATmega328P__)
// codice per UNO/Nano/Pro Mini
#endif
Pertanto, microcontrollori (schede Arduino) di diversi modelli avranno accesso a un codice personale che verrà passato al compilatore quando questa scheda verrà selezionata dall'elenco delle schede.
