0

This question is a searching for robust approach to prevent such errors in existing and new projects. A code bellow illustrate the problem. GCC compiler show warning only if macro are conflicts but when it enum vs macro - no.

I can't found gcc -Wxxx options to get warnings for such conflicts.

  • It's possible to use macro with __COUNTER__ instead of enum.
  • It's possible to change format rules for enum names.

But it's helpful solutions only for new projects.

What I can do with old projects ?

enum my_enum {
    NONE = -1,
// #define NONE -1

    ONE = 1,
    TWO,
    THREE,
};

// use name NONE something else in included header
#define NONE 2

#include "stdio.h"

int main(void)
{
    printf("NONE is %d, it's %s\n",
        NONE,
        (-1 == NONE) ? "OK" : "FAIL");

    return 0;
}

So result with commented macro NONE -1:

gcc ./c-test.c -o c-test && ./c-test
NONE is 2, it's FAIL

And uncommented.

gcc ./c-test.c -o c-test && ./c-test
./c-test.c:11: warning: "NONE" redefined
   11 | #define NONE 2
      | 
./c-test.c:4: note: this is the location of the previous definition
    4 | #define NONE -1
      | 

Env:

  • Linux 5.4.0-132-generic #148-Ubuntu
  • gcc (Ubuntu 9.4.0-1ubuntu1~20.04.1) 9.4.0
imbearr
  • 999
  • 7
  • 22
  • 2
    Macros are expanded by the preprocessor before the C compiler runs. – stark Jan 12 '23 at 11:21
  • 3
    How would the compiler know whether your usage of the macro is a mistake or intentional? The "robust approach" is not to use macros in the first place. A simple constant will do just fine here. – Thomas Jan 12 '23 at 11:24
  • 1
    [names beginning with `__` like `__COUNTER__` are reserved](https://stackoverflow.com/q/228783/995714), using it invokes undefined behavior. – phuclv Jan 12 '23 at 11:33
  • This is a naming mess problem, code bellow is expect to get `NONE is -1` but it's not. – imbearr Jan 12 '23 at 11:34
  • 2
    There is not much you can do for older projetcs. The enum values in your example are all uppercase and the convention is to use all uppercase only for macros. They should not have been all upercase in the first place, and now it's probably too late to change for existing projects. But fiddling with macros is pretty unsafe anyway unless you know exactly what you're doing. – Jabberwocky Jan 12 '23 at 11:47
  • There is no such method. You need to use more meaningful names. So not save your keyboard – 0___________ Jan 12 '23 at 11:49
  • 4
    Use proper prefixes: `MODULENAME_ENUMNAME_NONE`. And solution for old projects is simple: Refactor names. Yes, it takes some time, but in the long run, it is worth it. – user694733 Jan 12 '23 at 11:50
  • 1
    @stark: Preprocessing is integrated with the compiler in GCC and Clang and, testing suggests, MSVC. Even if it were not, the fact that preprocessing were done first would not prevent the C implementation from keeping whatever information it needed to detect and warning about situations in which an identifier were defined both as a preprocessing macro and as a an enumeration identifier. – Eric Postpischil Jan 12 '23 at 11:56
  • 1
    @Thomas: Re “How would the compiler know whether your usage of the macro is a mistake or intentional?”: There could be a warning switch that specifically requested a warning if an identifier were both defined as a preprocessing macro and as an enumeration identifier. Using the switch would effectively inform the compiler that any such use was a mistake. – Eric Postpischil Jan 12 '23 at 11:57
  • 1
    You can write code that scans a source file for enumeration definitions and then inserts `#define ` at the beginning of the source file for each `` that is declared as as an enumeration identifier. When `` is macro-replaced, it will be replaced with itself, and then the preprocessing rules prevent further replacement. And if another `#define` appears for `` with a different definition, the compiler will warn you. (This will not detect `#undef `, so it is not a perfect solution… – Eric Postpischil Jan 12 '23 at 12:00
  • 1
    … You could add another `#define ` at the end to also find cases where there was a `#undef ` followed by some other `#define`. That will detect nearly all cases, but it still could miss cases where there is also a final `#undef`. At that point though, you will likely have caught all conflicts other than those created by a deliberately antagonistic opponent. – Eric Postpischil Jan 12 '23 at 12:01
  • 2
    @EricPostpischil Some C implementations (as well as the original pcc implementation) run the preprocessor as an independent pre-pass, in which case there's no way for the compiler to know what macro names were used (since it doesn't see them - it just sees the expanded result). – Tom Karzes Jan 12 '23 at 12:02
  • @TomKarzes: So what? How does the fact that some tool exists that possibly would not solve the problem bring any light to the question of whether there is some solution to the problem OP asks about? First, they specifically tag and mention GCC. Second, even if compiler X did not provide whatever feature is needed, a person could use compiler Y or analyzer Z on their code solely for the purpose of detecting the error they are concerned about, even if final compilation is done with compiler X. – Eric Postpischil Jan 12 '23 at 12:07
  • @TomKarzes: Further, saying that, if preprocessing were done as an independent pre-pass, it would be impossible for the compiler to know what macro names were used is false. Even when preprocessing is in fact done as a separate pass (you can request it with GCC and Clang), and compiling is done later, the compiler still knows the original line numbers in that second pass. It knows them because the preprocessor embeds that information in its output. There is no reason information about macro replacement cannot be similarly embedded, or conveyed in a debugging-information file. – Eric Postpischil Jan 12 '23 at 12:09
  • @EricPostpischil It's not clear that it would be worth it. In most cases, if a macro name conflicts with some identifier, you'll likely get a compile-time error. And it's not *always* an error. People sometimes do things like temporarily define a macro to override something. Also, in OP's case, the macro name is seen *after* the `enum` definition, so you would want the info earlier than the macro definition point, which doesn't fit well with a typical C compiler flow (which is largely sequential). – Tom Karzes Jan 12 '23 at 12:27
  • @EricPostpischil Also, ANSI C went out of its way to allow the creation of wrapper macros, which do some stuff and ultimately expand into references of a function with the same name. For example, you can define a macro called `sin` whose expansion does some junk and then calls the library `sin` function. In those cases, you wouldn't want a warning. – Tom Karzes Jan 12 '23 at 12:32
  • @TomKarzes: That has nothing to do with OP’s issue of detecting macro names and enumeration constants defined with the same identifier. – Eric Postpischil Jan 12 '23 at 12:34
  • @EricPostpischil Ok, you could restrict it to warning about macro names conflicting with enumeration value names. That seems pretty safe, at least in cases where the enum name isn't being intentionally overridden by a macro. – Tom Karzes Jan 12 '23 at 12:46
  • @Jabberwocky "the convention is to use all uppercase only for macros" It's actually very common to use all uppercase for macros _and_ for constants, including enumeration constants. – Lundin Jan 12 '23 at 12:49

0 Answers0