cmdLP
cmdLP

Reputation: 1856

Redefining macros with old contents / Making a file include counter

I want to count the amount of includes of one specific file.

My first idea was to define a macro holding the count and redefining it when a specific file is included. I have to find a way that the macro is expanded, to be used in a new definition with the same name again, else its old value would simply be destroyed by undef. Is there a way to expand the macros when the macro is defined. Or is there an other way to count including files?

FILE main.c:

#define INCLUDE_COUNT 0

// some other files include the file include_counter.h here

You would think that the compiler could replace INCLUDE_COUNT by 0 or one of its redefinitions in the first line, but it doesn't because macros inside macro-definitions are expanded when the macro is used in non-macro code.

FILE include_counter.h:

#define OLD_INCLUDE_COUNT (INCLUDE_COUNT)
#undef INCLUDE_COUNT
#define INCLUDE_COUNT (OLD_INCLUDE_COUNT + 1)
// INCLUDE_COUNT would be expanded to ((INCLUDE_COUNT) + 1)

Upvotes: 1

Views: 458

Answers (2)

H Walters
H Walters

Reputation: 2674

Is there a way to expand the macros when the macro is defined.

Yes. But beware... using this as a means of generating unique symbols may cause unexpected linker issues if your symbols have external linkage.

Overview

The key problem here is that macros are not variables. With variables, if I have x=3*3; y=x;, then I'm assigning the value of x to y. With macros, if I have #define PPX 3*3 #define PPY PPX, then I'm literally assigning PPX to PPY. If I then evaluate PPY, it expands to PPX, and then to 3*3 but only because PPX is currently defined as 3*3.

However, there is a way around this. The preprocessor can evaluate expressions; it just uses conditional directives to do so. But due to the fact that this can branch, we can define macros conditionally based on PPX's value (as opposed to its replacement list). For example:

#if (PPX)%10==9
#  define PPX_DIGIT_0 9
#elif (PPX)%10==8
#  define PPX_DIGIT_0 8
...

So whereas PPY critically relies on PPX being defined as 3*3 to expand to 3*3; since PPX_DIGIT_0 is literally defined as 9, PPX can be undefined, and PPX_DIGIT_0 will still expand to 9. Note also that the conditional played a role in reducing the expression; (3*3)%10 is just equal to 9, causing the 9 branch to be hit. That loses the expression per se, picking up just the evaluation... which we want.

That's the idea. In practice, this requires a fair amount of code; fortunately, boost's preprocessor library already has it done for you, so here are two approaches using boost:

Approach 1: Using Boost Slots

include_counter.h:

#include <boost/preprocessor/slot/slot.hpp>
#if !(INCLUDE_COUNT)
# define INCLUDE_COUNT 0
# define BOOST_PP_VALUE INCLUDE_COUNT
# include BOOST_PP_ASSIGN_SLOT(1)
# undef  BOOST_PP_VALUE
# undef  INCLUDE_COUNT
# define INCLUDE_COUNT BOOST_PP_SLOT(1)
#endif
#define BOOST_PP_VALUE 1+INCLUDE_COUNT
#include BOOST_PP_ASSIGN_SLOT(1)

Boost slots evaluate macros and store their results using special internal states. You get 5 slots (code above is using slot 1), so you can store 5 numbers. This solution just initializes the slot to 0 before incrementing on the first include, then essentially just increments.

Approach 2: Use Boost Counter

include_counter.h:

#if !(INCLUDE_COUNT)
# include <boost/preprocessor/slot/counter.hpp>
# define INCLUDE_COUNT BOOST_PP_COUNTER
#endif
#include BOOST_PP_UPDATE_COUNTER()

Same idea, though it "consumes" boost counter.

Both boost solutions take the approach I just described. There's also a boost increment macro; feel free to ignore that, since (a) you're evaluating anyway, and (b) boost increment is a bit whimpy (with its maximum limit of 256).

You can also roll your own solution. But when you're done, it's going to look similar to boost's solution anyway. (The advantage is, your new "slot" will be independent of boost counter and boost slots).

Upvotes: 2

cup
cup

Reputation: 8267

An alternative is not to use the macros but the compiler. Many compilers have a "show includes" flag. For instance, in gcc, it is -H. For visual studio, it is /showincludes. Just pipe the compiler output to a file and count the number of lines.

# linux
gcc -H ... fred.c 1> incs.txt 2>&1
wc -l incs.txt

rem Windows
cl /showincludes ... fred.c 1> incs.txt 2>&1
find /v /c "" < incs.txt

# powershell
cl /showincludes ... fred.c 1> incs.txt 2>&1
get-content incs.txt | measure-object -l

Note that this will count the same file if it has been included multiple times, even though it does not get processed because of the guards.

Upvotes: 1

Related Questions