Using an already-defined struct as an anonymous member of a union - c++

Let's say I have a 32-bit hardware register Reg that I want to be able to access either as a 32-bit value (e.g. Reg = 0x12345678) or as bitfields (e.g. Reg.lsw = 0xABCD). I can achieve this by declaring a union with anonymous struct member, and declaring assignment and conversion operators to/from uint32_t. In a little-endian environment, the code might look like this:
#include <cstdint>
#include <cstdio>
typedef union
{
uint32_t val ;
struct
{
uint32_t lsw : 16 ;
uint32_t msw : 16 ;
} ;
operator = (uint32_t n) { val = n ; }
operator uint32_t() const { return val ; }
} HWR ;
int main()
{
HWR Reg ;
Reg = 0x12345678 ;
Reg.lsw = 0xABCD ;
printf ("%X\n", uint32_t(Reg)) ;
}
But now let's say I have a whole bunch of these registers, each with its own bitfield layout, and I have a header file FieldDefs.h that declares these bitfield layouts as named structures. How can I use these named structures in the above code, so that I can access the 32-bit value and also the individual bitfields? I could do it like this:
#include "FieldDefs.h" // Defines struct MyHWR
typedef union
{
uint32_t val ;
struct MyHWR field ;
operator = (uint32_t n) { val = n ; }
operator uint32_t() const { return val ; }
} MyHWRUnion ;
But now instead of Reg.lsw =..., I need to type Reg.field.lsw =...
Is there any way (in C++17) to declare an already defined struct as an anonymous member of a union? I am using g++ version 7.3.0 if it matters.

