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

Preprocessor proposal for C and C++

68 views
Skip to first unread message

Thiago Adams

unread,
May 13, 2019, 8:39:43 AM5/13/19
to
Proposal to add MACRO_ON / MACRO_OFF
What is does?
Is turn ON OFF macro expansion.

Motivation
Library implementers, especially inline functions, uses identifiers names
__LikeThis and one reason is to protect the code against some macro created
by the user.

In windows we have a classic conflict with min/max from windows.h and min max
from STL.

With this feature can protect the code against external interference.

How to use?
#define M 1
#pragma STDC MACRO_OFF

// the preprocessor will not expand any macro here...
int M = 2;

#pragma STDC MACRO_ON

Other preprocessor features are not affected and the expansion at for instance #if HERE still allowed.

David Brown

unread,
May 13, 2019, 9:37:59 AM5/13/19
to
I suspect that you'd quickly have trouble here - there are /lots/ of
macros defined in most compilations, and many of them are essential. A
quick test of a blank C/C++ file on my system gives 347 pre-defined
macros in C and 389 for C++. If I add "#include <stdint.h>", these
numbers go up to 579 and 671.

Some of these macros are going to be directly useful - header files can
quite reasonably include standard library headers like <stdbool.h>,
<stdint.h>, <stddef.h>, and many of the features are defined there as
macros. There are also many macros predefined by tools, or declared by
common OS/library include files, that are useful for more complex
libraries - letting the headers adjust themselves for different
compilers, different target OS's, etc.

For C++, the solution to this kind of problem is modules. We don't have
them yet, but I believe they should be here Real Soon Now™. This is in
addition to using other C++ features to avoid macros (like inline
functions, better "const", etc., and namespaces). I think it could be
counter-productive to have a second solution before them.

Other than that, I think perhaps you could just track #define's in
"application" code (the main C or C++ file, and any "" includes), and if
any of these are encountered while processing a system include (a <>
include), issue a warning. Aim to encourage users to structure their
code sensibly with their system includes at the start, before local
macro definitions, and to help users spot problems. Then library
writers can use nicer names.


Thiago Adams

unread,
May 13, 2019, 10:16:32 AM5/13/19
to
On Monday, May 13, 2019 at 10:37:59 AM UTC-3, David Brown wrote:
> On 13/05/2019 14:39, Thiago Adams wrote:
> > Proposal to add MACRO_ON / MACRO_OFF
> > What is does?
> > Is turn ON OFF macro expansion.
> >
> > Motivation
> > Library implementers, especially inline functions, uses identifiers names
> > __LikeThis and one reason is to protect the code against some macro created
> > by the user.
> >
> > In windows we have a classic conflict with min/max from windows.h and min max
> > from STL.
> >
> > With this feature can protect the code against external interference.
> >
> > How to use?
> > #define M 1
> > #pragma STDC MACRO_OFF
> >
> > // the preprocessor will not expand any macro here...
> > int M = 2;
> >
> > #pragma STDC MACRO_ON
> >
> > Other preprocessor features are not affected and the expansion at for instance #if HERE still allowed.
> >
>
> I suspect that you'd quickly have trouble here - there are /lots/ of
> macros defined in most compilations, and many of them are essential. A
> quick test of a blank C/C++ file on my system gives 347 pre-defined
> macros in C and 389 for C++. If I add "#include <stdint.h>", these
> numbers go up to 579 and 671.

It can be used in small blocks.


https://stackoverflow.com/questions/11544073/how-do-i-deal-with-the-max-macro-in-windows-h-colliding-with-max-in-std

One use case is this one:


#include <Windows.h> // includes WinDef.h which defines min() max()
#include <iostream>
using std::cin;
using std::cout;

#pragma STDC MACRO PUSH OFF

void Foo()
{
int delay = 0;
do
{
if(cin.fail())
{
cin.clear();
cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
}
cout << "Enter number of seconds between submissions: ";
} while(!(cin >> delay) || delay == 0);
}

#pragma STDC MACRO POP


In this sample max will not be expanded.

(I added PUSH and POP to restore the previous state)

Öö Tiib

unread,
May 13, 2019, 10:28:40 AM5/13/19
to
When there is a big enough piece of code without any macro
expansions now then there will be also necessity to add those
into it tomorrow. No matter if it is for debugging, testing, logging,
fixing some portability or compatibility issue ... it will happen.
Result will be code sliced with lot of #pragmas and the purpose
will be anyway broken on some line where both desirable and
undesirable macro expansion happen at same time.

Just define NOMINMAX if you ever use windows.h for anything.

Thiago Adams

