[Risolto]Dubbio su variabili statiche in metodi di classe

Premesso che delle classi in C++ ho una conoscenza rudimentale, ho scritto una classe per gestire un link bit-sincrono/tempo-asincrono 4 fili (tx, rx, handshake) tra due Arduini (poi si può discutere sulla sensatezza, ma non è questo il quesito):

class Link4F
{
    private:
        byte      ce_pin;        // comando entrante
        byte      cu_pin;        // comando uscente
        byte      tx_pin;
        byte      rx_pin;
        byte      blink_pin;     // indica attivita` link, 255 non usato
        byte      master;        // 1 master, 0 slave
        byte      txByte;        // byte da trasmettere
        boolean   txByteBusy;    // true = in trasmissione
        byte      rxByte;        // byte ricevuto
        boolean   rxByteReady;   // true = byte pronto
        byte      txBit_st;      // stato processo trasmissione byte
        byte      txBit_cnt;     // contatore processo trasm. byte
        byte      txBit_par;     // bit di parita` pari in trasmissione
        byte      rxBit_st;      // stato processo ricezione byte
        byte      rxBit_cnt;     // contatore processo ricez. byte
        byte      rxBit_par;     // bit di parita` pari in ricezione
        byte      blink_st;      // stato processo blink attivita`
        uint16_t  blink_cnt;     // contatore processo blink attivita`
        byte      syn_st;        // stato sincronismo link
        void      syncMaster();  // handshake lato master
        void      syncSlave();   // handshake lato slave
        void      txBit();       // ricezione bit ad ogni fronte
        void      rxBit();       // trasmissione bit ad ogni fronte
        void      blink_();      // lampeggio per attivita` link
    public:
        void      update();      // da chiamare continuamante
        boolean   rxAvailable(); // true se arrivato byte
        boolean   txAvailable(); // true se possibile trasmettere
        void      write(byte);   // inizia trasmissione di un byte
        byte      read();        // legge byte arrivato
                  Link4F(byte, byte, byte, byte, byte, byte);
};
//-------------------------------------------------------------------------
Link4F::Link4F(byte cu, byte ce, byte tx, byte rx, byte blk, byte ma)
{
    ce_pin = ce;        
    cu_pin = cu;        
    tx_pin = tx;        
    rx_pin = rx;        
    master = ma;
    blink_pin = blk;
    digitalWrite(cu, ma);  // master parte con CU = 1, slave 0
    pinMode(cu, OUTPUT);
    pinMode(ce, INPUT);
    digitalWrite(tx, 1);   // tx pin inizialmente HIGH
    pinMode(tx, OUTPUT);
    pinMode(rx, INPUT);
    if(blk != 255) pinMode(blk, OUTPUT);
    rxByteReady = false;
    txByteBusy = false;
    txBit_st = 0;
    rxBit_st = 0;
    blink_st = 0;
    blink_cnt = 600;
    syn_st = ma;           // master parte con stato 1
}
//-------------------------------------------------------------------------
void Link4F::write(byte n)
{
    txByte = n;
    txByteBusy = true;
}
//-------------------------------------------------------------------------
byte Link4F::read()
{
    rxByteReady = false;
    return rxByte;
}
//-------------------------------------------------------------------------
boolean Link4F::rxAvailable()
{
    return rxByteReady;
}
//-------------------------------------------------------------------------
boolean Link4F::txAvailable()
{
    return !txByteBusy;
}
//-------------------------------------------------------------------------
void Link4F::txBit()
{
    switch(txBit_st)
    {
        case 0:
            if(txByteBusy)
            {
                digitalWrite(tx_pin, 0);           // start bit
                txBit_par = 0;
                txBit_cnt = 8;
                txBit_st = 1;
            }
            break;
        case 1:
            if(txBit_cnt > 0)
            {
                byte b = (txByte & (1 << (txBit_cnt - 1))) != 0;
                txBit_par ^= b;
                digitalWrite(tx_pin, b);
                txBit_cnt--;
            }
            else
            {
                digitalWrite(tx_pin, txBit_par);  // parita` pari
                txBit_st = 2;
            }
            break;
        case 2:
            digitalWrite(tx_pin, 1);              // stop bit
            txByteBusy = false;
            txBit_st = 0;
    }
}
//-------------------------------------------------------------------------
void Link4F::rxBit()
{
    byte in = digitalRead(rx_pin);
    switch(rxBit_st)
    {
        case 0:
            if(in == 0)     // se ricevuto start bit
            { 
                rxBit_cnt = 8;
                rxBit_par = 0;
                rxBit_st = 1;
            }
            break;
        case 1:
            if(rxBit_cnt > 0)
            {
                rxByte = (rxByte << 1) | in;
                rxBit_par ^= in;
                rxBit_cnt--;
            }
            else
                rxBit_st = (rxBit_par ^ in) ? 0 : 2;
            break;
        case 2:
            if(in == 1) rxByteReady = true;
            rxBit_st = 0;
    }
}
//-------------------------------------------------------------------------
void Link4F::syncMaster() {
    byte in = digitalRead(ce_pin);
    if(syn_st == 0  &&  in == 0)
    {
        txBit();
        blink_();
        rxBit();
        digitalWrite(cu_pin, 1);
        syn_st = 1;
    }
    else if(syn_st == 1  &&  in == 1)
    {
        txBit();
        blink_();
        rxBit();
        digitalWrite(cu_pin, 0);
        syn_st = 0;
    }
}
//-------------------------------------------------------------------------
void Link4F::syncSlave() {
    byte in = digitalRead(ce_pin);
    if(syn_st == 0  &&  in == 1)
    {
        txBit();
        blink_();
        rxBit();
        digitalWrite(cu_pin, 1);
        syn_st = 1;
    }
    else if(syn_st == 1  &&  in == 0)
    {
        txBit();
        blink_();
        rxBit();
        digitalWrite(cu_pin, 0);
        syn_st = 0;
    }
}
//-------------------------------------------------------------------------
void Link4F::blink_()
{
    if(blink_pin != 255  &&  --blink_cnt == 0)
    {
        blink_cnt = 600;
        blink_st ^= 1;
        digitalWrite(blink_pin, blink_st);
    }
}
//-------------------------------------------------------------------------
void Link4F::update() {
    if(master) syncMaster(); else syncSlave();
}

