camomilla: C++ error simplification script

28 september 2016

I've recently released a script on my GitHub page that simplifies C++ compiler error messages: camomilla.

What does it do?

camomilla uses simple text transformations to make gcc and clang errors smaller and easier to read. During the development of ecst, a compile-time Entity-Component-System C++14 library developed for my BCS thesis, I encountered a lot of huge undeciphrable errors that sometimes completely filled my terminal buffer. Here's an example:

  • Before:

    g++: before camomilla

    g++: before camomilla

  • After:

    g++: after camomilla

    g++: after camomilla

The reason for these enormous outputs is the fact that both gcc and clang completely expand all nested typename definitions in the context of the error.

Transformations

The main text transformation used by camomilla to prevent full expansion of templates is "template typename collapsing", which hides nested typenames up to a user-specified depth. Example:

echo "metavector<metatype<metawhatever<int>>>::method()" | camomilla -d0
# outputs
"metavector<?>::method()"

echo "metavector<metatype<metawhatever<int>>>::method()" | camomilla -d1
# outputs
"metavector<metatype<?>>::method()"

echo "metavector<metatype<metawhatever<int>>>::method()" | camomilla -d2
# outputs
"metavector<metatype<metawhatever<?>>>::method()"

echo "metavector<metatype<metawhatever<int>>>::method()" | camomilla -d3
# outputs
"metavector<metatype<metawhatever<int>>>::method()"

The two other current transformations offered by camomilla are simple regex replacements that can act on namespaces or generic symbols. They can be defined in .json configuration files (which can recursively include each other!) as follows:

{
    // Add namespace replacements
    "namespaceReplacements": [
        "std": "",
        "boost::hana": "bh",
        "boost::fusion": "bf",
        "boost::spirit": "bs",
    ],

    // Add generic replacements
    "genericReplacements" : [
        "tuple": "tpl",
        "forward": "fwd"
    ]
}

The configuration file above can be loaded and used as follows:

g++ ./totally_real_file.cpp |& camomilla -c ./test_config.json

(Note: |& pipes both stdout and stderr to camomilla.)

Reprocessing

Ever felt like you were "debugging an error" when dealing with huge template-heavy compiler outputs? camomilla helps with that by automatically caching the last processed original error, so that the user can play around with different transformations and options. Example:

# Let's compile this `Boost.Hana`-heavy project:
g++ ./bhtest.cpp |& camomilla

# Error output: huge list of nested `boost::hana::tuple`
# and `boost::hana::integral_constant` instances.

# No problem! Reprocess the last error with additional options.
# (Setting "template typename collapsing" depth to `0`)
camomilla -r -d0

# Too many typenames hidden away? Play with the depth:
camomilla -r -d1
camomilla -r -d2
camomilla -r -d3
# ...

Solution or workaround?

(From camomilla/README.md.)

camomilla is merely a workaround for the fact that compilers do not filter (either automatically or through flags) the depth of template typenames. Errors in projects making use of libraries such as boost::hana or boost::fusion therefore include a lot of "typename boilerplate" that can make the error harder to read.

Library developers are sometimes forced to make use of techniques to erase the long typenames in order to simplify the errors and decrease compilation time: boost::experimental::di is an example.

I think this is something that should be addressed directly in the compilers - I've created a feature request/bug report both in the GCC Bug Tracker and in the Clang Bug Tracker.

Results

I currently pipe everything I compile on my machine to camomilla - I find the reduced noise/boilerplate quite useful when constantly reading template-heavy errors (...pretty much all of my code is template-heavy). This is a table that shows the size reduction of some errors generated from a real project, ecst, by simply mispelling a member field name in a template-heavy context:

Bytes (original) Bytes (after camomilla) Relative size change
g++ 6.1.1 38487 3680 -90.43%
clang++ 3.8.1 16856 2990 -82.26%

clang usually does a much better job than gcc in showing the source of the error and placing it at the top of the output, but the size reduction often means that mistakes are easier to pinpoint and track.

Using/hacking camomilla

You can get and install camomilla by cloning the official GitHub repository and using the setup.py script. Contributions are welcome - camomilla is written as a single Python 3 script file. (I used Python instead of C++ because camomilla is the "evolution" of an older poorly designed script that attempted to simplify errors for ecst.)


RSS Feed