Sign in to follow this  
Vanderry

Unity [C++] Proper enum usage

Recommended Posts

Hello GameDevs ! I really love the clear visualization of enums but I'm not entirely sure how it should be used properly. What I see is an opportunity to manage indices with easily editable text labels, so what I've done is declare enums for things like for instance in my application "error message grades".
enum ERRORGRADE
{
	NONCRITICAL,	// Does not terminate the program
	TERMINATE,		// Terminates the program
	TERMINATEANDMSG	// Terminates the program and shows a message box
};

The declaration is stored within the header file for the CError class, and whenever I call the method Error I pass the enum element appropriate for the specific error.
void Error( std::string file, int line, ERRORGRADE grade, std::string text1 = "", std::string text2 = "" );

Now to the actual "problem". I am able to pass the elements by themselves without any hassle, but I would very much like to be explicit and pass the argument as such ERRORGRADE::NONCRITICAL to avoid passing a bad keyword (i.e. accidently using an element from a different enum). This always results in a warning in Visual Studio 2008 (warning C4482: nonstandard extension used: enum 'ERRORGRADE' used in qualified name) and it looks very bad after a whole lot of function calls. So my question is: am I using enumerators in a proper way and is there some way to call the elements through the enum scope without getting all those warnings? Many thanks in advance ! - Dave

Share this post


Link to post
Share on other sites
The way you are using enums now is fine. Think of them as defining a type for which its values are from a finite set of distinct elements that are explicitly enumerated.

