Do you hate writing C or C++ header files?
Do you always forget where to place those inline
, virtual
, static
, and explicit
modifiers -- to the member-function definition or declaration?
Do you often find yourself avoiding to factor out a new method because you also need to add a method declaration to the class declaration in the header file?
Do you hate putting inline function into header files, in a special order?
If so, Preprocess may be your answer. With this tool, you write unit-style single-source-file modules in C++, from which it automatically generates header and implementation files.
preprocess -c
filename_base [ -o
outfile_base | -s
]
[ -p
prepend ]
[ -C
source_ext ] [ -H
header_ext ]
[ -e
tag_list ]
[ -i
] [ -t
] [ -l
| -L
] [ -v
] [ -d
]
sources...
Preprocess is a preprocessor for C++ modules. With this tool, you write unit-style single-source-file modules in C++.
Preprocess essentially does these things (and frees the programmer from doing them):
inline
" can be put into the implementation file (out of line). This can ease development (as it temporarily decreases dependencies on header files that change frequently) and debugging. Preprocess works by transforming unit-style single-source-file C++ modules into three output files: public header, private header, and implementation file. C and C++ comments are preserved during the transformation, keeping the resulting files readable and making it possible to run another preprocessor on Preprocess' output.
Modules contain two sections. The "
Class definitions deliberately lack declarations for member functions: Preprocess will add them automatically. Member-function definitions (in the implementation section) labelled From this input file (foo.cpp), Preprocess extracts three C++ source files that can then be processed using the standard C++ compiler toolchain:
|
|
First, Preprocess generates a public header file (foo.h). This header file contains all declarations from the interface section, with class definitions properly expanded to contain member-function declarations (shown red in the example on the right side).
It also contains declarations of non-" If desired, preprocess can also be instructed to hide all inline functions (and to generate them out-of-line instead), resulting in a public header file that better insulates clients from implementation details.
|
|
Second, a private header file containing all non-public type definitions (foo_i.h). This file can be used by debugging modules that need access to implementation-specific data structures. This file also contains all inline member functions belonging to classes declared here. (This feature is not shown in the example.)
|
|
Third, an implementation file (foo.cc). This file starts with declarations for all free "
|
|
Output files have names of the following form:
-
suffix].
source_ext .
header_ext _i.
header_ext -p
, outfile_base needs to be specified using -c
, suffix is an optional suffix that can be specified using the IMPLEMENTATION
directive, and source_ext and header_ext default to ".cc" and ".h" but can be overridden using -C
and -H
, respectively.
-c
filename_base -o
outfile_base -c
option. -s
IMPLEMENTATION
directives in the source files. -p
prepend /
"). -C
source_ext -H
header_ext -e
tag_list -s
option. See Section Conditional compilation for details on this option. -t
#include
directives in generated source files that are otherwise empty, resulting in increased compilation speed for these files. -i
inline
code. If this option is not given, all code (including code marked "inline
") is generated out-of-line. -l
#line
directives in output files. If this option is not given, #line
will be generated by default. -L
#line
directives in header files only. Using this option can speed up builds because the contents of header files change less frequently, as #line
directives for (member) function declarations do not have to be updated every time its definition in the source module changes its absolute position. (Of course, this assumes that the time stamp of header files are updated only when the contents of the files change. See Section Example Makefile fragment for a possible way to do this.) -v
preprocess
' parser pass. -d
-e
).
Preprocess understands a number of language directive that control its behavior.
INTERFACE:
IMPLEMENTATION
sections. Function definitions are not allowed in this section.
IMPLEMENTATION:
INTERFACE:
sections) end up in the internal header file -- except if a public inline function of a public class depends on the private class, in which case the private class' declaration will be put into the public header file.
PUBLIC
, PRIVATE
, and PROTECTED
explicit
, static
, and virtual
inline
inline NEEDS [
dependencies,
... ]
inline
, but additionally specifies types, functions, and #include
statements that this inline function depends on and that consequently need to be exported as well, in front of this inline function. Preprocess reorders definitions such that all dependencies are defined before the inline function. Example:
inline NEEDS["foo.h", some_func, Some_class,
Some_other_class::member_func]
int
foo ()
{ }
Language directives for advanced use
IMPLEMENTATION [
suffix]:
.cc
, the code ends up in outfile_base-
suffix.cc
. This directive is useful if there are several input files that together make up one input module (which are fed to Preprocess at the same time and which share one public and one private header file).
(This form of the IMPLEMENTATION directive works only if neither the -s
(no-suffix) nor the -e
(conditional compilation) options are used. See Section Conditional compilation for information on conditional compilation.)
EXTENSION class
classname {
... };
IMPLEMENT
inline NOEXPORT
inline ALWAYS_INLINE
-i
option is not used. Use this specifier for functions that absolutely must be inline even in debugging builds.
Conditional compilation is a Preprocess mode that is enabled by using the "-e
tag_list" option.
INTERFACE [
tag_expression]:
IMPLEMENTATION [
tag_expression]:
INTERFACE
or IMPLEMENTATION
section is included in the output only if it is true using the selectors specified in tag_list.
Examples:
INTERFACE [a,b]:
// This section is used whenever a or b is contained in the tag_listINTERFACE [a-b]:
// This section is used whenever a and b are contained in the tag_listINTERFACE [a,b-c]:
// This section is used whenever a, or b and c are // contained in the tag_listINTERFACE [!a]:
// This section is used whenever a is not contained in the tag_listINTERFACE [{a,b}-c]:
// This section is used whenever a and c, or b and c are // contained in the tag_listINTERFACE [!a,b-c]:
// This section is used whenever a is not contained in the tag_list, // or b and c are contained in the tag_list
When you use Preprocess, there are a few things you need to keep in mind.
#include
" the corresponding header files (or add forward declarations) in the "INTERFACE:
" section of your module. Private inline functions (which might end up in the public header file) need to specify their include dependencies in a "NEEDS[]
" clause. Also, if you use names declared in an (externally-defined) namespace (such as namespace "std
"), you must specify the fully-qualified name (including the namespace) in the function signature (unless you use a "using namespace
" directive in the "INTERFACE:
" section, which is not recommended).
NEEDS
" clause. Otherwise, Preprocess cannot guarantee the correct inline-function order in the output, and you may get compiler warnings like ``inline function is used but not defined.'' This problem is reinforced when using private inline functions, because Preprocess moves them out of the public header file (into the private header file) unless a public function's "NEEDS
" clause requires them.
This is an example fragment from a Makefile (for GNU Make) that generates This Makefile fragment needs GNU Make and the move-if-change script that only updates a target if it is different from the source.
This example assumes that you do not use the
|
|
Limitations and ideas for future extensions
#line
directives Preprocess generates sometimes are offset plus/minus one or two lines to the real code. #include
and #if 0
; it just copies all other direcitives into the output as it finds them. That makes it easy for you to shoot yourself into the foot.
.cpp
files into new .cpp
files, refactoring or renaming code on the fly. Preprocess is free software licensed under the GNU General Public License. Its implementation language is Perl, so you need a Perl5 interpreter to run it.
You can download Preprocess as CVS module "preprocess
" from the DROPS project's remote-CVS server. Please refer to the download instructions on DROPS' website.
There is a mailing list to which CVS-commit messages for changes made to preprocess are posted. Please ask me if you would like to be put on this list (see Section Author ).
New releases are periodically announced on the Freshmeat website. If you are a registered Freshmeat user, you can subscribe to these release announcements.
Michael Hohmuth <hohmuth@inf.tu-dresden.de>
move-if-change
(1) shell script
Preprocess was originally written for the Fiasco microkernel.