Modularità e "incapsulamento" in C

6

Questo vale per C (e probabilmente per qualsiasi altro linguaggio simile non orientato agli oggetti). Se ho un archivio dati centrale e un accesso potenzialmente concorrente ci sono due modi in cui posso vedere di proteggerlo.

Diciamo che ho un archivio dati con alcuni elementi di dati ...

struct MyStore
{
    int data1, data2, data3, ...., dataN;
} store[M];

In questo esempio i tipi di dati sono gli stessi ma immaginiamo che questo si applichi a qualcosa di un po 'più vario con tipi diversi ecc ... questo è solo per mantenere la domanda semplice.

Per consentire ad altri utenti l'accesso ai dati all'interno, potrei fare alcune cose.

  1. Potrebbe fornire funzioni di regione critiche e fare affidamento sul chiamante per ottenere la protezione corretta.
  2. Potrei fornire un setter e una funzione getter per ogni tipo di elemento dati nel negozio e gestire le aree critiche all'interno del modulo e proteggere l'utente dall'avere a che fare con qualsiasi logica.

Il mio problema con il primo metodo è che l'onere è posto sul chiamante.

Il mio problema con il secondo, almeno in C, è che in primo luogo finisco per scrivere un intero carico di accessori per le piastre della caldaia e in secondo luogo cose più complesse come test-and-set o che richiedono il blocco dei dati mentre si fa diverse operazioni diventano disordinate. Per esempio. per il metodo 2 ho bisogno di

int GetData1(unsigned int index) { 
    int data; 

    ENTER_CR(); 
    data = store[index].data1; 
    LEAVE_CR(); 

    return data;
}

...
...

int GetDataN(unsigned int index) { 
    int data; 

    ENTER_CR(); 
    data = store[index].dataN; 
    LEAVE_CR(); 

    return data;
}

E lo stesso per tutti i setter. E poi cosa succede se voglio impostare più oggetti atomicamente? Diventa difficile!

Come ottenere alcuni dei vantaggi di modularità e incapsulamento, ma hai ancora un'interfaccia flessibile?

    
posta Jimbo 02.12.2013 - 16:49
fonte

2 risposte

3

Quando fornisci un'interfaccia thread-safe (con o con OOP) devi assicurarti che le tue operazioni siano al livello che vuoi essere atomico. Se l'impostazione o la ricezione di un singolo campo è il livello di operazione atomica che si desidera supportare, l'opzione 1 funziona. (D'altra parte, ottenere e impostare int è atomico comunque)

La realtà è probabilmente che un semplice oggetto di dati non è il posto giusto in un progetto per garantire la sicurezza del thread, perché come hai notato tu non sai davvero quali sono le "reali" operazioni per cui i dati vengono utilizzati. Puoi ancora incapsulare la sicurezza del thread, ma deve essere dove stai facendo qualcosa, non solo memorizzare qualcosa.

    
risposta data 02.12.2013 - 17:10
fonte
4

Puoi fare in C tutto ciò che faresti in un'altra lingua per creare un'interfaccia pulita - devi solo farlo con meno supporto linguistico.

  1. se vuoi evitare gli accessi standard, salva tutti i tuoi tipi semplici in un sindacato: allora hai un singolo accesso per ID di campo numerato (cioè un indice in una serie di unioni), e il chiamante può preoccuparsi di quale tipo ogni campo è

    struct MyStore;
    union Atom {
        int i;
        double d;
        /* any other types */
    };
    
    /* access any type by index */
    union Atom *field(struct MyStore *, int id);
    
    • ovviamente perdi le garanzie di sicurezza del tipo che altre lingue potrebbero fornire, e gli errori dei client qui possono essere sgradevoli, specialmente se uno dei valori nel tuo sindacato è un puntatore
    • puoi renderlo un'unione discriminata se vuoi maggiore sicurezza e non preoccuparti di controllare il tag del tipo a mano
  2. se vuoi raggruppare più operazioni in un evento atomico, puoi farlo:

    struct MyStore;
    struct BatchOperation;
    
    struct BatchOperation *startBatch(struct MyStore*);
    void endBatch(struct BatchOperation*);
    
    /* force client to call startBatch before accessing */
    union Atom *field(struct BatchOperation *, int id);
    

    qui, l'avvio dell'operazione batch bloccherebbe il tuo negozio, e terminandolo sbloccherebbe il blocco.

    • di nuovo, non ottieni alcuna gestione automatica della BatchOperation lifetime, quindi il codice client deve terminare il batch o causare un deadlock

PS. Nota i tipi opachi usati sopra: solo il sindacato e le funzioni pubbliche sono completamente visibili al codice cliente. Questo è davvero l'incapsulamento, senza necessità di virgolette.

    
risposta data 02.12.2013 - 17:29
fonte

Leggi altre domande sui tag