Showing C header structure

Tuesday 3 February 2004This is over 20 years old. Be careful.

A co-worker had a mysterious C++ compile error: a symbol was undefined, but it was clearly present in the header file. We figured there was an ifdef affecting it, but how to find the culprit in the 11,000-line header file?

I tossed off this Python script:

# showifstruct.py
import re, sys

reDirective = re.compile(r'\s*#\s*([a-z]+)')

f = file(sys.argv[1])
targetLnum = int(sys.argv[2])

# A stack. Each element is a list of tuples.
# Each tuple is a linenumber/text pair of a directive that affects the current line.
ifstack = []

lnum = 0
for l in f.readlines():
    lnum += 1
    if lnum == targetLnum:
        break
    matchDir = reDirective.match(l)
    if matchDir:
        directive = matchDir.group(1)
        if directive in ['if', 'ifdef', 'ifndef']:
            # Start a group
            ifstack.append( [[lnum, l]] )
        elif directive == 'else':
            ifstack[-1].append( [lnum, l] )
        elif directive == 'endif':
            del ifstack[-1]

indent = ''
for ifs in ifstack:
    for lnum, line in ifs:
        print "%6d%s%s" % (lnum, indent, line),
    indent += '  '

Give it a file name and a line number, and it will track what preprocessor directives affect the line in question. It prints the lines that matter, indented to show structure, with line numbers:

$ showifstruct WinUser.h 11100
    12: #ifndef _WINUSER_
 10384:   #if(WINVER >= 0x0500)
 10483:     #ifndef NOWINABLE

Comments

[gravatar]
Ah--very nice indeed! I had a problem the other day that this would have helped with.
[gravatar]
Cool. Years ago at my first startup one of our senior developers became enamoured with C macros. Way, way, way too enamoured. He wrote nearly all of a very complex math package with C macros. The application developers were screaming in agony trying to debug the damn things when things went wrong. At least with C++ inlines you can usually disable the inlining for debugging purposes.
[gravatar]
At which point I once again think "I really must do some coding in Python some time."
[gravatar]
Or you could use gcc's -E option (/E in MSVC) to see the preprocessor output...
[gravatar]
Yes, MSVC has the -E option as well, but it doesn't help much: it would show that in fact the line you wanted was precompiled away, but you are still stuck trying to figure out why. In fact, after -E, it may be even harder to tell why, since the symbols in the #ifdefs will have been replaced with their values.
[gravatar]
Very nice, I do like browsing clean and concise python code.

A minor nit - instead of this:
  del ifstack[-1]

you could do this:
  ifstack.pop()

Add a comment:

Ignore this:
Leave this empty:
Name is required. Either email or web are required. Email won't be displayed and I won't spam you. Your web site won't be indexed by search engines.
Don't put anything here:
Leave this empty:
Comment text is Markdown.