Mechanism to generate annotated "error codes"

K

Keith Thompson

Ian Collins said:
Don Y said:
You need to maintain this "by hand". There is nothing that
prevents you from:
#define YES (2)
#define NO (2)

Nor:
#define YES (2)
...
#define ABSOLUTELY (27)
(assuming yes and absolutely are synonyms)
[...]

A minor point: parenthesizing macro definitions is a good habit, but
it's not necessary when the macro expands to a single token. This:

#define ABSOLUTELY 27

is just as safe as

#define ABSOLUTELY (27)

Or simply don't use macros for constants!

27!. Um, I mean ABSOLUTELY! :cool:}

But note that declaring:

const int ABSOLUTELY = 27;

gives you something that you can't use where a constant expression
is required, nor can you meaningfully use it in a #if directive.
For values in the range of int, you can work around that by using
an enum:

enum { ABSOLUTELY = 27 };

(In either case, you probably wouldn't use all-caps for the name.)
 
D

Don Y

Hi Kaz,

Do you often modify your gun to a completely different model,
and without touching the safety switch?

Is there NO possibility of someone OTHER THAN YOU having access
to that weapon? Someone who doesn't have the same sense of
discipline that you *might*?

You would adopt the same HABIT with the new weapon. Habits are
things that you shouldn't have to PAUSE AND THINK ABOUT. Do
you *consciously* think about the semicolon at the end of each
source statement? Do you *consciously* think about the parens
following a function invocation?

E.g., I only consciously think about semicolons on empty
statements. And, since these are so atypical, it gives me
reason to pause and DRAW ATTENTION to these "/* EMPTY */"
statements.

You shouldn't have to ask yourself, "Should I put the safety
on, now?". You should do it without thinking.

"Should I parenthesize this definition?"

The "stop and think" action should be the exception leading
to the more "risky" behavior: "Do I *really* want to be ready
to KILL someone?"

"Is there some reason why I *shouldn't* parenthesize?"

Establish behaviors that tend to decrease the chances of
people making undetected (or, expensive to detect) mistakes.
The compiler will complain if you add parens somewhere that
violates syntactic requirements. It *won't* if you fail to
add the parens. (yes, a friendly compiler can warn you
of this possibility -- but, doesn't *have* to)

IME, people mimic the styles and practices of the code
they are maintaining. You play fast and loose with how
you write things and you invite others to do the same.

You will note that the solution I am using from the original
post (Hmmm... original post? What's *that*?!) did not expose
#defines to the developer. It intentionally hid the "how's"
of the mechanism from the developer while providing the
desired functionality. The MakeErrorCode mechanism can be
implemented any way I choose -- so long as the code it
generates (none) is predictable and the functionality desired
is present. E.g., all I need to do is make sure the compiler
(when *it* sees the code "for real") knows how to resolve
"Err_TooManyDecimalPoints" -- whether as a manifest constant,
extern variable, a substitution performed by the preprocessor
(or other agent), etc.

Build a mechanism that is easy for others to adopt so that
it is easier for them to continue that behavior than to
come up with a different, ad hoc approach on their own.
(e.g., the *expensive* mechanism of storing error messages
in a real database instead of ad hoc strings littered through
the code and ad hoc mechanisms for presenting them to the user.)

Of course, if you're the only one maintaining your code
or can be assured people AT LEAST as competent as you
are the only ones that it is exposed to, you can get away
with whatever you want. I find that in heavily regulated
industries and large projects (100's of KLoC's), folks seem
to want to err on the side of more "consistency" than "less".
 
D

Don Y

Hi Ian,

You need to maintain this "by hand". There is nothing that
prevents you from:
#define YES (2)
#define NO (2)

Nor:
#define YES (2)
...
#define ABSOLUTELY (27)
(assuming yes and absolutely are synonyms)
[...]

A minor point: parenthesizing macro definitions is a good habit, but
it's not necessary when the macro expands to a single token. This:

#define ABSOLUTELY 27

is just as safe as

#define ABSOLUTELY (27)

Or simply don't use macros for constants!

Or, as described in my "solution", *hide* all of that mechanism from
the developer! *Don't* build large tables MANUALLY with bogus
constants in them. Let the machine do what it does best -- simple,
repetitive tasks (like expanding MakeErrorCode into something
useful for the functionality it provides).
 
K

Kaz Kylheku

Hi Kaz,



Is there NO possibility of someone OTHER THAN YOU having access
to that weapon? Someone who doesn't have the same sense of
discipline that you *might*?

Since #def SYM 27 is perfectly safe when accessed, this analogy falls
flat on its face, too.
You would adopt the same HABIT with the new weapon. Habits are
things that you shouldn't have to PAUSE AND THINK ABOUT.

Someone else's good habit is not writing parentheses around single tokens.

It's only a tiny bit more complex than the naive habit of always doing it, but
it's still a habit.

In a car, you have two pedals operated with the same foot that do something
quite opposite, yet your brain can generally work out which to use.
You don't think, "I want to slow down; is it the left one?"
 
S

Stefan Ram

Don Y said:
You need to maintain this "by hand". There is nothing that
prevents you from:
#define YES (2)
#define NO (2)

The include-file can be generated:

printf( "#define YES (%d)\n", ++i );
 
M

Malcolm McLean

There's no good answer.

The first thing to do is to divide your code into "pure functions" and
"procedures". A pure function shuffles bits, a prodeucre shuffles bits
and does IO.
A pure function can only fail in three circumstances, it's called with
invalid parameters, there's an internal error in its coding, or it
runs out of memory.
A procedure can fail in these circumstances, and also because of a
hardware problem, because the user provides erroneous or even
malicious data, because hardware is functioning but is overwhelmed by
the demands of the prodecure, or because of missing resources such as
non-existent files.

So let's take the situations one by one. If you have invalid
parameters, that normally indicates a programming error in calling
code. The rare exception is when caller can't reasonably be expected
to check the parameters for validity - e.g. a statistical procedure
might fail when the numbers have a distribution that becomes less like
a bell curve when you take means of a sample. Caller can't reasonably
be expected to check for that condition. If caller can't be expected
to check the parameters, an "abormal" result must be passed back as
part of the normal flow control of the program. If caller can, there's
not much point in shuting the error back up to a buggy caller. You
need to abort the program with an error message if it can be aborted,
suppress the error if abortion isn't an option.

Internal error - difficult. Your own code is buggy. There's no real
answer to this situation.

Out of memory - if it can reasonably be expected that the function
will run out of memory, shunt up an "out of memory" condition to
caller. If basically this can't happen (you need to store one filename
dynmaically ona amchine with 4GB of memory), abort with an out of
memory message.

Hardware problems: is in once in a blue moon, or can it reasonably be
expected? If it's once in a blue moon, simply report an IO error and,
usually, terminate. If it's expected, you will need to know how to
code round the expected harware failures.

Bad user data - usually you should assume that the user is a hacker
trying to make your program malfunction. Should be normal control path
of the program, and reported up to caller.

Overhelmed hardware - very difficult. If it will take a whole day to
write results to disk, is this acceptable or must be abort? It's often
not easy to answert these questions, or anticipate them. The file
might be quite small, the disk hardware very busy.

Missing resources - happens all the time. Treat as normal flow control
of program.

So generally the strategy is to treat so-called error conditions as
normal flow control, and pass the error condition up to caller. So
they're not really errors at all. The exception is errors caused by
programming mistakes. It's dangerous to pass an error back up to a
buggy caller. Normally you want to assert fail, which gives the
compiler the chice between reporting and aborting, or siliently
suppressing the error.

Centralised error systems mean that code will break if you try to move
it to a different project.
 
D

Don Y

Hi Malcolm,

There's no good answer.

The first thing to do is to divide your code into "pure functions" and
"procedures". A pure function shuffles bits, a prodeucre shuffles bits
and does IO.

So, your classification treats a parser as a "pure function" -- if
the I/O has been handled outside of the scope of the parser.
A pure function can only fail in three circumstances, it's called with
invalid parameters, there's an internal error in its coding, or it
runs out of memory.

In my case, a "process" (being imprecise in my terms) can also
*lose* a resource (e.g., memory) that it previously possessed.
But, its failure to act properly on the notification of that loss
would fall under the "bug" category (though there are some
degenerate cases where a resource may be withdrawn before the
process can act on the notification -- but the OS can deal with
that special case)
A procedure can fail in these circumstances, and also because of a
hardware problem, because the user provides erroneous or even
malicious data, because hardware is functioning but is overwhelmed by
the demands of the prodecure, or because of missing resources such as
non-existent files.

How are you differentiating the parser instance from the "malicious
data" instance? What if the data to be parsed originally came from
user I/O? What if it came from a corrupted data store? (is that
a "hardware failure"?)
So let's take the situations one by one. If you have invalid
parameters, that normally indicates a programming error in calling
code. The rare exception is when caller can't reasonably be expected
to check the parameters for validity - e.g. a statistical procedure
might fail when the numbers have a distribution that becomes less like
a bell curve when you take means of a sample. Caller can't reasonably
be expected to check for that condition. If caller can't be expected
to check the parameters, an "abormal" result must be passed back as
part of the normal flow control of the program. If caller can, there's
not much point in shuting the error back up to a buggy caller. You
need to abort the program with an error message if it can be aborted,
suppress the error if abortion isn't an option.

Parsing data provided (as an "input" to your pure function)
could nominally *expect* to find problems in that data through
no fault of the caller or the function operating on it. Yet,
the intent of moving the parsing into this "pure function"
is so the caller need not be aware of the requirements the
parser imposes on the data.

I.e., this looks like "bad data/parameters" but isn't a failure
of the code from either party. Since the parser didn't do the
I/O that originated the bad data, ...

You may not be able to communicate with the originator of that
data (e.g., if you read it from a file ... or, a const array
embedded somewhere in your image). And, you may not want to abort
the "program" -- just that *aspect* of the processing.
Internal error - difficult. Your own code is buggy. There's no real
answer to this situation.

It depends on the nature of your error (as do hardware errors!).
Your code might still be able to reliably report an error. It
just might not make sense to you -- until you examine your
code and see why your code is "deliberately" making that mistake.

I.e., if the results aren't safety critical, you make a best
effort and hope for the best. (I have some systems where
"/* CAN'T HAPPEN */" really should NEVER happen and, as a
result, you want to lock up the processor *hard* so that
it doesn't do anything potentially dangerous or lossy)
Out of memory - if it can reasonably be expected that the function
will run out of memory, shunt up an "out of memory" condition to
caller. If basically this can't happen (you need to store one filename
dynmaically ona amchine with 4GB of memory), abort with an out of
memory message.

But the user doesn't want to see "out of memory". The user doesn;t
need to *know* what's going on inside the code. The memory error
needs to be reinterpreted in the context of higher levels in the
application and expressed in a way that is appropriate to that
application and user base. This allows lower level libraries to
be reused between applications and *within* an application.

Running out of memory when trying to add a name to an address
book should yield a different message than when trying to print
a banner. The role of the memory allocation -- along with its
persistence and remedies -- varies based on that context.

I.e., "Your address book is full. Delete one or more entries
of you want to add this new contact" vs. "Image too large to
print. Try a lower resolution or size." Putting the "out of
memory" error into context adds value to the user (and thus
the product).
Hardware problems: is in once in a blue moon, or can it reasonably be
expected? If it's once in a blue moon, simply report an IO error and,
usually, terminate. If it's expected, you will need to know how to
code round the expected harware failures.

Bad user data - usually you should assume that the user is a hacker
trying to make your program malfunction. Should be normal control path
of the program, and reported up to caller.

[Again, how does this tie in with the parser "pure function"?]

IMO, you want to provide as much information to the user to enable
them to correct their entry (even if it is a malicious user). This
is where exposing internal "errors" from a lower level function
(e.g., parser) can benefit the user without burdening the application
or tying low level routines to a particular application.

For example, complaining that an integer value contained a decimal
point can be reported to the user regardless of whether the upper
layer that invoked the parsing routine was using it to fetch an
*age*, IQ, ZIP code, etc. The context associated with the upper
layer gives the "problem report" ("error" is a hard word to
avoid) some meaning; further qualification obtained from the
report of the lower level parser helps to clarify that:

Upper context: "The age that you entered is invalid."
Lower context: "The value must be a whole number -- it can not
contain a decimal point"
Overhelmed hardware - very difficult. If it will take a whole day to
write results to disk, is this acceptable or must be abort? It's often
not easy to answert these questions, or anticipate them. The file
might be quite small, the disk hardware very busy.

Missing resources - happens all the time. Treat as normal flow control
of program.

So generally the strategy is to treat so-called error conditions as
normal flow control, and pass the error condition up to caller. So
they're not really errors at all. The exception is errors caused by
programming mistakes. It's dangerous to pass an error back up to a
buggy caller. Normally you want to assert fail, which gives the
compiler the chice between reporting and aborting, or siliently
suppressing the error.

If the assertion is going to just terminate the program, then
the user has learned nothing. He reruns the program and it
dies, again.
Centralised error systems mean that code will break if you try to move
it to a different project.

I don't see that as a consequence of this sort of approach.
My scheme pieces errors (and descriptions) together as they
are encountered in the executable. Out_Of_Memory might resolve
to 0x2345678 today, 0x107 tomorrow and NEVER HAPPEN some time
next week. (i.e., grep the sources and you never FIND that
string in it!)

Furthermore, *your* instance of the sources might have a
different set of errors (and, thus, codes) than *my* image
based on the types of errors that our respective assignments
require us to detect/encounter.

And, when our images are merged, we magically end up with the
union of error messages and "codes" that we have individually
used.

As I said, I already have a version of this working -- borrowing
algorithms from my IDL compiler (same issues -- automatically
managing "message types"). But, it is hard to tell make(1)
when the error codes might have changed. So, any change to
a source file requires rebuilding the error code catalog.
The only way I can currently get make to cooperate is if I
introduce an intermediate file that I only touch(1) if the
error codes need to be updated. Then, put a separate dependency
on that file that triggers the building of the error codes
for other files. As projects get bigger, this approach doesn't
scale well :< (or, it coerces you into doing things in
less than optimal ways *just* to workaround the tools)

[I also have a problem using __FILE__, __func__ and __LINE__ as
components of unique identifiers as I might have 20 files named
get.c. Each with a function called get() within. And the
structure of each get() highly resembling the other 19 get()'s
(i.e., signaling errors on the same __LINE__s though physically
different files). And, I place strong restrictions on what can
be done with these "error codes" -- they aren't "regular numbers"]

If you try to hard-code error "codes", then you ARE stuck with a
system that only works for *you* and *your* application. Not a
very smart move, IMO.

Barring some other clever approach, I will just write a utility to
build the error codes and add it into the build process -- using
the IDL compiler as a rough framework (same sorts of issues). I
don't want to head down that path until I know that's the *best*
option!
 
J

James Kuyper

Hi Kaz,

....
"Is there some reason why I *shouldn't* parenthesize?"

Well, Ben Pfaff has provided one example where parenthesizing a macro
definition interferes with it's intended usage. A parenthesized string
literal is not a string literal, and will therefore not be merged with
adjacent string literals. Many string literal macros are intended to be
used in the construction of larger string literals, so parenthesizing
them would interfere with that use case.
 
J

James Kuyper

What happens when someone experiments with the value 27 by trying this:

#define ABSOLUTELY 27+1

The same thing that happens when somebody experiments as follows:

#define ABSOLUTELY (27)+1

Or

#define ABSOLUTELY (27)+(1)

If someone doesn't understand why the parenthesis are unnecessary for
(27), then that person can't be counted on to understand whey they are
necessary in (27+1). For such a person, it may be best to parenthesize
everything, just to be safe - the compiler will almost certainly
complain if someone attempts string literal merging with a parenthesized
string literal. I think it's preferable, however, to gain the small
amount of additional understanding needed to realize why (27) is in fact
unnecessary, and ("Absolutely") may actually be a problem.
 
M

Malcolm McLean

Hi Malcolm,




So, your classification treats a parser as a "pure function" -- if
the I/O has been handled outside of the scope of the parser.
A parser is a pure function (assuming IO is not handled by the
function). It takes one sequence of bits - the script - and it returns
a another sequence of bits - the result. So all it's doing is
shuffling bits about in memory. If a pure function calls a procedure,
it's no longer a pure function.

However most parsers will do IO. That leads to a question. If a pure
function takes a function pointer as a parameter, and that parameter
is to a procedure, is it still a pure function? The answer is no, but
here the classification is beginning to lose its usefulness.

Parsers are an obvious case of invalid input not being reasonably
caught by caller. You could require that caller pass only correct
scripts to the parser, but that means caller has to do almost as much
work as the parser itself to verify that the script is correct. So you
have to treat malformed scripts as part of the normal flow control of
the parser. The caler's then got the problem. If the script was typed
into the source byt he programer, it's an internal error. If the
script was provided by outside, it should usually be treated as normal
flow control - you expect complex user-provided scripts to be
malformed.
 
M

Malcolm McLean

And, when our images are merged, we magically end up with the
union of error messages and "codes" that we have individually
used.
You've described the problem.

Fred scatters his code with

log_error(ERR_TOOMANYFILESOPEN);

Bert does the same with

fprintf_error(EC_FILECOUNTEXCEEDED, "Too many files open (%d)\n",
Nfiles);

Now when we merge Fred's code with Bert's, what's going to happen?
 
K

Keith Thompson

Don Y said:
Hi Keith,


Keeping a gun's safety "on" is "a good habit". Do you often
leave it *off* just because you don't *think* you need it
"on" at the present time? Or, do you wait until you
really *need* it "off" (i.e., just prior to discharge)?

Good habits are kept -- as a matter of HABIT! :>

For the most part, I tend to agree with your point about good habits.
For example, I consistently use curly braces on compound statements
whether they're necessary or not; I write:

if (cond) {
stmt;
}

rather than

if (cond)
stmt;

But for parenthesizing macro definitions, I don't agree. When I define
a macro whose expansion is to be used as an expression, I put
parentheses around the whole expansion and around each parameter
reference.

Mostly.

But if the entire expansion is a single token, I don't bother with the
parentheses:

#define ANSWER 42

nor do I parenthesize a parameter reference if it's immediately
surrounded by parentheses or square brackets:

#define ELEM(i) (arr)

Why? Because I find the fact that the parentheses are unnecessary
in this case to be sufficiently obvious that omitting them doesn't
bother me, and if I change the definition later I'm not going to
forgot to add parentheses if they're needed:

#define ANSWER (6*9)

I don't claim to be entirely consistent on this point. The reasoning
that leads me to use curly braces in all cases is pretty much the same
reasoning that I ignore for single-token macro definitions.

You haven't changed my mind, but you have forced me to think about the
issue. Thank you for that.
 
P

Phil Carmody

BartC said:
Keith Thompson said:
Don Y said:
You need to maintain this "by hand". There is nothing that
prevents you from:
#define YES (2)
#define NO (2)

Nor:
#define YES (2)
...
#define ABSOLUTELY (27)
(assuming yes and absolutely are synonyms)
[...]

A minor point: parenthesizing macro definitions is a good habit, but
it's not necessary when the macro expands to a single token. This:

#define ABSOLUTELY 27

is just as safe as

#define ABSOLUTELY (27)

What happens when someone experiments with the value 27 by trying this:

#define ABSOLUTELY 27+1

Exactly the same thing that happens when someone experiments with the value
27 by trying this:

#define ABSOLUTELY (27)+1

Such arguments are called "Straw Men".

Phil
--
I'd argue that there is much evidence for the existence of a God.
Pics or it didn't happen.
-- Tom (/. uid 822)
 
K

Kaz Kylheku

Keith Thompson said:
Don Y said:
You need to maintain this "by hand". There is nothing that
prevents you from:
#define YES (2)
#define NO (2)

Nor:
#define YES (2)
...
#define ABSOLUTELY (27)
(assuming yes and absolutely are synonyms)
[...]

A minor point: parenthesizing macro definitions is a good habit, but
it's not necessary when the macro expands to a single token. This:

#define ABSOLUTELY 27

is just as safe as

#define ABSOLUTELY (27)

What happens when someone experiments with the value 27 by trying this:

#define ABSOLUTELY 27+1

That same someone might experiment with (27) this way:

#define ABSOLUTELY (27)+1

You can't possibly be suggesting that when you're coding, you're not only
responsible for what you're actually writing, but also for all possible
careless or clueless mutations of it by someone else?

If you're thinking that this someone is actually yourself, later, then change
the habits of that someone. Don't experiment with a constant 27 by changing it
to 27+1, but just replace it with 28. Always use some kind of version control
which can tell you at any time where your experimental changes are, so
you don't need to use +1 as a flag denoting an experimental change, or
to remind you of what the stock value is.

Secondly, acquire a smarter habit: that of ensuring that every macro
definition you touch, which produces an expression, produces a primary
expression. 27 is already a primary expression, so needs nothing.
21+1 is not a primary expression, and so it needs parentheses, or a
rewrite to 28.

Even the habit of "always add parentheses" is still conditional on something!
Because the dummy who does this will surely not add parentheses every time an
edit is made, like an automaton. E.g.

#define ABSOLUTELY ((27)+1) /* experiment */

#define ABSOLUTELY (((27)+1)+1) /* another experiment */

Even the always-parenthesizing dummy still recognizes that the expression is
already parenthesized (and that experiments can go into the parentheses).

If your habit is conditional anyway, you might as well choose a smart condition
for it: "if expr not already primary, make primary".
 
K

Kaz Kylheku

nor do I parenthesize a parameter reference if it's immediately
surrounded by parentheses or square brackets:

#define ELEM(i) (arr)

Why? Because I find the fact that the parentheses are unnecessary
in this case to be sufficiently obvious that omitting them doesn't


It's not that parentheses are unnecessary here, it's rather that has them
already, in the form of square brackets.

Syntactically, braces do exactly the same job of delimiting a phrase unit as
do parentheses.

In either (i) or , the i material has been placed into a phrase structure
rule where it is guarded by a pair of nonterminal symbols, and so the parser
cannot possibly break i into pieces that get folded incorrectly into
surrounding syntax.

Though [E] is not a primary expression by itself, that is irrelevant to the
issue.
 
M

Malcolm McLean

  #define ABSOLUTELY ((27)+1) /* experiment */

  #define ABSOLUTELY (((27)+1)+1) /* another experiment */

Even the always-parenthesizing dummy still recognizes that the expressionis
already parenthesized (and that experiments can go into the parentheses).
Too many parentheses make code hard to read.

double clamp(double x, double low, double high)
{
if(x < low)
return low;
if(x > high)
return high;
return x;
}

#define clamp(x,low,high) x < low ? low : x > high ? high : x

#define clamp(x, low, high) ((x) < (low) ? (low) : (x) > (high) ?
(high) : (x))

The last is a lot harder to read than the first.
 
S

Stefan Ram

Don Y said:
I.e., "Your address book is full. Delete one or more entries
of you want to add this new contact" vs. "Image too large to
print. Try a lower resolution or size." Putting the "out of
memory" error into context adds value to the user (and thus
the product).

