1
votes

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)
2
I think you've answered your own question as to why you can't do what you want. Why do you want to do it? What utility is there in counting the number of times a file is included?Peter
I'll let someone smarter than I on macros cite the chapter and verse, but how is the compiler ever supposed to rely on the definition of a constant if you attempt to change it all the time?David C. Rankin
The macro INCLUDE_COUNT is helping to set a specific name of a struct in a file, which is included multiple times, it should emulate a c++-template in c, only that it is built by including a file and defining the template-parameters by macros before the include and undefined after.cmdLP
Look up the compiler specific __COUNTER__ macro. It's supported by gcc and Microsoft compilers.kaylum
While I also want to have a linear increasing counter, which should be the count of includes, __COUNTER__ increases outside the header, when otherwise used. But thanks for the tip.cmdLP

2 Answers

2
votes
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).

1
votes

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.