I understand the question title is mightily vague, hence the body text :)
I have several enum
s I use for identifying file types and other stuff that needs easy differentiating. My former approach was this:
namespace my_namespace
{
namespace fileType
{
enum fileType
{
SOURCE,
HEADER,
RESOURCE
};
}
}
using namespace my_namespace::fileType;
Which allowed me to define a function:
fileType someFunction( const std::string &someFile, const fileType someType )
{
//...
return fileType::HEADER;
}
And I could define and compare variables like this:
fileType type = fileType::SOURCE;
Which is awesome. Though there were some caveats. Headers (without any using
directives) required doubling the intended enum name to let the compiler know you're using the type, not namespace:
my_namespace::fileType::fileType soeFunction( const std::string &someFile, const my_namespace::fileType::fileType someType );
Which does look silly, is hard to read and painful to understand. Additionally, MSVC complains at warning level one about a non-standard extension used (due to the doule fileType
in the example). Strange that GCC does not complain at the strictest settings, but hey, that's a different story.
I now want to rewrite my enum
s in a way that they are (anonymously) enclosed in a struct instead of a namespace, allowing for the single qualification when declaring functions, thus shutting up MSVC's warning. But How do I write the return
statement in this case. Is it absolutely necessary to provide a constructor/conversion operator or is there a way around this I did not see?
Example:
// enum definition
namespace my_namespace
{
struct fileType
{
enum
{
SOURCE,
HEADER,
RESOURCE
};
}
}
using my_namespace::fileType;
// function declaration in header
my_namespace::fileType someFunction( const std::string &s, const my_namespace::fileType type );
// function implementation in .cpp file
using my_namespace::fileType;
fileType someFunction( const string &s, const fileType type )
{
//...(problem is situated below)
return fileType::SOURCE;
}
This illustrated what I'd like to do. I'd like to avoid explicitely calling the enum struct's constructor: fileType(fileType::SOURCE)
which would leave me with a double fileType
use.
Thanks for the help!
PS: if this question has been answered before, I apologize, but I didn't find a good alternative with google or on SO's previous questions on this subject.
From my point of view having a namespace or a struct that does not contain anything but a single enum has already proven that namespace/struct to be useless. Thus you should simply drop it.
You should try if
suits you better. Often enough though in that case programmers like to prefix the actual enum values with the name of the enum thus resulting in
here you may even choose to rename the enum itself to
Also enums are better used for group of values that are in itself incomplete and never to be extended in the future, e.g. an enum for weekdays.
If you consider that an enum fileType is likely to be eternally incomplete (since there are hundreds of files types out there) you may want to take yet another approach - use constant groups. Using constant groups has the advantage over enum that if you extend that constant group you need only recompile modules/files that make use of the new added values, whereas if you add a value to an enum you may be required to recompile all sources that uses that enum.
A constant group may look like this:
Of course if you prefer you can move the typedef statement into the fileType namespace or get rid of it completely and use 'const unsigned short' directly everywhere.
Since I don't have compiler at home the above code might not compile from scratch though. But I'm confident that you'll get the idea.
With this approach your source code should read like this:
True, if you want different enums in the same scope to have a 'last' member it can not be done. In that case you would need to prefix that 'last' value with the name of the enum for example (similar to the second enum example.
As for having a 'last' entry for argument checking I may have missed your point. For example if you pass the enum as argument to a function e.g.
It does not provide more 'type safety' than fileType has without a 'last' element.
The only reason I can see for a last element (and then for a first element as well!) is if you do want to iterate over the elements of the enum elements in a loop. For example:
if that is the case isn't it rather artificial to want to have two different enums have the same named 'last' element listed? Where is the advantage of having one 'last' in two different enums?
Does it improve readability compared to e.g. having E_last and F_last?
Or does it help to save writing even a single line of code?
Also what is using 'last' in scoped enums for? Scoped enums do not convert to int by default. (But maybe I misunderstood what you wanted to do...)
About "you can't keep the enum values short..." I'm afraid I did not get what you wanted to say here. Was it about me using 'const unsigned short' in the constant group example?
Also don't get me wrong, in your case (wanting to handle file types) I advocated to use constant groups. And with them it is easy and natural to use the same 'first' and 'last' member (but with different values) in different groups. See
As for 'namespace - namespace - enum' I dislike the additional indirection without having substantial benefits. As for 'namespace - struct/class - enum', that is more philosophical:
C is, so to speak, a slick and fast motorbike that made writing assembler code virtually unnecessary. This is also pretty much owned to the C cast operator which allows to completely drop type safety and do what you want without getting the compiler in your way.
C++ still likes to be a super set of C. It added classes and object oriented programming but still wanted to keep the motorbike. Which was a good. Now adding more 'type safe' scoped enums while still keeping cast operators looks to me like wanting to add airbags and safety belts to the motorbike.
I am not against revising enums to get better enums (if needs really be), but if one does so one should also occasionally (e.g every 5 years) drop the stuff now recognized as old, outdated and evil. Otherwise the language just gets more and more cluttered and ever so harder to learn by beginners. And like every tool programming languages need to be easy to use and that includes not having a dozen ways to do the same thing.
Personally I use a very simple trick:
In C++0x, you can have scoped enumerators directly:
Which will be really great :)
Note: scoped enum are also not subject to integral promotion, which is really great