15

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.
Krazy Glew
  • 7,210
  • 2
  • 49
  • 62
  • Why not use `~0ULL` always? –  Feb 06 '13 at 22:32
  • 1
    `-1U` isn't the constant `-1` with a suffix of `U` - it's the constant `1U` as the operand of the unary negation operator `-`. – caf Feb 06 '13 at 22:35
  • 2
    `-1` is the correct way, why the heck does gcc warn about that, that's stupid. - Oh, you compile as C++, then gcc might have a point (not sure, but it says C++11 doesn't allow it). – Daniel Fischer Feb 06 '13 at 22:36
  • @H2CO3 - ~0ULL gets -Woverflow errors. // Plus, I live in a world where uint128_t may be expected to be used sometimes soon, and possibly even uin512_t. ~0ULL is not guaranteed to be the widest possible number. – Krazy Glew Feb 06 '13 at 22:40
  • 1
    @DanielFischer: -1 is correct, what the programmer intended. It's just a warning --- and, frequently, assigning a negative number to an unsigned is the sort of bug that you want a compiler to catch. (I am pretty sure that it has caused buffer overflow security flaws). What I want is a way to say "I am sure right here, don't give me the -Wnarrowing right here, but please continue to do so elsewhere." – Krazy Glew Feb 06 '13 at 22:42
  • @DanielFischer: interestingly, GCC 4.7.2 gave me the -narrowing error, whether compiled as C++ or C. By the way, it does suggest that the standard disallows -1 specifically in {} initializers. – Krazy Glew Feb 06 '13 at 22:44
  • @DanielFischer: From http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2011/n3242.pdf // 8.5.1 Aggregates [dcl.init.aggr] // ... // 2 When an aggregate is initialized by an initializer list, ... If the initializer-clause is an expression and a narrowing conversion (8.5.4) is required to convert the expression, the program is ill-formed. // I.e. it does look like a legitimate warning from GCC 4.7.2 – Krazy Glew Feb 06 '13 at 22:50
  • Interesting. 4.7.1 only warns for C++, and I tend to believe it when it says it's malformed in C++11. In C, I don't expect a warning, since it's perfectly okay to do that. I guess the only way to silence it is to know which type you need and cast. – Daniel Fischer Feb 06 '13 at 22:51
  • @ecatmur asked if this is C or C++. I think I used C++ above, but I am equally happy with answers in C or C++. As I admitted later, this is in generated code - so we can generate either C or C++. // As mentioned in other comments, gcc 4.7.2 complains for both C and C++, although the error message seems to apply specifically to C++. – Krazy Glew Feb 12 '13 at 19:44

7 Answers7

6

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.

