Usando -1 para inicializar una inicialización sin firmar en { } de estructura o matriz

Usando -1 para inicializar una inicialización sin firmar en { } de estructura o matriz


MUY BREVE


¿Cómo se crea una constante sin firmar que tiene todos los bits configurados?


...que puede usar para inicializar un campo con { }s,


...que no recibe una advertencia -Wnarrowing de GCC 4.7.2.


Los siguientes no son satisfactorios:


 struct U { unsigned ufield; };
struct Uc { unsigned char ufield; };
struct ULL { unsigned long long ufield; };
struct U32 { unsigned ufield; };
struct U64 { uint64_t ufield; }
typedef
//any of the above U Uc ULL U32 U64, we will arbitrarily choose:
U Ueg;
// somewhere far away
Ueg u = {-1}; // well defined by C standard, GCC 4.7.2 -Wnarrowing warning
Ueg u = {~0U}; // finit width constant, warnings in some places, silent non-all-1s-mask others
Ueg u = {~0ULL}; // ditto
Ueg u = {-1ULL}; // ditto

Básicamente, el usuario, el tipo que escribe la inicialización {},
no sabe el tipo del ufield.
Solo sabe que es un tipo sin firmar, pero no qué ancho.
No exactamente qué tipo sin firmar es.


* Otra razón por la que quiero una sintaxis lo más simple y elegante posible *


También podría mencionar algo más:el "usuario" aquí en realidad no está escribiendo un programa C o C++.
Está editando un archivo de configuración.
Un programa, un simple script de Perl o Python, procesa el config y genera código C.
Este programa no es muy sofisticado y, por el momento, pasa por fragmentos de texto que parecen


 Foo: {-1,2,3};

para generar
typedef
struct Some_Struct { unsigned a; sin firmar b, sin firmar c; }
Some_Struct ={-1,2,3}; // lo mismo


Básicamente, quiero poder tener una buena sintaxis fácil de usar para un literal que diga
"Todos los bits en este valor sin firmar están establecidos".
Sin tener que saber qué tan grande es sin firmar.
Y sin que el programa que maneja el archivo de configuración se vuelva demasiado complicado.


Para que el posible proveedor de respuestas no se queje de que se trata de una nueva restricción, no realista, etc.:

He tenido exactamente el mismo problema con las plantillas.
Es decir. con tipos de plantilla, donde quiero escribir un literal que sea "sin firmar de cualquier ancho, todos 1".
En una plantilla, podría estar más dispuesto a hacer algo de la sintaxis fea, fea y fea
que obviamente es capaz de hacer esto:
pero realmente desearía que hubiera una sintaxis simple y elegante.


* La verdadera pregunta *


P:¿Hay alguna forma de crear una constante que sea "todos los 1 establecidos"
sin activar la advertencia de GCC 4.7.2?


BREVE


Me encontré con un programa que usaba la constante literal -1 para inicializar un campo de una estructura, por ejemplo,


> cat ./-1u.cpp
#include <stdio.h>
struct U { unsigned ufield; } ustruct = { -1 };
int main(int argc, char** argv)
{
printf("ustruct.ufield = %08x\n",ustruct.ufield);
}

Aunque las versiones anteriores de GCC aceptaron esto sin previo aviso,
la versión relativamente reciente de GCC 4.7.2 proporciona una advertencia:


> /SOME-PATH/import/gcc/gcc-4.7.2.pkg/installdir/bin/g++ -Wall ./-1u.cpp
./-1u.cpp:3:46: warning: narrowing conversion of '-1' from 'int' to 'unsigned int' inside { } is ill-formed in C++11 [-Wnarrowing]

Nota:esto es solo una advertencia. El resultado de convertir -1 a sin firmar está bien definido en los estándares C/C++:


> ./a.out
ustruct.ufield = ffffffff

No me gustan las advertencias, por lo que me gustaría silenciar esta molesta advertencia. Prefiero no usar #pragmas que se aplican a todo el archivo, ya que eso puede desactivar una advertencia de errores reales.