union
{
// ...
struct
{
// ...
};
This is an anonymous struct. Anonymous structs are ill-formed in C++. Only unions may be anonyous. This is different from C where anonymous structs are allowed (since C11).
Is there any way (in C++17) to declare an already defined struct as an anonymous member of a union?
No. Unnamed members cannot have a named type.
You'll need to make a choice between the unnamed member and the pre-declared class. Given that the anonymous struct is non-standard in the first place, I recommend going with the named member and pre-defined class. Maybe give it a short name to minimise verbosity.

I suppose none will like this answer, neither OP (since requires g++ 9.1), neither C++ gurus (UB smells?), but I am still a little proud of tinkering it.
There is [[no_unique_address]] attribute coming in C++20 and g++ 9.1 already supports it (even without -std=c++2a flag).
How can it be utilized here?
By test and trials it seems that if we create proxy member val marked with it it will take address of object1.
Thus we can create Proxy class which has operator=(uint32_t) and operator uint32_t that treats this as uint32_t. The proxy object has no address, does not increase size of struct that utilizes it.
Bitfields names have to be added by inheritance, which got wrapped in simple template, for consistency named HWR.
Voilà, we have HWR<bitfield> object which can be assigned to uint32_t directly, by val member and gives access to bitfields names.
https://godbolt.org/z/N2xEmz
#include <bits/stdint-uintn.h>
#include <cstddef>
#include <cstdint>
#include <cstdio>
// Example bifields, I assumed you have such in "FieldDefs.h"
struct bitfield {
uint32_t lsw : 16;
uint32_t msw : 16;
};
struct ThisProxy {
uint32_t& operator=(uint32_t n) {
auto& uint = *reinterpret_cast<uint32_t*>(this);
uint = n;
return uint;
}
operator uint32_t() const { return *reinterpret_cast<const uint32_t*>(this); }
};
template <typename Bitfield>
struct HWR : Bitfield {
static_assert(sizeof(Bitfield) == 4, "Bad things would happen");
HWR& operator=(uint32_t n) {
this->val = n;
return *this;
}
operator uint32_t() const { return this->val; }
[[no_unique_address]] ThisProxy val;
};
int main() {
HWR<bitfield> Reg;
// Sanity check that proxy points at &Reg and does not increase size
static_assert(offsetof(HWR<bitfield>, val) == 0, "");
static_assert(sizeof(HWR<bitfield>) == 4, "");
Reg = 0x12345678;
Reg.val = 0x8765432A;
Reg.lsw = 0xABCA;
printf("%X\n%ld\n", uint32_t(Reg), sizeof(Reg));
return 0;
}
Edit:
As it turned out that access by Reg.val is not mandatory the trick with inheritance + reinterpret_cast can be reused in pre-C++20 code.
template <typename Bitfield> struct HWR : Bitfield {
static_assert(sizeof(Bitfield) == 4, "Bad things would happen");
HWR &operator=(uint32_t n) {
*reinterpret_cast<uint32_t *>(this) = n;
return *this;
}
operator uint32_t() const {
return *reinterpret_cast<const uint32_t *>(this);
}
};
There is still smell of reinterpret_cast and I need to find out oine thing to fully recommend this code. Whenever bitfield can be interpreted by underlying type uint32_t.
1 I am not sure whenever offset of 0 is guaranteed by P0840R2.
PS. g++ complains with warning: offsetof within non-standard-layout type ‘HWR<bitfield>’ is conditionally-supported [-Winvalid-offsetof], but I didn't try to find workaround for it.
PPS. No anonymous structs!

Related

Initializing C struct with unions declared in C++17

This question is specifically about options in C++17. Assuming following declaration in C library (I cannot change them):
typedef enum {
TYPEA = 0,
TYPEB = 2,
TYPEC = 4
} SPECIFIC_TYPE_t;
typedef struct {
uint16_t Method : 1;
uint16_t Access : 2;
uint16_t VendorSpecific : 1;
uint16_t Direction : 1;
uint16_t Persistent : 1;
uint16_t Internal : 4;
uint16_t Reserved : 6;
} PROPERTY_t;
typedef RESULT_t (*CB_DataPointRead_t) (void *Service, uint8_t Pinpoint, bool VendorSpecific,
uint16_t GroupID, uint16_t ElementID, void *Data,
uint8_t *DataLengthInOut);
typedef RESULT_t (*CB_DataPointWrite_t) (void *service, uint8_t Pinpoint, bool VendorSpecific,
uint16_t GroupID, uint16_t ElementID, void *Data,
uint8_t DataLength);
typedef struct {
uint16_t GroupID;
uint16_t ElementID;
uint8_t Pinpoint;
SPECIFIC_TYPE_t Type;
uint8_t Size;
PROPERTY_t Property;
union {
struct {
CB_DataPointRead_t Read;
CB_DataPointWrite_t Write;
} Callback;
struct {
void *Data;
} DirectAccess;
} AccessType;
} DataPoint_t;
Status in pure C
To initialize the last element designator initializers work well in C:
uint16t dataPointValue = 0;
const DataPoint_t firstDatapointConfig = {
/*...*/,
.DirectAccess = { (void*)&dataPointValue; }
};
Designator initializers in C++
They appear in C++20 and aren't compatible with C in many aspects.
Problem
I'd like to initialize variable like firstDatapointConfig as const qualified in C++17. So far I don't see a way other than write a function in C (compiled as C code) and return the initialized structure to a variable before use. I tried various ways, including gnuc++17 which handles designator initializers, except it tells me:
error: 'const DataPoint_t' has no non-static data member named 'DirectAccess'
and MSVC don't digest this method of initialization at all without C++20 enabled.
Addressing the last element outside initializer, don't work either:
datapoint.DirectAccess = { &value };
results in the following error:
error: 'struct DataPoint_t' has no member named 'DirectAccess'
Comment
It was much easier to use these structures in Rust after processing them through bindgen :-)
Question
Is there a way to initialize the variable of DataPoint_t type in C++17 with DirectAccess element filled with the right value?
Is there a way to initialize the variable of DataPoint_t type in C++17 with DirectAccess element filled with the right value?
Yes, even though my way of doing it is a little cumbersome. There may be easier ways:
// Make a usable type of that anonymous entity:
using AccessType_t = decltype(DataPoint_t::AccessType);
// Use the new type and prepare what you need:
AccessType_t at;
at.DirectAccess.Data = nullptr;
// initialize your DataPoint_t
const DataPoint_t firstDatapointConfig{1,2,3,TYPEA,4, PROPERTY_t{}, at};
If you do this a lot you could make a helper function:
using AccessType_t = decltype(DataPoint_t::AccessType);
using Callback_t = decltype(AccessType_t::Callback);
using DirectAccess_t = decltype(AccessType_t::DirectAccess);
template<class U>
constexpr auto init_AccessType(U u) {
AccessType_t at;
if constexpr (std::is_same_v<U,Callback_t>) {
at.Callback = u;
} else if constexpr (std::is_same_v<U,DirectAccess_t>) {
at.DirectAccess = u;
} else {
// uninitialized
}
return at;
}
const DataPoint_t firstDatapointConfig{1,2,3,TYPEA,4, PROPERTY_t{},
init_AccessType(DirectAccess_t{nullptr})};
I'd like to initialize variable like firstDatapointConfig as const qualified in C++17
With all that C++ has to offer:
constexpr DataPoint_t create_DataPoint_t(uint16_t *v) {
DataPoint_t r{};
r.AccessType.DirectAccess.Data = v;
return r;
}
const DataPoint_t firstDatapointConfig = create_DataPoint_t(&dataPointValue);
with designated initializers:
const DataPoint_t firstDatapointConfig2 = {
.AccessType = {
.DirectAccess = {
.Data = &dataPointValue
}
}
};
Addressing the last element outside initializer, don't work either:
datapoint.DirectAccess = { &value };
Because there is no such element, there is datapoint.AccessType.DirectAccess.Data.
To initialize the last element designator initializers work well in C:
uint16t dataPointValue = 0;
const DataPoint_t firstDatapointConfig = {
/*...*/,
.DirectAccess = { (void*)&dataPointValue; }
};
The presented code is invalid - uint16t is meant to be uint16_t and ; is a typo. And still after fixing the typos, no, the presented code is invalid in "pure C" and "does not work well" godbolt link. There is no such thing as DirectAccess in DataPoint_t - there is such member in the unnamed union declared inside DataPoint_t. You can do in C:
const DataPoint_t firstDatapointConfig3_in_C = {
.AccessType = {
.DirectAccess = { (void*)&dataPointValue }
}
};
or
const DataPoint_t firstDatapointConfig4_in_C = {
.AccessType.DirectAccess = { (void*)&dataPointValue }
};
or
const DataPoint_t firstDatapointConfig4_in_C = {
.AccessType.DirectAccess.Data = (void*)&dataPointValue
};
The cast to void* is superfluous - all pointers are implicitly converted to void*. Note that the following:
const DataPoint_t firstDatapointConfig5_in_C = {
.AccessType = { (void*)&dataPointValue }
};
would be equal to:
const DataPoint_t firstDatapointConfig6_in_C = {
.AccessType.Callback.Read = (void*)&dataPointValue
};
Most probably you are coding under -fms-extensions with GNU gcc or with MSVC, in which case you should be aware that you are using an extension that imports unnamed structure members to parent structure. The code is invalid in "pure C", it's using an extension to C.

