Nacro: A better C/C++ macro extension implemented in Clang plugins

Hi Clang folks,

Recently I have been working on a side project, Nacro (, that you might find interesting.

Nacro is a small DSL aim to provide a better C/C++ macro experience. Here is an example:

#pragma nacro rule myPrint
(val:$expr) → $stmt {
printf("%d\n", val * 2)

#pragma nacro rule genColors
(cases:$expr*) → {
$loop(c in cases) {
case c:
printf(“the color is %s\n”, $str(c));

enum Color { RED, BLUE, YELLOW };
void printColor(enum Color color) {
switch(color) genColors(RED, BLUE, YELLOW)

int main() {
myPrint(1 + 3) // print out ‘8’
printColor(RED); // print ‘the color is RED’
return 0;

You can create a nacro rule with a pragma directive and follows with definitions written a simple DSL. The defined nacro rule works just like a normal macro function (i.e. expanded during preprocessing and called like a function), but with some extra features:

  • Multiline definitions without ‘\’.
  • Expression Protection: As shown in the ‘myPrint’ nacro above, you don’t need to add parens around expressions anymore, the nacro preprocessor will do that for you.
  • Loops, one of the most exciting features in nacro: You can use “$loop” directive to ‘unroll’ a list of argument (argument type trailing with ‘’, like ‘$expr’. It’s equivalent to VA_ARGS) during preprocessing.
  • Detecting invalid capturing. This is still an unstable feature, but basically it’s aiming to detect issues like this:

#define foo(arg) {
int x = 0;
return arg + x;

// ERROR: caller will always return 0
// regardless the argument value
int caller(int x) {

Please checkout the its wiki page ( for more info.

Another important thing is that nacro doesn’t require a custom Clang / LLVM. All of the features are implemented in a Clang plugin. So all you need is a Clang 10 by your hand and the plugin.

Though not modifying Clang actually put a tons of restrictions and force me to create many hacky workarounds that might be broken in newer version of Clang. For example, as a preprocessor plugin, all i can do is mutating and injecting tokens. But the preprocessor doesn’t like it, or in other words, current preprocessor doesn’t have a good support on this kind of actions. Especially while I was dealing with SourceLocation: there are many time i need to use an imprecise one or even fake one.

Another hard part is the Invalid Capture Detection. My original plan was to automatically fix it rather than throwing an error message. However, that will require symbol table and scope info that merely exist in AST. Though Parser/Sema has those info but currently we don’t have any plugin support on Parser/Sema.

Hope you find this project interesting, or at least find it amusing.


Sounds interesting. It would be a good addition if you can support a bit of type system and ensuring type sanity too somehow.

This seems similar in goal (and somewhat similar in style) to rust’s declarative macros.

Yes, as I put in the Github’s description. This project was originally inspired by Rust’s (declarative) macro.

Would this be able to support similar things to that, or would that be beyond the scope of this?

My original plan was to port full set of Rust macro syntax. But it just didn’t work out without changing Clang’s code base: Rust’s macro system has a deeper integration into its frontend.
But that saying, it would be cool to modify Clang to see how many changes do we need to achieve Rust’s macro.


Sounds interesting. It would be a good addition if you can support a bit of type system and ensuring type sanity too somehow.

That was part of my original plan, still thinking on how to make good use of the type system.