43

I'm interested in using something other than the C preprocessor to preprocess my C and Objective-C source code. Are there good alternatives?

An example would be something that allowed one to escape out into a python or perl snippet in the middle of C code, and where the snippet spit out C that is then compiled as normal.

Brian Tompsett - 汤莱恩
  • 5,753
  • 72
  • 57
  • 129
Ken
  • 12,933
  • 4
  • 29
  • 32
  • 1
    Could you provide a concrete example that shows how (and why) you'd like to generate source code at compile-time? That might help us come up with suggestions that target your specific needs. (For example, I'm guessing that C++ templates won't work for your case, but it would be good to confirm.) – reuben Dec 28 '08 at 20:59
  • 2
    Not a bad idea: Here is an IBM article where the author uses a perl script to generate a lookup table. http://www.ibm.com/developerworks/linux/library/l-metaprog1/index.html –  Aug 26 '11 at 20:45

13 Answers13

66

You can use PHP as a C preprocessor. The advantages are:

  • very similiar syntax, so syntax highlighting works.
  • <? and ?> are not used in standard C (with non-standard C, the only thing that gets broken is old GCC extension operator that returns min/max)
  • it's rich in libraries.
  • it's turing complete.
  • usage of macros is very explicit. (compared to sneaky C preprocessor macros)

For serious use though, making PHP print the #line directives is needed for debugging preprocessed code.

<?php include_once "stdio.h"; ?>

int main()
{
    <?php
        for($i = 0; $i < 20; $i++)
            echo 'printf("%d\n", '.$i.');';
    ?>
}
milleniumbug
  • 15,379
  • 3
  • 47
  • 71
11

Cog isn't exactly a pre-processor, but it does go in-line in the code and generates stuff on the fly.

Michael Kohne
  • 11,888
  • 3
  • 47
  • 79
10

You might want to consider m4.
http://www.gnu.org/software/m4/

