0

I have a file with hundreds of preprocessor macros of the form...

#define foo (bar)

Is there an easy one-line way to define each one only if it doesn't already exist?

For a single macro once could do the following...

#ifndef foo
#define foo (bar)
#endif

But I have hundreds of these macros. I was hoping for something like...

#define_if_not_defined foo (bar)

I actually tried creating my own macro

#define DEFINE_IFNDEF(foo,bar)\
    #ifndef foo\
    #define foo (bar)\
    #endif

Then I could just substitute... #define foo(bar) with DEFINE_IFNDEF(foo,bar)

But apparently there doesn't seem to be a good way to put preprocessor directives inside a preprocessor macro.

BACKGROUND

I was writing some safety certified code that had to replicate some functionality from a large preexisting library of several thousand files that wasn't safety certified.

There were several useful macro definitions in the original library scattered throughout a very large number of files. Including the whole files would become a huge expensive burden from a code review standpoint but including a few macros from some of the files to avoid re-inventing the wheel wasn't a problem. Therefore, I collected a lot of pre-processor macros here and there from a very large number of files in that original library and put them into one file.

My library has some major advantages in terms of smaller code size, faster speed, lower code complexity, and easier review safety wise but doesn't have every function that the original library had. So now some developers want to try and use the original library side by side with my code library.

The problem is that there are now hundreds of duplicate preprocessor macros that generate warnings. We would like to eliminate the warnings.

If all the macros came from the same header file, I could just use a #ifdef around the whole batch of them. But in my case, it's not so simple and I would need a lot of #ifdefs.

11
  • 3
    I'm not good with macros, but my bet is "no, there is no workaround". Commented Aug 19, 2024 at 22:48
  • 4
    Put succinctly, the answer is "No". Write a script that reads the define lines and spits out the #ifndef and #endif lines. Commented Aug 19, 2024 at 22:56
  • 3
    If the desired definition is identical to the prior definition, then simply allow it to be repeated. This is allowed by the C standard (C 2018 6.10.3 2), and there is no need to conditionally suppress this. If the desired definition differs from the prior definition, then you do not want to suppress the definition anyway, since, presumably, some later code relies on the new definition. Commented Aug 19, 2024 at 22:56
  • 1
    Edit the question to provide a minimal reproducible example. That should include at least two #define directives for one macro name that result in a compiler diagnostic (e.g., take the source file of one compilation and delete everything from it but the directives needed to show the problem) plus the name of the compiler used, its version number, and the switches used with it. That should resolve questions about how you are getting these diagnostics and whether the macro definitions are actual full duplicates or just reuses of the same name with different definitions. Commented Aug 19, 2024 at 23:20
  • 2
    I very much doubt it is acceptable in a safety certified code base to do things like "I collected a lot of pre-processor macros here and there from a very large number of files". That's equivalent to creating a lot of additional hazards with macro mismatches or redefinitions. Commented Aug 20, 2024 at 6:35

2 Answers 2

1

Easy! But not directly in C.

Do a text file defines.txt like:

DEF_A=123
DEF_B=456

And pass it through an external script make_defines.pl:

#!/usr/bin/perl
while(<>) {
   my ($def, $val) = split /=/;
   print "#ifndef $def\n#define $def $val#endif\n\n";
}

Now, you all you need is to run it:

$ perl make_defines.pl defines.txt > defines.h

You can also add it into makefile

defines.h: defines.txt
    perl make_defines.pl $< > $@

Done.

Sign up to request clarification or add additional context in comments.

3 Comments

I had been considering scripting, or even just pasting the lines of code into Microsoft Excel to modify them in bulk. It's a good and valid solution. But I was hoping for something that exists within the C language itself. If it turns out that doesn't exist, I will consider a scripting-based solution.
In the end I wound up just pasting my whole header file into Microsoft Excel and then writing a macro that expanded out all the #define lines into #ifndef #define #endif.
That is a possible solution, of course. But... it is a single-use one. Not exactly convenient to repeat for a new batch of defines. But whatever satisfy the customer - will do.
0

No, but macros should, in general, not be used that way.

Avoid conflicts

Generally speaking, your macro names should absolutely not conflict with any other macro names. When they do it is a sign that you need to find (and use) new names — even if the purpose of the macro is similar to or the same as other extant macros!

A good way to do this is to simply prefix all your macros with the name of your library. For example, if your library is named “Quux”, then your macros should be named things like QUUX_ADD and QUUX_IFY.

Purposeful replacement of macro definitions

If your purpose is to replace the value of another macro with your definition, then you will have to use the whole 4+ line #ifdef/#undef/#endif/#define bit ··· you only need #undef before #define.

This has a fairly bad smell to it. Avoid!

Guarantee macro availability

If your purpose is solely to ensure that a given macro exists, your options are to properly include the header where it properly comes from (assuming such a header exists), and then test that it is actually defined.

A common case is with something like STRINGIFY. As far as I know there is no place where this macro is required to exist, but it is common enough that you should guard against surprises anyway:

#ifndef STRINGIFY
  #define STRINGIFY(s) STRINGIFY_(s)
  #define STRINGIFY_(s) #s
#endif

Yet again, this still runs the risk that someone else’s code will attempt to create a STRINGIFY macro but fail to guard against the possibility that it already exists.
Alas if you are stuck using untouchable library code written by some noob.

This takes us back to the first point: avoid conflicts by using a unique name.

#define QUUX_STRINGIFY(s) QUUX_STRINGIFY_(s)
#define QUUX_STRINGIFY_(s) #s

P.S. Throwaway macro names

As a final addendum, for all those macros that have temporary purpose (in a single file, whether header or source!), feel free to name them something like “F”, but do not forget to undefine them when you are done with them.

For an example using the X-macro trick:

#define FRUITS(F) \
  F(Strawberry) \
  F(Banana) \
  F(Apple) \
  F(Orange) \
  F(Grape)

#define F(name) name,
enum Fruit { FRUITS(F) NUM_FRUITS };
#undef F

Notice how we immediately rid ourselves of the temporary macro name once done with it.

Again, it is possible that some careless programmer somewhere left a stray F lying around to goober your compilation, but any code that does that should be summarily tossed (and burned) because you cannot trust that the person(s) who wrote it hasn’t also been exceedingly careless with something more important. (But that’s my not-so-humble opinion on that matter.)

10 Comments

"If your purpose is solely to ensure that a given macro exists, your options are to properly include the header where it properly comes from (assuming such a header exists), and then test that it is actually defined." While this is generally good advice, it would cause me to have to do code review on tens of thousands of lines of third-party code as part of our safety certification. Having to spend an extra few hundred thousand dollars in engineering salaries and adding months to the schedule is also a valid reason not to do it.
I agree with your suggestion to add a unique prefix to my own macros. I always do that when I am defining something new. It is a great way to avoid getting into naming conflicts to begin with. But it doesn't help me get out of a situation where I just re-used a bunch of other people's stuff and now have pre-existing naming conflicts. I suppose I could go through hundreds of macros one by one and refactor them. But I was hoping for a solution that would allow me to just do a 10 second find and replace of all the #defines in a selected block of text.
@user4574 It is the second part, “test that it is actually defined", that is the important part, and it does not involve reviewing tens of thousands of lines of third party code. Wrap your macro with an #ifdef or use a unique macro name. (Though, honestly, it is code rot if you find a macro being used when you do not know where it came from!)
Ah... you are dealing with code conflict between two packages that are not your code?
Re “If your purpose is to replace the value of another macro with your definition, then you will have to use the whole 4+ line #ifdef/#undef/#endif/#define bit”: That is unnecessary. #undef / #define suffices.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.