I have a codebase with byte defined like so:
typedef unsigned char byte;
And this enum:
enum SymbolTypes
{
VARIABLE_SYMBOL_TYPE = 0,
IDENTIFIER_SYMBOL_TYPE = 1,
STR_CONSTANT_SYMBOL_TYPE = 2,
INT_CONSTANT_SYMBOL_TYPE = 3,
FLOAT_CONSTANT_SYMBOL_TYPE = 4,
UNDEFINED_SYMBOL_TYPE = 5
};
At some point there is a comparison between a byte value and a member of this enum:
byte symbol_type = 0;
...
bool is_variable() { return (symbol_type == VARIABLE_SYMBOL_TYPE); }
When setting the compiler to use C++17, I get these errors:
error: reference to ‘byte’ is ambiguous
So I figured that maybe I'm supposed to be using std::byte now. After changing symbol_type to a std::byte, there is a new compiler error telling me that there's no operator == that matches the operands of std::byte and SymbolTypes. This SO answer says we can use an underlying type of std::byte like so:
enum SymbolTypes : std::underlying_type_t<std::byte>
{
VARIABLE_SYMBOL_TYPE = 0,
IDENTIFIER_SYMBOL_TYPE = 1,
STR_CONSTANT_SYMBOL_TYPE = 2,
INT_CONSTANT_SYMBOL_TYPE = 3,
FLOAT_CONSTANT_SYMBOL_TYPE = 4,
UNDEFINED_SYMBOL_TYPE = 5
};
However, the compiler still complains that there's no == operator with operands of type std::byte and SymbolTypes. How do I get this simple comparison line to compile correctly? Or should I be doing something else to avoid the usage of std::byte?
Related
This question already has answers here:
C++ Structure Initialization [duplicate]
(17 answers)
Closed 2 years ago.
I'm a trying to migrate some legacy C code for Embedded targets to C++ for compatibility issues and I am encountering some issues with unions in C++.
in our project we have the following style of union to reduce the amount of RAM usage:
typedef union unionTest
{
struct
{ uint32_t nField1:5
; uint32_t nField2:4
; uint32_t nField3:23
;}
; uint32_t nRaw ///< Raw data
;} unionTest;
// A global variable for configuration
unionTest myCUnion = {.nField1 = 1, .nField2 = 2, .nField3 = 3 };
This works well, is easy to use and the compiler initializes the bit fields at the correct values at compile time
Then when converting to C++
typedef union unionTest
{
struct
{ uint32_t nField1:5
; uint32_t nField2:4
; uint32_t nField3:23
;}
; uint32_t nRaw ///< Raw data
;} unionTest;
// Using const saves RAM, one of the reason to switch to C++
const unionTest myCppUnion = {.nField1 = 1, .nField2 = 2, .nField3 = 3 }; // Error
does not work anymore, I have a "too many initializers" error. Is there anyway to keep the C behavior. I know that anonymous structures are prohibited by ISO C++ but changing this has a huge impact on the code base, and was not judged important for now.
And I also tried with a named struct
typedef union unionTest
{
struct notAnonymous
{ uint32_t nField1:5
; uint32_t nField2:4
; uint32_t nField3:23
;} notAnonymous
; uint32_t nRaw ///< Raw data
;} unionTest;
// Using const saves RAM, one of the reason to switch to C++
const unionTest myCppUnion2 = {.notAnonymous.nField1 = 1, .notAnonymous.nField2 = 2, .notAnonymous.nField3 = 3 }; // expected primary-expression before '.' error
const unionTest myCppUnion3 = { .notAnonymous = { .nField1 = 1, .nField2 = 2, .nField3 = 3 } }; // Compiles
and I can't find a solution that keeps the original C behavior.
I have not yet tested if the solution "myCppUnion3" fills the bitfields correctly, but I a m more interested by finding a solution/workaround for solution "myCppUnion"
If anyone has any leads I'll glady take them !
In C++ you need an extra set of braces to reflect that the initializers are in a sub-struct:
const unionTest myCppUnion = { {.nField1 = 1, .nField2 = 2, .nField3 = 3 } };
I have a "too many initializers" error.
The solution to this is simple. You need a set of braces to explicitly express that you are initialising the nested class within the union:
{ // the union
{ // the struct
.nField1 = 1, // members of the struct
.nField2 = 2,
.nField3 = 3,
}
};
I know that anonymous structures are prohibited by ISO C++
Indeed, that is another thing that must be fixed for C++. With that fixed, you can use a designated initaliser for the struct as in your example.
Third problem you'll likely encounter is that C++ does not allow reading from an inactive union member, which seems likely to be what you do in C.
Fourth potential problem is that if you ever switch the compiler, you may find that the order of your bitfields changes which may be a problem if your program relies on particular order.
Consider an example where I want to create an array of arrays of bool:
int main() {
using type = bool[1];
bool a1[1] = {true};
bool a2[1] = {true};
bool a3[1] = {true};
type block_types[3] = {{a1}, {a2}, {a3}};
}
This code compiles for Clang 7.0.0, GCC 8.2 and MSVS v19.16.
Now, let's change bool to int:
int main() {
using type = int[1];
int a1[1] = {1};
int a2[1] = {1};
int a3[1] = {1};
type block_types[3] = {{a1}, {a2}, {a3}};
}
Now, the code stops compiling on any of those compilers, with errors similar to:
error: invalid conversion from 'int*' to 'int' [-fpermissive]
type block_types[3] = { {a1}, {a2}, {a3}};
^
Note: this exact error message comes from GCC 8.1.
Why is that? Why are bool[]s treated differently than int[]s? What's the rationale behind this?
In the first, block_types is an array of three arrays of one boolean. Each of the inner blocks in your initializer needs to provide a boolean value. You've given a pointer for each one, and pointers will convert to bool.
In the second, block_types is an array of three arrays of one integer, and each inner block of your initializer needs to provide an integer value. You have again given pointers, and they don't convert to integer.
So, no: the arrays are decaying on the same rules, but resulting pointer conversions distinguish the examples.
Say I have such a class:
enum class Flags : char
{
FLAG_1 = 1;
FLAG_2 = 2;
FLAG_3 = 4;
FLAG_4 = 8;
};
Now can I have a variable that has type flags and assign a value 7 for example? Can I do this:
Flags f = Flags::FLAG_1 | Flags::FLAG_2 | Flags::FLAG_3;
or
Flags f = 7;
This question arises because in the enum I have not defined value for 7.
You need to write your own overloaded operator| (and presumably operator& etc.).
Flags operator|(Flags lhs, Flags rhs)
{
return static_cast<Flags>(static_cast<char>(lhs) | static_cast<char>(rhs));
}
Conversion of an integer to an enumeration type (scoped or not) is well-defined as long as the value is within the range of enumeration values (and UB otherwise; [expr.static.cast]/p10). For enums with fixed underlying types (this includes all scoped enums; [dcl.enum]/p5), the range of enumeration values is the same as the range of values of the underlying type ([dcl.enum]/p8). The rules are trickier if the underlying type is not fixed - so don't do it :)
It's maybe better to make use of std::underlying_type instead of hard-coding char type.
Flags operator|(Flags lhs, Flags rhs) {
return static_cast<Flags>(
static_cast<std::underlying_type<Flags>::type>(lhs) |
static_cast<std::underlying_type<Flags>::type>(rhs)
);
}
Now, you can change the underlying type of your enumeration without needing to update that type in every bitwise operator overload.
It should handle any enumeration type. I'm not sure it doesn't have any side effects and is completely valid C++ code. Let me know if there are some issues.
template<class T, std::enable_if_t<std::is_enum_v<T>, int> = 0>
constexpr T operator|(T lhs, T rhs)
{
return static_cast<T>(
static_cast<std::underlying_type<T>::type>(lhs) |
static_cast<std::underlying_type<T>::type>(rhs));
}
Please don't do this. If you need to do this, enum classs probably aren't what you need.
#T.C. showed you how to do it so long as you specify underlying type, but you will run into places where your program does things it just shouldn't.
An example is where you use a switch and have a case for every defined enum value.
e.g.
enum class my_enum: unsigned int{
first = 1,
second = 2,
third = 4,
fourth = 8
};
int main(){
auto e = static_cast<my_enum>(static_cast<unsigned int>(my_enum::first) | static_cast<unsigned int>(my_enum::second));
switch(e){
case my_enum::first:
case my_enum::second:
case my_enum::third:
case my_enum::fourth:
return 0;
}
std::cout << "Oh, no! You reached a part of the program you weren't meant to!\n";
return 1;
}
Will output:
Oh, no! You reached a part of the program you weren't meant to!
then return the error code 1.
Which is also an example of why you should always have a default case, of course, but that isn't my point.
Of course, you could argue that so long as the user of the enum class never directly uses the value other than passing to a function; it would be a good way of restricting the values of a bitset. But I've always been a little too trustworthy and find std::uint[n]_t and some constexpr variables the best way (if a user sets an invalid bit it simply does nothing).
What you're doing just isn't really suitable for enum class, because it defeats the purpose of having a scoped enumeration. You can no longer enumerate the values if you set it to an undefined one.
I realize this question is a bit old, but I will write out a method I have used to do this.
(If anything, if I Google this again in the future I have it documented to find again.)
Personally I like this method because intellisense (at least the VSCode version of it... I don't have Visual Studio on Linux...) will automatically pick up on what you're doing and give you useful hints. Also, it avoids the use of macros, so the compiler can warn you if it is not happy. Lastly, without the comments, it isn't a lot of code. You're not making a class and setting a bunch of overloads or anything, but you're still getting the benefit of scoped enums so that you can reuse a flag name/value for another enum. Anyway onto the example.
namespace FlagsNS
{
/* This is an old/classic style enum so put it in a
namespace so that the names don't clash
(ie: you can define another enum with the values of
Flag_1 or Flag_2, etc... without it blowing up)
*/
enum Flags
{
Flag_1 = 1 << 0, //Same as 1
Flag_2 = 1 << 1, //Same as 2
Flag_3 = 1 << 2, //Same as 4
Flag_4 = 1 << 3 //Same as 8
};
}
/* This is telling the compiler you want a new "type" called Flags
but it is actually FlagsNS::Flags. This is sort of like using the
#define macro, except it doesn't use the preprocessor so the
compiler can give you warnings and errors.
*/
using Flags = FlagsNS::Flags;
//Later in code.... so int main() for example
int main()
{
//If you don't mind c-style casting
Flags flag = (Flags)(Flags::FLAG_1 | Flags::FLAG_2 | Flags::FLAG_3);
//Or if you want to use c++ style casting
Flags flag = static_cast<Flags>(Flags::FLAG_1 | Flags::FLAG_2 | Flags::FLAG_3);
//Check to see if flag has the FLAG_1 flag set.
if (flag & Flags::FLAG_1)
{
//This code works
}
}
The code in question doesn't compile. But you can do something like this,
enum class Flags : char
{
FLAG_1 = 1,
FLAG_2 = 2,
FLAG_3 = 4,
FLAG_4 = 8,
};
int main() {
Flags f = static_cast<Flags>(7);
Flags f1 = static_cast<Flags>( static_cast<char>(Flags::FLAG_1) | static_cast<char>(Flags::FLAG_2) | static_cast<char>(Flags::FLAG_3) );
return 0;
}
and it works
At this point, It probably makes sense to define your own class to handle this.
/** Warning: Untested code **/
struct Flag {
static Flag Flag_1;
static Flag Flag_2;
static Flag Flag_3;
static Flag Flag_4;
Flag operator = (Flag);
private:
char const value;
};
Flag operator | (Flag, Flag);
While reading an article, I came across the following function:
SolidColor::SolidColor(unsigned width, Pixel color)
: _width(width),
_color(color) {}
__attribute__((section(".ramcode")))
Rasterizer::RasterInfo SolidColor::rasterize(unsigned, Pixel *target) {
*target = _color;
return {
.offset = 0,
.length = 1,
.stretch_cycles = (_width - 1) * 4,
.repeat_lines = 1000,
};
}
What is the author doing with the return statement? I haven't seen anything like that before, and I do not know how to search for it... Is it valid for plain C too?
Edit:
link to the original article
This isn't valid C++.
It's (sort of) using a couple features from C known as "compound literals" and "designated initializers", which a few C++ compilers support as an extension. The "sort of" comes from that fact that to be a legitimate C compound literal, it should have syntax that looks like a cast, so you'd have something like:
return (RasterInfo) {
.offset = 0,
.length = 1,
.stretch_cycles = (_width - 1) * 4,
.repeat_lines = 1000,
};
Regardless of the difference in syntax, however, it's basically creating a temporary struct with members initialized as specified in the block, so this is roughly equivalent to:
// A possible definition of RasterInfo
// (but the real one might have more members or different order).
struct RasterInfo {
int offset;
int length;
int stretch_cycles;
int repeat_lines;
};
RasterInfo rasterize(unsigned, Pixel *target) {
*target = color;
RasterInfo r { 0, 1, (_width-1)*4, 1000};
return r;
}
The big difference (as you can see) is that designated initializers allow you to use member names to specify what initializer goes to what member, rather than depending solely on the order/position.
It is a C99 compound literal. This feature is specific to C99, but gcc and clang choose to implement it in C++ as well(as extension).
6.26 Compound Literals
ISO C99 supports compound literals. A compound literal looks like a
cast containing an initializer. Its value is an object of the type
specified in the cast, containing the elements specified in the
initializer; it is an lvalue. As an extension, GCC supports compound
literals in C90 mode and in C++, though the semantics are somewhat
different in C++.
I'm playing around with the Visual Studio 11 Beta at the moment. I'm using a strongly typed enum to describe some flags
enum class A : uint32_t
{
VAL1 = 1 << 0,
VAL2 = 1 << 1,
};
uint32_t v = A::VAL1 | A::VAL2; // Fails
When I attempt to combine them as above I get the following error
error C2676: binary '|' : 'A' does not define this operator or a conversion to a type acceptable to the predefined operator
Is this a bug with the compiler or is what I'm attempting invalid according to the c++11 standard?
My assumption had been that the previous enum declaration would be equivalent to writing
struct A
{
enum : uint32_t
{
VAL1 = 1 << 0,
VAL2 = 1 << 1,
};
};
uint32_t v = A::VAL1 | A::VAL2; // Succeeds, v = 3
Strongly typed enums are not implicitly convertible to integer types even if its underlying type is uint32_t, you need to explicitly cast to uint32_t to achieve what you are doing.
Strongly typed enums do not have operator | in any form. Have a look there: http://code.google.com/p/mili/wiki/BitwiseEnums
With this header-only library you can write code like
enum class WeatherFlags {
cloudy,
misty,
sunny,
rainy
}
void ShowForecast (bitwise_enum <WeatherFlags> flag);
ShowForecast (WeatherFlags::sunny | WeatherFlags::rainy);
Add: anyway, if you want uint32_t value, you'll have to convert bitwise_enum to uint32_t explicitly, since it's what enum class for: to restrict integer values from enum ones, to eliminate some value checks unless explicit static_casts to and from enum class-value are used.