For your second question, enums do not have their own scope. You could put them inside another scope (like a class's), or use a namespace instead (with the side-effect of it requiring a slightly longer identifier for the type):


namespace ErrorGrade {
enum Enum {
NONCRITICAL, // Does not terminate the program
TERMINATE, // Terminates the program
TERMINATEANDMSG // Terminates the program and shows a message box
};
}

ErrorGrade::Enum grade = ErrorGrade::TERMINATE;

Share this post


Link to post
Share on other sites
Quote:
Original post by Normandy
I'm fairly sure this is a problem they are addressing in C++0x. Right now enums aren't even type-safe, so I presume they would add something like scope resolution for enum members.

C++0x doesn't fix the issue, it just sidesteps the issue by introducing a new type of enumeration, the "enum class" type. The enum class type is type safe, although you can use explicit casting to convert to integral type..

Share this post


Link to post
Share on other sites
Enum to int mapping is conceptually undefined, even though many languages support one way or another. Enum 'ERROR' is just that - 'ERROR'. If you choose to assign it a numeric value, the mapping is up to you. Enums conceptually also do not have relation between values. ERROR is different from WARNING, yet neither is greater as far as enums go.

Even without taking C++'s int/enum equivalence, your use of enums is not suitable for the purpose.

IMHO, the following is how the API should be used in this particular case:

void Error( std::string file, int line, std::string text1 = "", std::string text2 = "", ErrorGrade g = ErrorGrade::None );

...

Error("File.cpp", 17, "", "");
Error("File.cpp", 17, "", ErrorGrade::Terminate);
Error("File.cpp", 17, "", ErrorGrade::Terminate || ErrorGrade::MessageBox);


How the above is implemented depends. Values could be simple ints, or they could be more complex classes. The ErrorGrade above can be polymorphic as well, implementing the error behavior by itself. This design is more representative for values which can be combined (noncritical = 00, terminate = 01, terminate+box = 11, consequently just box = 10).

Alternatively, if grades are ordered, then providing a wrapper that defines order, as well as strong type safety will be better solution.

Given real world use, I'm consistently disappointed with enums and can't think of many uses for them, even in other languages. They reek too much of hardcoding. If they define functionality, then there are other ways to implement it (polymorphism, parameters). If they define data, then the data will often be best served from outside. And if for error or labeling, then const int values do the job, even if wrapped in some class.

Share this post


Link to post
Share on other sites
Quote:
Original post by Antheus
And if for error or labeling, then const int values do the job, even if wrapped in some class.


But...an enum IS an integer! It just gives a cute name for a number. People forget numbers, names not so quickly. It's actually really handy, the enum, though its a pity that C++ seems to go so difficult about it. I have to do a lot of casting when I add an integer to an enum value. Also the way we can't define the type for the integer is a pity, as well as it's not encapsulated like a class.

I do agree that most uses of enums are best from outside, though I still prefer something that names my integers. Would be better if we could 'append' enums too, where all elements of 2 enums have unique values. In other words, make it easy for library users to extend, for example add new events. Sure, you can start from the end of the last, but what if you have 3 enums? Just getting garbage.

If 'TERMINATEANDMSG' equals 2, then why is 'TERMINATEANDMSG' - 1 not just 1 and thus 'TERMINATE'? Because it is, except that C++ wants you to add a bunch of needless castings.

Share this post


Link to post
Share on other sites
Quote:
Original post by Decrius
But...an enum IS an integer!
Nope, an enum is just a name. That C/C++ choose to implement them in terms of integers is immaterial.
Quote:
It just gives a cute name for a number.
Nope, as Antheus already mentioned, const int is how one applies a cute name to a number in C/C++.
Quote:
***truncated***
And those are all problems which using const int instead of enum solves in one fell swoop.

Share this post


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

It just gives a cute name for a number. People forget numbers, names not so quickly.


As soon as you introduce ordering or treat them as ordinal types, you open a new can of bugs. Such as: ERROR + WARNING = FILE_NOT_FOUND, while WARNING * ERROR = EXIT and EXIT - FILE_NOT_FOUND + WARNING = DEFAULT.

And if treating them as bitmasks, the following problems arise:

LEFT,
RIGHT,
TOP,
BOTTOM,
LEFT_BUTTON,
RIGHT_BUTTON
DEVICE_OFF

Left, right, top, bottom is the direction of stick.
Buttons can be pressed at any time.
Device off means other values must be 0 or unspecified.

But this is what one uses in actual design, and enums fail to address any of those issues.

Share this post


Link to post
Share on other sites
Working on reasonably old code here (15 or so years in some places), and there are many places where plain integers are passed to and returned from functions to indicate things, with a (usually) outdated, incorrect comment explaining what that value meant there. Sometimes 2 or 3 values are used for the same thing, and sometimes the same value is used more than once for different things.

Replacing those with enums (NOT const ints) has been very helpful because now when building the compiler gives me errors when just passing in integers, and some well-hidden bugs are being removed. Though not completely type-safe, just the fact that it doesn't let ints pretend to be enums (without some explicit casting), and the choices are limited to only what's valid, (and can't use cross-enums either) has been very, VERY helpful. With hundreds, maybe thousands of constant identifiers (whether #defined or const int'ed or enumed) it's also very helpful to know which set of identifiers I will be choosing from, rather than try to decypher a bunch of code and #defines which are poorly named in the first place.

These are cases where the actual value of the identifier is really irrelevant, but they just need to be unique. And we don't want to use a CompressionType where we want to know which HlsBand we are using. Are these not good candidates for enums??

Share this post


Link to post
Share on other sites
Well, talking on the usage side of things:
There are almost always alternatives to the usage of named integers or enumerations.

The question becomes: Is it worth it? In the end the answer is "sometimes yes, sometimes no." More succinctly: "Maybe."

This depends heavily on your current design, on the goals of your project, on the technologies employed by your project, and many other issues as well.

The example used, which is one that deals with errors, seems a bit... wonky. I would never have an enumeration for my error severity. In fact, I'm not entirely certain what the code is trying to convey. Lets look at a few questions about the demonstration code:
Is the Error function exit-able, or does entrance to the function pose the potential to halt execution within the function?

Does the behavior of the Error function radically change depending on the parameters passed in?

Why aren't you using exceptions (and hence RAII)?

Depending on the answers to these questions your usage of an enumeration could be entirely...stupid. As an example: If the behavior of the function changes radically based on the ERRORGRADE enumeration, then you shouldn't have those behaviors all bound up in one function, but across a number of functions. If the function has the potential to exit the application without returning, then are you properly dealing with scoped objects? Less you forget: If the function never returns then some objects whose destructors would normally be called will not be. This means open files may be left dangling, sockets will abort mysteriously, and other resources you have allocated can be left in a dangling or unknown state (such as named mutexes).

Then there is the question of "Why is this in a class?" Depending on your usage of the class it may or may not be appropriate to have these functions in a class. If your simply calling these methods as single shot calls, then there's no point in using a class, free functions will be cleaner.

Share this post


Link to post
Share on other sites
Wow, thank you guys for those very interesting reflections. If I understand things correctly: it should not cause me problems to just pass one enum element as an argument in my custom functions and use for comparison with the same enum elements as long as they are within the same program on the same machine?

So far I have only checked for equivalence as such:


if( grade == ERRORGRADE::TERMINATE )
{
PostQuitMessage( 0 );
}
else if( grade == ERRORGRADE::TERMINATEANDMSG )
{
std::string message = text1 + text2;
MessageBox( 0, message.c_str(), "Critical Error", MB_OK | MB_ICONSTOP | MB_APPLMODAL );
PostQuitMessage( 0 );
}



I like the idea of using an enum to control a switch, since it seems to accept the enum as an integral variable. Is this a situation where its usage might be considered unsafe?

Share this post


Link to post
Share on other sites
Quote:
Original post by popsoftheyear
These are cases where the actual value of the identifier is really irrelevant, but they just need to be unique. And we don't want to use a CompressionType where we want to know which HlsBand we are using. Are these not good candidates for enums??


If you get a consensus across all developers that enums are not ints, and that there is no relation between those, unless via from_int/to_int, then they are suitable.

In practice and for historical, practical or cultural reasons, enums and ints are used interchangeably, offering no advantage.

As with everything in C++, one needs to settle on a subset of features to be productive. In case of enums, treating them as named ints is not beneficial, despite language not only allows it, but historically design was in favor of it.

Otherwise, code will rot over time, which brings us full circle. At some point, someone will simply need to use an int instead of an enum, and will force one into another.

Hence, strongly typed values are likely better, but they add complexity and verbosity. They can however result in perfectly safe code, which is still convenient to use.

It's just the age-old C++ issue, jack of all trades, master of none. It can be trained, but requires considerable upfront work on boiler-plate functionality.

Share this post


Link to post
Share on other sites
@Washu
I feel perhaps I should explain this particular implementation a bit better.

The whole application is built upon classes that return either success or failure when they are initiated, so should any call return false it will attempt to close down along the "function call branch" back into the main loop which would end seeing as a quit message was posted.

I'm sure this isn't the most fool-proof implementation, but all I really want to do with the enum is to provide myself with a lazy way of managing what each individual error call will do as I manually write the calls into the rest of the code.

I only really want critical errors at the "end" of a function call branch to display a message box, to let the user know what really caused all the trouble. The rest of the error calls should only log the error in a text file.

This probably washes out the discussion a bit but I really appreciate all of your concern :)