Most C-program processes do not create such nice error
messages when the memory for objects with automatic storage
duration is exhausted. Sometimes, they just die
ungracefully.

Saying that the address book was full, when in fact there is
no memory with dynamic storage duration available anymore,
might mislead the user into believing that the specific
memory of his address book was full when in fact the
general dynamic memory is exhausted, which means that now
all of his "books" suddenly are full. It might make him
beliefe the false idea that there is a fixed number of
entries reserved for his address book, which now is reached.

If the lack of dynamic memory of the C-program process is a
lack of system memory, something less destructive, such as
just closing an unused window, might be more appropriate as
a counter measure than deleting precious address-book entries.

Usually most users will get a better, more correct idea of
the state of things from "Out of memory" than from "Address
book full" in the case that the process encounters a null
result from malloc.
 
B

Ben Bacarisse

Kaz Kylheku said:
nor do I parenthesize a parameter reference if it's immediately
surrounded by parentheses or square brackets:

#define ELEM(i) (arr)

Why? Because I find the fact that the parentheses are unnecessary
in this case to be sufficiently obvious that omitting them doesn't


It's not that parentheses are unnecessary here, it's rather that has them
already, in the form of square brackets.

Syntactically, braces do exactly the same job of delimiting a phrase unit as
do parentheses.

