Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Using of Macros

56 views
Skip to first unread message

JiiPee

unread,
Jan 21, 2017, 5:12:47 AM1/21/17
to
I hear experts all the time saying "dont use macros". But in my real
coding I see benefits sometimes using them. One is debugging/tracing
values in a debug mode:


Trace(x != 8, "x is wrong");


Using macro seems to be the only way to do this trace so that the
release version ignores that line and does not compile it. How would
"dont-use-macros" person do this other way?

Alf P. Steinbach

unread,
Jan 21, 2017, 6:21:54 AM1/21/17
to
There is no practical alternative to a macro for that, for example
because you then risk a stream of silly-warnings about dead code etc.,
warnings that you otherwise wouldn't want to disable.

That said there is tendency to think that since a macro must be involved
in this case, everything should be done with macros, like it's macros or
not macros, sort of George Bush-land: either you're with us, for
everything, or you're with the enemy, for everything.

Instead I suggest limiting the use of macros to the things that macros
are needed for, which in the above case are

• Code elimination when the checking is undesired (e.g. in release builds).

• Picking up the source location.

For the latter it can IMO be a good idea to define a class that
represents a source location, with a macro to create an instance.

I guess what I'm saying is the age old advice of separating concerns
also when it comes to use of macros. Don't think that because macros
evidently are needed, the whole problem area is macro-land. There are
just some pieces of the problem that require macros: these basic special
purpose macros can support much functionality that doesn't need to also
be expressed as macros.


Cheers & hth.,

- Alf

Hergen Lehmann

unread,
Jan 21, 2017, 8:45:15 AM1/21/17
to
Am 21.01.2017 um 12:21 schrieb Alf P. Steinbach:

>> Trace(x != 8, "x is wrong");
>>
>> Using macro seems to be the only way to do this trace so that the
>> release version ignores that line and does not compile it. How would
>> "dont-use-macros" person do this other way?
>
> There is no practical alternative to a macro for that,

An inline function should be able provide a very practical alternative
in this case:

inline void Trace(bool b,const char *s)
{
if (TraceMode && b) std::cout << s <<std::endl;
}

If TraceMode=false, the compiler should optimize that out without any
silly warnings and leftover dead code.

> I guess what I'm saying is the age old advice of separating concerns
> also when it comes to use of macros. Don't think that because macros
> evidently are needed, the whole problem area is macro-land. There are
> just some pieces of the problem that require macros: these basic special
> purpose macros can support much functionality that doesn't need to also
> be expressed as macros.

Agreed.

Hergen

JiiPee

unread,
Jan 21, 2017, 8:59:09 AM1/21/17
to
On 21/01/2017 11:21, Alf P. Steinbach wrote:
> On 21.01.2017 11:12, JiiPee wrote:
>> I hear experts all the time saying "dont use macros". But in my real
>> coding I see benefits sometimes using them. One is debugging/tracing
>> values in a debug mode:
>>
>>
>> Trace(x != 8, "x is wrong");
>>
>>
>> Using macro seems to be the only way to do this trace so that the
>> release version ignores that line and does not compile it. How would
>> "dont-use-macros" person do this other way?
>>
>
> There is no practical alternative to a macro for that, for example
> because you then risk a stream of silly-warnings about dead code etc.,
> warnings that you otherwise wouldn't want to disable.

But its not only about getting those trace-warnings but its also that
they slow down the runtime. Think about tracing that 10 milliona times
per second...

>
> That said there is tendency to think that since a macro must be
> involved in this case, everything should be done with macros, like
> it's macros or not macros, sort of George Bush-land: either you're
> with us, for everything, or you're with the enemy, for everything.

hehe
you have a point. Yes, I agree... and also the same thing I would say
with discussion with C-programmers. They say sometimes: "NONE of that
C++ is good at all... only C is good!!". But then C++ programmer can
also say: "everything must be C++, dont use functions , use
classes....always!!". I think its just that what works is
good....Although I dont see reason to use pure-old C only.. why not use
consts and those things?

