Should WM_USER-like things be defines or consts?

D

dragan

This is not a Windows question. It's a question about defining manifest
constants (at least I think that the WM_USER-like stuff fits that
definition). Windows is just the example platform. Actually, thinking about
error IDs prompted me to post this but this is also not a question about
exceptions and exceptions are explicitly off-topic in this thread. Flames
about macros being evil are also off-topic.

While the Windows SDK preceded C++ popularity and is written in C, is a
define still the preferred way to do the WM_USER+1... WM_USER+n thing rather
than using consts in a C++ program? It seems easier and more efficient in
time and space. Which do you prefer and why? What are the pros and cons of
each technique? I do the define thing, as the const thing seems like too
much trouble for no gain.
 
J

Jonathan Lee

It's a question about defining manifest
constants (at least I think that the WM_USER-like stuff fits that
definition).

For things where the value of the constant is irrelevant, but I
need uniqueness, I prefer enums. So WM_XXX events would fit in
this category for me. It's less maintenance, and I don't feel
compelled to use all-capitals for the name, which I find ugly.

Then I put the enum in a namespace. Something like
namespace Event {
enum event_t { Quit, Paint, Move, Resize, User };
}

If the user is free to create event_t's, they do so as ints, ex.,
(Event::User + 1). Then, whatever interface I've written accepts
ints. For example, an event loop that has a postEvent(int) function.

On the other hand, when the value is meaningful (such as a bit
mask) then I'll use a static const global. Unless it's something
I want to be seen from other translation units, but that doesn't
happen often for me.

--Jonathan
 
J

James Kanze

For things where the value of the constant is irrelevant, but
I need uniqueness, I prefer enums. So WM_XXX events would fit
in this category for me. It's less maintenance, and I don't
feel compelled to use all-capitals for the name, which I find
ugly.

Typically, all-capitals are reserved for macros, which don't
obey scope.
Then I put the enum in a namespace. Something like
namespace Event {
enum event_t { Quit, Paint, Move, Resize, User };
}
If the user is free to create event_t's, they do so as ints,
ex., (Event::User + 1). Then, whatever interface I've written
accepts ints. For example, an event loop that has a
postEvent(int) function.

It's also possible to reserve a zone of values in an enum. You
don't want to use int's unless absolutely necessary, since you
loose type checking.
On the other hand, when the value is meaningful (such as a bit
mask) then I'll use a static const global. Unless it's
something I want to be seen from other translation units, but
that doesn't happen often for me.

C++ has defined enum's so that they can be used for bit masks as
well. With, again, strict type checking. (But you have to
overload the | and & operators.)
 
J

Jonathan Lee

It's also possible to reserve a zone of values in an enum.

Really? I have *never* seen this. Unless you mean defining
something like

enum event_t { Quit, Paint, Move, Resize, User = 100,
UserEnd = 1000 };

in which case you can write a registration function like Qt
does (I believe it goes like this):

event_t RegisterEvent(int i) {
// FIXME: check bounds
return static_cast<event_t>(User + i);
}

I don't see a whole lot of value in this since my code
generally looks like this

switch(int_value) {
case Quit: handleQuit(); break;
case Paint: handlePaint(); break;
default: virtualUserEventHandler(int_value);
break;
}

i.e., the user gets the int back as he wrote it in the
first place. So the type is consistent.
C++ has defined enum's so that they can be used for bit masks as
well.  With, again, strict type checking.  (But you have to
overload the | and & operators.)

I understood that this only worked for ints (until C++0x). Not
for unsigned or unsigned long, for example. For my
particular uses I need unsigned masks, so static const it is.

Of course, I'm open to better practices.

--Jonathan
 
D

dragan

James said:
Typically, all-capitals are reserved for macros, which don't
obey scope.

And manifest constants. At least _I_ am one who likes those things to stand
out plainly in the code, but maybe I like code that is more visually
"immediately" comprehensible. I also don't follow the convention that macros
are all upper case: I make them title case. To each their own (unless you
are in the awful position of being a "team coder" in a corporate setting
"managed" by a post-menapausal MBA who has 1 million "business rules" for
you to integrate into HER software program! Hell hath no fury like...!).
 
J

James Kanze

Really? I have *never* seen this. Unless you mean defining
something like
enum event_t { Quit, Paint, Move, Resize, User = 100,
UserEnd = 1000 };

Exactly.

Alternatively, in one or two cases, I've done somthing like:

enum ErrorType { a, b, c, fromX = 0x100 } ;

