OK to disguise a macro function as a function?

  • Thread starter Tomás Ó hÉilidhe
  • Start date
T

Tomás Ó hÉilidhe

I'm not a fan of ALL CAPS, but of course I use it for macro
functions to warn the programmer to be picky about what arguments they
pass (for fear of multi-evaluation).

However, I wonder what people think about _not_ using all caps when
the macro function doesn't have any parameters, or when the arguments
won't be multi-evaluated? For instance, in my own code, instead of
having:

inline void RowPinHigh(unsigned const i) { PORTA |= 1u << i; }

, I have the following:

#define RowPinHigh(i) ((void)(PORTA |= 1u << (i)))

Firstly, the argument will only be evaluted once, so there's no
problems on that front. The only other problem I can see is the issue of
being able to take the function's address; I already ran into this
problem in my own code:

void (*RowPinAssert)(unsigned) = (first_pass ? RowPinHigh :
RowPinLow);

Still though, this isn't really so much a problem in that it will
introduce bugs -- you'll simply get a compile error saying RowPinHigh
wasn't declared (or perhaps if your compiler is set to Ancient Mode, a
linker error saying a function called RowPinHigh that returns int
couldn't be found).

Currently I'm using an embedded systems compiler for programming
microcontrollers, and it's not exactly 100% ANSI-compliant so I've had
to do a few things such as:
1) Use macros fuctions instead of inline functions (this actually
_does_ result in my code being smaller and actually fitting onto the
chip's memory).
2) Use global variables instead of local variables in some instances
because there's no stack (another strategy to save memory). I haven't
tried using recursive functions but my bet is they don't work because
static memory is used instead of a stack.

Also, the compiler is very particular about what it considers const.
For instance, you _can_ do the following:

void Func(void)
{
int const i = 7;
}

, but you _can't_ do this:

void Func(int x)
{
int const i = x; /* Compile Error */
}

The reason it gives a compile error is because when it sees the word
"const", it assumes it can store it in read-only memory, i.e. a part of
memory in the chip that can't be written to while the program is
running. Therefore, you can't use const in the example immediately
above.
However, given that I didn't want to pick up the habit of omitting
const, I decided to do the following instead:

#define immutable

void Func(int x)
{
int immutable i = x;
}

, and from there I use "const" when something is trully read-only, and
"immutable" when I just don't want to change it. Then if I migrate my
code to another system, I can just do:

#define immutable const

And if even that's too disgusting for some programmers, I can always do
a "Find and Replace in all files" to change immutable to const.

But anyway... back to the topic at hand, what do you think about _not_
using all caps when there's no parameters or when the parameters won't
be multi-evaluated?
 
U

user923005

    I'm not a fan of ALL CAPS, but of course I use it for macro
functions to warn the programmer to be picky about what arguments they
pass (for fear of multi-evaluation).

    However, I wonder what people think about _not_ using all caps when
the macro function doesn't have any parameters, or when the arguments
won't be multi-evaluated? For instance, in my own code, instead of
having:

    inline void RowPinHigh(unsigned const i) { PORTA |= 1u << i; }

, I have the following:

    #define RowPinHigh(i)    ((void)(PORTA |= 1u << (i)))

    Firstly, the argument will only be evaluted once, so there's no
problems on that front. The only other problem I can see is the issue of
being able to take the function's address; I already ran into this
problem in my own code:

    void (*RowPinAssert)(unsigned) = (first_pass ? RowPinHigh :
RowPinLow);

    Still though, this isn't really so much a problem in that it will
introduce bugs -- you'll simply get a compile error saying RowPinHigh
wasn't declared (or perhaps if your compiler is set to Ancient Mode, a
linker error saying a function called RowPinHigh that returns int
couldn't be found).

    Currently I'm using an embedded systems compiler for programming
microcontrollers, and it's not exactly 100% ANSI-compliant so I've had
to do a few things such as:
    1) Use macros fuctions instead of inline functions (this actually
_does_ result in my code being smaller and actually fitting onto the
chip's memory).
    2) Use global variables instead of local variables in some instances
because there's no stack (another strategy to save memory). I haven't
tried using recursive functions but my bet is they don't work because
static memory is used instead of a stack.

    Also, the compiler is very particular about what it considers const.
For instance, you _can_ do the following:

    void Func(void)
    {
        int const i = 7;
    }

, but you _can't_ do this:

    void Func(int x)
    {
        int const i = x;  /* Compile Error */
    }

    The reason it gives a compile error is because when it sees the word
"const", it assumes it can store it in read-only memory, i.e. a part of
memory in the chip that can't be written to while the program is
running. Therefore, you can't use const in the example immediately
above.
    However, given that I didn't want to pick up the habit of omitting
const, I decided to do the following instead:

    #define immutable

    void Func(int x)
    {
        int immutable i = x;
    }

, and from there I use "const" when something is trully read-only, and
"immutable" when I just don't want to change it. Then if I migrate my
code to another system, I can just do:

    #define immutable const

And if even that's too disgusting for some programmers, I can always do
a "Find and Replace in all files" to change immutable to const.

But anyway... back to the topic at hand, what do you think about _not_
using all caps when there's no parameters or when the parameters won't
be multi-evaluated?

The ALL-CAPS sdreaming nature of function like macros has two purposes
that I can see.
1. It lets you know that you have no type safety.
2. It lets you know that horrible side-effects are possible because
you have no function call sequence point.

