proxygen
|
Pump is Useful for Meta Programming.
Template and macro libraries often need to define many classes, functions, or macros that vary only (or almost only) in the number of arguments they take. It's a lot of repetitive, mechanical, and error-prone work.
Variadic templates and variadic macros can alleviate the problem. However, while both are being considered by the C++ committee, neither is in the standard yet or widely supported by compilers. Thus they are often not a good choice, especially when your code needs to be portable. And their capabilities are still limited.
As a result, authors of such libraries often have to write scripts to generate their implementation. However, our experience is that it's tedious to write such scripts, which tend to reflect the structure of the generated code poorly and are often hard to read and edit. For example, a small change needed in the generated code may require some non-intuitive, non-trivial changes in the script. This is especially painful when experimenting with the code.
Pump (for Pump is Useful for Meta Programming, Pretty Useful for Meta Programming, or Practical Utility for Meta Programming, whichever you prefer) is a simple meta-programming tool for C++. The idea is that a programmer writes a foo.pump
file which contains C++ code plus meta code that manipulates the C++ code. The meta code can handle iterations over a range, nested iterations, local meta variable definitions, simple arithmetic, and conditional expressions. You can view it as a small Domain-Specific Language. The meta language is designed to be non-intrusive (s.t. it won't confuse Emacs' C++ mode, for example) and concise, making Pump code intuitive and easy to maintain.
The following Pump code (where meta keywords start with $
, [[
and ]]
are meta brackets, and $$
starts a meta comment that ends with the line):
will be translated by the Pump compiler to:
In another example,
will generate one of the following lines (without the comments), depending on the value of n
:
We support the following meta programming constructs:
$var id = exp | Defines a named constant value. $id is valid util the end of the current meta lexical block. |
---|---|
$range id exp..exp | Sets the range of an iteration variable, which can be reused in multiple loops later. |
$for id sep [[ code ]] | Iteration. The range of id must have been defined earlier. $id is valid in code . |
$($) | Generates a single $ character. |
$id | Value of the named constant or iteration variable. |
| Value of the expression. |
$if exp [[ code ]] else_branch | Conditional. |
[[ code ]] | Meta lexical block. |
cpp_code | Raw C++ code. |
$$ comment | Meta comment. |
Note: To give the user some freedom in formatting the Pump source code, Pump ignores a new-line character if it's right after $for foo
or next to [[
or ]]
. Without this rule you'll often be forced to write very long lines to get the desired output. Therefore sometimes you may need to insert an extra new-line in such places for a new-line to show up in your output.
You can find the source code of Pump in scripts/pump.py. It is still very unpolished and lacks automated tests, although it has been successfully used many times. If you find a chance to use it in your project, please let us know what you think! We also welcome help on improving Pump.
You can find real-world applications of Pump in Google Test and Google Mock. The source file foo.h.pump
generates foo.h
.
[[]]
, which inserts an empty string. For example Foo$j[[]]Helper
generate Foo1Helper
when j
is 1.[[]]
followed by a new line. Since any new-line character next to [[
or ]]
is ignored, the generated code won't contain this new line.