Share this post


Link to post
Share on other sites
Quote:
Original post by swiftcoder
Nope, an enum is just a name. That C/C++ choose to implement them in terms of integers is immaterial.


This is true, but it's also silly. In my mind, the most intuitive use for something like an enum is to simply be a wrapper around such const int functionality. Enums SHOULD be the same as const ints. Conceptually, there's really no reason to ever use enums... in fact, they're kind of a trap. For what you would intuitively think they're useful for, they are actually undefined :(

Share this post


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

The whole application is built upon classes that return either success or failure when they are initiated, so should any call return false it will attempt to close down along the "function call branch" back into the main loop which would end seeing as a quit message was posted.

This is what exceptions do.

Quote:
I only really want critical errors at the "end" of a function call branch to display a message box, to let the user know what really caused all the trouble. The rest of the error calls should only log the error in a text file.


These are two different things, one is logging, the other are fatal errors, so they can't be handled in same way.

try {
while (...) {
// main loop stuff
log(INFO, "Hello world"); // don't quit, just log

try {
load_stuff();
} catch (file_not_found_exception & e) {
log(WARNING, "It's ok, file might not exist");
}
}
} catch (std::exception & e) {
// unhandled exception, nobody caught it, we can't handle it, let's quit
PostQuitMessage( 0 );
}

Share this post


Link to post
Share on other sites
Quote:
Original post by Washu
Quote:
Original post by Normandy
I'm fairly sure this is a problem they are addressing in C++0x. Right now enums aren't even type-safe, so I presume they would add something like scope resolution for enum members.

C++0x doesn't fix the issue, it just sidesteps the issue by introducing a new type of enumeration, the "enum class" type. The enum class type is type safe, although you can use explicit casting to convert to integral type..


I guess it depends what you mean by "C++0x doesn't fix the issue", but it does indeed cause the values to be scoped to the defining enum class, solving the problem the OP asked about.

Share this post


Link to post
Share on other sites
Quote:
Original post by jwezorek
Quote:
Original post by theOcelot
Doesn't C++0x also allow you to specify the underlying type of the enum, including allowing non-integer types?


I hadn't heard this. So you could define an enum as being a string?


It actually does still require integral types. Any integral type except wchar_t is suitable.

http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2347.pdf

See section 3.1 specifically.

Share this post


Link to post
Share on other sites
I must say that I'm a bit surprised so many of you oppose the use of enums when it seems so widely used, not the least in many professional DirectX-applications I have studied. I guess that might be one of those fields where their pros outweigh their cons.

Share this post


Link to post
Share on other sites
Quote:
Original post by Vanderry
I must say that I'm a bit surprised so many of you oppose the use of enums when it seems so widely used, not the least in many professional DirectX-applications I have studied. I guess that might be one of those fields where their pros outweigh their cons.


I'm only opposed towards treating enums and ints as equivalent, or using them as typesafe replacement for numeric constants.

At some point, they need to be written somewhere, and then there's that old mapping problem again, and the first thing will be assuming literal int interpretation. But then this is written externally, code changes, and suddenly old values no longer make sense, yet there is no check in place, or even possible.

And not just C or C++. For example, before 1.5/5, Java lacked enum type, so a cookie-cutter version was patternized. Except that the implementation (static/singletonish-based one even) that was de-facto standard had some obscure serialization/initialization issues which would result in same enum labels having different values, sometimes even inside same VM when serialization was involved.

IMHO - either full static type safety, or full dynamic language. The things in between tend to annoying in the long term, even if over short-term a tool, perhaps an inference type analyzer can help a bit.

This is especially true for C++ under modern compilers which are good at eliminating unused code, or minimizing memory layout of such convenience wrappers.

While likely overbloated and over-engineered, perhaps look at boost::error's design.

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  

  • Announcements

  • Forum Statistics

    • Total Topics
      628346
    • Total Posts
      2982201
  • Similar Content

    • By koto
      Hello,

      I'm developing a roguelike game. Currently it's in a playable state and I think it's a high time to show it and hopefully get some feedback.

      The game has following features:
      - turn based
      - procedurally generated levels
      - tons of loot
      - spells system
      - trading system
      - some quests
      - some limited crafting
      - door/key/lever mechanics
      - quite a lot of different (look/behaviour) monsters

      Trailer:
      Demo: http://kotogames.com/wp-content/uploads/2017/11/OuaD_demo.zip
      Regards,
      Tom
       
    • By Afambus
      Hello,
      I have a full experienced team that looking for a composer and another 3d designer, im expecting as much from you there are other people in this server that can help you we have a determined team, we have done many things in our game there is more information in the discord about everyone and there skills there region and about the game, if you join the discord pm asap
      https://discord.gg/b2teN3m
    • By BAG Labs
      Mobile SoS

      Platform: Android
      Genre: Board
      Link: Google Play
       
      This games sharpen memory and test your strategies to place S-O-S pattern within time limit and serve 3 difficulties as Easy, Normal, and Hard.

      Goals of the game is to put S-O-S words in patterns (Horizontal, Vertical, and Diagonal) alternately with enemy.
       
      Features:
      Single Player Multiplayer Achievements Leaderboards  
       
      Screenshot:

       

       

       
      Link: Google Play
       
      Please help us improve this game with review
    • By abarnes
      Hello!
      I am a game development student in my second year of a three year program and I would like to start building my portfolio. I was thinking of creating some games to show what I can do to potential employers since I wont have any work related experience when I graduate. But as I'm sure you all know there are tons of ways to approach developing/designing a game and I'm curious if anyone had any insight as to any "standards" that come with this? Is it okay to use game engines like Unity, Unreal, Game Maker etc? Or would it be better to make a game from scratch to better show case your skills? Any and all advice will be greatly appreciated!
    • By Hilster
      Hello 2D Artists,
      I've started making a 2D Puzzle Adventure game for mobile and I'm looking for someone who would want in on creating assets for the game. The core of the programming is pretty much complete, you can walk within the grid laid out and push boxes, when there is an object on top of a pressure pad it will activate the linked objects or if there is one object with multiple linked pressure pads it requires you to activate all points for the object to become active. 

      The level iteration for the game is quick and simple, a Photoshop file that is made of individual pixels that represents objects is put into the game and it creates the level out of those pixels with the assigned objects.
      The objects that need sprites created so far is the character, box, pressure pad, door, trap door, the walls, the stairs and the tiled background.
      I intend to add more objects so the amount I'd like to add will be extended.
      My motivations for posting here is to have something that looks nice to be able to display on my portfolio, so if you're looking for a working game that you can place your art into and improve the look of your portfolio then we're in business.
      Please reply with a few past examples of your art below and I'll be in touch!
  • Popular Now