Usage of signed vs unsigned variables for flags in C++
Asked Answered
C

3

10

Is there any "good practice" or habit of using signed variables vs unsigned variables for flags? Personally I would use unsigned variable, but I can see even signed variable used for flags in some code. I mean especially in library interface where it matters.

UDPATE I cannot accept answer "use Enum", because it is implementation dependent and this it cannot be used in library interface.

Changeable answered 22/4, 2015 at 10:48 Comment(16)
flags implies to me that it should be an enum..Viburnum
Do you mean for sets of bit flags? Or just a single flag? In the latter case I'd use a bool.Vainglorious
@Viburnum - for me, flag is not an enum - because flags can be used with bitwise operators like |, & and '~'.Chorea
Actually, when you want to manage bits, manage the sign is more complex and not needed. I also use unsigned variables.Gleaning
Since you don't intend to use the variable as a decimal there is no benefit in using a signed type. You could also consider std::bitset.Telemechanics
@Chorea enums can be used with bitwise operators.Bennettbenni
I mean flags which have up to (8 boolean variables = bits) in one byte and which can combined together. Even enum must have signed or unsigned variable in the background.Changeable
Enum cannot be used in library interface because it depends on compiler implementation if it is signed or unsigned. see implementation dependentChangeable
Unless you use a typed enum.Bennettbenni
@ user2079303 Strongly typed enums is feature of C++11, I can't use this, I need to support different compilers from old embedded up to modern one. Anyway if I use strongly typed enums, should it be signed or unsigned? I know it can be both, but is there a habit, good practice?Changeable
Why does it matter if on one platform enum translates to int and another to unsigned int? If you only operate with enum type without casting it to an int or unsigned it, it should not matter what type it is. Or am I missing something?Ranged
It matter a lot. If you build library in VS2013 and use it in application build in QT with different compiler, it could represent the enum in different way signed vs unsigned.Changeable
If you are using C++ interfaces to your library (not extern "C") you will be unable to use the library compiled by a different compiler than the user of your library uses. The C++ ABI changes from compiler to compiler and even from version to version...Logogriph
@qub1n I still don't see how that matters? Int and unsigned int are the same bitwise, so unless you are doing some arithmetic operations with them it should not matter. Usually you only use bitwise operators with flags and it should not matter if one is int and the other is unsigned int. Or am I still missing something? :)Ranged
@Ranged Yes, the problem is the unless. 1. I cannot guarantee that some of programmers in the team does not make such mistake which will emerge after porting to different platform. I would rather avoid to different binary interpretation in interfaces.Changeable
@Logogriph I use only extern "C" interfaces because of this.Changeable
T
3

I think an unsigned type is a better representation of a set of flags. Since you need a specific amount of equivalent bits to represent your flags. In a signed type the first bit is kind of special.

Probably the std::bitset could also meet your requirements. And will serve you with the basic logical operators and conversion methods.

#include <iostream>
#include <bitset>
using namespace std;

int main() {
    std::bitset<4> flags;
    flags.set(1);
    flags.set(3);
    std::cout << flags.to_string() << std::endl;
    std::cout << flags.to_ulong() << std::endl;
    return 0;
}

DEMO

Telemechanics answered 22/4, 2015 at 11:6 Comment(1)
And for those who are too lazy to follow the link: There are overloads for '&', '|', '^', and even '[]'.Petula
G
0

It is known that the flags are always non-negative integers, but why would you use int / unsigned int / short / unsigned short for a flag? Use #define, or even better, enum type.