aschepler
  • 70,891
  • 9
  • 107
  • 161
  • +1 - *"A slightly more user-friendly version..."* - In fact looks like a completely superior version and the solution to the OP's problem (didn't test it though, but I'm going to trust you here). In the end this doesn't require any modification or reflection of the struct type, as the OP wanted. – Christian Rau Feb 07 '13 at 17:05
  • By the way, `constexpr` wouldn't be a bad idea for this one either, would it? – Christian Rau Feb 07 '13 at 17:12
  • @aschepler Could you please explain how `constexpr operator T () const { return ~T(); }` works? I have difficulties understanding it... :( – Ali Feb 07 '13 at 18:16
  • @Ali: `T()` value-initializes a temporary of the given type. For a numeric type, that means a zero value. And `~` is the plain old bitwise-not operator (and not part a pseudo-destructor name). You could also put `~T(0)` or `static_cast(-1)`. – aschepler Feb 07 '13 at 18:21
  • +1 A cool solution & I have learned something new today, thanks. – Ali Feb 07 '13 at 18:23
  • 1
    Maybe `static_cast(-1)` would have been better, since otherwise `unsigned char/short` get promoted to `int` during the `~`, and the negation of `0` not neccessarily being `-1` for `int` by standard (even if probably working in practice). Whereas `static_cast(-1)` should always be well-defined. Damn compiler, just use the type I tell you, I hate integral promotions. ;) – Christian Rau Feb 07 '13 at 18:40
  • @ChristianRau: Wouldn't that mean that even `void f(unsigned char& x) { x = ~x; }` has implementation-defined behavior? – aschepler Feb 07 '13 at 20:34
  • @aschepler Indeed, if `~(int)x` is negative and `int` is not twos-complement (which the standard doesn't require), then `(unsigned char)(~(int)x)` doesn't need to equal the plain negation of `x`, since signed-to-unsigned conversion operates on the values, while negation operates on the bit representation, and the bit representation *is* changed when converting a non-twos-complement negative number to unsigned (which is always twos-complement by standard). That's why messing with numbers smaller than `unsigned int` and binary operations can be a pain to keep *completely* platform-independent. – Christian Rau Feb 07 '13 at 22:19
  • @aschepler For a ones-complement `int` implementation the above for `f(5 = 00000101)` would result in `~int(5) = int(-5)` and `(unsigned char)(-5) = 11111011`, which is, as we know, `~5+1 != ~5`. Though, of course those observations are rather of theoretical nature, since nowadays you probably won't find that many non-twos-complement architectures, but you never know and technically it is implementation-defined. – Christian Rau Feb 07 '13 at 22:22
  • @ChristianRau: thanks, this is the most elegant - I figured there was something like this. A template conversion to T within a template. It may be a bit ugly to put syntax like all 1s in a config file, but it is certainly the most elegant I have seen. // Hey, this gives me an idea. – Krazy Glew Feb 08 '13 at 23:40
  • ... Hey, this gives me an idea. // Nope, I thought maybe I could create a free function template conversion operator T() that would work only for argument -1. But I get error: 'operator T(int)' must be a nonstatic member function – Krazy Glew Feb 09 '13 at 00:01
4

How about this? It only works for unsigned types but the question specifically says unsigned. (See rubenvb's comments below.)

#include <cinttypes>
#include <iomanip>
#include <iostream>
#include <limits>
#include <type_traits>

template <typename T>
T all_bits_one() {
    static_assert(std::is_unsigned<T>::value, "the type must be unsigned");
    return std::numeric_limits<T>::max();
}

struct Ui {
    typedef unsigned int the_type;
    the_type ufield;
};

struct ULL {
    typedef unsigned long long the_type;
    the_type ufield;
};

struct U64 {
    typedef uint64_t the_type;
    the_type ufield;
};

int main() {

    using namespace std;

    Ui  ui  = { all_bits_one< Ui::the_type>() };
    ULL ull = { all_bits_one<ULL::the_type>() };
    U64 u64 = { all_bits_one<U64::the_type>() };

    cout << hex;
    cout << "unsigned int:       " <<  ui.ufield << endl;
    cout << "unsigned long long: " << ull.ufield << endl;
    cout << "unsigned int 64:    " << u64.ufield << endl;

    //all_bits_one<int>(); // causes compile-time error if uncommented

    return 0;
}

The user doesn't have to know the exact type of the_type or the number of bits it is represented on.

There is some code duplication which could be removed but that would require a better understanding of your code and the problem you are dealing with.

I guess you simplified your code before posting. As it stands, your structs make no sense to me, a simple typedef would suffice.

Ali
  • 56,466
  • 29
  • 168
  • 265
  • `all_bits_one()` doesn't have all bits one. You'd have to use `-1` for the signed ones through some SFINAE. And make the function `constexpr` while you're at it `;-)`. – rubenvb Feb 07 '13 at 12:18
  • 1
    @rubenvb Sorry, the question specificly says **unsigned.** – Ali Feb 07 '13 at 12:20
  • True, but generality isn't an offence. And your template, being a template, returns the wrong "answer" for some inputs. – rubenvb Feb 07 '13 at 12:39
  • @rubenvb OK, I updated the answer. In any case, we should know more about the actual problem: As it stands, you can solve the whole thing just with typedefs, there is no need for structs, etc. I will not put more energy into solving a problem that I don't even know. – Ali Feb 07 '13 at 12:46
  • @Ali *"As it stands, you can solve the whole thing just with typedefs"* - I think the OP wants to not make any changes to the `struct` and neither know anything about its field's type (except that it is *some* unsigned integer). Therefore a typedef solution will only shift the problem to the designer of the `struct` having to explicitly name the type, which might not be possible with a closed and independent design of the struct *"somewhere far away"*. Nevertheless +1. *"solving a problem that I don't even know"* - I think the question has made the problem and requirements pretty clear. – Christian Rau Feb 07 '13 at 18:26
  • @ChristianRau Thanks for the feedback. It is not said explicitly that he has no control over the `struct`s. I had no idea how to interpret "somewhere far away". As the structs are currently now, they just wrap up the integer and you get the same effect by a `typedef`, no need to introduce a structs. Perhaps the OP simplified his code and that is why I don't understand his problem. As for *"solving a problem that I don't even know"* was an answer to rubenvb, and it is trying to say that I would need more information of the actual problem the OP is trying to solve. – Ali Feb 08 '13 at 09:02
  • @ChristianRau Anyway, I upvoted aschelper's answer, in my opinion, that is the nicest answer. – Ali Feb 08 '13 at 09:03
2

Why not provide the mask along with the type?

C:

 struct U { unsigned ufield; };
 #define U_MASK (-1U)

 // somewhere far away
 U u = {U_MASK};

C++:

struct U { unsigned ufield; static constexpr unsigned MASK = -1; };

 // somewhere far away
 U u = {U::MASK};
ecatmur
  • 152,476
  • 27
  • 293
  • 366
2

All fancy template code aside, here's some fancy C++11 code:

struct Foo
{
  unsigned a;
  unsigned long b;
  unsigned long long c;
};

Foo foo = { decltype(Foo::a)(-1), decltype(Foo::b)(-1), decltype(Foo::c)(-1) };

which is error-prone, but functional.

The best solution is still to use a (typed) enum (class) for this.

rubenvb
  • 74,642
  • 33
  • 187
  • 332
  • Of course `decltype`, forgot about that. Well, you need to know the name of the field, but at least you can stay ignorant of its type. – Christian Rau Feb 07 '13 at 17:09
  • 1
    @Christian note I don't really recommend this, it's just a cheap workaround. Why are you not setting this value in the constructor? It looks to be little more than a default, for which the constructor is perfectly suited. – rubenvb Feb 07 '13 at 19:14
1

Inspired by Ali, but using Template Argument Deduction.

T all_bits_one(T& dummy) { return ~(T()); }
unsigned long u = all_bits_one(u);
MSalters
  • 173,980
  • 10
  • 155
  • 350
  • Am I right in that this works because `u` already has a valid but unitialized value when calling `all_bits_one`? Could you maybe point to the standard for this? – Christian Rau Feb 07 '13 at 17:20
  • Yes, some sort of explanation would be nice, I have difficulties understanding this approach. – Ali Feb 07 '13 at 18:18
  • @Ali He just uses the type of the variable itself (but not its value) to determine the correct type of the return value (which in turn is just `~0` in the appropriate type) using template argument dedcution. Though in the OP's example it would rather be `Ueg u = { all_bits_one(u.ufield) };` – Christian Rau Feb 07 '13 at 18:31
  • Christian is right. I'm using the `dummy` argument only to determine which _type_ to return. No lvalue-to-rvalue conversion is needed. i.e. I'm not reading the uninitialized variable. – MSalters Feb 08 '13 at 07:21
  • @MSalters By the way, you're suffering from the same problem of `unsigned char/short` getting promoted to `int` before `~` and `~int(0)` not neccessarily being equal to `-1` in the presence of non-twos-complement `int`s. – Christian Rau Feb 08 '13 at 09:08
  • `return -1;` might be preferable – M.M Jan 25 '16 at 14:43
1

Another way

// C++03 and C++11
Ueg u = { (Ueg().ufield - 1) };

// C99 and C11 (works only inside of functions)
Ueg u = { (Ueg){0}.ufield - 1 };
Johannes Schaub - litb
  • 496,577
  • 130
  • 894
  • 1,212
0

Maybe what you want by initializing an uint to -1 is that all bits are set to '1'?
In that case:

typedef uint MyUIntType;
MyUIntType mID = (MyUIntType)~0;

~ applies a "1's complement" to 0 which effectively flips all its bits.
The result will be the biggest value that your uint type can hold, which is useful in cases where 0 is a meaningful value and variables need to be initialized to "something else".

brita_
  • 487
  • 7
  • 13
  • Yes, but when I posed the problem I said “the user does not know the type of the value”. The user does not know what type to put in the cast. The user does not even know the name of an opaque type associated with the value. You might also observe that I used various ~0 flavors in my initial examples. – Krazy Glew Aug 29 '18 at 13:22