unread,
May 13, 2019, 10:52:38 AM5/13/19
to
On Monday, May 13, 2019 at 11:28:40 AM UTC-3, Öö Tiib wrote:
...
> When there is a big enough piece of code without any macro
> expansions now then there will be also necessity to add those
> into it tomorrow. No matter if it is for debugging, testing, logging,
> fixing some portability or compatibility issue ... it will happen.
> Result will be code sliced with lot of #pragmas and the purpose
> will be anyway broken on some line where both desirable and
> undesirable macro expansion happen at same time.
>
> Just define NOMINMAX if you ever use windows.h for anything.

If you think your code will be sliced then dont use this feature.

Daniel

unread,
May 13, 2019, 12:45:26 PM5/13/19
to
On Monday, May 13, 2019 at 10:16:32 AM UTC-4, Thiago Adams wrote:
>
> #pragma STDC MACRO PUSH OFF
>
> cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
>
> #pragma STDC MACRO POP
>

Practically all open source projects that have Windows users would have that
as

cin.ignore((std::numeric_limits<std::streamsize>::max)(), '\n');

either from the start, or as soon as the first issue was raised.

Daniel

Bonita Montero

unread,
May 13, 2019, 12:59:33 PM5/13/19
to
Things the world doesn't need ...

Cholo Lennon

unread,
May 13, 2019, 4:46:12 PM5/13/19
to
On 5/13/19 1:59 PM, Bonita Montero wrote:
> Things the world doesn't need ...

Let me ask you a question: Why are you so rude to answer
questions/posts? You were not like that :-O

Regards

--
Cholo Lennon
Bs.As.
ARG

Thiago Adams

unread,
May 14, 2019, 9:25:13 AM5/14/19
to
I think "disable all macros" can be a little radical and not suitable
for all uses.
I think an alternative can be better. Instead of "disable all macros"
we can think on scopes for #defines.

A way to say "I am using these macros here".
For libraries it is the same of saying "I am using these macros
everything else is not from this context" and I don't need
to worry about it.

Or we can say "I am not using any macro here except this one" like
the situation where we want to disable min.

And the last option is a way of override some macro inside the scope.

The syntax could be :

"I am using all macros except min"
#pragma PUSH SCOPE -min
#pragma POP SCOPE

"I am not using any macro except assert and LOG"
#pragma PUSH SCOPE -* assert LOG
#pragma POP SCOPE


"I will redefine assert here"
#pragma PUSH SCOPE -assert

#define assert redefing_assert_here

#pragma POP SCOPE













Thiago Adams

unread,
May 14, 2019, 10:18:20 AM5/14/19
to
On Tuesday, May 14, 2019 at 10:25:13 AM UTC-3, Thiago Adams wrote:
> On Monday, May 13, 2019 at 11:52:38 AM UTC-3, Thiago Adams wrote:
> > On Monday, May 13, 2019 at 11:28:40 AM UTC-3, Öö Tiib wrote:
> > ...
> > > When there is a big enough piece of code without any macro
> > > expansions now then there will be also necessity to add those
> > > into it tomorrow. No matter if it is for debugging, testing, logging,
> > > fixing some portability or compatibility issue ... it will happen.
> > > Result will be code sliced with lot of #pragmas and the purpose
> > > will be anyway broken on some line where both desirable and
> > > undesirable macro expansion happen at same time.
> > >
> > > Just define NOMINMAX if you ever use windows.h for anything.
> >
> > If you think your code will be sliced then dont use this feature.
>
> I think "disable all macros" can be a little radical and not suitable
> for all uses.
> I think an alternative can be better. Instead of "disable all macros"
> we can think on scopes for #defines.

Searching for 'scope macro' I found:

//#scope:
A simple scoping mechanism for the C/C++ preprocessor
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2004/n1614.pdf

//The #scope extension for the C/C++ preprocessor
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2004/n1625.pdf

Öö Tiib

unread,
May 15, 2019, 9:40:11 AM5/15/19
to
On Tuesday, 14 May 2019 16:25:13 UTC+3, Thiago Adams wrote:
>
> Or we can say "I am not using any macro here except this one" like
> the situation where we want to disable min.
>
> And the last option is a way of override some macro inside the scope.
>
> The syntax could be :
>
> "I am using all macros except min"
> #pragma PUSH SCOPE -min
> #pragma POP SCOPE
>
> "I am not using any macro except assert and LOG"
> #pragma PUSH SCOPE -* assert LOG
> #pragma POP SCOPE
>
>
> "I will redefine assert here"
> #pragma PUSH SCOPE -assert
>
> #define assert redefing_assert_here
>
> #pragma POP SCOPE

Interesting things. Something close to those is already
implementable using some preprocessor metaprogramming.
Some tricks generating needed #ifdef, #ifndef, #define,
#undef and #include directives.

