Sign in to follow this  
EyeSize

[C++] Enums

Recommended Posts

Hi all, At the moment I am working on a project written in Visual Studio 2008 (2005 compatible). I am trying to convert this project to an eclipse CDT project (in combination with GCC) (because I want to have a project that is also compilable on Linux / Mac / XBox / PS3 / etc). The existing project contains 2 types of enums:
enum myEnum
enum myEnum : unsigned int

The second enum declaration is not working in GCC (which is normal because it's a Visual Studio definition). I have created the following declaration to keep the project compatible with visual studio:
#ifdef COMPILER_MSVC
#define ENUM8PRE
#define ENUM8POST : unsigned char
#else
#define ENUM8PRE __attribute__((__packed__))
#define ENUM8POST
#endif

I then define my enums as following:
enum ENUM8PRE myEnum ENUM8POST

When i create a single file (as a test project) this works like a charm. But when I add it to the main project (which consists of 2 seperate libraries) I get a lot of 'does not name a type', 'was not declared in this scope' and 'has not been declared' errors. The anscii drawing below shows the project setup: | - API | - myClass.cpp (includes platform.h && myenums.h) | - Core | - platform[/indent] | - platform.h[/indent][/indent] | - Shared | - myenums.h Any ideas on why the project is giving so much errors about enums not being defined With kind regards, EyeSize [Edited by - EyeSize on February 17, 2010 4:41:39 AM]

Share this post


Link to post
Share on other sites
Quote:
Original post by EyeSize
I have created the following declaration to keep the project compatible with visual studio:
#ifdef COMPILER_MSVC
...


Test for the definition of the _MSC_VER macro instead - it's predefined by MSVC itself. (You might want to use the one on that page for GCC too)

Quote:
But when I add it to the main project (which consists of 2 seperate libraries) I get a lot of 'does not name a type', 'was not declared in this scope' and 'has not been declared' errors.

Could you post some of them, with relevant code snippets?

What version of GCC are you using?

Are you sure your #defines are getting defined in files with these enum declarations in?

Share this post


Link to post
Share on other sites
Here you go :
Platform.h (Defined in the Core Project (which is just a library of shared source))
#ifndef PLATFORM_H
#define PLATFORM_H

#ifdef _MSC_VER
#define COMPILER_MSVC
#endif

#ifdef __GNUC__
#define COMPILER_GCC
#endif

#ifdef _WIN32
#define PLATFORM_WINDOWS
#endif

#ifdef _WIN64
#define PLATFORM_WINDOWS
#endif

#ifndef PLATFORM_WINDOWS
#define PLATFORM_LINUX
#endif

#ifdef COMPILER_MSVC
#define ENUM8PRE
#define ENUM8POST : unsigned char
#else
#define ENUM8PRE __attribute__((__packed__))
#define ENUM8POST
#endif

#endif //PLATFORM_H


enumdefs.h (resides in the shared folder and is not inside any of the projects (because it only contains header files)

#ifndef IOP_STDDEFS_HPP
#define IOP_STDDEFS_HPP

#include "../library/platform/platform.h"
enum ENUM8PRE eTiming ENUM8POST
{
TIMING_NOTSET = 0,
TIMING_EXPECTED_STREAMSTART = 1,
TIMING_EXPECTED_STREAMSTOP = 2,
TIMING_EXPECTED_RACESTART = 3,
TIMING_EXPECTED_RACESTOP = 4,
TIMING_DEFINITE_STREAMSTART = 5,
TIMING_DEFINITE_STREAMSTOP = 6,
TIMING_DEFINITE_RACESTART = 7,
TIMING_DEFINITE_RACESTOP = 8,
TIMING_TYPE_MAX = 16,
};

enum ENUM8PRE eStartLights ENUM8POST
{
STARTLIGHT_NOTSET = 0,
STARTLIGHT_CONFIG_0 = 1,
STARTLIGHT_CONFIG_1 = 2,
STARTLIGHT_CONFIG_2 = 3,
STARTLIGHT_CONFIG_3 = 4,
STARTLIGHT_CONFIG_4 = 5,
STARTLIGHT_CONFIG_5 = 6,
STARTLIGHT_CONFIG_6 = 7,
STARTLIGHT_CONFIG_7 = 8,
//...
STARTLIGHT_RESET_CONFIG = 20,
STARTLIGHT_FORCE_START = 21,
STARTLIGHT_MAX,
};
#endif


kernelinfo.h (Inside the API project)
#ifndef KERNELINFO_HPP
#define KERNELINFO_HPP

#include "library/network/ip4address.h"
#include "shared/iopenerdefs.h"

namespace MyNamespace
{
/** Some 'global' information, needed by several subsystems
*/

struct KernelSettings
{
eTiming m_expected ;
char m_datafilename[512];
StreamAddress m_serverAddress;
unsigned short m_flags;
char reserved[30];

KernelSettings()
{
m_expected = TIMING_EXPECTED_STREAMSTART;
m_flags = 0;
}
}
}
#endif



[Edited by - EyeSize on February 17, 2010 4:05:16 AM]

Share this post


Link to post
Share on other sites
I can only suggest checking the output of the preprocessor for the files in question, or putting something like this before a usage to double-check:

#if !defined(ENUM8PRE) || !defined(ENUM8POST)
#error Oops, packed enum defines not here
#endif


That said, isn't this a nicer way of doing the macros in the first place?:

#if defined(_MSC_VER)
#define PACKED_ENUM(e) enum e : unsigned char
#elif defined(__GNUC__)
#define PACKED_ENUM(e) enum __attribute__((__packed__)) e
#else
// Fallback - don't use packed enums
#define PACKED_ENUM(e) enum e
#endif

// Usage:
PACKED_ENUM(eTiming)
{
TIMING_NOTSET = 0,
TIMING_EXPECTED_STREAMSTART = 1,
// ...
};



Are you sure you actually need packed enums in the first place? They would be arguably useful for serialization purposes (to disk or network), but otherwise consider just letting the compiler choose the default type instead.

Share this post


Link to post
Share on other sites
Hi mattd,

Thank you for your reply. Some enums need to be packed because they are send over the internet. There are about 20 data packages each second so we would like to keep the size of each package as small as possible. I like your way of doing the enum as is makes it all a bit more readable.

I'll try your solution thanks,

With kind regards,

EyeSize

Share this post


Link to post
Share on other sites
Hi again,

I tried the solution provided by mattd earlier but I get an error (As shown in the text below). I have created a simple project (as shown in the source box below).

**** Internal Builder is used for build ****
g++ -O0 -g3 -Wall -c -fmessage-length=0 -osrc\TestProject.o ..\src\TestProject.cpp
..\src\TestProject.cpp:47: error: use of enum `iOpStartLights' without previous declaration
..\src\TestProject.cpp:47: error: expected unqualified-id before '{' token
..\src\TestProject.cpp: In function `int main()':
..\src\TestProject.cpp:63: error: `iOpStartLights' was not declared in this scope
..\src\TestProject.cpp:63: warning: unused variable 'iOpStartLights'
Build error occurred, build is stopped
Time consumed: 297 ms.

#include <iostream>
using namespace std;

#ifdef _MSC_VER
#define COMPILER_MSVC
#endif

#ifdef __GNUC__
#define COMPILER_GCC
#endif

#ifdef _WIN32
#define PLATFORM_WINDOWS
#endif

#ifdef _WIN64
#define PLATFORM_WINDOWS
#endif

#ifndef PLATFORM_WINDOWS
#define PLATFORM_LINUX
#endif

#if defined(COMPILER_MSVC)
#define PACKED_ENUM(e) enum e : unsigned char
#elif defined(COMPILER_GCC)
#define PACKED_ENUM(e) enum __attribute__((__packed__)) e
#else
// Fallback - don't use packed enums
#define PACKED_ENUM(e) enum e
#endif

PACKED_ENUM(iOpStartLights)
{
STARTLIGHT_NOTSET = 0,
STARTLIGHT_CONFIG_0 = 1,
STARTLIGHT_CONFIG_1 = 2,
STARTLIGHT_CONFIG_2 = 3,
STARTLIGHT_CONFIG_3 = 4,
STARTLIGHT_CONFIG_4 = 5,
STARTLIGHT_CONFIG_5 = 6,
STARTLIGHT_CONFIG_6 = 7,
STARTLIGHT_CONFIG_7 = 8,
STARTLIGHT_RESET_CONFIG = 20,
STARTLIGHT_FORCE_START = 21,
STARTLIGHT_MAX,
};

int main() {
cout << sizeof(iOpStartLights) << endl;
cout << "!!!Hello World!!!" << endl; // prints !!!Hello World!!!
return 0;
}


When i try to define (see below) it gives an error:
enum __attribute__((__packed__)) myEnumOld
{
NUMBER1 = 0,
NUMBER2 = 1,
NUMBER3 = 2,
};
Errors:
..\src\TestProject.cpp:13: error: use of enum `myEnumOld' without previous declaration
..\src\TestProject.cpp:13: error: expected unqualified-id before '{' token

When I define it as (see below) it all works oke:
enum myEnum
{
NUMBER1 = 0,
NUMBER2 = 1,
NUMBER3 = 2,
}__attribute__((__packed__));

Any ideas on why it does this ?

@Ozak
I know it's hell for the moment (especially because it needs to stay compatible with Visual Studio). I also have 2 installs off gcc (first one on ubuntu version 4.something second on windows version 3.4.5 (which is even worse).

With Kind Regards,

EyeSize

[Edited by - EyeSize on February 17, 2010 4:27:18 AM]

Share this post


Link to post
Share on other sites
You will receive network packet, which contains an enum:
enum Foo {
A = 1,
B = 2,
C = 3
};


The network packet value for enum will be 17.

Second case:
enum Foo {
A = 1,
B = 65534,
C = 65535
D = 65536 // D is added after a revision and wasn't originally present
};


How do you handle these cases?

Send as 32-bit int, and waste 30 bits per value (after all, we only have 4 bits of information)? Pack the data - how do you handle the change in bit count (8 vs. 16 vs. 32, or 2 bits vs. 3 bits)?

This is in response to a recent debate where I made a strong case against using enums as ints. This thread, along with all the compiler-specific ifdef hell demonstrates the underlying problem of why enums should never be treated as ints.

As alternative, I propose being explicit about such enums. Serialization, deserialization can then be implemented safely via template-based iteration. Those enums are also safe in the way values are assigned, and allow for compact, per-bit storage on network, and even compact in-memory representation using templates for storage selection. And even though they are not values, they can be iterated over, they can have first and last value exposed as iterators (not all of this is implemented in the example).

More importantly - none of that requires any compiler-specific behavior, which becomes a nightmare if trying to write portable code, nor does it come with any performance penalty.

Share this post


Link to post
Share on other sites
Hi Antheus,

I must say you address an interesting problem here. The structs and enums are indeed used for network transmition (and luckely the server is also written by us). This means we always know what data is being send and what the maximum size of an enumeration is. When the output of the server changes the API changes (meaning our clients are forced to update there application).

As for your example I like the fact that it is possible to actually iterate through the enumeration. I saw you implemented it as an integer but I can see how we can implemented as an enumeration. I will have a better look at it because its looks good.

With kind regards,

EyeSize

Share this post


Link to post
Share on other sites
Quote:
Original post by EyeSize

(and luckely the server is also written by us).

It doesn't matter who it is written by. Who will be used by? Can the bytes on network be corrupted by malicious user, or a bug?

Quote:
I saw you implemented it as an integer but I can see how we can implemented as an enumeration.


You really want to implement it as an integer, not as enum mapped to integer. More debate.

Share this post


Link to post
Share on other sites
Quote:
Original post by EyeSize
Some enums need to be packed because they are send over the internet.


Or you could just cast to the necessary type as part of your serialization process...

Share this post


Link to post
Share on other sites
Quote:
Original post by ozak
You are in for a lot of fun. GCC 4 is extremely ansi nazi,


Nitpicks:
* C++ = ISO/IEC, not ANSI
* I wouldn't call it Nazi when a compiler encourages correct programming by default


Quote:
but there are switches to turn some of the checks off :)

You shouldn't if not justified. That smiley would make me nervous if you were in my team ;)

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this