Style bits vs. Separate bool's

6

La mia piattaforma principale (WinAPI) utilizza ancora pesantemente bit per gli stili di controllo ecc. ( esempio ).

Quando introduco i controlli personalizzati, mi chiedo in modo permanente se seguire lo stile o piuttosto usare i singoli bool. Diamoci l'uno contro l'altro:

enum EMyCtrlStyles
{
   mcsUseFileIcon = 1,
   mcsTruncateFileName = 2,
   mcsUseShellContextMenu = 4,
};

void SetStyle(DWORD mcsStyle);
void ModifyStyle(DWORD mcsRemove, DWORD mcsAdd);
DWORD GetStyle() const;

...
ctrl.SetStyle(mcsUseFileIcon | mcsUseShellContextMenu);

vs.

CMyCtrl & SetUseFileIcon(bool enable = true);
bool GetUseFileIcon() const;

CMyCtrl & SetTruncteFileName(bool enable = true);
bool GetTruncteFileName() const;

CMyCtrl & SetUseShellContextMenu(bool enable = true);
bool GetUseShellContextMenu() const;

ctrl.SetUseFileIcon().SetUseShellContextMenu();

Come la vedo io,

Bit stile Pro

  • Coerente con la piattaforma
  • meno codice della libreria (senza aumentare la complessità), meno posti da modificare per aggiungere un nuovo stile
  • codice del chiamante in meno (senza perdere notevole leggibilità)
  • più facile da usare in alcuni scenari (ad esempio, ricordare / trasferire impostazioni)
  • L'API binaria rimane stabile se vengono introdotti nuovi bit di stile

Ora, il primo e l'ultimo sono minori nella maggior parte dei casi.

Pro singoli booleani

  • Gli strumenti di Intellisense e di refactoring riducono lo sforzo "less typing"
  • Entità per singolo scopo
  • codice più alfabetico (come in "scorre più come una frase")
  • Nessuna modifica di paradim per le proprietà non bool

Questi sembrano più moderni, ma anche vantaggi "morbidi". Devo ammettere che la "coerenza della piattaforma" è molto più allettante di quanto potrei giustificare, meno codice può perdere un sacco di qualità è un bel bonus.

1. Cosa preferisci? Soggettivamente, per scrivere la biblioteca, o per scrivere il codice cliente?
2. Qualche (semi) obiettività, studi, ecc.?

    
posta peterchen 25.09.2012 - 08:31
fonte

3 risposte

11

In realtà, non credo che la "coerenza con la piattaforma" sia un punto "minore" nella maggior parte dei casi. Al contrario, dovresti progettare una libreria come è probabile che gli altri si aspettino di usarla . Se il pubblico di destinazione principale per, o la piattaforma che è targettata da, la tua biblioteca utilizza bitfield, allora sei probabilmente meglio usare i bitfield.

Detto questo, i bitfield hanno la loro giusta quota di svantaggi. Hanno debuttato quando i vincoli della memoria erano una preoccupazione molto reale; se stai cercando di spremere qualcosa di utile in qualche dozzina di kilobyte di RAM o anche meno, allora ogni byte conta. Si consideri che l'API di Windows risale ai primi anni '80, con Windows 1.0 rilasciato nel 1985 e che richiede un enorme 256 KB di RAM e due unità disco floppy o un singolo disco rigido. Questo è molto meno preoccupante oggi, visto che praticamente tutto ha diversi gigabyte di RAM disponibili per applicazioni server o desktop, oltre a uno spazio di scambio sostanzialmente illimitato, comparativamente estremamente veloce (non credo che Windows 1.0 abbia mai fatto lo swap < em> affatto , anche se sono abbastanza sicuro che abbia fatto lo spurgo della pagina di memoria), più compilatori JIT, ottimizzatori ecc.

