Fino a che punto andare a scrivere tipi primitivi come int


14

Ho visto il codice C ++ come il seguente con molti typedefs.

Quali sono i vantaggi dell'utilizzo di molti messaggi di typedefquesto tipo rispetto all'utilizzo di primitivi C ++? C'è un altro approccio che potrebbe anche ottenere quei benefici?

Alla fine, i dati sono tutti archiviati in memoria o trasmessi sul filo come bit e byte, importa davvero?

types.h:

typedef int16_t Version;
typedef int32_t PacketLength;
typedef int32_t Identity;
typedef int32_t CabinetNumber;
typedef int64_t Time64;
typedef int64_t RFID;
typedef int64_t NetworkAddress;
typedef int64_t PathfinderAddress;
typedef int16_t PathfinderPan;
typedef int16_t PathfinderChannel;
typedef int64_t HandsetSerialNumber;
typedef int16_t PinNumber;
typedef int16_t LoggingInterval;
typedef int16_t DelayMinutes;
typedef int16_t ReminderDelayMinutes;
typedef int16_t EscalationDelayMinutes;
typedef float CalibrationOffset;
typedef float AnalogValue;
typedef int8_t PathfinderEtrx;
typedef int8_t DampingFactor;
typedef int8_t RankNumber;
typedef int8_t SlavePort;
typedef int8_t EventLevel;
typedef int8_t Percent;
typedef int8_t SensorNumber;
typedef int8_t RoleCode;
typedef int8_t Hour;
typedef int8_t Minute;
typedef int8_t Second;
typedef int8_t Day;
typedef int8_t Month;
typedef int16_t Year;
typedef int8_t EscalationLevel;

Sembra logico cercare di assicurarsi che lo stesso tipo sia sempre usato per una cosa particolare per evitare overflow, ma vedo spesso codice in cui "int" è stato appena usato praticamente ovunque. L' typedefing spesso porta a un codice simile a questo però:

DoSomething(EscalationLevel escalationLevel) {
    ...
}

Che poi mi chiedo quale token sta effettivamente descrivendo il parametro: il tipo di parametro o il nome del parametro?


2
IMHO, sembra un esercizio abbastanza inutile, ma sono sicuro che alcuni non sarebbero d'accordo ...
Nim,

1
Quei tipi sembrano nomi di variabili.
Capitan Giraffe,

11
Nota che questo crea l'impressione che sia sicuro per i tipi, ma non lo è affatto: i typedef creano solo alias, ma nulla ti impedisce di passare ad esempio Minutea una funzione che ha un argomento dichiarato come tipo Second.
Jesper,

2
@Mark: guardalo in un altro modo. Se si commette un errore nel decidere il tipo intero o in futuro emergeranno nuovi requisiti e quindi si desidera modificarlo, si desidera cambiare un singolo typedef o si desidera cercare nel codice ogni funzione che manipola un anno e cambiare la sua firma? 640k è abbastanza per chiunque e tutto il resto. Il rovescio della medaglia corrispondente al typedef è che le persone scrivono accidentalmente o deliberatamente codice che si basa sul fatto che Year è esattamente 16 bit, quindi cambia e il loro codice si rompe.
Steve Jessop,

1
@Steve Jessop: Non riesco a decidere se pensi che sia una buona o cattiva idea :-) La prima parte sembra essere a favore, la seconda contro. Immagino che abbia pro e contro allora.

Risposte:


13

Il nome di un parametro dovrebbe descrivere cosa significa - nel tuo caso il livello di escalation. Il tipo è come viene rappresentato il valore - l'aggiunta di typedefs come nel tuo esempio offusca questa parte della firma della funzione, quindi non lo consiglierei.

I typedef sono utili per i modelli o se si desidera modificare il tipo utilizzato per determinati parametri, ad esempio durante la migrazione da una piattaforma a 32 bit a una piattaforma a 64 bit.


Questo sembra quindi essere il consenso generale. Quindi in genere ti limiteresti a "int"? Devo essere molto efficiente in questa app quando si tratta di trasferire dati, quindi sarebbe un caso di semplice conversione in int16_t (o qualunque sia il tipo di rappresentazione più grande necessario per un particolare elemento) durante la serializzazione?

@Mark: Sì, è così che dovresti farlo. Utilizzare typedefs per esprimere la dimensione dei tipi di dati utilizzati, ma non differenziare lo stesso tipo utilizzato in contesti diversi.
Björn Pollex,

Grazie - e solo per chiarire, non ti preoccuperesti di usare int8_t invece di int generalmente nel codice .. Suppongo che la mia preoccupazione principale fosse qualcosa come "Identità" che in realtà è un'identità generata da un database. Al momento è a 32 bit, ma non sono ancora sicuro che alla fine possa diventare 64 bit. Inoltre, che ne dici di int32_t vs int? int di solito è uguale a int32_t, ma potrebbe non essere sempre immagino su una piattaforma diversa? Sto pensando che dovrei semplicemente attenermi a "int" in generale, e "int64_t" dove necessario .. grazie :-)

@Mark: L'importante di typedefs int32_tè che devi assicurarti che siano corretti durante la compilazione su piattaforme diverse. Se ti aspetti che l'intervallo Identitycambi a un certo punto, penso che preferirei apportare le modifiche direttamente in tutto il codice interessato. Ma non sono sicuro, perché avrei bisogno di saperne di più sul tuo progetto specifico. Potresti voler fare una domanda separata.
Björn Pollex,

17