>
> Instead I suggest limiting the use of macros to the things that macros
> are needed for, which in the above case are
>
> • Code elimination when the checking is undesired (e.g. in release
> builds).
>
> • Picking up the source location.
>
> For the latter it can IMO be a good idea to define a class that
> represents a source location, with a macro to create an instance.
>
> I guess what I'm saying is the age old advice of separating concerns
> also when it comes to use of macros. Don't think that because macros
> evidently are needed, the whole problem area is macro-land. There are
> just some pieces of the problem that require macros: these basic
> special purpose macros can support much functionality that doesn't
> need to also be expressed as macros.

yes.
Also I found handy that that macro itself does not need to contain
variables but can be just a function call:

#define Trace(matrix) TraceFunc(matrix);
// and for release:
#define Trace


So intead of putting the matrix code inside the macro, we can just make
a normal function (TraceFunc) and call it from the macro. I think better
like this. so now:

int v=9;

Matrix m;

Trace(m);

...

Trace is only done for debug. otherwise we get TraceFunc(m);


JiiPee

unread,
Jan 21, 2017, 9:05:24 AM1/21/17
to
On 21/01/2017 13:34, Hergen Lehmann wrote:
> Am 21.01.2017 um 12:21 schrieb Alf P. Steinbach:
>
>>> Trace(x != 8, "x is wrong");
>>>
>>> Using macro seems to be the only way to do this trace so that the
>>> release version ignores that line and does not compile it. How would
>>> "dont-use-macros" person do this other way?
>>
>> There is no practical alternative to a macro for that,
>
> An inline function should be able provide a very practical alternative
> in this case:
>
> inline void Trace(bool b,const char *s)
> {
> if (TraceMode && b) std::cout << s <<std::endl;
> }
>
> If TraceMode=false, the compiler should optimize that out without any
> silly warnings and leftover dead code.

oh, you might be right... never thought about that (optimizing)!
clever... compiler will see that this function is never run so it does
not compile it at all. But is this guaranteed?

Alf P. Steinbach

unread,
Jan 21, 2017, 9:27:28 AM1/21/17
to
On 21.01.2017 14:34, Hergen Lehmann wrote:
> Am 21.01.2017 um 12:21 schrieb Alf P. Steinbach:
>
>>> Trace(x != 8, "x is wrong");
>>>
>>> Using macro seems to be the only way to do this trace so that
>>> the release version ignores that line and does not compile it.
>>> How would "dont-use-macros" person do this other way?
>>
>> There is no practical alternative to a macro for that,
>
> An inline function should be able provide a very practical
> alternative in this case:
>
> inline void Trace(bool b,const char *s) { if (TraceMode && b)
> std::cout << s <<std::endl; }

Continuing the quote that you snipped mid-sentence:

>> , for example because you then risk a stream of silly-warnings about
>> dead code etc., warnings that you otherwise wouldn't want to disable.


> If TraceMode=false, the compiler should optimize that out without
> any silly warnings and leftover dead code.

Opinion doesn't trump actuality.


>> I guess what I'm saying is the age old advice of separating
>> concerns also when it comes to use of macros. Don't think that
>> because macros evidently are needed, the whole problem area is
>> macro-land. There are just some pieces of the problem that require
>> macros: these basic special purpose macros can support much
>> functionality that doesn't need to also be expressed as macros.
>
> Agreed.


[code]
#include <iostream>

namespace g{
bool const trace_mode = false;
} // namespace g

inline void trace( bool const b, char const* const s )
{
if( g::trace_mode and b ) { std::cout << s <<std::endl; }
}

auto main()
-> int
{
trace( true, "A" );
trace( false, "B" );
}
[/code]

[result]
[C:\my\forums\clc++\049]
> cl foo.cpp
foo.cpp
foo.cpp(9): warning C4127: conditional expression is constant
foo.cpp(7): warning C4100: 'b': unreferenced formal parameter

[C:\my\forums\clc++\049]
> _
[/result]

Jorgen Grahn

unread,
Jan 21, 2017, 12:46:02 PM1/21/17
to
On Sat, 2017-01-21, JiiPee wrote:
> I hear experts all the time saying "dont use macros". But in my real
> coding I see benefits sometimes using them. One is debugging/tracing
> values in a debug mode:

Usually those experts mention exactly that exception (and include
guards). They just don't want C programmers to keep using macros for
tasks where C++ offers better alternatives.

/Jorgen

--
// Jorgen Grahn <grahn@ Oo o. . .
\X/ snipabacken.se> O o .

Mr Flibble

unread,
Jan 21, 2017, 12:53:42 PM1/21/17
to
As long as the macro doesn't affect program behaviour (as is the case
here) then it is perfectly fine and is little different to using assert.

/Flibble


David Brown

unread,
Jan 21, 2017, 1:00:50 PM1/21/17
to
On 21/01/17 15:26, Alf P. Steinbach wrote:
> On 21.01.2017 14:34, Hergen Lehmann wrote:
>> Am 21.01.2017 um 12:21 schrieb Alf P. Steinbach:
>>
>>>> Trace(x != 8, "x is wrong");
>>>>
>>>> Using macro seems to be the only way to do this trace so that
>>>> the release version ignores that line and does not compile it.
>>>> How would "dont-use-macros" person do this other way?
>>>
>>> There is no practical alternative to a macro for that,
>>
>> An inline function should be able provide a very practical
>> alternative in this case:
>>
>> inline void Trace(bool b,const char *s) { if (TraceMode && b)
>> std::cout << s <<std::endl; }
>
> Continuing the quote that you snipped mid-sentence:
>
>>> , for example because you then risk a stream of silly-warnings about
>>> dead code etc., warnings that you otherwise wouldn't want to disable.
>
>
>> If TraceMode=false, the compiler should optimize that out without
>> any silly warnings and leftover dead code.
>
> Opinion doesn't trump actuality.

Actuality with which compiler and options? The code you provide works
perfectly without extra warnings when I tried it with clang and gcc (-O2
-Wall -Wextra). It is a /long/ time since gcc warned about dead code
and constant conditionals, precisely because such warnings were so often
false.

And if you are really worried, you can write:

inline void trace( bool const b, char const* const s )
{
if( g::trace_mode and b ) {
std::cout << s <<std::endl;
} else {
std::ignore = b;
std::ignore = s;
}
}

That should avoid any warnings.


David Brown

unread,
Jan 21, 2017, 1:03:16 PM1/21/17
to
Why not:

#ifdef DEBUG

static void Trace(int a) ....

#else

static inline void Trace(int a) {};

#endif

There is no need to go through an extra macro.

Alf P. Steinbach

unread,
Jan 21, 2017, 1:44:27 PM1/21/17
to
On 21.01.2017 19:00, David Brown wrote:
> On 21/01/17 15:26, Alf P. Steinbach wrote:
>> On 21.01.2017 14:34, Hergen Lehmann wrote:
>>> Am 21.01.2017 um 12:21 schrieb Alf P. Steinbach:
>>>
>>>>> Trace(x != 8, "x is wrong");
>>>>>
>>>>> Using macro seems to be the only way to do this trace so that
>>>>> the release version ignores that line and does not compile it.
>>>>> How would "dont-use-macros" person do this other way?
>>>>
>>>> There is no practical alternative to a macro for that,
>>>
>>> An inline function should be able provide a very practical
>>> alternative in this case:
>>>
>>> inline void Trace(bool b,const char *s) { if (TraceMode && b)
>>> std::cout << s <<std::endl; }
>>
>> Continuing the quote that you snipped mid-sentence:
>>
>>>> , for example because you then risk a stream of silly-warnings about
>>>> dead code etc., warnings that you otherwise wouldn't want to disable.
>>
>>
>>> If TraceMode=false, the compiler should optimize that out without
>>> any silly warnings and leftover dead code.
>>
>> Opinion doesn't trump actuality.
>
> Actuality with which compiler and options?

From the example that you snipped:

cl foo.cpp

Is it snippety-snip day today?

The default options I use are `/nologo /EHsc /GR /W4 /FI "iso646.h"`, of
which only `/W4` is relevant for warnings. When you're designing for no
warnings then this is the only possible option. With this compiler.