In either (i) or , the i material has been placed into a phrase structure
rule where it is guarded by a pair of nonterminal symbols, and so the
parser cannot possibly break i into pieces that get folded incorrectly into
surrounding syntax.


(Did you mean "terminal symbols" here?)

The trouble is that C's macros are not syntactic. Thus

ELEM(i][0)

is "valid". This might be considered a rare enough error (or one that
is so likely to be picked up elsewhere) that it can be ignored, but it
does show that there is an argument, however weak, for writing

#define ELEM (arr[(i)])

<snip>
 
B

BartC

Kaz Kylheku said:
That same someone might experiment with (27) this way:

#define ABSOLUTELY (27)+1

In the case of (27), it's far more likely that it would be written as
(27+1).

In practice, everyone writes 27, and expects anyone modifying it to insert
any parentheses needed. But (27) is still marginally safer than just 27 for
those few cases where it doesn't happen.
 
S

Stefan Ram

BartC said:
In the case of (27), it's far more likely that it would be written as
(27+1).
In practice, everyone writes 27, and expects anyone modifying it to insert
any parentheses needed. But (27) is still marginally safer than just 27 for
those few cases where it doesn't happen.

From the point of view of a Java programmer, the main problem with

#define ABSOLUTELY (27)+1

and all of the other above define directives is the lack of a
documentation comment. ¯¯¯¯¯¯¯¯¯
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
If the (human) editor does not know what the intended use of this
macro is, he cannot possibly know whether it is helpful to change
it at all and what kind of changes are legal.
 

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
474,082
Messages
2,570,589
Members
47,212
Latest member
JaydenBail

Latest Threads

Top