macro

I

Ian Collins

Fair enough.

But one could argue that translating if/else to ?: and ";" to "," for
the sole purpose of making a chunk of code usable as a macro is equally
hackish.

Sure, "do...while(0)" is a hack -- but it's also an idiom that's widely
used and recognized by C programmers.

It is also a strong indication that whatever you are attempting to do is
an inappropriate use of a macro!
 
D

dspfun

It is also a strong indication that whatever you are attempting to do is
an inappropriate use of a macro!

What is the reason that this is an indication that the "do...while(0)"
in a macro is an inappropriate use of a macro?

Brs,
Markus
 
I

Ian Collins

[what adds these random lines?]
What is the reason that this is an indication that the "do...while(0)"
in a macro is an inappropriate use of a macro?

Because in most cases it gets used where a function or as in your code,
a macro wrapping a function, is more appropriate. The rare exceptions
are where macros are used as poor man's templates.

I guess the use of "do...while(0)" (and function-like macros in general)
is a hang over from 80s and 90s compiler which didn't inline functions.
 
D

dspfun

[what adds these random lines?]








What is the reason that this is an indication that the "do...while(0)"
in a macro is an inappropriate use of a macro?

Because in most cases it gets used where a function or as in your code,
a macro wrapping a function, is more appropriate.  The rare exceptions
are where macros are used as poor man's templates.

I guess the use of "do...while(0)" (and function-like macros in general)
is a hang over from 80s and 90s compiler which didn't inline functions.

Thanks!

What would be an example of such a poor man's template?

Brs,
Markus
 
B

Ben Pfaff

Ian Collins said:
I guess the use of "do...while(0)" (and function-like macros in
general) is a hang over from 80s and 90s compiler which didn't inline
functions.

Mostly.

In the tree I'm looking at now, I see two uses that don't fall
into that category. The first is where I had a notion that
macros would generate better code, specifically for these macros:

#define HASH_ROT(x, k) (((x) << (k)) | ((x) >> (32 - (k))))

#define HASH_MIX(a, b, c) \
do { \
a -= c; a ^= HASH_ROT(c, 4); c += b; \
b -= a; b ^= HASH_ROT(a, 6); a += c; \
c -= b; c ^= HASH_ROT(b, 8); b += a; \
a -= c; a ^= HASH_ROT(c, 16); c += b; \
b -= a; b ^= HASH_ROT(a, 19); a += c; \
c -= b; c ^= HASH_ROT(b, 4); b += a; \
} while (0)

#define HASH_FINAL(a, b, c) \
do { \
c ^= b; c -= HASH_ROT(b, 14); \
a ^= c; a -= HASH_ROT(c, 11); \
b ^= a; b -= HASH_ROT(a, 25); \
c ^= b; c -= HASH_ROT(b, 16); \
a ^= c; a -= HASH_ROT(c, 4); \
b ^= a; b -= HASH_ROT(a, 14); \
c ^= b; c -= HASH_ROT(b, 24); \
} while (0)

In an attempt to demonstrate that they do generate better code, I
converted them to the following inline functions:

static inline uint32_t
hash_rot(uint32_t x, int k)
{
return (x << k) | (x >> (32 - k));
}

static inline void
hash_mix(uint32_t *a, uint32_t *b, uint32_t *c)
{
*a -= *c; *a ^= hash_rot(*c, 4); *c += *b;
*b -= *a; *b ^= hash_rot(*a, 6); *a += *c;
*c -= *b; *c ^= hash_rot(*b, 8); *b += *a;
*a -= *c; *a ^= hash_rot(*c, 16); *c += *b;
*b -= *a; *b ^= hash_rot(*a, 19); *a += *c;
*c -= *b; *c ^= hash_rot(*b, 4); *b += *a;
}

static inline void
hash_final(uint32_t *a, uint32_t *b, uint32_t *c)
{
*c ^= *b; *c -= hash_rot(*b, 14);
*a ^= *c; *a -= hash_rot(*c, 11);
*b ^= *a; *b -= hash_rot(*a, 25);
*c ^= *b; *c -= hash_rot(*b, 16);
*a ^= *c; *a -= hash_rot(*c, 4);
*b ^= *a; *b -= hash_rot(*a, 14);
*c ^= *b; *c -= hash_rot(*b, 24);
}