All'inizio ho pensato "Perché no", ma poi mi è venuto in mente che se hai intenzione di fare così tanto per separare i tipi in quel modo, allora fai un uso migliore della lingua. Invece di usare gli alias, definisci effettivamente i tipi:

class AnalogueValue
{
public:
    // constructors, setters, getters, etc..
private:
    float m_value;
};

Non c'è differenza di prestazioni tra:

typedef float AnalogueValue;
AnalogValue a = 3.0f;
CallSomeFunction (a);

e:

AnalogValue a (3.0f); // class version
CallSomeFunction (a);

e hai anche i vantaggi di aggiungere la validazione dei parametri e la sicurezza dei tipi. Ad esempio, considera il codice che tratta i soldi usando i tipi primitivi:

float amount = 10.00;
CallSomeFunction(amount);

A parte i problemi di arrotondamento, consente anche qualsiasi tipo che può essere convertito in float:

int amount = 10;
CallSomeFunction(amount);

In questo caso non è un grosso problema, ma le conversioni implicite possono essere una fonte di bug che è difficile da individuare. L'uso di a typedefnon aiuta qui, poiché sono semplicemente un alias di tipo.

L'uso completo di un nuovo tipo significa che non ci sono conversioni implicite a meno che non si codifichi un operatore di casting, il che è una cattiva idea in particolare perché consente conversioni implicite. Puoi anche incapsulare dati aggiuntivi:

class Money {
  Decimal amount;
  Currency currency;
};

Money m(Decimal("10.00"), Currency.USD);
CallSomeFunction(m);

Nient'altro si adatterà a quella funzione a meno che non scriviamo il codice per farlo accadere. Le conversioni accidentali sono impossibili. Possiamo anche scrivere tipi più complessi secondo necessità senza troppi problemi.


1
Potresti persino scrivere alcune macro orribili per fare tutta questa creazione di classe per te. (Dai, Skizz. Sai che vuoi farlo.)

1
Per alcuni di questi, potrebbe essere utile una libreria di unità di tipo sicuro ( tuoml.sourceforge.net/html/scalar/scalar.html ), piuttosto che scrivere una classe personalizzata per ciascuno.
Steve Jessop,

@Chris - Sicuramente non una macro, ma probabilmente una classe modello. Come sottolinea Steve, quelle classi sono già state scritte.
Kevin Cline,

4
@ Chris: La macro si chiama in BOOST_STRONG_TYPEDEFrealtà;)
Matthieu M.

3

L'uso di typedefs per tipi primitivi è simile al codice di stile C.

In C ++ otterrai errori interessanti non appena provi a sovraccaricare le funzioni per, diciamo, EventLevele Hour. Ciò rende i nomi dei tipi extra piuttosto inutili.


2

Noi (nella nostra azienda) lo facciamo molto in C ++. Aiuta a comprendere e mantenere il codice. Il che è positivo quando si spostano le persone tra le squadre o si fa refactoring. Esempio:

typedef float Price;
typedef int64_t JavaTimestmap;

void f(JavaTimestamp begin, JavaTimestamp end, Price income);

Riteniamo che sia una buona pratica creare typedef al nome della dimensione dal tipo di rappresentazione. Questo nuovo nome rappresenta un ruolo generale in un software. Il nome di un parametro è un ruolo locale . Come in User sender, User receiver. In alcuni punti può essere ridondante, come void register(User user), ma non lo considero un problema.

In seguito si potrebbe avere l'idea che floatnon sia la migliore per rappresentare i prezzi a causa delle speciali regole di arrotondamento della prenotazione, quindi si scarica o implementa un tipo BCDFloat(decimale con codice binario) e cambia il typedef. Non c'è ricerca e sostituzione di lavoro da floata BCDFloatche sarebbe indurito dal fatto che ci sono probabilmente molti altri carri nel codice.

Non è un proiettile d'argento e ha i suoi avvertimenti, ma pensiamo che sia molto meglio usarlo che no.


O come ha suggerito Mathieu M. al posto di Skizz si può andare come BOOST_STRONG_TYPEDEF(float, Price), ma non andrei così lontano su un progetto medio. O forse lo farei. Ci devo dormire. :-)
Notinlist

1

typedeffondamentalmente ti permette di dare un alias per un type.
Ti dà la flessibilità di evitare di digitare type namesripetutamente il long e di renderlo typepiù facilmente leggibile in cui il nome alias indica l'intento o lo scopo di type.

È più una questione di scelta se desideri avere nomi più leggibili typedefnel tuo progetto.
Di solito, evito di usare typedeftipi primitivi, a meno che non siano insolitamente lunghi da scrivere. Mantengo i nomi dei miei parametri più indicativi.


0

Non farei mai una cosa del genere. Accertarsi che abbiano tutte le stesse dimensioni è una cosa, ma devi solo fare riferimento a loro come tipi integrali, quindi.


0

Usare i typedef in questo modo va bene purché chi finisce per usarli non abbia bisogno di sapere nulla della loro rappresentazione sottostante . Ad esempio, se si desidera passare un PacketLengthoggetto a uno printfo scanf, è necessario conoscerne il tipo effettivo in modo da poter scegliere l'identificatore di conversione corretto. In casi del genere, il typedef aggiunge semplicemente un livello di offuscamento senza acquistare nulla in cambio; potresti anche aver appena definito l'oggetto come int32_t.

Se è necessario applicare la semantica specifica per ciascun tipo (come intervalli o valori consentiti), è meglio creare un tipo di dati astratto e funzioni per operare su quel tipo, piuttosto che creare un typedef.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.