> The code you provide works
> perfectly without extra warnings when I tried it with clang and gcc (-O2
> -Wall -Wextra). It is a /long/ time since gcc warned about dead code
> and constant conditionals, precisely because such warnings were so often
> false.
>
> And if you are really worried, you can write:
>
> inline void trace( bool const b, char const* const s )
> {
> if( g::trace_mode and b ) {
> std::cout << s <<std::endl;
> } else {
> std::ignore = b;
> std::ignore = s;
> }
> }
>
> That should avoid any warnings.

> cl bar.cpp
bar.cpp
bar.cpp(9): warning C4127: conditional expression is constant
bar.cpp(12): error C2039: 'ignore': is not a member of 'std'
D:\installed\Microsoft Visual Studio 14.0\VC\INCLUDE\iostream(12): note:
see declaration of 'std'
bar.cpp(12): error C2065: 'ignore': undeclared identifier
bar.cpp(13): error C2039: 'ignore': is not a member of 'std'
D:\installed\Microsoft Visual Studio 14.0\VC\INCLUDE\iostream(12): note:
see declaration of 'std'
bar.cpp(13): error C2065: 'ignore': undeclared identifier

Hm.

Hergen Lehmann

unread,
Jan 21, 2017, 1:45:12 PM1/21/17
to
Am 21.01.2017 um 15:05 schrieb JiiPee:

> oh, you might be right... never thought about that (optimizing)!
> clever... compiler will see that this function is never run so it does
> not compile it at all. But is this guaranteed?

Since && is evaluated left-to-right, it is guaranteed that neither the
predicate nor the output instruction will be executed. There is a small
chance, that the trace disable flag itself is checked each time by a
compiler with a very weak optimizer, but that should not affect the
performance too much.

Hergen

Alf P. Steinbach

unread,
Jan 21, 2017, 1:52:19 PM1/21/17
to
On 21.01.2017 19:03, David Brown wrote:
>[snip]
> Why not:
>
> #ifdef DEBUG
>
> static void Trace(int a) ....
>
> #else
>
> static inline void Trace(int a) {};
>
> #endif
>
> There is no need to go through an extra macro.

That can work.

It doesn't support turning on and off tracing within a translation unit,
à la standard `assert`, but that seems to be the only limitation.

With standard `assert` the standard guarantees that you can change the
macro definition by defining or undefining `NDEBUG` and re-including the
`<assert.h>` header, so evidently some people have needed that.


Cheers!,

- Alf

Gareth Owen

unread,
Jan 21, 2017, 1:52:26 PM1/21/17
to
Macros are also a convenient way to add __LINE__ and __FILE__ in such
tracing code

Ian Collins

unread,
Jan 21, 2017, 2:11:19 PM1/21/17
to
Missing include?

$ clang++ -std=c++14 -Wall -Wextra ~/x.cc
$ cat ~/x.cc
#include <iostream>
#include <tuple>

namespace g{
bool const trace_mode = false;
} // namespace g

inline void trace( bool const b, char const* const s )
{
if( g::trace_mode and b ) {
std::cout << s <<std::endl;
} else {
std::ignore = b;
std::ignore = s;
}
}

auto main()
-> int
{
trace( true, "A" );
trace( false, "B" );
}



--
Ian

Alf P. Steinbach

unread,
Jan 21, 2017, 2:17:53 PM1/21/17
to
Oh. Thanks, I wasn't aware that a `std::ignore` existed. Learned
something today also then. :)

But I only intended to show the MSVC silly-warning.

Very undesired.


Cheers!,

- Alf

David Brown