Why no default hash for C++ POD structs?

I want to use a POD struct as a hash key in a map, e.g.
struct A { int x; int y; };
std::unordered_map<A, int> my_map;
but I can't do this, since no hash function is auto-generatable for such structs.
Why does the C++ standard not require a default hash for a POD struct?
Why do compilers (specifically, GCC 4.x / 5.x) offer such a hash, even if the standard doesn't mandate one?
How can I generate a hash function, using a template, in a portable way, for all of my POD structures (I'm willing to make semantic assumptions if necessary)?
As from the documentation, a possible implementation in your case would be:
#include<functional>
#include<unordered_map>
struct A { int x; int y; };
namespace std
{
template<> struct hash<A>
{
using argument_type = A;
using result_type = std::size_t;
result_type operator()(argument_type const& a) const
{
result_type const h1 ( std::hash<int>()(a.x) );
result_type const h2 ( std::hash<int>()(a.y) );
return h1 ^ (h2 << 1);
}
};
}
int main() {
std::unordered_map<A, int> my_map;
}
The compiler us not allowed to generate such a specialization because of the standard that does not define anything like that (as already mentioned in the comments).
There is a method to generate hash for POD, like good old c style. Only for real POD with no any linked data on the outside of struct. There is no checking of this requirements in code so use it only when you know and can guarantee this. All fields must be initialized (for example by default constructor like this A(), B() etc).
#pragma pack(push) /* push current alignment to stack */
#pragma pack(1) /* set alignment to 1 byte boundary */
struct A { int x; int y; };
struct B { int x; char ch[8] };
#pragma pack(pop) /* restore original alignment from stack */
struct C { int x __attribute__((packed)); };
template<class T> class PodHash;
template<>
class PodHash<A> {
public:
size_t operator()(const A &a) const
{
// it is possible to write hash func here char by char without using std::string
const std::string str =
std::string( reinterpret_cast<const std::string::value_type*>( &a ), sizeof(A) );
return std::hash<std::string>()( str );
}
};
std::unordered_map< A, int, PodHash<A> > m_mapMyMapA;
std::unordered_map< B, int, PodHash<B> > m_mapMyMapB;
UPD:
Data structure must be defined in data packing section with value of one byte or with pack attribute for prevent padding bytes.
UPD:
But I need to warn that replace deafult packing will make data loading/storing from/to memory for some fields little slowly, to prevent this need to arrange structure data fields with granularity that corresponding your (or most popular) architecture.
I suggest that you can add by yourself additional unused fields not for using but for arrange fields in your data structure for best prformance of memory loading/storing. Example:
struct A
{
char x; // 1 byte
char padding1[3]; // 3 byte for the following 'int'
int y; // 4 bytes - largest structure member
short z; // 2 byte
char padding2[2]; // 2 bytes to make total size of the structure 12 bytes
};
#pragma pack is supported by, at least:
Microsoft compiler
GNU compiler (webarchive)
clang-llvm compiler (webarchive)
Embarcadero (Borland) compiler (webarchive)
Sun WorkShop Compiler (webarchive)
Intel compiler is compatible with GCC, CLANG and Microsoft compiler
More flexible way is to declare comparision class and use it as template param of std::unordered_map.
struct A { int x; int y; };
emplate<class T> class MyHash;
template<>
class MyHash<A> {
public:
size_t operator()(const A &a) const
{
result_type const h1 ( std::hash<int>()(a.x) );
result_type const h2 ( std::hash<int>()(a.y) );
return h1 ^ (h2 << 1);
}
};
std::unordered_map<CString,CString,MyHash> m_mapMyMap;
You may want another Hash for same objects. Flexibility appear with code like this:
std::unordered_map<CString,CString, *MyAnotherHas* > m_mapMyMap;

Member array with size defined by template parameter, but why no warning for zero-size array?

I was trying to write a templated base class to store a fixed number of data types, each with varying length. Here is a simplified version of much what I was trying to do:
template< int NINT, int NR0 >
class EncapsulatedObjectBase
{
public:
EncapsulatedObjectBase();
~EncapsulatedObjectBase();
double m_real[NR0];
int m_int[NINT];
}
Yeah...so the template parameters can be zero, thus declaring a zero-length array of objects. There will be multiple derived classes for this base, each defining their own number of variables. I have two questions:
1) Is this approach fundamentally flawed?
2) If so...why doesn't icc13 or gcc4.7.2 give me warnings about this when I instantiate a zero-length array? For gcc I use -wall and -wextra -wabi. The lack of warnings made me think that this sort of thing was OK.
EDIT:
Here is the contents of a file that show what I am talking about:
#include <iostream>
template< int NINT, int NR0 >
class EncapsulatedObjectBase
{
public:
EncapsulatedObjectBase(){}
~EncapsulatedObjectBase(){}
double m_real[NR0];
int m_int[NINT];
};
class DerivedDataObject1 : public EncapsulatedObjectBase<2,0>
{
public:
DerivedDataObject1(){}
~DerivedDataObject1(){}
inline int& intvar1() { return this->m_int[0]; }
inline int& intvar2() { return this->m_int[1]; }
};
class DerivedDataObject2 : public EncapsulatedObjectBase<0,2>
{
public:
DerivedDataObject2(){}
~DerivedDataObject2(){}
inline double& realvar1() { return this->m_real[0]; }
inline double& realvar2() { return this->m_real[1]; }
};
int main()
{
DerivedDataObject1 obj1;
DerivedDataObject2 obj2;
obj1.intvar1() = 12;
obj1.intvar2() = 5;
obj2.realvar1() = 1.0e5;
obj2.realvar2() = 1.0e6;
std::cout<<"obj1.intvar1() = "<<obj1.intvar1()<<std::endl;
std::cout<<"obj1.intvar2() = "<<obj1.intvar2()<<std::endl;
std::cout<<"obj2.realvar1() = "<<obj2.realvar1()<<std::endl;
std::cout<<"obj2.realvar2() = "<<obj2.realvar2()<<std::endl;
}
If I compile this with "g++ -Wall -Wextra -Wabi main.cpp" I get no warnings. I have to use the -pedantic flag to get warnings. So I still don't know how unsafe this is. In retrospect, I feel as though it must not be a very good idea...although it would be pretty useful if I could get away with it.
Zero-sized arrays are actually illegal in C++:
[C++11: 8.3.4/1]: [..] If the constant-expression (5.19) is present, it shall be an integral constant expression and its value shall be greater than zero. The constant expression specifies the bound of (number of elements in) the array. If the value of the constant expression is N, the array has N elements numbered 0 to N-1, and the type of the identifier of D is “derived-declarator-type-list array of N T”. [..]
For this reason, your class template cannot be instantiated with arguments 0,0 in GCC 4.1.2 nor in GCC 4.7.2 with reasonable flags:
template< int NINT, int NR0 >
class EncapsulatedObjectBase
{
public:
EncapsulatedObjectBase();
~EncapsulatedObjectBase();
double m_real[NR0];
int m_int[NINT];
};
int main()
{
EncapsulatedObjectBase<0,0> obj;
}
t.cpp: In instantiation of 'EncapsulatedObjectBase<0, 0>':
t.cpp:17: instantiated from here
Line 10: error: ISO C++ forbids zero-size array
compilation terminated due to -Wfatal-errors.
clang 3.2 says:
source.cpp:10:17: warning: zero size arrays are an extension [-Wzero-length-array]
(Note that, in any case, you won't get any error until you do try to instantiate such a class.)
So, is it a good idea? No, not really. I'd recommend prohibiting instantiation for your class template when either argument is 0. I'd also look at why you want to have zero-length arrays and consider adjusting your design.
In C using a zero-sized array as the last member of a struct is actually legal and is commonly used when the struct is going to end up with some sort of dynamically-created inline data that's not known at compile-time. In other words, I might have something like
struct MyData {
size_t size;
char data[0];
};
struct MyData *newData(size_t size) {
struct MyData *myData = (struct MyData *)malloc(sizeof(struct MyData) + size);
myData->size = size;
bzero(myData->data, size);
return myData;
}
and now the myData->data field can be accessed as a pointer to the dynamically-sized data
That said, I don't know how applicable this technique is to C++. But it's probably fine as long as you never subclass your class.
1) Add to declaration of your class C++11 static_assert or BOOST_STATIC_ASSERT and you will have compile-time diagnostic for zero length array:
....
BOOST_STATIC_ASSERT(NR0 > 0);
BOOST_STATIC_ASSERT(NINT > 0);
double m_real[NR0];
int m_int[NINT];
};
2) Use std::array or boost::array and you will have run-time diagnostic (in debug mode) for index overflow problem in such code:
BOOST_STATIC_ASSERT(NR0 > 0);
BOOST_STATIC_ASSERT(NINT > 0);
boost::array<double, NR> m_real; //double m_real[NR0];
boost::array<int, NINT> m_int; //int m_int[NINT];
};
Remark:
class boost::array has specialisation for zero-size array
3) Use size_t but not int for size of array.
Your design is quite dangerous:
DerivedDataObject1 a;
a.m_real[2] = 1; // size of m_real == 0 !!!
I think it will better to change design of your class EncapsulatedObjectBase. May be it will better to use:
template<typename T, size_t N> class EncapsulatedObjectBase
{
....
};
class DerivedDataObject1 : public EncapsulatedObjectBase<int,2>
{
....
};
class DerivedDataObject2 : public EncapsulatedObjectBase<double,2>
{
....
};
class DerivedDataObject3 : public EncapsulatedObjectBase<double,2>
, public EncapsulatedObjectBase<int,2>
{
....
};

