Utilizzo di -1 per inizializzare un'inizializzazione in { } senza segno di struct o array

Utilizzo di -1 per inizializzare un'inizializzazione in { } senza segno di struct o array


DAVVERO BREVE


Come si crea una costante senza segno con tutti i bit impostati?


...che puoi utilizzare per inizializzare un campo con { }s,


...che non riceve un avviso -Wnarrowing da GCC 4.7.2.


I seguenti non sono soddisfacenti:


 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

Fondamentalmente, l'utente, il ragazzo che scrive l'{} inizializzazione,
non conosce il tipo di ufield.
Sa solo che è un tipo senza segno, ma non quanto ampio.
No esattamente che tipo non firmato è.


* Un altro motivo per cui voglio una sintassi il più semplice ed elegante possibile *


Potrei anche menzionare qualcos'altro:l'"utente" qui non sta effettivamente scrivendo un programma C o C++.
Sta modificando un file di configurazione.
Un programma, un semplice script Perl o Python, elabora il config e genera codice C.
Questo programma non è molto sofisticato e al momento passa attraverso blocchi di testo simili a


 Foo: {-1,2,3};

per generare
typedef
struct Some_Struct { unsigned a; b senza segno, c senza segno; }
Some_Struct ={-1,2,3}; // idem


Fondamentalmente, voglio essere in grado di utilizzare una sintassi piacevole e intuitiva per un valore letterale che dice
"Tutti i bit in questo valore senza segno sono impostati".
Senza dover sapere quanto è grande un valore senza segno.
E senza che il programma che gestisce il file di configurazione diventi troppo complicato.


Per evitare che il potenziale fornitore di risposte si lamenti che si tratta di un nuovo vincolo, non realistico, ecc:

Ho avuto esattamente lo stesso problema con i modelli.
Ad es. con i tipi di template, dove voglio scrivere un letterale che sia "senza segno di qualsiasi larghezza, tutti 1s".
In un template potrei essere più disposto a fare alcune delle sintassi brutte, brutte e brutte
che ovviamente è in grado di farlo:
ma vorrei davvero che ci fosse una sintassi semplice, elegante.


* La vera domanda *


D:esiste un modo per creare una costante che sia "tutti gli 1 impostati"
senza attivare l'avviso GCC 4.7.2?


BRIEF


Mi sono imbattuto in un programma che utilizzava la costante letterale -1 per inizializzare un campo di una struttura, ad es.


> 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);
}

Sebbene le versioni precedenti di GCC lo accettassero senza preavviso,
la versione abbastanza recente GCC 4.7.2 fornisce un avviso:


> /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:questo è solo un avvertimento. Il risultato della conversione di -1 in unsigned è ben definito negli standard C/C++:


> ./a.out
ustruct.ufield = ffffffff

Non mi piacciono gli avvisi, quindi vorrei mettere a tacere questo fastidioso avviso. Preferisco non usare #pragmas che si applicano all'intero file, poiché ciò potrebbe disabilitare un avviso per bug reali.


(A proposito, ricevi questo avviso solo durante l'inizializzazione di un campo. Non durante l'inizializzazione di un non campo


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

Fare


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

silenzia il bug.


Ma è stato sottolineato che se il tipo del campo non è unsigned, ma, invece, uint64_t,
allora ~0U fornisce un risultato diverso da -1:0x00000000FFFFFFFF anziché 0xFFFFFFFFFFFFFFFF.
(cioè 32 bit di 1s, anziché 64 bit di 1s.)


La struttura U e il codice di inizializzazione possono risiedere in luoghi completamente diversi,
e vorremmo essere in grado di aumentare la dimensione del campo, una maschera di bit, senza informare gli utenti.
E l'intento è quello di ottenere una "maschera di tutti gli 1" di qualsiasi tipo non firmato venga utilizzato.


Allo stesso modo


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

silenzia l'insetto. (Con mia sorpresa - non sapevo che -1 potesse essere considerato un unsigned.)


Ma è anche una costante di larghezza finita.


DETTAGLIO


Ecco un programma di test.
(A proposito, tutto ciò che chiedo è l'uso della costante letterale con segno -1
per inizializzare un membro senza segno. Gli altri avvisi sono solo test.
Non c'è bisogno di spiegarmi che un numero a 64 bit non rientra in 32 bit.)


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]

Non si verifica in un compilatore precedente:


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.

Risposte:


Una versione leggermente più intuitiva della risposta di @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;
}

A seconda di cosa vuoi che accada in un tentativo di conversione al tipo firmato, puoi cambiare il enable_if per consentire tutti i tipi integrali, o aggiungere un altro overload con un bel static_assert .