I use this when my code calls other code (X) which can also
generate errors: if X generates an error, I or in fromX, and
return the results. (It's then trivial to provide code which
will extract X's error.)
in which case you can write a registration function like Qt
does (I believe it goes like this):
event_t RegisterEvent(int i) {
// FIXME: check bounds
return static_cast<event_t>(User + i);
}
I don't see a whole lot of value in this since my code
generally looks like this

switch(int_value) {
case Quit: handleQuit(); break;
case Paint: handlePaint(); break;
default: virtualUserEventHandler(int_value);
break;
}
i.e., the user gets the int back as he wrote it in the
first place. So the type is consistent.

But the type is an int. The point is that you want the type to
be something else. (But yes, the type will be different in the
user code. The virtualUserEventHandler will have to start with
something like:

user_event_t event = static_cast< user_event_t >(value);

But it should do this anyway. (In my case above, I provide a
function for extracting X's error.)
I understood that this only worked for ints (until C++0x).

What only worked for ints? You can't overload | and & for ints.
But something like:

enum BitMasks
{
zero = 1 << 0,
one = 1 << 1,
two = 1 << 2,
three = 1 << 3
};

inline BitMasks operator|( BitMasks lhs, BitMasks rhs )
{
return static_cast< BitMasks >( static_cast< unsigned >( lhs )
| static_cast< unsigned
( rhs ) );
}

inline BitMasks operator&( BitMasks lhs, BitMasks rhs )
{
return static_cast< BitMasks >( static_cast< unsigned >( lhs )
& static_cast< unsigned
( rhs ) );
}

is quite frequent.
Not for unsigned or unsigned long, for example. For my
particular uses I need unsigned masks, so static const it is.

You can cast an enum to any integral type which will hold all of
the values. And an enum is guaranteed to be able to hold the
`or' of all of its values.
Of course, I'm open to better practices.

I don't know if its "better practice", but the above is
certainly the most widespread practice I've seen.
 
J

James Kanze

And manifest constants.

That's not the generally recommended practice in C++. (It was
recommended practice in C, but that was at least partially
because manifest constants were macros.)
At least _I_ am one who likes those things to stand out
plainly in the code, but maybe I like code that is more
visually "immediately" comprehensible.

But what do you want to stand out?

At least at present, in most environments, you have a lot less
alternatives than you have distinctions. Even important
distinctions: in order to simply parse the language, you need to
separate macros, type names, template names, and all of the
rest. That's four distinct categories, and I don't know of four
distinct naming conventions which can easily be used. (An
interesting convention would be for templates to be in italics,
but the standard doesn't support semantic meaning for different
type faces, and nor do any of the compilers I know, even as an
extension.) I've also seen a lot of people who want to
distinguish functions from non-functions, even to the point of
using the same conventions for functions and types.

Distinguishing between constants and variables, on the other
hand, is a sort of hungarian notation which will bite you in the
long run. One man's constant is another man's variable, as they
say, and today's manifest constant will be read from a
configuration file in the future.
I also don't follow the convention that macros are all upper
case: I make them title case. To each their own (unless you
are in the awful position of being a "team coder" in a
corporate setting "managed" by a post-menapausal MBA who has 1
million "business rules" for you to integrate into HER
software program! Hell hath no fury like...!).

There's no inherent merit in one convention over another, as
long as it is coherent, and the important differences are made.
Given this, it's a lot easier to flow with the croud; people
coming to your code from other projects will have to adapt less,
and you won't have to adapt when you want to read their code.
All caps for macros is about the only convention for which a
sort of a consensus exists, so it's really pretty stupid not to
follow it. (Regretfully, there aren't any other consensuses that
I know of, so I usually have to re-adapt every time I change
jobs. It would be nice if there really was one universal
indentation style, and universal naming conventions.)

And if you're producing quality software, you're a team
programmer, because one person, working alone, with no code
reviews and no discussions with other programmers, can't produce
quality code. Programming is a team activity.
 
J

Jonathan Lee

What only worked for ints?

Sorry, what I meant was (I thought) enums could only hold
values as large as int could hold. So, imagine int is 16-bit
and long is 32-bit. I thought it would be impossible to
assign an enum the value 0xFFFFFF in this case.

I understood the "underlying type" of enum is int, and so
was limited to the same range of values. (Until C++0x
extends this).

Since what I mask is an usually an unsigned long (say,
of flags from a file header) enums have always been ruled
out.

--Jonathan
 
J

James Kanze

Sorry, what I meant was (I thought) enums could only hold
values as large as int could hold. So, imagine int is 16-bit
and long is 32-bit. I thought it would be impossible to
assign an enum the value 0xFFFFFF in this case.
I understood the "underlying type" of enum is int, and so was
limited to the same range of values. (Until C++0x extends
this).

The underlying type is int in C. In C++, the underlying type is
implementation defined, but 1) must be large enough to contain
an ``or'' of all of the values, and 2) cannot be gratuiously
larger than int or unsigned int.
Since what I mask is an usually an unsigned long (say, of
flags from a file header) enums have always been ruled out.

If the constants in the enum require an unsigned long, the
implementation will use unsigned long for the underlying type.
 
A

Alf P. Steinbach

* James Kanze:
The underlying type is int in C. In C++, the underlying type is
implementation defined, but 1) must be large enough to contain
an ``or'' of all of the values, and 2) cannot be gratuiously
larger than int or unsigned int.

By "gratuiously" you probably mean "gratuitously", and you probably intended to
express that the standard guarantees that the underlying type of an enum will
not be larger than int unless that's necessary to accomodate some enum value.

If the constants in the enum require an unsigned long, the
implementation will use unsigned long for the underlying type.



Cheers,

- Alf
 
J

James Kanze

* James Kanze:
By "gratuiously" you probably mean "gratuitously", and you
probably intended to express that the standard guarantees that
the underlying type of an enum will not be larger than int
unless that's necessary to accomodate some enum value.

Yes. (That's what gratuitously means, I think.)
 
D

dragan

James said:
That's not the generally recommended practice in C++. (It was
recommended practice in C, but that was at least partially
because manifest constants were macros.)

Well, given the responses thus far to my OP, I see no reason to to:

1. Stop using '#define' for the WM_USER-like things.
2. Stop typing WM_USER-like things in all upper case.

Not that I can't see a reason to do something other than 1 above: to get a
distinct type. enums don't seem to be good at that without the ugly
namespace hack that someone proposed.
But what do you want to stand out?

The WM_USER-like things. I called them 'manifest constants' but I'm not sure
if that is the correct use of that terminology.
At least at present, in most environments, you have a lot less
alternatives than you have distinctions.

Yes. That I take creative liberty with the coding style says that I feel the
need to lessen that situation.
Even important
distinctions: in order to simply parse the language, you need to
separate macros, type names, template names, and all of the
rest. That's four distinct categories, and I don't know of four
distinct naming conventions which can easily be used.

To say that you can't distinquish EVERY separate class of elements so you
shouldn't try to distinguish ANY of them may be your way, but not mine
obviously.
(An
interesting convention would be for templates to be in italics,
but the standard doesn't support semantic meaning for different
type faces, and nor do any of the compilers I know, even as an
extension.)

Syntax highlighters are your friend. Though I've not seen any that single
out templates as a separate class of code. It sounds like maybe you should
contact your vendor and ask for that feature.
I've also seen a lot of people who want to
distinguish functions from non-functions, even to the point of
using the same conventions for functions and types.

To each (team) their own. That's why coding style guidelines (laws) are
required on projects.
Distinguishing between constants and variables, on the other
hand, is a sort of hungarian notation which will bite you in the
long run. One man's constant is another man's variable, as they
say, and today's manifest constant will be read from a
configuration file in the future.

Probably not for the well-defined things like WM_USER.
There's no inherent merit in one convention over another, as
long as it is coherent, and the important differences are made.

When I said I make macros title case, I meant those that are keyword-like,
and those I put in a file named usertype.dat which Visual Studio uses to
afford the developer separate syntax highlighting. For those macros that are
used just to save typing, I use all upper case.
Given this, it's a lot easier to flow with the croud; people
coming to your code from other projects will have to adapt less,
and you won't have to adapt when you want to read their code.

Except that I've already concluded that my coding style is the best! ;)
All caps for macros is about the only convention for which a
sort of a consensus exists,

There are different kinds of macros. WM_USER is a macro if you want to
define 'macro' that broadly so as to encompass everything that the
preprocessor #define defines. I prefer to distinguish the usage and to avoid
the theory of the extreme positions that are like: "the preprocessor is
evil", "goto is evil", etc.
so it's really pretty stupid not to
follow it.

Or... maybe... just maybe.... it's.... SATAN?!! Well isn't that special.
(LOL).
(Regretfully, there aren't any other consensuses that
I know of, so I usually have to re-adapt every time I change
jobs. It would be nice if there really was one universal
indentation style, and universal naming conventions.)

There is: MINE! :)
And if you're producing quality software, you're a team
programmer, because one person, working alone, with no code
reviews and no discussions with other programmers, can't produce
quality code. Programming is a team activity.

You seem to be practical and knowledgeable some times, and then you blurt
out stuff like that which requires me to keep the salt shaker on my desk
nearby and handy.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,755
Messages
2,569,536
Members
45,012
Latest member
RoxanneDzm

Latest Threads

Top