(Of course this required some changes to the users also.)
However, when I looked at the generated code, it was completely
identical with GCC 4.4.5. So I've now submitted a patch to
change these to inline functions:
http://openvswitch.org/pipermail/dev/2012-January/014369.html

I ran out of time so I won't be able to describe my other use
case :)
 
K

Kaz Kylheku

Because in most cases it gets used where a function or as in your code,
a macro wrapping a function, is more appropriate. The rare exceptions
are where macros are used as poor man's templates.

I guess the use of "do...while(0)" (and function-like macros in general)
is a hang over from 80s and 90s compiler which didn't inline functions.

There are UUOM's (useless uses of macros) that can be replaced by functions.
But then there are function-like macros that cannot be written as functions.

If you don't write UUOM's in your code, then the remaining uses are hardly
rare exceptions. If you know what you're doing, 100% of your macros are
in fact necessary.

Here is a requirement:

or(x, y)

This must evaluate X. If X is nonzero, then the value X is the result, and Y
is not evaluated. Otherwise the result is the value of Y. X is evaluated
exactly once, and Y at most once.

Good luck making a function for this. Or even an entirely clean macro,
for that matter.

(Is that really "poor" man's template, by the way? Okay, show me the "rich"
man's C++ template for it, then.)
 
K

Keith Thompson

Ian Collins said:
[what adds these random lines?]

Do you mean the blank lines that you didn't quote? It looked like you
were complaining about the attribution lines (which I'm sure you
wouldn't do).
 
J

Joe keane

I guess the use of "do...while(0)" (and function-like macros in general)
is a hang over from 80s and 90s compiler which didn't inline functions.

Talking today, if someone said 'doesn't inline function work well?'
[assuming it works well] i'd say 'well yeah, inline function is great'.

I think people learned how to do stuff with macros [that is, if you
aren't adept at reading/writing macros, you don't really know C], then
they learned that inline function is available everywhere.

"If the macros work well, why do we want inline function, we will stick
with the stuff we are used to."

I wonder if this will change over time, where more people can't think of
a day when inline functions are not universal. Maybe a bit, but i don't
think it's going to a world where almost everyone doesn't use macros and
doesn't really understand them.

I think this is a feature that C inherited from C++. It seems to me,
once C++ had inline function and it worked, then everyone in C world
said 'well yeah, let's put that in'. Oh and, Stroustrup said his goal
is to reduce use of the preprocessor.
 
K

Kaz Kylheku

I guess the use of "do...while(0)" (and function-like macros in general)
is a hang over from 80s and 90s compiler which didn't inline functions.

Talking today, if someone said 'doesn't inline function work well?'
[assuming it works well] i'd say 'well yeah, inline function is great'.

I think people learned how to do stuff with macros [that is, if you
aren't adept at reading/writing macros, you don't really know C], then
they learned that inline function is available everywhere.

"If the macros work well, why do we want inline function, we will stick
with the stuff we are used to."

Even if inline isn't available, you can use static functions as a fallback.

In one freeware program that I maintain, I have a configure test for
various ways of doing inline, in a particular order. Two source files
are written written defining the same inline function and linked
together, to verify that the chosen method is not only accepted as
a valid function definition, but will not cause multiple definition clashes
when it is pulled into multiple translation units:

printf "Checking how to declare inline functions ... "

if [ -z "$inline" ] ; then
for inline in \
"inline" "static inline" "extern inline" \
"__inline__" "static __inline__" "extern __inline__" \
"static"
do
cat > conftest1.c <<!
$inline int func(void)
{
return 0;
}

int main(void)
{
return func();
}
!
cat > conftest2.c <<!
$inline int func(void)
{
return 0;
}
!
rm -f conftest2
if ! make conftest2 > conftest.err 2>&1 ; then
continue
fi
break
done
fi

printf '"%s"\n' "$inline"
printf "#define INLINE $inline\n" >> config.h

If there is no inline, and the compiler doesn't automatically inline
small static functions, you get code bloat. Get a better compiler for my
program, or live with the bloated code.

When configuring with GCC's gcc, since I don't use the C99 dialect, guess which
item on that list works? Everything fails right up until "static __inline__".
With GCC's g++, "inline" tests successfully, of course.
I wonder if this will change over time, where more people can't think of
a day when inline functions are not universal.

Even if inline functions aren't universal, INLINE functions can be. :)
 
I

Ian Collins

There are UUOM's (useless uses of macros) that can be replaced by functions.
But then there are function-like macros that cannot be written as functions.

If you don't write UUOM's in your code, then the remaining uses are hardly
rare exceptions. If you know what you're doing, 100% of your macros are
in fact necessary.

Here is a requirement:

or(x, y)

This must evaluate X. If X is nonzero, then the value X is the result, and Y
is not evaluated. Otherwise the result is the value of Y. X is evaluated
exactly once, and Y at most once.

Good luck making a function for this. Or even an entirely clean macro,
for that matter.

(Is that really "poor" man's template, by the way? Okay, show me the "rich"
man's C++ template for it, then.)

Not really, at least for c++03. With C++0x I guess you could use a
simple variable arguments template if both expressions are the same type:

template <typename Fn, typename... Args>
bool do_or( bool b, Fn fn, Args... args)
{
if( b ) return b;

return fn( args... );
}

Life gets more interesting when they are different types.
 
I

Ian Collins

Not really, at least for c++03. With C++0x I guess you could use a
simple variable arguments template if both expressions are the same type:

template<typename Fn, typename... Args>
bool do_or( bool b, Fn fn, Args... args)
{
if( b ) return b;

return fn( args... );
}

Life gets more interesting when they are different types.

I couldn't resist following this up, so here's a rather OT C++0x solution:

template <typename X, typename Y, typename... Args>
auto do_or( X x, Y y, Args... args ) -> decltype(x+y( args...))
{
if( x ) return x;

return y( args...);
}
 
J

James Kuyper

There's a trivial problem with the suggestion of using C++ to implement
or(): 'or' is keyword in C++, with the same meaning as the 'or' #defined
by <iso646.h> in C. Any attempt to define any identifier with that name
would fail, usually by being interpreted as a syntax error. So let's
change the name of the requested function to Or().
Not really, at least for c++03. With C++0x I guess you could use a
simple variable arguments template if both expressions are the same type:

template <typename Fn, typename... Args>
bool do_or( bool b, Fn fn, Args... args)
{
if( b ) return b;

return fn( args... );
}

Life gets more interesting when they are different types.

So, could you show how a non-macro implementation of 'Or()' could built
in C++ from do_or()? Let's be more specific. Given

int a=2;

How could Or(1,a++) implemented, so as to prevent evaluation of a++?
 
I

Ian Collins

There's a trivial problem with the suggestion of using C++ to implement
or(): 'or' is keyword in C++, with the same meaning as the 'or' #defined
by<iso646.h> in C. Any attempt to define any identifier with that name
would fail, usually by being interpreted as a syntax error. So let's
change the name of the requested function to Or().


So, could you show how a non-macro implementation of 'Or()' could built
in C++ from do_or()? Let's be more specific. Given

int a=2;

How could Or(1,a++) implemented, so as to prevent evaluation of a++?

I don't see how it can, a++ will always be evaluated as a function
parameter in either language.
 
J

James Kuyper

I don't see how it can, a++ will always be evaluated as a function
parameter in either language.

That was precisely the point Kaz was making. He's suggesting that it can
be implemented using a macro, though I'm not quite sure how that would work.

The key point is that controlling the number of times that an argument
gets evaluated is possible for macros, but not for functions.

What this has to do with the do {...} while(0) idiom, I have no idea,
since no matter how Or() is implemented, it certainly can't be
implemented by use of that idiom, since it doesn't allow returning a value.
 
I

Ian Collins

That was precisely the point Kaz was making. He's suggesting that it can
be implemented using a macro, though I'm not quite sure how that would work.

The key point is that controlling the number of times that an argument
gets evaluated is possible for macros, but not for functions.

I'd like to see a macro solution for this problem, I don't see how it
could work considering the value of the first argument isn't known at
compile time.

I would also like to see a macro solution where the two arguments are
function calls.

The addition of automatic return type deduction and variable argument
templates increases the utility of templates in areas where macros were
the only option.
What this has to do with the do {...} while(0) idiom, I have no idea,
since no matter how Or() is implemented, it certainly can't be
implemented by use of that idiom, since it doesn't allow returning a value.

:)
 
K

Keith Thompson

Ian Collins said:
Not really, at least for c++03. With C++0x I guess you could use a
simple variable arguments template if both expressions are the same type:
[snip]

C++0x is now C++11.
 
B

Ben Bacarisse

I couldn't resist following this up, so here's a rather OT C++0x solution:

template <typename X, typename Y, typename... Args>
auto do_or( X x, Y y, Args... args ) -> decltype(x+y( args...))
{
if( x ) return x;

return y( args...);
}

This may be a close as one can get (I can't get any closer!) but it
doesn't strike me as being "a solution" because of the need to wrap the
original expression, y, in a function to delay its execution. C++11's
lambdas make this simple enough, but it still won't look as readable as
the original:

do_or(a++, [&b]{return b++;})
vs
or(a++, b++)

That may, however, be an advantage, since having odd evaluation semantics
for the arguments of something that looks like a function is asking for
trouble. (Yes, I know it was posed as an academic exercise so this is
an incidental point).

BTW, what's the purpose of the extra args? I'd have though that the
availability of lambdas renders these unnecessary. If it's to allow a
non-lambda function as a wrapper for the expression, then would you not
need to write Args &... args to allow for modification?
 
S

Shao Miller

[...]
Here is a requirement:

or(x, y)

This must evaluate X. If X is nonzero, then the value X is the result, and Y
is not evaluated. Otherwise the result is the value of Y. X is evaluated
exactly once, and Y at most once.

Good luck making a function for this. Or even an entirely clean macro,
for that matter.
[...]

Well, I don't know a lot about C++, but in C1X, I think you might be
able to get close with the code below. I think it could be simplified a
lot if there was a C '_TypeOf'!

I've commented out the '_Thread_local' as I guess that's for C > C1X.

Some restrictions apply:
- There are only certain, specified, supported types for the 'M_OR'
macro arguments.
- If the 'M_OR' macro invocation is indeterminately sequenced relative
to another 'M_OR' macro invocation, the behaviour is undefined.
- Without '_Thread_local', use of 'M_OR' is not thread-safe.
- Other stuff I'm forgetting at the moment, but you folks will notice,
I'm sure.

Here is the code:

#include <stddef.h>
#include <stdio.h>

#define M_COPY_OF_TYPES_(u_, m_) \
u_(unsigned char, m_) \
u_(signed char, m_) \
u_(char, m_) \
u_(unsigned short, m_) \
u_(signed short, m_) \
u_(unsigned int, m_) \
u_(signed int, m_) \
u_(unsigned long, m_) \
u_(signed long, m_) \
u_(float, m_) \
u_(double, m_)

#define M_COPY_OF_CASE_(t_, m_) \
t_: \
(t_[1]) { _Generic(m_, t_: m_, default: 0) },

#define M_COPY_OF(x_) ( \
_Generic( \
(x_), \
M_COPY_OF_TYPES_(M_COPY_OF_CASE_, (x_)) \
default: \
0 \
) \
)

#if 0
_Thread_local static void * or_helper_;
#endif
static void * or_helper_;

#define M_OR_HELPER_GET_CASE_(t_, m_) \
t_: \
(t_ *) or_helper_,

#define M_OR_HELPER_GET_(x_) ( \
_Generic( \
(x_), \
M_COPY_OF_TYPES_(M_OR_HELPER_GET_CASE_, (x_)) \
default: \
0 \
) \
)

#define M_OR_HELPER_SET_(x_) ( \
(or_helper_ = M_COPY_OF(x_)), \
M_OR_HELPER_GET_(x_) \
)

#define M_OR(x_, y_) ( \
*M_OR_HELPER_SET_(x_) ? *M_OR_HELPER_GET_(x_) : (y_) \
)

int main(void) {
int x;
int * ip;
float y, z;
float * fp;

puts("M_COPY_OF demonstrations:");
x = 42;
ip = M_COPY_OF(x++);
printf("x == %d && *ip == %d\n", x, *ip);

ip = M_COPY_OF(13);
printf("*ip == %d\n", *ip);

fp = M_COPY_OF(3.14f);
printf("*fp == %f\n", *fp);

puts("");

x = 2;
y = 3.14;
printf("Initial conditions: x == %d && y == %f\n", x, y);

z = M_OR(x--, y++);
printf(
"After first M_OR: x == %d && y == %f && z == %f\n",
x,
y,
z
);

z = M_OR(x--, y++);
printf(
"After second M_OR: x == %d && y == %f && z == %f\n",
x,
y,
z
);

z = M_OR(x--, y++);
printf(
"After third M_OR: x == %d && y == %f && z == %f\n",
x,
y,
z
);

return 0;
}
 

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

Forum statistics

Threads
473,744
Messages
2,569,482
Members
44,901
Latest member
Noble71S45

Latest Threads

Top