preprocessor and enum

C

ccwork

Hi all,
We can define some magic number with #define:

#define OPERATION_1 0x00000001
#define OPERATION_2 0x00000002
#define OPERATION_3 0x00000003
....

We can also do that with enum:
enum OPERATION
{
OPERATION_1=0x00000001,
OPERATION_2=0x00000002,
OPERATION_3=0x00000003,
...
};

What make you prefer preprocessor? What make you prefer enum? Any
experience support that?
 
R

Raymond Martineau

Hi all,
We can define some magic number with #define:

#define OPERATION_1 0x00000001
#define OPERATION_2 0x00000002
#define OPERATION_3 0x00000003
...

We can also do that with enum: [...]
What make you prefer preprocessor? What make you prefer enum?

Preprocessor is find with C, but should be avoided as a #define does not
have any knowledge of context and can mess things up if you don't expect
them to. (e.g. a header file defines the macro begin, which clobbers local
variable definitions.)
 
A

Arthur J. O'Dwyer

We can define some magic number with #define:

#define OPERATION_1 0x00000001
#define OPERATION_2 0x00000002
#define OPERATION_3 0x00000003
...

We can also do that with enum:
enum OPERATION
{
OPERATION_1=0x00000001,
OPERATION_2=0x00000002,
OPERATION_3=0x00000003,
...
};

What make you prefer preprocessor? What make you prefer enum? Any
experience support that?

What I say: Enums are the way to go. They give you all the benefits
of a compile-time constant, plus the benefits of the language's type
checking.
What I do: #define, #define, #define. Why? Because #defines are
easier to type. They don't require you to insert = signs everywhere,
or to count commas. (Yes, you just need to stick a comma after each
enumeration value --- but that's still a lot of extra typing, even if
it is mindless.)
There's another dumb reason I use #defines over enums: I don't have
to think about capitalization. #defined constants in C code are ALL_CAPS,
always. Enumeration constants... well, I'd have to think about that.
I'd probably lower_case enum values in most cases. But the important
thing is, I'd have to think about it. With #defines, there's a default
style that everyone knows by heart, so it's one less thing to worry
about.
Another dumb reason: Enum definitions define a new "type." Which I
usually feel deserves a name, such as

enum suit {
Hearts, Spades, Clubs, Diamonds,
};

Now, suppose I write a function that takes a suit as a parameter. Do
I write

void pick_a_card(suit s);

or do I write

void pick_a_card(int s);

? With #defines, there's no decision to make. No new type, no tricky
style decisions.

So there you have it --- I recommend 'enum' in theory, but when it
comes to actual coding (of relatively tiny stuff, I must add), I almost
always use '#define' instead, because it means fewer time-consuming
style decisions for me.

HTH, but get a second opinion,
-Arthur
 
C

CBFalconer

Arthur J. O'Dwyer said:
.... snip ...
Another dumb reason: Enum definitions define a new "type." Which I
usually feel deserves a name, such as

enum suit {
Hearts, Spades, Clubs, Diamonds,
};

Now, suppose I write a function that takes a suit as a parameter.
Do I write

void pick_a_card(suit s);

Which would be wrong. No suit type has been defined, only "enum
suit".
 
D

d.kaufmann

there is a small difference when it comes to compilation.
imagine you have a small number of OPERATIONS that would
fit into one byte.
your declaration would then look like this :

#define OPERATION_1 0x01
#define OPERATION_2 0x02
#define OPERATION_3 0x03

or

enum OPERATION
{
OPERATION_1=0x01,
OPERATION_2=0x02,
OPERATION_3=0x03
};

now imagine you compile the statements

unsigned char OP;
....
if(OP == OPERATION_1) { DoSomething() }

for an 8 bit processor (they are still used in embedded systems)
the comparison would take 3 assembler statements (2 byte compares
and a conditional jump) for the enum version, cause the compiler
would typecast OP to int. (typecasts always to the bigger type)
it would only take 2 assembler statements (1 compare, 1 conditional
jump) for the #define version.
 
C

Chris Dollin

there is a small difference when it comes to compilation.
imagine you have a small number of OPERATIONS that would
fit into one byte.
your declaration would then look like this :

#define OPERATION_1 0x01
#define OPERATION_2 0x02
#define OPERATION_3 0x03

or

enum OPERATION
{
OPERATION_1=0x01,
OPERATION_2=0x02,
OPERATION_3=0x03
};

now imagine you compile the statements

unsigned char OP;
...
if(OP == OPERATION_1) { DoSomething() }

for an 8 bit processor (they are still used in embedded systems)
the comparison would take 3 assembler statements (2 byte compares
and a conditional jump) for the enum version, cause the compiler
would typecast OP to int. (typecasts always to the bigger type)
it would only take 2 assembler statements (1 compare, 1 conditional
jump) for the #define version.