I am still skewed towards defining NOMINMAX either as
default command line option or before including windows.h
in that stdafx.h or pch.h or whatever it is. It will be less to
type than those pragmas (or calls of metaprogramming
macros) and more pleasant for eye to read. Kind of done
once and then forgotten forever with works(TM).

Thiago Adams

unread,
May 15, 2019, 10:11:05 AM5/15/19
to
On Wednesday, May 15, 2019 at 10:40:11 AM UTC-3, Öö Tiib wrote:
...
> I am still skewed towards defining NOMINMAX either as
> default command line option or before including windows.h
> in that stdafx.h or pch.h or whatever it is. It will be less to
> type than those pragmas (or calls of metaprogramming
> macros) and more pleasant for eye to read. Kind of done
> once and then forgotten forever with works(TM).

I put the sample of windows.h min/max just as reference.
I am not worried about this specific case.

At the #scope proposal pdf (A simple scoping mechanism for
the C/C++ preprocessor) we can find more motivation at 'the
problem' section.

"
The problem
We need to protect code, especially code in header files, from
accidental matches of macros. The basic traditional defense is to
define all macros as ALL_CAPS and never define other identifiers
with all capital letters. Unfortunately, in much code not all macros
are ALL_CAPS and some identifiers (notably some enumerators and some
consts) are defined using all capital letters (and thus especially
vulnerable to macro substitution). All useful programs must use
headers, but we cannot control how macros are defined in headers
nor can a writer of a header control how an #includeing program
use identifiers.
Therefore, “house style rules” cannot in general prevent accidents,
and errors are common.
These problems are well known and partially effective remedies
are widely adopted. However, there is a huge variety in the kind
of remedies adopted and the degree to which they are systematically
applied. In all cases, the result is defensively written code
that to various degrees departs from the ideal expression of the
ideas it represents.

The seriousness of this problem increases with the number of macros used,
the number of headers included, and the number of independent sources
of headers. Most large organizations – even quite mature and experienced
ones – are regularly “bitten” by macro.

"

The main point for me is:

"Unfortunately, in much code not all macros
are ALL_CAPS and some identifiers (notably some enumerators and some
consts) are defined using all capital letters (and thus especially
vulnerable to macro substitution). All useful programs must use
headers, but we cannot control how macros are defined in headers
nor can a writer of a header control how an #includeing program
use identifiers. "

for C++, modules will help this I guess.

Öö Tiib

unread,
May 15, 2019, 12:23:34 PM5/15/19
to
On Wednesday, 15 May 2019 17:11:05 UTC+3, Thiago Adams wrote:
>
> The main point for me is:
>
> "Unfortunately, in much code not all macros
> are ALL_CAPS and some identifiers (notably some enumerators and some
> consts) are defined using all capital letters (and thus especially
> vulnerable to macro substitution). All useful programs must use
> headers, but we cannot control how macros are defined in headers
> nor can a writer of a header control how an #includeing program
> use identifiers. "

I agree. But the motivator is not really motivator for me. There *will*
*be* *defects* that are caused by incompatibilities introduced by
cooperation in huge and deep, potentially recursive cascade of
header writers and header includers. These will be there. Period.

It is way wider issue than macros and even orthogonal to
macros. Some sort of macro scopes added may simplify fixing
a subset of those defects caused but in general that will just
complicate the things up even more and worse.

> for C++, modules will help this I guess.

Yes. The modules can not be some kind of rocket science.
Look at PASCAL module (unit) as example:

unit UnitName;
interface

{* declare your
interface here. *}

implementation

{* define your
implementation and
internal stuff here. *}

Usage of that unit in other module is like that:

uses UnitName;

That was so done at 1970 and works perfectly, no complaints.
The obvious question arises then why in C++ we need to
#include millions of lines of irrelevant to us code of typical
#include cascade into each cpp file for to call couple of
functions from elsewhere? If it is because compiler wants to
optimize that whole call cascade that might form that way
then who holds it back from doing that without it all being
vomited onto my table? I still want to see the names in
interfaces that I have said that I use and not to see anything
else. Easy.



Alf P. Steinbach

unread,
May 15, 2019, 1:55:46 PM5/15/19
to
On 14.05.2019 15:24, Thiago Adams wrote:
> [snip]
> The syntax could be :
>
> "I am using all macros except min"
> #pragma PUSH SCOPE -min
> #pragma POP SCOPE

AFAIK `#pragma push_macro` is already supported by Visual C++ and g++,
hence presumably also Intel and Dang. It's not quite as general as what
you outline. But mostly what's needed in practice.

Good luck working for standardization of existing `#pragma` practice.

The case of `#pragma once` shows that it's not easy.


[snip]
Cheers & hth.,

- Alf
0 new messages