enum flags
{
    FLAG0 = 0,
    FLAG1 = 1,
    FLAG2 = 2,
    ...
    FLAGN = n
};
Gasholder answered 22/4, 2015 at 10:57 Comment(11)
No, don't use #define.Bennettbenni
please read the comment of user2079303 another time, in fact read it as many times needed to remain it. And apply it, widely.Coldhearted
#define can be useful put in the header file if you have little flags (e.g. 2 or 3) and enum is like breaking down an open door in this situation. Although, in bigger projects I suggest always using enum.Gasholder
Ok, if I use define, the question remain unanswered... Should it be signed or unsigned, is there some practice, habit?Changeable
Better use enum. But if you are stubborn with #define, it does not really matter. The good practice is for the flags to be consecutive integers starting from 0 (as in the enum type in my post). But it is your flag. And anybody who wants to use it uses the name of the flag, not its value. So the choice is all yours.Gasholder
It is known that the flags are always positive integers, this isn't true. The underlying type of an enumeration shall be an integer type large enough to fit all the values of the enum; this is usually an int. Also each enumerated type shall be compatible with char or a signed/unsigned integer type.Vestibule
@PaperBirdMaster, ok, perhaps I went too far with the word 'always'. But have you ever seen a negative-value flag? Have you heard of somebody that does that? I think it is safe to say that It is known that the flags are ALMOST always positive integers since you have over 2 * 10^9 positive-integer flags to enumarate in C++. And I would say it is a good practice to do that as well.Gasholder
@FilipKowalski yes, I've seen negative values into enumerations, many times. enum e { UNKNOWN = -1, FOO = 0, BAR = 2, BAZ = 4 }; remember that the enums aren't used only for flagging. Anyway, if we want to ensure signess or unsigness for the enum underlying type we should be moving to C++ strong enums.Vestibule
@Vestibule So your opinion is that the signed type is commonly use and n-1 bits are used to be positive?Changeable
@qub1n My opinion is that you cannot make assumptions about the type of a flag unless you have total control over your source code (for example: if you're NOT developing a library to be used by other team) but if you make a choice you must stick wit it for consistency. Using integers for flagging (signed or unsigned) is error prone due to unexpected/uncontrolled type conversions and because an integer doesn't have flag semantics; if it was my choice I'll avoid integers (signed or unsigned) for this task and use an enum or a bitset or a vector<bool>.Vestibule
@qub1n but answering your question, if you must stick with integers for flagging, use signed ones if (and only if) the negative values have some kind of meaning and if (and only if) you're working in only one platform to avoid the different negative number representations problems which could have different binary layout in different platforms.Vestibule
R
0

If you decide to use enum for your flags, here is a useful macro that creates code for bitwise operators for your enum type.

#define GENERATE_ENUM_FLAG_OPERATORS(enumType) \
    inline enumType operator| (enumType lhs, enumType rhs) \
    { \
        return static_cast<enumType>(static_cast<int>(lhs) | static_cast<int>(rhs)); \
    } \
    inline enumType& operator|= (enumType& lhs, const enumType& rhs) \
    { \
        lhs = static_cast<enumType>(static_cast<int>(lhs) | static_cast<int>(rhs)); \
        return lhs; \
    } \
    inline enumType operator& (enumType lhs, enumType rhs) \
    { \
        return static_cast<enumType>(static_cast<int>(lhs) & static_cast<int>(rhs)); \
    } \
    inline enumType& operator&= (enumType& lhs, const enumType& rhs) \
    { \
        lhs = static_cast<enumType>(static_cast<int>(lhs) & static_cast<int>(rhs)); \
        return lhs; \
    } \
    inline enumType operator~ (const enumType& rhs) \
    { \
        return static_cast<enumType>(~static_cast<int>(rhs)); \
    }

Usage:

enum Test
{
    TEST_1 = 0x1,
    TEST_2 = 0x2,
    TEST_3 = 0x4,
};
GENERATE_ENUM_FLAG_OPERATORS(Test);

Test one = TEST_1;
Test two = TEST_2;

Test three = one | two;
Ranged answered 22/4, 2015 at 11:15 Comment(4)
I'd rather use std::underlying_type<enumType>::type instead of int.Beginning
erm, auto three = Test(TEST_1 | TEST_2) also works nicely with the default operators...Viburnum
I work with a C++03 compiler, so my code does not include C++11 syntax :)Ranged
@Viburnum Sure, but you need to type cast everytime. For me it is function(TEST_1 | TEST_2) vs function(Test(TEST_1 | TEST_2)) And all those operators are inline and should cost nothing in runtime.Ranged

© 2022 - 2024 — McMap. All rights reserved.