The compiler is perfectly at liberty to implement the enum comparison
the "short way" if it so wishes. I presume C compilers for 8-bit
machines are very likely to make wishes of that kind.
 
F

Flash Gordon

there is a small difference when it comes to compilation.
imagine you have a small number of OPERATIONS that would
fit into one byte.
your declaration would then look like this :

#define OPERATION_1 0x01
#define OPERATION_2 0x02
#define OPERATION_3 0x03

or

enum OPERATION
{
OPERATION_1=0x01,
OPERATION_2=0x02,
OPERATION_3=0x03
};

now imagine you compile the statements

unsigned char OP;
...
if(OP == OPERATION_1) { DoSomething() }

for an 8 bit processor (they are still used in embedded systems)
the comparison would take 3 assembler statements (2 byte compares
and a conditional jump) for the enum version, cause the compiler
would typecast OP to int.

If the compiler does that then the optimiser is not very good. It should
see at compile time whether OPERATION_1 is within the range of OP and if
it isn't eliminate the code and if it is do a test of the byte against
the requisite value.
> (typecasts always to the bigger type)
it would only take 2 assembler statements (1 compare, 1 conditional
jump) for the #define version.

Actually, 0x01 also has a type of int, so if the compiler does not
optimise the comparison as described above you still have the problem.
 
D

David Resnick

ccwork said:
Hi all,
We can define some magic number with #define:

#define OPERATION_1 0x00000001
#define OPERATION_2 0x00000002
#define OPERATION_3 0x00000003
...

We can also do that with enum:
enum OPERATION
{
OPERATION_1=0x00000001,
OPERATION_2=0x00000002,
OPERATION_3=0x00000003,
...
};

What make you prefer preprocessor? What make you prefer enum? Any
experience support that?

enums are the way to go I believe. They allow the compiler some more
information, which it can in turn use to help you out.

Example:

switch (foo) {
case OPERATION_1:
...
break;
case OPERATION_2:
...
break;
}

If foo is an int, that can't get you a diagnostic. If foo is an enum
the compiler (e.g. gcc with -Wall) might be able to give you something
like this:
foo.c:20: warning: enumeration value `OPERATION_2' not handled in switch

I like that. If I have a switch that doesn't have a default and doesn't
cover all the enumerated cases I think it is broken, nice that the
compiler can detect it. There may be other cases where the compiler
can do useful things given the knowledge that a value is an enumeration,
not sure though.

-David
 
F

Fred L. Kleinschmidt

ccwork said:
Hi all,
We can define some magic number with #define:

#define OPERATION_1 0x00000001
#define OPERATION_2 0x00000002
#define OPERATION_3 0x00000003
...

We can also do that with enum:
enum OPERATION
{
OPERATION_1=0x00000001,
OPERATION_2=0x00000002,
OPERATION_3=0x00000003,
...
};

What make you prefer preprocessor? What make you prefer enum? Any
experience support that?

I like to use:
typedef enum {
XXX=0,
YYY,
ZZZ,
...
} MyEnumType_t;

This makes it easy do define a large number of UNIQUE values - using
#define to define 20 or 30 values may seem ok, but you could easily
define two to be the same value.
(Note - I only ever specify the first value for the enum).

By using a typedef, I avoid the problem mentioned by Mr. O'Dwyer:
foo( MyEnumType_t arg );
 
K

Keith Thompson

ccwork said:
Hi all,
We can define some magic number with #define:

#define OPERATION_1 0x00000001
#define OPERATION_2 0x00000002
#define OPERATION_3 0x00000003
...

We can also do that with enum:
enum OPERATION
{
OPERATION_1=0x00000001,
OPERATION_2=0x00000002,
OPERATION_3=0x00000003,
...
};

What make you prefer preprocessor? What make you prefer enum? Any
experience support that?

I consider the use of enum this way to be a trick; it creates a
superfluous type when all that's needed is a set of constants.

However, it's a fairly common idiom, and I have no problem with using
it. I'd probably drop the tag (since you never want to use the type)
and add a comment explaining the purpose of the declaration, something
like:

/*
* This enum declaration is intended only to create a set of
* constants; the type itself is not used.
*/
enum {
OPERATION_1 = 0x00000001,
OPERATION_2 = 0x00000002,
OPERATION_3 = 0x00000003,
...
};

One drawback is that it can only create constants of type int.
 

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

Similar Threads

Preprocessor commands usage. 11
preprocessor bug? 2
Maximum Nr. of bitflags ... ? 5
preprocessor trick 4
pipelining in C++ 0
Preprocessor problem 15
Preprocessor eating backslashes?? 5
Optimize a discovery algorithm 15

Members online

No members online now.

Forum statistics

Threads
473,754
Messages
2,569,525
Members
44,997
Latest member
mileyka

Latest Threads

Top