Ho cercato di pensare a un modo di dichiarare typedef strongmente tipizzati, per catturare una certa classe di bug nella fase di compilazione. Spesso succede che digiterò un int in diversi tipi di ID o un vettore in posizione o velocità:
typedef int EntityID;
typedef int ModelID;
typedef Vector3 Position;
typedef Vector3 Velocity;
Questo può rendere più chiaro l'intento del codice, ma dopo una lunga nottata di codifica si potrebbero commettere errori stupidi come confrontare diversi tipi di ID o aggiungere una posizione ad una velocità.
EntityID eID;
ModelID mID;
if ( eID == mID ) // <- Compiler sees nothing wrong
{ /*bug*/ }
Position p;
Velocity v;
Position newP = p + v; // bug, meant p + v*s but compiler sees nothing wrong
Sfortunatamente, i suggerimenti che ho trovato per typedef strongmente tipizzati includono l'uso di boost, che almeno per me non è una possibilità (ho almeno c ++ 11). Quindi, dopo un po 'di riflessione, mi sono imbattuto in questa idea, e volevo gestirla da qualcuno.
Per prima cosa dichiari il tipo di base come modello. Il parametro template non viene utilizzato per qualcosa nella definizione, tuttavia:
template < typename T >
class IDType
{
unsigned int m_id;
public:
IDType( unsigned int const& i_id ): m_id {i_id} {};
friend bool operator==<T>( IDType<T> const& i_lhs, IDType<T> const& i_rhs );
};
Le funzioni di amicizia devono essere inoltrate prima della definizione della classe, che richiede una dichiarazione in avanti della classe template.
Quindi definiamo tutti i membri per il tipo base, ricordando semplicemente che si tratta di una classe template.
Infine, quando vogliamo usarlo, lo digitiamo come:
class EntityT;
typedef IDType<EntityT> EntityID;
class ModelT;
typedef IDType<ModelT> ModelID;
I tipi ora sono completamente separati. Le funzioni che richiedono un EntityID genereranno un errore del compilatore se si tenta di fornire loro un ModelID, ad esempio. Oltre a dover dichiarare i tipi di base come modelli, con i problemi che comporta, è anche abbastanza compatto.
Speravo che qualcuno avesse commenti o critiche su questa idea?
Un problema che mi è venuto in mente mentre scrivevo questo, nel caso di posizioni e velocità, per esempio, sarebbe che non posso convertire tra i tipi liberamente come prima. Dove prima moltiplicare un vettore con uno scalare darebbe un altro vettore, quindi potrei fare:
typedef float Time;
typedef Vector3 Position;
typedef Vector3 Velocity;
Time t = 1.0f;
Position p = { 0.0f };
Velocity v = { 1.0f, 0.0f, 0.0f };
Position newP = p + v*t;
Con il mio typedef strongmente tipizzato dovrei dire al compilatore che multypling a Velocity by a Time risulta in una posizione.
class TimeT;
typedef Float<TimeT> Time;
class PositionT;
typedef Vector3<PositionT> Position;
class VelocityT;
typedef Vector3<VelocityT> Velocity;
Time t = 1.0f;
Position p = { 0.0f };
Velocity v = { 1.0f, 0.0f, 0.0f };
Position newP = p + v*t; // Compiler error
Per risolvere questo, penso che dovrei specializzare ogni conversione in modo esplicito, il che può essere un po 'fastidioso. D'altra parte, questa limitazione può aiutare a prevenire altri tipi di errori (ad esempio, moltiplicando una Velocity by a Distance, forse, che non avrebbe senso in questo dominio). Quindi sono distrutto e mi chiedo se le persone hanno qualche opinione sul mio problema originale o sul mio approccio alla risoluzione.