unread,
Jan 22, 2017, 12:33:33 PM1/22/17
to
On 21/01/17 19:43, Alf P. Steinbach wrote:
> On 21.01.2017 19:00, David Brown wrote:
>> On 21/01/17 15:26, Alf P. Steinbach wrote:
>>> On 21.01.2017 14:34, Hergen Lehmann wrote:
>>>> Am 21.01.2017 um 12:21 schrieb Alf P. Steinbach:
>>>>
>>>>>> Trace(x != 8, "x is wrong");
>>>>>>
>>>>>> Using macro seems to be the only way to do this trace so that
>>>>>> the release version ignores that line and does not compile it.
>>>>>> How would "dont-use-macros" person do this other way?
>>>>>
>>>>> There is no practical alternative to a macro for that,
>>>>
>>>> An inline function should be able provide a very practical
>>>> alternative in this case:
>>>>
>>>> inline void Trace(bool b,const char *s) { if (TraceMode && b)
>>>> std::cout << s <<std::endl; }
>>>
>>> Continuing the quote that you snipped mid-sentence:
>>>
>>>>> , for example because you then risk a stream of silly-warnings about
>>>>> dead code etc., warnings that you otherwise wouldn't want to disable.
>>>
>>>
>>>> If TraceMode=false, the compiler should optimize that out without
>>>> any silly warnings and leftover dead code.
>>>
>>> Opinion doesn't trump actuality.
>>
>> Actuality with which compiler and options?
>
> From the example that you snipped:
>
> cl foo.cpp
>
> Is it snippety-snip day today?

I don't know what compiler "cl" might be. Guessing by your apparent
assumption that it is so common that you expect people /do/ know what it
is, and also that you have slashes for the options below, could it be
Microsoft's C compiler? It is not a tool I have ever used myself.
Did you try it with the "#include <tuple>" that I helpfully forgot?
Looking at the exact warnings here, I'm guessing you'd still get the
C4127 warning.

But okay, it gives unhelpful warnings on at least one common compiler.
Warnings are not standardised, making it difficult to handle them in
portable code.

David Brown

unread,
Jan 22, 2017, 12:36:38 PM1/22/17
to
I used std::ignore here as I thought it might be a more portable way to
avoid warnings in case the warnings in question were about unused
parameters b and s (they are in the expression, but are not used since
g::trace_mode can be evaluated at compile time). Normally I just write
"(void) b;" to indicate that "b" is unused, but some compilers might
give other warnings here.

David Brown

unread,
Jan 22, 2017, 12:37:49 PM1/22/17
to
On 21/01/17 19:51, Alf P. Steinbach wrote:
> On 21.01.2017 19:03, David Brown wrote:
>> [snip]
>> Why not:
>>
>> #ifdef DEBUG
>>
>> static void Trace(int a) ....
>>
>> #else
>>
>> static inline void Trace(int a) {};
>>
>> #endif
>>
>> There is no need to go through an extra macro.
>
> That can work.
>
> It doesn't support turning on and off tracing within a translation unit,
> à la standard `assert`, but that seems to be the only limitation.
>

True.

> With standard `assert` the standard guarantees that you can change the
> macro definition by defining or undefining `NDEBUG` and re-including the
> `<assert.h>` header, so evidently some people have needed that.

Or at least, some people have /thought/ that some people have needed it!

>
>
> Cheers!,
>
> - Alf
>

Richard

unread,
Jan 23, 2017, 2:07:12 PM1/23/17
to
[Please do not mail me a copy of your followup]

n...@notvalid.com spake the secret code
<ouGgA.35785$dT1....@fx16.am4> thusly:

>I hear experts all the time saying "dont use macros".

IMO, I believe the advice you're hearing has been truncated.

The advice I've seen has always been "don't use macros when there are
better alternatives".

The better alternatives are things that have been around for ages in
many cases:

- use enums for named integral constants
- use const values for named non-integral constants
- use inline functions for small bits of executable code
- use templates for code that varies by type

Things where macros are still unavoidable:

- turning a token into a string (preprocessor # operator)
- combining two tokens into a new token (preprocessor ## operator)
- include guards

Macros are still useful to eliminate large amounts of boiler plate:

- aggregate initializers where many values are repeated and only a few
differ
- common class boilerplate is required for derived classes
- tables of data that must be declared (e.g. wxBEGIN_EVENT_TABLE in
wxWidgets <http://docs.wxwidgets.org/3.0/overview_events.html>)

In this case, we're eliminating duplicate patterns of tokens that
can't be eliminated in other ways: inline functions, template
functions, etc. That's not to say that there isn't an alternative
design that avoids macros. For instance, some GUI toolkits express
event mechanisms by overriding virtual functions instead of declaring
tables mapping events to handlers.
--
"The Direct3D Graphics Pipeline" free book <http://tinyurl.com/d3d-pipeline>
The Terminals Wiki <http://terminals-wiki.org>
The Computer Graphics Museum <http://computergraphicsmuseum.org>
Legalize Adulthood! (my blog) <http://legalizeadulthood.wordpress.com>

Christian Gollwitzer

unread,
Jan 23, 2017, 2:47:03 PM1/23/17
to
Am 22.01.17 um 18:33 schrieb David Brown:
> I don't know what compiler "cl" might be. Guessing by your apparent
> assumption that it is so common that you expect people /do/ know what it
> is, and also that you have slashes for the options below, could it be
> Microsoft's C compiler?

Indeed it is. cl is the commandline version of the Visual C++ compiler,
Microsoft distributes free copies these days.

> It is not a tool I have ever used myself.
>

Blessed be. I had to fight with the infamous version 6.0 for a long
time. VC6.0 predates C++98, but it was for a long time the only compiler
that produces binaries without dependencies on Windows. All later
versions depend on a C++ runtime (a different one for each compiler
release), which must be installed on the user's machine - and this of
course requires admin priviledges. Therefore VC6 stuck around a lot...
with many ugly warts. The worst one is for variable scoping:

for (int i=0;...) { body; }
// here i is defined in VC6

Christian

Dombo

unread,
Jan 23, 2017, 4:04:28 PM1/23/17
to
Op 23-Jan-17 om 20:46 schreef Christian Gollwitzer:
IIRC VC6 did have a compiler option to make the variable scoping
conforming, unfortunately this was not enabled by default.

Alf P. Steinbach

unread,
Jan 23, 2017, 5:20:08 PM1/23/17
to
On 23.01.2017 20:46, Christian Gollwitzer wrote:
>
> Blessed be. I had to fight with the infamous version 6.0 for a long
> time. VC6.0 predates C++98, but it was for a long time the only compiler
> that produces binaries without dependencies on Windows. All later
> versions depend on a C++ runtime (a different one for each compiler
> release), which must be installed on the user's machine - and this of
> course requires admin priviledges. Therefore VC6 stuck around a lot...
> with many ugly warts. The worst one is for variable scoping:
>
> for (int i=0;...) { body; }
> // here i is defined in VC6
>

For me one of the worst things about VC6 was that if you tried to do a
centralized exception translation thing (which at that time could be
useful e.g. for using IBM's Lotus Notes access library, and some
others), like this:

int main()
{
try
{
something();
return EXIT_SUCCESS;
}
catch( ... )
{
cerr << "!" << x_description() << endl;
}
return EXIT_FAILURE;
}

with `x_description` defined like

std::string x_description()
{
try
{
throw;
}
catch( std::exception const& x )
{
return x.what();
}
catch( Silly_IBM_integer_exception const x )
{
return blah( x );
}
catch( ... )
{
return "Unknown exception";
}
}

then you risked

• catching low-level SEH exceptions such as div-by-zero, and

• having a standard exception /destruced twice/.

It was ungood.

But DevStudio 6.0 was nice. :)


Cheers!,

- Alf

Alf P. Steinbach

unread,
Jan 23, 2017, 5:35:42 PM1/23/17
to
On 23.01.2017 23:19, Alf P. Steinbach wrote:
> catch( std::exception const& x )

Oh sorry, I forgot that VC6 didn't even have standard exceptions. But
anyway.


- Alf


Tim Rentsch

unread,
Jan 27, 2017, 2:12:00 AM1/27/17
to
IME some segements of the C++ community are overly dogmatic in
wanting to avoid using macros. Depending on the specifics, there
may be a way to implement a Trace() construct that doesn't use
macros (as others have explained), or in other cases macros may
be essentially the only viable choice (again per other posters).
I suggest choosing the approach that you think makes the most
sense, considering both current requirements and likely future
requirements. If someone suggests a different design choice
would be better, do listen carefully but then decide whether the
reasons offered make sense or if they are just being dogmatic.
0 new messages