Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using -1 to initialize an unsigned in { } initialization of struct or array

REALLY BRIEF

How do you create an unsigned constant that has all bits set?

...that you can use to initialize a field with { }s,

...that does not get a -Wnarrowing warning from GCC 4.7.2.

The following are not satisfactory:

 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

Basically, the user, the guy writing the {} initialization, does not know the type of the ufield. He only knows that it is an unsigned type, but not how wide. Not exactly which unsigned type it is.

* Another Reason Why I want as simple and elegant a syntax as possible *

I might as well mention something else: the "user" here is not actually writing a C or C++ program. He is editing a config file. A program, a simple Perl or Python script, processes the config file, and generates C code. This program is not very sophisticated, and at the moment passes through chunks of text that look like

 Foo: {-1,2,3};

to generate typedef struct Some_Struct { unsigned a; unsigned b, unsigned c; } Some_Struct = {-1,2,3}; // ditto

Basically, I want to be able to a nice user friendly syntax for a literal that says "All of the bits in this unsigned value are set". Without having to know how big of an unsigned. And without the program that handles the config file getting too complicated.

Lest the potential answer-provider complain that this is a new constraint, not realistic, etc:
I have had exactly the same problem with templates. I.e. with template types, where I want to write a literal that is "unsigned of any width, all 1s". In a template I might be more willing to do some of the ugly, Ugly, UGLY syntax that is obviously able to do this: but I really wish there was a simple, elegant, syntax.

* The Real Question *

Q: is there any way to create a constant that is "all 1s set" without triggering the GCC 4.7.2 warning?

BRIEF

I ran across a program that was using the literal constant -1 to initialize a field of a struct, e.g.

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

Although earlier versions of GCC accepted this without warning, the fairly recent version GCC 4.7.2 provides a warning:

> /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]

Note: this is only a warning. The result of converting -1 to unsigned is well defined in the C/C++ standards:

> ./a.out
ustruct.ufield    = ffffffff

I dislike warnings, so I would like to silence this annoying warning. I prefer not to use #pragmas that apply to the entire file, since that may disable a warning for real bugs.

(By the way, you get this warnng only when initializing a field. Not when initializing a non-field

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

Doing

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

silences the bug.

But it was pointed out that if the type of the field is not unsigned, but, instead, uint64_t, then ~0U provides a different result than -1: 0x00000000FFFFFFFF rather than 0xFFFFFFFFFFFFFFFF. (I.e. 32 bits of 1s, rather than 64 bits of 1s.)

The struct U and the initializaton code may live in completely different places, and we would want to be able to increase the size of the field, a bitmask, without informing users. And the intent is to get a "mask of all 1s" of whatever unsigned type is being used.

Similarly

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

silences the bug. (To my surprise - I did not know that -1 could be considered an unisgned.)

But is also a finite-width constant.

DETAIL

Here's a test program. (By the way, all I am asking about is the use of the signed literal constant -1 to initialize an unsigned member. The other warnings are just tests. You don;t need to explain to me that a 64 bit number doesn't fit in 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]

Doesn't occur in an earlier compiler:

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.
like image 679
Krazy Glew Avatar asked Feb 06 '13 22:02

Krazy Glew


1 Answers

A slightly more user-friendly version of @Ali's answer:

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

Depending on what you want to happen on an attempted conversion to signed type, you could change the enable_if to allow all integral types, or add another overload with a nice static_assert.

like image 162
aschepler Avatar answered Sep 17 '22 12:09

aschepler