Funziona, posso creare un link con:

Link4F Link(CU_PIN, CE_PIN, TX_PIN, RX_PIN, BLINK_PIN, 1);

E usarne i metodi:

update
rxAvailable
txAvailable
write
read

Però i metodi usano delle variabili statiche. Cosa succede se creo più istanze? Vengono trattate come variabili di istanza indipendenti, o sono comuni a tutte le istanze della classe?

Poi, i due metodi write e read mi vengono evidenziati come nomi speciali, c'è qualche controindicazione ad usarli?


Per curiosità ho misurato che il link "a vuoto" genera spontaneamente una frequenza di handshake di poco più di 12kHz, pari a 24kbit/s full duplex.

Non vedo variabili statiche di classe, che quindi sono legate alla classe e no alle istanze, ciò significa che tutte le istanze condividono quelle variabili. Nel tuo caso hai impiegato variabili statiche di metodo, il quale è legato ad una istanza, per cui ogni istanza avrà un set di variabili statiche di metodo. Almeno questo per via di logica è il comportamento più sensato e ragionevole che mi viene in mente.

Per toglierti il dubbio, replica l'esperimento con il compilatore nativo per il tuo pc, così eviti di scrivere la flash e poi fare più rapidamente il debug.

PS: lo farei io, ma dubito di avere il tempo, sono sotto pressione a 360 gradi.
PS1: Ci sono anche i metodi statici di classe e quindi tutte le istanze usano lo stesso metodo non legato alla istanza ma alla classe.

Ciao.

Purtroppo ti sbagli, Mauro: in C++ tutto ciò che è static all'interno di una classe o di un suo metodo appartiene alla classe stessa. Nel caso specifico, se crei più istanze vi sarà comunque una sola copia di quelle variabili, su cui agiranno TUTTE le istanze. Se questo non è quel che vuoi, falle diventare normali membri della classe.

Cambierebbe qualcosa se ci fosse un template di mezzo, ma non è il caso.

Ok grazie, ho tolto le statiche (riempiendo di prefissi le variabili di istanza rendendole così illeggibili :() e aggiornato il codice a cui mancava anche qualche inizializzazione.

Speravo di poter mantenere lo stesso "ordine", nel senso di confinamento solo dove servono, che le statiche permettono nelle funzioni normali rispetto ad usare le globali (dopo tutto le variabili di istanza stanno ai metodi come la variabili globali stanno alle funzioni).

Purtroppo ti sbagli, Mauro: in C++ tutto ciò che è static all'interno di una classe o di un suo metodo appartiene alla classe stessa.

Orrrroreeee, mi rendo conto di perdere colpi su colpi con il passare del tempo, saranno gli acciacchi o le cause che mi tengono lontano dal PC o altro, insomma a qualcuno la devo pure dare la colpa. 8)

Per fortuna il C non l'ho ancora dimenticato, ma se passo ancora 6 mesi lontano dallo sviluppo, il rischio di dimenticare anche quello c'è.

Ciao.