David Poole
  • 3,432
  • 5
  • 34
  • 34
  • 2
    To me, m4 feels too much like a science project and not enough like a pre-processor language. Last time I looked, it didn't have a looping construct, but instead had instructions on how to make one using recursion. I took that as a clear signal that the authors were too smart for my own good. – Michael Kohne Dec 28 '08 at 20:55
  • 1
    looping constructs, like these? [http://www.gnu.org/software/m4/manual/html_node/Forloop.html#Forloop][http://www.gnu.org/software/m4/manual/html_node/Foreach.html#Foreach] – Hasturkun Dec 29 '08 at 08:20
  • 3
    `m4` really is extremely versatile, but that syntax is just so brutal.! I really wish we had a more modern unix macro processor, but one compiled from C, like the venerable `m4`. – J. M. Becker Jun 06 '13 at 18:06
  • 2
    I wrote a blog post about using m4 with C which might help a few get started: http://kvanberendonck.id.au/using-m4-with-c/ – kvanbere Apr 27 '14 at 01:28
  • @TechZilla, Michael, David, Hasturkun, Folks, am I missing something? What's wrong with using a real scripting language like PHP/JavaScript/Perl as a preprocessor? – Pacerier May 16 '15 at 00:37
  • @Pacerier, You mean for the use case, or just generally? I was speaking generally, for this use case, yea all those options would do the job. Generally speaking, I'd be better served with a general purpose, yet modern macro language. Something that is specific to this extremely common domain, and yet actual scripting would not be required. – J. M. Becker Aug 21 '15 at 17:02
  • I was chocked that M4 wasn't mentioned earlier. You can redefine basically every aspect of it, the "syntax" is whatever you tell it to be. Want it to parse C preprocessing? Done. Autoconf scripts with ugly functions all over? Done. Macros for an assembler using much more C-like syntax? Done - see https://kevinpt.github.io/opbasm/rst/m4.html#c-style-syntax – Christoffer Bubach Dec 07 '20 at 18:14
9

The idea that you run code, the result of which is then spliced in is called quasiquotation. The code you run is antiquoted.

I know how to solve this problem using Lua. I've used string.gsub with an antiquotation function I wrote myself. I've used shell syntax for the antiquotation. As in the shell the antiquoted code returns a string which is then spliced into the code.

Below prog is the C code with antiquoted text, and antiquote is the antiquotation function. I've used Lua's special string quoting double square brackets to full advantage. In practice you wouldn't do this; you'd put prog in a separate file.

names = { 'John', 'Paul', 'George', 'Ringo' }

local prog = [===[
#include <stdio.h>

main() {
  $(local out = { }
    for _, n in ipairs(names) do
      table.insert(out, string.format([[  printf("The name is %%s\n", %q);]], n))
    end
    return table.concat(out, '\n  ')
   )
}
]===]


local function antiquote(s)
  local body = s:match '^%$%((.*)%)$'
  return assert(loadstring(body))()
end

prog = prog:gsub('%$%b()', antiquote)
io.stdout:write(prog)

In use, the program looks like this:

: nr@curlycoat 1181 ; lua /home/nr/tmp/emit-c.lua
#include <stdio.h>

main() {
    printf("The name is %s\n", "John");
    printf("The name is %s\n", "Paul");
    printf("The name is %s\n", "George");
    printf("The name is %s\n", "Ringo");
}
Norman Ramsey
  • 198,648
  • 61
  • 360
  • 533
6

Sure, the standard C preprocessor is very limited.
I've done such a tool recently: https://github.com/d-ash/perlpp

For example this

<?
    my @types = ('char', 'int', 'long'); 
    foreach (@types) {
?>
        <?= $_ ?> read_<?= uc($_) ?>(<?= $_ ?>* v);
<?  } ?>

becomes this

char read_CHAR(char* v);
int read_INT(int* v);
long read_LONG(long* v);

Syntax is similar to PHP, but it uses Perl instead, and can capture texts into Perl stings.

Edit by cxw — With @d-ash's approval, I am also a maintainer of perlpp. If you have questions, feel free to drop me a line!

cxw
  • 16,685
  • 2
  • 45
  • 81
drewcape
  • 61
  • 1
  • 2
5

If you abstract your problem a bit, then you are in fact looking for a templating engine for your code. Just as most websites inserts dynamically generated content in static templates, you want to insert dynamically generated code into your program.

I currently use Jinja2 (Python) for most templating work - I've found it to be very configurable in every way.

Morten Siebuhr
  • 6,068
  • 4
  • 31
  • 43
5

If you're prepared to get your hands dirty with some C++, there's the Wave parser in Boost, which is built using the Spirit recursive descent parser. It's a complete C pre-processor that conforms to all the latest specs for C and C++ (and, by extension, Objective C, AFAICS).

It's highly modular so you can switch your own driver in that could do the extras you want.

http://www.boost.org/libs/wave/doc/introduction.html

Thomas Perl
  • 2,178
  • 23
  • 20
philsquared
  • 22,403
  • 12
  • 69
  • 98
3

I've thought about this same problem in the past. Make sure you are OK with the fact that anyone who wants to compile your code will need the new pre-processing tool as well. If you are the only one who will ever work on it, no problem, but if you want to make the code available to others, then you might want to consider whether or not adding a tool requirement is a good idea.

Michael Kohne
  • 11,888
  • 3
  • 47
  • 79
  • I'm definitely not sure I'm okay with it. But I'm interested in looking. :-) – Ken Dec 28 '08 at 21:54
  • @Ken, this consideration isn't exactly so cut and dry, for example... how many times have you extracted a tar.gz and ran `./configure`? All the source for regenerating that was still right there, but you had no requirement to go chase down the correct versions of automake/autoconf. So all you must do, is provide BOTH the original code, and an already processed version. – J. M. Becker Jun 02 '13 at 22:28
  • @TechZilla - that's not the best idea in the world, frankly. If you provide already processed code, then people will end up MODIFYING the already processed code, and unless you are REALLY lucky, someone's going to modify the generated sections (in spite of the 'don't modify - it's generated!' warnings). Then if you need to deal with that code down the line, you have a SERIOUS problem on your hands. – Michael Kohne Jun 03 '13 at 00:25
  • 1
    @MichaelKohne: I don't dispute this concern, but it's not the problem you originally mentioned, "Make sure you are OK with the fact that anyone who wants to compile your code will need the new pre-processing tool as well." Additionally this new concern you mentioned, should equally apply to my automake/makefile analogy. Every automake dependant project would also need to address these same concerns, which was my initial point. The reason for providing both, just like automake dependant projects, is so regular compiling end-users don't require the code-generating tools. – J. M. Becker Jun 06 '13 at 18:01
3

The short answer is "no." The preprocessor is so intimately tied to the semantics of C that you can't really remove it, and in fact in some compilers isn't even a separate phase like it used to be in the old days --- compiling Objective C on a Mac just parses the Objective C syntax. So while you could certainly use another macro-processor, like m4, to process your source text before passing it to C, you wouldn't be eliminating the C preprocessor, you'd be adding another step of preprocessing.

But there's a deeper question here: what do you want to gain by eliminating the CPP phase?

Charlie Martin
  • 110,348
  • 25
  • 193
  • 263
  • I agree that he should supplement, not replace, the CPP. However, I disagree that preprocessing is forced by the compiler. At least GCC provides a method for skipping the preprocessing stage. – strager Dec 28 '08 at 20:58
  • I don't expect to eliminate the cpp phase - an additional phase is fine. I'm interested in alternatives because cpp is limited and full of pitfalls. No loops, no arrays - anything at all complex is probably just a bad idea in cpp. – Ken Dec 28 '08 at 21:02
  • Then I'd be really tempted to use something like Perl and be done with it. M4 will certainly do it -- it's Turing-complete -- but it's covered in hair and unpleasantly arcane. Even compared to perl. – Charlie Martin Dec 28 '08 at 22:07
  • If you feed a C sourcefile to perl, it just won't compile. You could instead make a perl script that embeds the C source as more or less one big string that gets printed out, but it'd be ugly. – Ken Dec 28 '08 at 22:42
  • Dude, I don't mean feed the C to Perl. Write a Perl script that does templating using a C source file as input. Something like the Template toolkit http://template-toolkit.org/ – Charlie Martin Dec 29 '08 at 00:39
  • So you're saying I should write my own preprocessor. :-) Yes, that's an option, but I was aware of that one. – Ken Dec 29 '08 at 06:51
  • No, I'm saying there are a zillion existing macro processors. Do you actually *have* something you want to do, or are you just saying "the C preprocessor isn't sufficiently wonderful"? – Charlie Martin Dec 29 '08 at 17:16
  • CPP cannot recurse for example, the syntax is terrible. boost::preprocessor makes things easier, but it's still hard and tedious, and the recursion problem is hard to solve. – chila Dec 10 '14 at 19:45
  • I think we've kinda lost the question here. Gods know there are slicker preprocessors than CPP. Hell, UNIX had M4 a zillion years ago. But until #define and #include are no longer in the code, you can't *eliminate* CPP, and even if you don't use them, CPP will still be lying around in the package. – Charlie Martin Dec 10 '14 at 23:09
3

CPP does many important things for C code that you probably don't need re-implemented. What you seem to be looking for instead may be a templating process that emits C code.

Cheetah is just one of many that allows you to use python. There are others that use python and still more in other languages, but Cheetah is known for being output-agnostic where some templating engines are very heavily geared towards HTML/XML. Do your research.

HUAGHAGUAH
  • 1,063
  • 6
  • 3
2

You can use your favourite programming language to build a script/tool to generate source files (.c/.cpp or .h, or whatever). Simply #include them or compile them into your project. It may help to have comments near the #include to identify what/where the tool is and what is generated.

This may not be as handy (or clean) as using a "real" preprocessor, but it would work. Then again, it really depends on your case.

strager
  • 88,763
  • 26
  • 134
  • 176
1

I see a 2001 paper introducing a python-like pre-processor http://ray.cg.tuwien.ac.at/rft/Papers/PYM/pym.html. It's not clear that anyone is using it..

Ken
  • 12,933
  • 4
  • 29
  • 32
  • 1
    The link is dead --- I have posted a mirror [on GitHub](https://github.com/cxw42/pym). Contributions welcome! – cxw Feb 20 '17 at 17:04
1

I'd be interested to see what people come up with. I've tended to do small custom things with preprocessors written in Perl. It's easy to rig up a Makefile that calls the preprocessor. For example, here's a rule to call a program named 'meta' to generate 'file.c' from 'file.c.meta'.

% :: %.meta
    meta $< > $@

I'm doing fun things with 'meta' like generating custom fit C data structures. It's definitely a direction I'd suggest exploring. My hope is to eventually come up with a meta library roughly parallel to C++ templates.

Nathan Kurz
  • 1,649
  • 1
  • 14
  • 28