Tengo un código que he estado utilizando con éxito durante algunos años para implementar un "objeto de tipo variante"; es decir, un objeto C++ que puede contener valores de varios tipos, pero solo usa (aproximadamente) tanta memoria como el mayor de los tipos posibles. El código es similar en espíritu a una unión etiquetada, excepto que también admite tipos de datos que no son POD. Realiza esta magia mediante el uso de un búfer char, ubicación nueva/eliminar y reinterpret_cast <>.Colocación nueva contra gcc 4.4.3 reglas de alias estrictas
Recientemente he probado la elaboración de este código bajo gcc 4.4.3 (con -O3 y -Wall), y tiene un montón de advertencias similares a estas:
warning: dereferencing type-punned pointer will break strict-aliasing rules
Por lo que he leído, esto es una indicación que el nuevo optimizador de gcc podría generar código "defectuoso", que obviamente me gustaría evitar.
He pegado una 'versión de juguete' de mi código a continuación; ¿hay algo que pueda hacer con mi código para hacerlo más seguro en gcc 4.4.3, mientras sigo soportando tipos de datos que no sean POD? Sé que, como último recurso, siempre podría compilar el código con -fno-strict-aliasing, pero sería bueno tener un código que no se rompa en la optimización, así que preferiría no hacerlo.
(Tenga en cuenta que me gustaría evitar introducir un impulso o una dependencia de C++ 0X en la base de código, así que mientras las soluciones boost/C++ 0X son interesantes, preferiría algo un poco más anticuado)
#include <new>
class Duck
{
public:
Duck() : _speed(0.0f), _quacking(false) {/* empty */}
virtual ~Duck() {/* empty */} // virtual only to demonstrate that this may not be a POD type
float _speed;
bool _quacking;
};
class Soup
{
public:
Soup() : _size(0), _temperature(0.0f) {/* empty */}
virtual ~Soup() {/* empty */} // virtual only to demonstrate that this may not be a POD type
int _size;
float _temperature;
};
enum {
TYPE_UNSET = 0,
TYPE_DUCK,
TYPE_SOUP
};
/** Tagged-union style variant class, can hold either one Duck or one Soup, but not both at once. */
class DuckOrSoup
{
public:
DuckOrSoup() : _type(TYPE_UNSET) {/* empty*/}
~DuckOrSoup() {Unset();}
void Unset() {ChangeType(TYPE_UNSET);}
void SetValueDuck(const Duck & duck) {ChangeType(TYPE_DUCK); reinterpret_cast<Duck*>(_data)[0] = duck;}
void SetValueSoup(const Soup & soup) {ChangeType(TYPE_SOUP); reinterpret_cast<Soup*>(_data)[0] = soup;}
private:
void ChangeType(int newType);
template <int S1, int S2> struct _maxx {enum {sz = (S1>S2)?S1:S2};};
#define compile_time_max(a,b) (_maxx< (a), (b) >::sz)
enum {STORAGE_SIZE = compile_time_max(sizeof(Duck), sizeof(Soup))};
char _data[STORAGE_SIZE];
int _type; // a TYPE_* indicating what type of data we currently hold
};
void DuckOrSoup :: ChangeType(int newType)
{
if (newType != _type)
{
switch(_type)
{
case TYPE_DUCK: (reinterpret_cast<Duck*>(_data))->~Duck(); break;
case TYPE_SOUP: (reinterpret_cast<Soup*>(_data))->~Soup(); break;
}
_type = newType;
switch(_type)
{
case TYPE_DUCK: (void) new (_data) Duck(); break;
case TYPE_SOUP: (void) new (_data) Soup(); break;
}
}
}
int main(int argc, char ** argv)
{
DuckOrSoup dos;
dos.SetValueDuck(Duck());
dos.SetValueSoup(Soup());
return 0;
}
Ese es el código extraño ... –
¿Ha enviado este código al equipo de GCC, tal vez como un informe de error? – curiousguy