C++: Custom data type - typecasting and union issues

What I'm trying to do is create a new custom data type that behaves like all other primitive types. Specifically, this data type appears like a Fixed Point fraction.
I've created a class to represent this data type, called "class FixedPoint", and in it there are ways to typecast from "FixedPoint" to "int" or "double" or "unsigned int", etc. That is fine.
Now, what if I want to cast from "int" to "FixedPoint"? Originally my solution was to have a constructor:
FixedPoint(int i) { /* some conversion from 'int' to 'FixedPoint' in here */ }
This works...but you cannot put it into a union like so:
union {
FixedPoint p;
};
This will break, because "FixedPoint" does not have an implicit trivial constructor (we just defined a constructor, "FixedPoint(int i)").
To summarize, the whole issue is "we want to cast from some type T to type FixedPoint without explicitly defining a constructor so we can use our type FixedPoint in a union".
What I think the solution is but cannot find any evidence online:
Define an overloaded global typecast operator to cast from "int" to "FixedPoint".
Is there a way to do this without using class constructors? I'd like to be able to use this class in a union. What I've tried (in global scope):
operator (FixedPoint f, int a) { ... } //compiler complains about this is not a method or non-static.
And a little example to show unions don't like constructors (they like POD)
class bob
{
public:
bob(int a) { m_num = a; }
private:
int m_num;
};
void duck()
{
union
{
bob a;
};
}
This error seen in Visual Studio is:
error C2620: member 'duck::<unnamed-tag>::a' of union 'duck::<unnamed-tag>' has user-defined constructor or non-trivial default constructor
Any ideas?
Thanks
I am having a hard time at seeing what you would try to use this for. It seems smelly to have to constantly ensure that sizeof(FixedPoint) == sizeof(int) and, assuming that, there are other hidden gotchas, like endianness. Maybe I should back up a little bit here, unions only "convert" a value from one type to another in that it takes a chunk of memory and references it as a different type. i.e.
union BadConverter
{
int integer;
double fraction;
};
BadConverter.fraction = 100.0/33.0;
BadConverter.integer = ?;
I am pretty sure integer is not going to be 3, it is going to whatever the memory chunk of the double is that the integer bytes share with it.
Unions don't seem to be a very good fit for this sort of thing. I would think just defining a bunch of assignment operators from all the primitive types. i.e.
class FixedPoint
{
FixedPoint& operator=(int value);
FixedPoint& operator=(double value);
..etc..
//Maybe something like this?
template<typename T>
FixedPoint& operator=(const T& value)
{
value = boost::lexical_cast<int>(value);
return *this;
}
}
Custom conversion operators must be a member of the class that is being converted from. A non-trivial constructor is not required.
EDIT: I reworked the example to utilize a union since this is what you were asking about.
EDIT2: See below if you are trying to go the other way (construction) and don't want constructors.
#include <string>
#include <sstream>
using namespace std;
class FixedPoint
{
public:
operator std::string() const
{
stringstream ss;
ss << x_ << ", " << y_;
return ss.str();
}
int x_, y_;
};
union Items
{
FixedPoint point_;
int val_;
};
int main()
{
Items i;
i.point_.x_ = 42;
i.point_.y_ = 84;
string s = i.point_;
}
If you're trying to go the other way -- eg, from an int to FixedPoint in my example -- then the normal way to do this is indeed to use a conversion constructor. Given that you don't want a non-trivial constructor, you have to resort to a conversion function.
FixedPoint make_fixed_point(int x, int y)
{
FixedPoint ret;
ret.x_ = x;
ret.y_ = y;
return ret;
}
union Items
{
FixedPoint point_;
int val_;
};
int main()
{
Items i;
i.point_ = make_fixed_point(111,222);
}
Can't you just add a default constructor that'll allow it to be part of the union.
For example:
class bob
{
public:
bob(int a=0) { m_num = a; }
private:
int m_num;
};
void duck()
{
union
{
bob a;
};
}
By giving the a=0 default, it should be able to be put in a union. I didn't try it myself, though.

