PCPP: A C99 Preprocessor
PCPP is a pure Python implementation of a C99 preprocessor. It's designed to preprocess C and C++ source code, handling directives like `#include`, `#define`, and conditional compilation. A key use case is for processing header-only C++ libraries into single-file includes and for integration with documentation tools like Doxygen. The library is actively maintained, though with an infrequent release cadence, with the latest significant update in late 2021.
Common errors
-
ModuleNotFoundError: No module named 'pcpp'
cause The 'pcpp' library is not installed in the current Python environment.fixEnsure the library is installed using pip: `pip install pcpp` -
FileNotFoundError: No such file or directory: 'some_header.h'
cause The preprocessor could not find an included file (e.g., via `#include <some_header.h>` or `#include "some_header.h"`) because its directory was not added to the search paths.fixAdd the directory containing the header file to the preprocessor's include paths using `p.add_path('/path/to/headers')` or the `-I` command-line option. Relative paths are often resolved from the current working directory, but explicit paths are robust. -
SyntaxError: invalid preprocessor expression in #if/#elif directive
cause The C preprocessor expression within an `#if` or `#elif` directive is malformed or uses constructs not supported by C99, leading to a parsing error.fixReview the expression for correct C99 preprocessor syntax. Ensure all macros used in the expression are defined or handle undefined macros gracefully. PCPP v1.30 improved expression evaluation with a yacc-based parser, fixing many issues from older `eval()`-based approaches.
Warnings
- breaking In v1.21, the paths emitted by `pcpp` into `#line` directives became relative to the working directory where the `Preprocessor` was initialized. Previously, these might have been absolute or behaved differently.
- gotcha Starting with v1.20, `pcpp` no longer collapses whitespace in the output by default. If your application expects compact output, you must explicitly enable compression.
- gotcha Special C preprocessor 'magic' macros like `__DATE__`, `__TIME__`, `__FILE__`, `__LINE__`, and `__COUNTER__` have specific handling. By default, `pcpp` might expand these, but if you need them passed through unexpanded (e.g., for Doxygen), specific options are required.
Install
-
pip install pcpp
Imports
- Preprocessor
from pcpp.preprocessor import Preprocessor
Quickstart
from pcpp.preprocessor import Preprocessor
import io
# Example C code as a string
c_code = '''
#define MY_MACRO "Hello, World!"
#define VERSION 10
#if VERSION > 5
const char* message = MY_MACRO;
#else
const char* message = "Old version";
#endif
int main() {
printf("%s\n", message);
return 0;
}
'''
# Create a Preprocessor instance
p = Preprocessor()
# Define additional macros (optional)
p.define("DEBUG")
# Prepare input stream (from string or file)
input_stream = io.StringIO(c_code)
# Parse and preprocess the input
p.parse(input_stream)
# Get the preprocessed output
output = p.stream()
# You can iterate through the tokens or get the full string output
processed_code = ''.join(str(x) for x in output)
print(processed_code)
# Expected output for VERSION=10 and DEBUG defined:
# const char* message = "Hello, World!";
#
# int main() {
# printf("%s\n", message);
# return 0;
# }