I don't like function macros, but if I have to use them (e.g. I would
probably have to use them in your situation) then I would make them
all caps.
 
P

Peter Nilsson

Tomás Ó hÉilidhe said:
    I'm not a fan of ALL CAPS, but of course I use it for
macro functions to warn the programmer to be picky about
what arguments they pass (for fear of multi-evaluation).

There are other schemes, though they are considerably
less popular than all caps.
    However, I wonder what people think about _not_ using
all caps when the macro function doesn't have any
parameters, or when the arguments won't be multi-
evaluated? For instance, in my own code, instead of
having:

    inline void RowPinHigh(unsigned const i)
{ PORTA |= 1u << i; }

, I have the following:

    #define RowPinHigh(i)    ((void)(PORTA |= 1u << (i)))

It's not at all uncommon to implement 'inline' with macros...

#define RowPinHigh(i) \
((void)(PORTA |= 1u << (unsigned int)(i)))

void (RowPinHigh)(unsigned const);

[P.S. note the extra cast in the parameter.]

The most common usage for many people is in <stdio.h>,
though they may not realise it.

    ... given that I didn't want to pick up the habit
of omitting const, I decided to do the following instead:

    #define immutable

    void Func(int x)
    {
        int immutable i = x;
    }

, and from there I use "const" when something is trully
read-only, and "immutable" when I just don't want to
change it. Then if I migrate my code to another system,
I can just do:

    #define immutable const

And if even that's too disgusting for some programmers,
I can always do a "Find and Replace in all files" to
change immutable to const.

Or do...

#ifdef SET_IMMUTABLE_TO_CONST
#define immutable const
#else
#define immutable
#endif

That way the code never changes, the default behaviour is
for your principle compiler, and you can set the macro
based on a compiler command line (or other) option. Many
compilers for hosted systems will let you predefine
macros, e.g. gcc -DMACRO_NAME. [Actually, I can't think
of any that don't, but there are probably some tucked
away.]
But anyway... back to the topic at hand, what do you
think about _not_ using all caps when there's no
parameters or when the parameters won't be multi-
evaluated?

It's a style issue. Like many style issues, the only
real issue how consistently you apply your style. The
biggest mistake you can make is to mix styles.
 
K

Kaz Kylheku

    I'm not a fan of ALL CAPS, but of course I use it for macro
functions to warn the programmer to be picky about what arguments they
pass (for fear of multi-evaluation).

Some years ago I developed a run-time detection for this situation. If
you have a macro m(x) whose expansion evaluates x more than once, it
can produce a warning if x looks like it might be an expression with
side effects.

To use it you'd just #include "sfx.h", add sfx.c to your program and
then write your macro like this:

#define m(x) (one_use(SFX_CHECK(x)), another_use(x))

instead of just this:

#define m(x) (one_use(x), another_use(x))

SFX_CHECK does the rest. It prints diagnostics on stderr, but of
course it can be hacked to do whatever you want: assert, etc. There
are some hooks in the source code if thread or interrupt safety is
required. There is a run-time penalty which can only be eliminated by
separate builds.
    However, I wonder what people think about _not_ using all caps when
the macro function doesn't have any parameters, or when the arguments
won't be multi-evaluated?

Standard practice. If I made a list_for_each macro, I would call it
that, and not shout in all caps.

ANSI C has standard macros that are in lower case.

Maybe we should even drop the silly habit of all caps even for
preprocessor based constants. There is no need for it. There is
vanishingly little value in knowing that some FOO is a preprocessor
constant as opposed to some other kind of constant.. There is
sometimes value in knowing /that/ FOO is a constant, regardless of
what kind.

For instance, in my own code, instead of
having:

    inline void RowPinHigh(unsigned const i) { PORTA |= 1u << i; }

, I have the following:

    #define RowPinHigh(i)    ((void)(PORTA |= 1u << (i)))

    Firstly, the argument will only be evaluted once, so there's no
problems on that front. The only other problem I can see is the issue of
being able to take the function's address;

That problem is solved by having both implementations. This is how
getc and getchar are typically implemented.

Note that a function-like macro is only recognized if followed by an
opening parenthesis, so you can have this:

int foo();
#define foo() ... inline expansion ..

The address-of operator works just fine: &foo takes the address of the
function, without the need to #undef foo.
 
A

Army1987

Tomás Ó hÉilidhe said:
I'm not a fan of ALL CAPS, but of course I use it for macro
functions to warn the programmer to be picky about what arguments they
pass (for fear of multi-evaluation).

However, I wonder what people think about _not_ using all caps when
the macro function doesn't have any parameters, or when the arguments
won't be multi-evaluated? For instance, in my own code, instead of
having:

inline void RowPinHigh(unsigned const i) { PORTA |= 1u << i; }

, I have the following:

#define RowPinHigh(i) ((void)(PORTA |= 1u << (i)))

Firstly, the argument will only be evaluted once, so there's no
problems on that front. The only other problem I can see is the issue of
being able to take the function's address;
You can do:
#define RowPinHigh(i) ((void)(PORTA |= 1u << (i)))
inline void (RowPinHigh)(unsigned i) { RowPinHigh(i); }

(But, for the macro to *actually* have the semantics of the functions, you
ought to cast i to unsigned.
 

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,013
Latest member
KatriceSwa

Latest Threads

Top