Why do people use enums in C++ as constants while they can use const?

Why do people use enums in C++ as constants when they can use const?
Bruce Eckel gives a reason in Thinking in C++:
In older versions of C++, static const was not supported inside classes. This meant that const was useless for constant expressions inside classes. However, people still wanted to do this so a typical solution (usually referred to as the “enum hack”) was to use an untagged enum with no instances. An enumeration must have all its values established at compile time, it’s local to the class, and its values are available for constant expressions. Thus, you will commonly see:
#include <iostream>
using namespace std;
class Bunch {
enum { size = 1000 };
int i[size];
};
int main() {
cout << "sizeof(Bunch) = " << sizeof(Bunch)
<< ", sizeof(i[1000]) = "
<< sizeof(int[1000]) << endl;
}
Enums are distinct types, so you can do type-oriented things like overloading with them:
enum Color { Red,Green,Blue };
enum Size { Big,Little };
void f( Color c ) {
}
void f( Size s ) {
}
int main() {
f( Red );
f( Big );
}
An enumeration implies a set of related constants, so the added information about the relationship must be useful in their model of the problem at hand.
There's a historical reason too when dealing with template metaprogramming. Some compilers could use values from an enum, but not a static const int to instantiate a class.
template <int N>
struct foo
{
enum { Value = foo<N-1>::Value + N };
};
template <>
struct foo<0>
{
enum { Value = 0; }
};
Now you can do it the more sensible way:
template <int N>
struct foo
{
static const int Value = foo<N-1>::Value + N;
};
template <>
struct foo<0>
{
static const int Value = 0;
};
Another possible reason, is that a static const int may have memory reserved for it at runtime, whereas an enum is never going to have an actual memory location reserved for it, and will be dealt at compile time. See this related question.
Enums are more descriptive when used. Consider:
int f(int fg, int bg)
versus
int f(COLOR fg, COLOR bg)
In addition, enums give a bit more type-safety, because
integers are not implicitly convertible to enum types
enum of one type is not implicitly convertible to enum of another type
I like the automatic behavior that can be used with enums, for example:
enum {NONE, START, HEY, HO, LAST};
Then it is easy to loop until LAST, and when a new state (or whatever is represented) is added, the logic adapts.
for (int i = NONE; i < LAST; i++)
{
// Do stuff...
}
Add something...
enum {NONE, START, HEY, WEE, HO, LAST};
The loop adapts...
Before compiler vendors implemented the ISO/IEC 14882:1998 C++ standard, this code to define a constant in a class scope resulted in a compile error:
class Foo {
static const int MAX_LEN = 80;
...
};
If the constant is an integer type, a kludgy work around is define it in an enum inside the class:
class Foo {
enum {
MAX_LEN = 80
};
...
};
enums also can be used as a type name. So you can define a function that takes an enum as a parameter, which makes it more clear what kinds of values should be given as arguments to the function, as compared to having the values defined as const variables and the function accepting just "int" as an argument.
Consider:
enum my_new_fangled_type {
baz = 0,
meh = 1
};
void foo (my_new_fangled_type bar) // bar can be a value listed in the enum
{
...
}
versus:
int const baz = 0;
int const meh = 1;
void foo (int bar) // what are valid values for bar?
{
...
}
Some debuggers will show the enumeration name instead of its value when debugging. This can be very helpful. I know that I would rather see day_of_week = MONDAY than day_of_week = 1.
It's partly because older compilers did not support the declaration of a true class constant
class C
{
const int ARealConstant = 10;
};
so had to do this
class C
{
enum { ARealConstant = 10 };
};
For this reason, many portable libraries continue to use this form.
The other reason is that enums can be used as a convenient syntactic device to organise class constants into those that are related, and those that are not
class DirectorySearcher
{
enum options
{
showFiles = 0x01,
showDirectories = 0x02,
showLinks = 0x04,
};
};
vs
class Integer
{
enum { treatAsNumeric = true };
enum { treatAsIntegral = true };
enum { treatAsString = false };
};
Using an enum documents the valid choices in a terse manner and allows the compiler to enforce them.
If they are using enum store global constants, like Pi, for example, then I don't know what their goal is.
One reason is that const requires more typing:
enum { Val1, Val2, Val3 };
...versus...
const int Val1=0, Val2=1, Val3=2;