Search code examples
c++c-preprocessorlanguage-design

Rationale why is macro redefinition without undef leads to ill-formed program


According the standard macro redefinition is prohibited without using #undef before:

But the following redefinitions are invalid:

#define OBJ_LIKE    (0)         // different token sequence
#define OBJ_LIKE    (1 - 1)     // different whitespace

At the same time it's allowed to #undef for macros that were not defined at all before:

It is ignored if the specified identifier is not currently defined as a macro name.

I'm interested what is the rationale for this, i.e. forbidding redefinition without #undef in the middle? Some compiler optimization purposes or what?


Solution

  • Because it was inherited from C.

    ISO/IEC 9899:1990 §6.8.3 (AKA C90) contains similar wording:

    An identifier currently defined as a macro without use of lparen (an object-like macro) may be redefined by another #define preprocessing directive provided that the second definition is an object-like macro definition and the two replacement lists are identical.

    An identifier currently defined as a macro using lparen (a function-like macro) may be redefined by another #define preprocessing directive provided that the second definition is a function-like macro definition that has the same number and spelling of parameters, and the two replacement lists are identical.

    And looking at N802 produced by WG14 (https://www.open-std.org/jtc1/sc22/wg14/www/docs/n802.pdf) "Rationale for International Standard - Programming Language - C" §6.8.3, the reason is stated as:

    The Committee desired to disallow "pernicious redefinitions'' such as
    (in header1.h)

    #define NBUFS 10
    

    (in header.h)

    #define NBUFS 12
    

    which are clearly invitations to serious bugs in a program. There remained, however, the question of "benign redefinitions,'' such as (in header1.h)

    #define NULL_DEV 0
    

    (in header.h)

    #define NULL_DEV 0
    

    The Committee concluded that safe programming practice is better served by allowing benign redefinition where the definitions are the same. This allows independent headers to specify their understanding of the proper value for a symbol of interest to each, with diagnostics generated only if the definitions differ.

    So "benign" redefinitions are encouraged, where the alternative would be something like:

    #ifdef SPEED_OF_LIGHT
    #if SPEED_OF_LIGHT != 299792458
    #error Some other header defines SOME_UNIVERSAL_CONSTANT wrong
    #endif
    #else
    #define SPEED_OF_LIGHT 299792458
    #endif
    

    Simply #define SPEED_OF_LIGHT 299792458 will generate a warning if another header defines it differently.


    And a similar thing for #undef. The rationale given for §6.8.3.5:

    It is explicitly permitted to #undef a macro that has no current definition. This capability is exploited in conjunction with the standard library (see §7.1.8).

    Where §7.1.8 of C99 / §7.1.7 of C90 says:

    The use of #undef to remove any macro definition will also ensure than an actual function is referred to.

    For example, a standard library header might have double abs(double); as well as #define abs(x) _BUILTIN_abs(x), and #undef abs was legal and allowed you to make sure abs referred to a bona fide function.

    Of course, this doesn't apply to C++, where standard library functions cannot be macros. But the other rationale ("It is explicitly permitted to #undef a macro that has no current definition") still applies, i.e., "the committee says so".