(Por cierto, recibe esta advertencia solo cuando inicializa un campo. No cuando inicializa un campo que no es


unsigned u = -1;  // no cmpiler warning.

Haciendo


struct U { unsigned ufield; } ustruct = { ~0U };

silencia el error.


Pero se señaló que si el tipo del campo no está sin firmar, sino uint64_t,
entonces ~0U proporciona un resultado diferente a -1:0x00000000FFFFFFFF en lugar de 0xFFFFFFFFFFFFFFFF.
(es decir, 32 bits de 1s, en lugar de 64 bits de 1s.)


La estructura U y el código de inicialización pueden vivir en lugares completamente diferentes,
y nos gustaría poder aumentar el tamaño del campo, una máscara de bits, sin informar a los usuarios.
Y la intención es obtener una "máscara de todos los 1" de cualquier tipo sin firmar que se esté utilizando.


Del mismo modo


struct U { unsigned ufield; } ustruct = { -1u };

silencia el bicho. (Para mi sorpresa, no sabía que -1 podía considerarse un no firmado).


Pero también es una constante de ancho finito.


DETALLE


Aquí hay un programa de prueba.
(Por cierto, todo lo que pregunto es sobre el uso de la constante literal firmada -1
para inicializar un miembro sin firmar. Las otras advertencias son solo pruebas.
No es necesario que me expliques que un número de 64 bits no cabe en 32 bits).


sh-3.2$ cat ./-1u.cpp 
#include <stdio.h>
unsigned um1 = -1;
unsigned un0u = ~0u;
unsigned un0ull = ~0ull;
struct Foo {
unsigned um1;
unsigned un0u;
unsigned un0ull;
};
Foo foo = { -1, ~0u, ~0ull };
int main(int argc, char** argv)
{
printf("um1 = %08x\n",um1);
printf("un0u = %08x\n",un0u);
printf("un0ull = %08x\n",un0ull);
printf("foo.um1 = %08x\n",foo.um1);
printf("foo.un0u = %08x\n",foo.un0u);
printf("foo.un0ull = %08x\n",foo.un0ull);
}
sh-3.2$ /mips/proj/performance/import/gcc/gcc-4.7.2.pkg/installdir/bin/gcc -Wall ./-1u.cpp
./-1u.cpp:7:20: warning: large integer implicitly truncated to unsigned type [-Woverflow]
./-1u.cpp:15:28: warning: narrowing conversion of '-1' from 'int' to 'unsigned int' inside { } is ill-formed in C++11 [-Wnarrowing]
./-1u.cpp:15:28: warning: narrowing conversion of '18446744073709551615ull' from 'long long unsigned int' to 'unsigned int' inside { } is ill-formed in C++11 [-Wnarrowing]
./-1u.cpp:15:28: warning: large integer implicitly truncated to unsigned type [-Woverflow]
sh-3.2$ /mips/proj/performance/import/gcc/gcc-4.7.2.pkg/installdir/bin/g++ -Wall ./-1u.cpp
./-1u.cpp:7:20: warning: large integer implicitly truncated to unsigned type [-Woverflow]
./-1u.cpp:15:35: warning: narrowing conversion of '-1' from 'int' to 'unsigned int' inside { } is ill-formed in C++11 [-Wnarrowing]
./-1u.cpp:15:35: warning: narrowing conversion of '18446744073709551615ull' from 'long long unsigned int' to 'unsigned int' inside { } is ill-formed in C++11 [-Wnarrowing]
./-1u.cpp:15:35: warning: large integer implicitly truncated to unsigned type [-Woverflow]

No ocurre en un compilador anterior:


sh-3.2$ /usr/bin/g++ -Wall ./-1u.cpp
./-1u.cpp:7: warning: large integer implicitly truncated to unsigned type
./-1u.cpp:15: warning: large integer implicitly truncated to unsigned type
/usr/bin/g++ --version
g++ (GCC) 4.1.2 20080704 (Red Hat 4.1.2-51)
Copyright (C) 2006 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

Respuestas:


Una versión un poco más fácil de usar de la respuesta de @Ali:


#include <type_traits>
struct all_ones_type {
template <typename T,
typename = typename std::enable_if<std::is_unsigned<T>::value>::type>
constexpr operator T () const
{ return static_cast<T>(-1); }
} const all_ones;
#include <iostream>
struct X {
unsigned short a;
unsigned long b;
unsigned long long c;
};
int main() {
X x = { all_ones, all_ones, all_ones };
std::cout << x.a << "\n"
<< x.b << "\n"
<< x.c << std::endl;
}

Dependiendo de lo que desee que suceda en un intento de conversión a tipo firmado, puede cambiar el enable_if para permitir todos los tipos integrales, o agregar otra sobrecarga con un bonito static_assert .