I bitfield sono opachi; è molto più facile dire if (frob.isFrobnicated() && frob.isGlobbified()) rispetto a if (frob.getState() & (FROB_FROBNICATED | FROB_GLOBBIFIED) == (FROB_FROBNICATED | FROB_GLOBBIFIED)) , figuriamoci quando si guarda l'oggetto in un debugger e si vede che frob.state == 0x27a e si prova a capire cosa significhi quel valore.

Un grande vantaggio nell'usare variabili di stato separate e funzioni accessorie / mutator è che è molto più facile applicare invarianti tra stati correlati. Ad esempio, se lo stato A non ha requisiti speciali, ma lo stato B richiede che uno o meno entrambi gli stati C e D siano impostati, questo è banale da applicare in fase di compilazione se si utilizzano funzioni di mutatore di stato separate, ma non proprio così ovvio quando si usano bitfield.

Lavoro principalmente con .NET e Java, entrambi i quali aggrottano la fronte ai bitfield per lo stato esposto nelle routine di libreria. (C'è sono eccezioni, ma sono rari.) C è quasi certamente diverso anche in questi giorni, e immagino che il tipo C ++ rappresenti il gap.

I campi di bit hanno un enorme vantaggio: sono atomici. Se accedi a frob.state , puoi esaminare il valore con la certezza che non può cambiare tra i confronti all'interno della stessa istruzione (come ad esempio negli esempi if sopra). In un'applicazione multithread, se sei sfortunato, lo stato di frob potrebbe cambiare tra le due chiamate a isFrobnicated() e isGlobbified() . Tuttavia, penserei che se questa è una fonte importante di bug, allora hai altri problemi che devi affrontare (come lo stato globale thread-common che viene modificato mentre viene usato).

In conclusione: puoi certamente utilizzare i campi di bit internamente se ti senti a tuo agio con loro, ma per stato esposto , andrei con valori separati a meno che ci sono ragioni specifiche (come le aspettative sull'utilizzo della piattaforma) per fare diversamente, e sei disposto a vivere con gli svantaggi di quella scelta. E anche in questo caso, potresti considerare di farlo come alternativa piuttosto che come unico approccio disponibile.

    
risposta data 25.09.2012 - 09:51
fonte
1

Preferisco usare enum bitflags sia nella libreria che nel codice client per una raccolta di opzioni booleane che possono essere attivate indipendentemente. È un idioma che conosco e mi consente di salvare la configurazione in un singolo int e di passarla senza dover definire una nuova struttura o classe per contenere tutti i flag. Riduce anche la digitazione senza sacrificare la chiarezza.

Oggettivamente, una struttura contenente bool sarebbe più sicura per i caratteri in C ++, come discusso in questo QUINTA domanda . Irrilevante se stai utilizzando C semplice.

    
risposta data 25.09.2012 - 09:36
fonte
1

I booleani hanno quasi tutti i vantaggi. Tutti i piccoli vantaggi che hai citato, ad eccezione di "Coerentemente con la piattaforma", possono essere completamente superati con l'imballaggio accurato dei booleani in classi. I booleani rendono il codice estremamente più pulito e un design migliore.

Tuttavia, i bit hanno un vantaggio molto grande. Sono più veloci e utilizzano meno memoria. se hai 10 milioni di oggetti, ognuno con un set di 60 valori booleani, il tuo programma funzionerà più velocemente e userà molta meno memoria se usi i bit. (Questo è il motivo per cui la piattaforma usa ancora bit). Devi decidere all'inizio di un progetto quanto siano importanti le prestazioni, perché sarà molto difficile passare i tuoi booleani a bit più tardi.

Se avessi un paio di booleani in una classe che potrebbe finire con un paio di centinaia di istanze, rimarrò con i booleani. Se sono i 10 milioni e 60, e le prestazioni contano, io vado con i bit. Se è nel mezzo, finisco per pensare un po '.

    
risposta data 25.09.2012 - 16:21
fonte

Leggi altre domande sui tag